文档章节

关于爬虫实现分页的一些思考

黄亿华
 黄亿华
发布于 2013/08/04 21:54
字数 889
阅读 7591
收藏 20

在抓取一些新闻、博客类页面时,我们会遇到这样的需求:有些文章会分成几页显示,每页都是不同的HTML页面,而我们最终想要的数据,肯定是一个整合好的结果。那么,如何把这些分页显示的文章整合起来呢?

这个功能在Spiderman中已经实现,使用的方式是:一旦发现分页,则进入递归下载和解析模式,直到下载完成,然后合并、保存!但是在webmagic中,所有的页面解析都是独立的,解析器没有办法去调用一个下载方法,并返回结果。替代方案是:可以在Request里保存上一次抽取的结果,然后在最后的页面把它们拼装起来。

但是这种方式并不是那么完美:这里假定了爬取的顺序,而如果第一次进入的不是第一页,而是中间某个页面呢?那么会不会存在部分抓取或者重复抓取?同时在抽取过程中,又重新递归的启动一个爬虫,也让整个爬虫的生命周期管理变得更困难。

秉承每个抽取器互相独立的目标,最后决定不要在抽取逻辑中进行处理,而放到抽取之后。每个抽取仍然相互独立,但是在最后输出时有一道“把关”,它等待知道分页的所有结果都返回后,再进行输出。

在webmagic里,Pipeline是可以嵌套的,于是就有了PagedPipeline。这里会收集所有带分页的数据,延迟直到数据整合完成,再统一输出。虽然稍微绕了点,但是保证了模块独立性(Pipeline之前的模块无须知道分页逻辑)。目前是用内存中Map实现的,如果要分布式的话,用远程存储就可以了。

这里面有几个问题:

  • 哪些分页该聚合到一起?

    可以根据页面元素抽取出一个公共ID作为key。

  • 如何知道分页有没有都抓取到?

    每个页面都列出自己还要抓取哪些页面,当待抓取集合和已抓取结合重合时,则结束并合并到一条记录中输出。

  • 页面顺序如何?

    一般根据page数字从小到大排列。

  • 结果如何拼接?

    实现一个combine方法吧。

于是设计了这么一个接口:

<!-- lang: java -->
public interface PagedModel {

    public String getPageKey();

    public Collection<String> getOtherPages();

    public String getPage();

    public PagedModel combine(PagedModel pagedModel);

}

需要分页的对象,实现这个接口就相当于回答了这几个问题。

拿网易新闻做了一个例子:

<!-- lang: java -->
@TargetUrl("http://news.163.com/\\d+/\\d+/\\d+/\\w+*.html")
public class News163 implements PagedModel, AfterExtractor {

    @ExtractByUrl("http://news\\.163\\.com/\\d+/\\d+/\\d+/(\\w+)*\\.html")
    private String pageKey;

    @ExtractByUrl(value = "http://news\\.163\\.com/\\d+/\\d+/\\d+/\\w+_(\\d+)\\.html", notNull = false)
    private String page;

    private List<String> otherPage;

    @ExtractBy("//h1[@id=\"h1title\"]/text()")
    private String title;

    @ExtractBy("//div[@id=\"epContentLeft\"]")
    private String content;

    @Override
    public String getPageKey() {
        return pageKey;
    }

    @Override
    public Collection<String> getOtherPages() {
        return otherPage;
    }

    @Override
    public String getPage() {
        if (page == null) {
            return "1";
        }
        return page;
    }

    @Override
    public PagedModel combine(PagedModel pagedModel) {
        News163 news163 = new News163();
        News163 pagedModel1 = (News163) pagedModel;
        news163.content = this.content + pagedModel1.content;
        return news163;
    }

    @Override
    public String toString() {
        return "News163{" +
                "content='" + content + '\'' +
                ", title='" + title + '\'' +
                ", otherPage=" + otherPage +
                '}';
    }

    public static void main(String[] args) {
        OOSpider.create(Site.me().addStartUrl("http://news.163.com/13/0802/05/958I1E330001124J_2.html"), News163.class)
                .clearPipeline().pipeline(new PagedPipeline()).pipeline(new ConsolePipeline()).run();
    }

    @Override
    public void afterProcess(Page page) {
        Selectable xpath = page.getHtml().xpath("//div[@class=\"ep-pages\"]//a/@href");
        otherPage = xpath.regex("http://news\\.163\\.com/\\d+/\\d+/\\d+/\\w+_(\\d+)\\.html").all();
    }
}

算不上简单,看看以后怎么优化吧。

© 著作权归作者所有

黄亿华

黄亿华

粉丝 2436
博文 131
码字总数 116344
作品 7
程序员
私信 提问
加载中

评论(11)

urtica
urtica
引用来自“quanyongan”的评论
楼主,请问一下PagedPipeline这个有实现么?我看不到代码,能发一份到我邮箱么?quanyongan@126.com非常感激
@9楼 @10楼
请问有PagedPipeline的代码吗,我也想要一份,邮箱urtica@163.com,非常感谢
语

引用来自“quanyongan”的评论

楼主,请问一下PagedPipeline这个有实现么?我看不到代码,能发一份到我邮箱么?quanyongan@126.com非常感激
请问有PagedPipeline的代码吗,我也想要一份,邮箱liu_yulin_alvin@126.com,非常感谢
quanyongan
quanyongan
楼主,请问一下PagedPipeline这个有实现么?我看不到代码,能发一份到我邮箱么?quanyongan@126.com非常感激
m
myboyliu
PagedPipeline这个类的代码在哪里。怎么看不到。那个超链接是错误还是?
m
myboyliu
PagedPipeline这个类的代码在哪里。怎么看不到。那个超链接是错误还是?
黄亿华
黄亿华 博主

引用来自“艾丽斯顿”的评论

虽然没看懂,但还是支持一下啊,你的jsoup研究的就不错

多谢支持13
艾丽斯顿
艾丽斯顿
虽然没看懂,但还是支持一下啊,你的jsoup研究的就不错
黄亿华
黄亿华 博主

引用来自“shichaopeng”的评论

引用来自“黄亿华”的评论

引用来自“shichaopeng”的评论

可不可以推荐一下xpath和网页爬虫的学习资料,谢谢。。。。。

xpath就w3c那个教程就挺好了,实际使用中边用边查吧,xpath2.0水挺深,我自己也是半桶水,就不祸害人了。
网页爬虫,我能说《HTTP权威指南》么=.=

哥。。。我只是不知道爬虫怎么就能根据xpath找见内容...

你是说html解析么?最后是把html解析成一棵抽象语法树,然后xpath在树里做查找...这部分东西还是比较复杂的,想搞的话可以看一看编译原理...
西二三旗
西二三旗

引用来自“黄亿华”的评论

引用来自“shichaopeng”的评论

可不可以推荐一下xpath和网页爬虫的学习资料,谢谢。。。。。

xpath就w3c那个教程就挺好了,实际使用中边用边查吧,xpath2.0水挺深,我自己也是半桶水,就不祸害人了。
网页爬虫,我能说《HTTP权威指南》么=.=

哥。。。我只是不知道爬虫怎么就能根据xpath找见内容...
黄亿华
黄亿华 博主

引用来自“shichaopeng”的评论

可不可以推荐一下xpath和网页爬虫的学习资料,谢谢。。。。。

xpath就w3c那个教程就挺好了,实际使用中边用边查吧,xpath2.0水挺深,我自己也是半桶水,就不祸害人了。
网页爬虫,我能说《HTTP权威指南》么=.=
关于NoSQL数据库LiteDB的分页查询解决过程

1.关于数据库排序与分页 2.LiteDB的查询排序 3.LiteDB分页之渐入佳境 4.LiteDB的疑问 5.资源   在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑) 与 .NET平台开源...

leet123
2016/04/03
183
0
Mybatis-Plus 多表联查分页

上一节,简单讲述了 Mybatis-Plus 搭建与使用入门,这一节,简单讲一下如何使用 MP 实现多表分页。 原理 使用的工程,依旧是 spring-boot,关于分页,官网给出了一个单表的 demo,其实多表分...

殷天文
2018/06/05
0
0
Archx/spring-agg

#SPRING-AGG 这个一个框架整合案列,包含 SpringMVC/MyBatis/Apache Shiro 。 演示数据 演示数据请导入 db.sql 演示账号密码均为 SpringMVC 配置 演示项目使用的是 RESTful 风格,不是传统的...

Archx
2015/01/12
0
0
【开源访谈】 Spiderman作者赖伟威访谈实录

【作者简介】 赖伟威 毕业刚满一年的Java Coder,立志做可靠的系统架构师。大学期间与几位志同道合的同学创办CFuture工作室。现在深圳打拼中。 【软件简介】 Spiderman 是一个基于微内核+插件...

丫头潘潘
2013/06/26
3.1K
12
关于 mybatis数据库分页 里面的问题

@邸星星 你好,想跟你请教个问题:能够讲解下 其中 mybatis 运行时 分页实现的原理 嘛 , 看了源码我对其中的一些地方 也不懂, 就分页时候的架构啊等等 我在网上面 看了 关于 mybatis 的 整...

唐小_兵
2013/03/14
418
3

没有更多内容

加载失败,请刷新页面

加载更多

对于初学者怎么学好画画?

怎样才能学好绘画?想学好绘画需求做什么?光影怎么运用?学习绘画难吗?便是不知道怎么才能绘画好自己作品的光影! 先灵魂起稿画一个大概 在根据前面几何体的理解运用在练习上 假设一个顶光...

热爱画画的我
31分钟前
4
0
Android studio初次安装启动时弹出unable to access android sdk add-on list提示的解决方法

一、问题描述 初次安装Android Studio,启动后,报错如下: unable to access android sdk add-on lis 如图: 二、原因分析 AS启动后,会在默认路径下检测是否有Android SDK,如果没有的话,...

风君子博客
45分钟前
5
0
程序员面试,为什么不跟我谈高并发?

作为一个看过几千份简历,面试过几百人的面试官,常常会看到简历中有如下文字: 对业务逻辑解耦,高并发等有比较深入的研究和丰富的开发实战经验 对解决高并发问题有深入理解 熟悉大并发技术...

程序员修BUG
49分钟前
8
0
Java中UUID版本5使用

问题 生成UUID版本5作为唯一ID。某些场景不能依赖数据库来生成唯一ID,就需要使用UUID来生成唯一性ID。 解决 Java private static final String NAMESPACE_URL = "6ba7b811-9dad-11d1-80b4...

亚林瓜子
49分钟前
7
0
js 设置焦点 判断控件是否获得焦点 判断哪个控件获得焦点

<html> <head> <title>设置焦点</title> <mce:script language ="javascript"> <!-- function init(){ var ctrl=document.getElementById("UserName"); ctrl.focus(); } // --> </mce:scrip......

前端老手
51分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部