WebMagic爬虫

1.  总体架构

Webmagic的架构分为Downloader、PageProcessor、Scheduler、Pipeline四大总结,由Spider将它们组织起来。这四大组件对应爬虫生命周期中的下载、处理、管理和持久化等功能。
总体架构图:

image

 

1.1  四个组件

1.1.1 Downloader:从网上下载页面,以便后续处理。默认使用了Apache HttpClient作为下载工具。
1.1.2 PageProcessor:解析页面,抽取有用信息,以及发现新的链接。使用Jsoup作为HTML解析工具,并基于其开发了解析XPath的工具Xsoup。在这四个组件中,PageProcessor对于每个站点每个页面都不             一样,是需要使用者定制的部分。

1.1.3 Scheduler:管理待抓取的URL和针对待抓取的URL、已抓取的URL去重的工作。默认提供了JDK的内存队列来管理URL,并用集合来进行去重。也支持使用Redis进行分布式管理。除非项目有一些特殊的             分布式需求,否则无需自己定制Scheduler。
1.1.4 Pipeline:抽取结果的处理,包括计算、持久化到文件、数据库等。默认提供了“输出到控制台”和“保存到文件”两种结果处理方案。Pipeline定义了结果保存的方式,如果你要保存到指定数据库,则需要            编写对应的Pipeline。对于一类需求一般只需编写一个Pipeline。

2.用于数据流转的对象

2.1 Request

Request是对URL地址的一层封装,一个Request对应一个URL地址。如果值爬取一个或多个普通(无需加请求头等信息)地址的数据,可以在创建Spider爬虫时调用.addUrl(url)方法即可。使用Request可以对请求方式、body等做一些设置。

例:Request request = new Request(请求地址);
       request.setMethod(HttpConstant.Method.POST);
       HttpRequestBody body = HttpRequestBody.json(JSON.toJSONString(body参数), "UTF-8");
       body.setContentType(ContentType.JSON);
       request.setRequestBody(body);
再用 spider.addRequest(request) 使其生效。

2.2 Page

page代表从网上下载的一个html或json或其他文本格式的内容。

2.2.1  html内容的话用page.getHtml()获取到页面,然后使用Xpath、Jsoup、Xsoup等语法或工具进行解析。
2.2.2  json内容的话用page.getJson()获取json串,然后使用JSONObject.parseObject(json.toString(),class)转成对应的对象即可。

        也可以用page.getJson().jsonPath("$. name").get();这种方式获取对应的数据
         解析完html或json后,需要将解析的内容流转到Pipeline进行数据的持久化存储,这时在page里调用putFiled方法,可以把解析的数据可以流转到Pipeline
2.3. ResultItems

      resultItems.get(putFiled方法的key)可以获取到放进的数据,然后进行持久化操作就可以了。

3.爬虫运转的引擎–Spider

Spider是Webmagic内部流程的核心。Downloader、PageProcessor、Scheduler、Pipeline都是Spider的一个属性,这些属性可以自由设置,以实现不同的功能。Spider也是Webmagic操作的入口,它具备爬虫的创建、启动、停止、多线程等功能。
例:Spider.create(new GithubRepoPageProcessor())
      .addUrl("https://github.com/code4craft")   //从https://github.com/code4craft开始抓
      .setScheduler(new RedisScheduler("localhost")) //设置Scheduler,使用Redis来管理URL队列
      .addPipeline(new JsonFilePipeline("D:\\data\\webmagic")) //设置Pipeline,将结果以json方式保存到文件
     .thread(5) //开启5个线程同时执行
     .run(); //启动爬虫

4.编写基本的爬虫

4.1 爬虫的配置、启动和终止

4.1.1 Spider

Spider是爬虫启动的入口。在启动爬虫之前,需要使用一个PageProcessor创建一个Spider对象,然后使用run()进行启动。同时Spider的其他组件(Downloader、Scheduler、Pipeline)都可以通过set方法来进行设置。

方法

说明

示例

create(PageProcessor)创建SpiderSpider.create(new GithubRepoProcessor())
addUrl(String…)添加初始的URLspider .addUrl("http://webmagic.io/docs/")
addRequest(Request...)添加初始的Requestspider .addRequest("http://webmagic.io/docs/")
thread(n)开启n个线程spider.thread(5)
run()启动,会阻塞当前线程执行spider.run()
start()/runAsync()异步启动,当前线程继续执行spider.start()
stop()停止爬虫spider.stop()
test(String)抓取一个页面进行测试spider .test("http://webmagic.io/docs/")
addPipeline(Pipeline)添加一个Pipeline,一个Spider可以有多个Pipelinespider .addPipeline(new ConsolePipeline())
setScheduler(Scheduler)设置Scheduler,一个Spider只能有个一个Schedulerspider.setScheduler(new RedisScheduler())
setDownloader(Downloader)设置Downloader,一个Spider只能有个一个Downloaderspider .setDownloader(new SeleniumDownloader())
get(String)同步调用,并直接取得结果ResultItems result = spider .get("http://webmagic.io/docs/")
getAll(String…)同步调用,并直接取得一堆结果List<ResultItems> results = spider .getAll("http://webmagic.io/docs/", "http://webmagic.io/xxx")

4.1.2 Site

对站点本身的一些配置信息,例如编码、HTTP头、超时时间、重试策略等、代理等,都可以通过设置Site对象来进行配置。

方法

说明

示例

setCharset(String)设置编码site.setCharset("utf-8")
setUserAgent(String)设置UserAgentsite.setUserAgent("Spider")
setTimeOut(int)设置超时时间,单位是毫秒site.setTimeOut(3000)
setRetryTimes(int)设置重试次数site.setRetryTimes(3)
setCycleRetryTimes(int)设置循环重试次数site.setCycleRetryTimes(3)
addCookie(String,String)添加一条cookiesite.addCookie("dotcomt_user","code4craft")
setDomain(String)设置域名,需设置域名后,addCookie才可生效site.setDomain("github.com")
addHeader(String,String)添加一条addHeadersite.addHeader("Referer","https://github.com")
setHttpProxy(HttpHost)设置Http代理site.setHttpProxy(new HttpHost("127.0.0.1",8080))

4.2 实现PageProcessor

这部分我们直接通过GithubRepoPageProcessor这个例子来介绍PageProcessor的编写方式。我将PageProcessor的定制分为三个部分,分别是爬虫的配置、页面元素的抽取和链接的发现。

public class GithubRepoPageProcessor implements PageProcessor {

    // 部分一:抓取网站的相关配置,包括编码、抓取间隔、重试次数等
    private Site site = Site.me().setRetryTimes(3).setSleepTime(1000);

    @Override
    // process是定制爬虫逻辑的核心接口,在这里编写抽取逻辑
    public void process(Page page) {
        // 部分二:定义如何抽取页面信息,并保存下来
        page.putField("author", page.getUrl().regex("https://github\\.com/(\\w+)/.*").toString());
        page.putField("name", page.getHtml().xpath("//h1[@class='entry-title public']/strong/a/text()").toString());
        if (page.getResultItems().get("name") == null) {
            //skip this page
            page.setSkip(true);
        }
        page.putField("readme", page.getHtml().xpath("//div[@id='readme']/tidyText()"));

        // 部分三:从页面发现后续的url地址来抓取
        page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/[\\w\\-]+/[\\w\\-]+)").all());
    }

    @Override
    public Site getSite() {
        return site;
    }

    public static void main(String[] args) {

        Spider.create(new GithubRepoPageProcessor())
                //从"https://github.com/code4craft"开始抓
                .addUrl("https://github.com/code4craft")
                //开启5个线程抓取
                .thread(5)
                //启动爬虫
                .run();
    }
}

4.2.1 爬虫的配置

第一部分关于爬虫的配置,包括编码、抓取间隔、超时时间、重试次数等,也包括一些模拟的参数,例如User Agent、cookie。在这里我们先简单设置一下:重试次数为3次,抓取间隔为一秒。

4.2.2 页面元素的抽取

爬虫的核心部分:对于下载到的Html页面,WebMagic里主要使用了三种抽取技术:XPath、正则表达式和CSS选择器。另外,对于JSON格式的内容,可使用JsonPath进行解析。

4.2.2.1 XPath

XPath本来是用于XML中获取元素的一种查询语言,但是用于Html也是比较方便的。
例如:

page.getHtml().xpath("//h1[@class='entry-title public']/strong/a/text()")


它的意思是“查找所有class属性为'entry-title public'的h1元素,并找到他的strong子节点的a子节点,并提取a节点的文本信息”。

4.2.2.2 CSS选择器

CSS选择器是与XPath类似的语言。如果做过前端开发,肯定知道$('h1.entry-title')这种写法的含义。它比XPath写起来要简单一些,但是如果写复杂一点的抽取规则,就相对要麻烦一点。

4.2.2.3 正则表达式

正则表达式则是一种通用的文本抽取语言。

page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());


它表示匹配所有"https://github.com/code4craft/webmagic"这样的链接。

4.2.2.4 JsonPath

JsonPath是与XPath很类似的一个语言,它用于从Json中快速定位一条内容。WebMagic中使用的JsonPath格式可以参考这里:https://code.google.com/p/json-path/

4.2.3 链接的发现

一个站点的页面是很多的,我们可以通过两种方法来获取:
(1)

page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());


page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all() 用于获取所有满足"(https:/ /github\.com/\w+/\w+)"这个正则表达式的链接,page.addTargetRequests() 则将这些链接加入到待抓取的队列中去。
(2)通过xpath获取html.xpath("//div[@class='rowContent']//p[3]//a/@href").all(); 可以获取到所有class为rowContent的div下所有的第三个P元素标签下所有a标签href的属性值(链接),然后再调                 用 page.addTargetRequests() 将这些链接添加到待抓取的队列中去。

4.3 使用Selectable抽取元素

Selectable相关的抽取元素链式API是WebMagic的一个核心功能。使用Selectable接口,可以直接完成页面元素的链式抽取,无需去关心抽取的细节。

4.3.1 抽取部分API

方法

说明

示例

xpath(String xpath)使用XPath选择html.xpath("//div[@class='title']")
$(String selector)使用Css选择器选择html.$("div.title")
$(String selector,String attr)使用Css选择器选择html.$("div.title","text")
css(String selector)功能同$(),使用Css选择器选择html.css("div.title")
links()选择所有链接html.links()
regex(String regex)使用正则表达式抽取html.regex("\<div\>(.\*?)\")</div\>
regex(String regex,int group)使用正则表达式抽取,并指定捕获组html.regex("\<div\>(.\*?)\",1)</div\>
replace(String regex, String replacement)替换内容html.replace("\","")


4.3.2 获取结果的API

方法

说明

示例

get()返回一条String类型的结果String link= html.links().get()
toString()功能同get(),返回一条String类型的结果String link= html.links().toString()
all()返回所有抽取结果List links= html.links().all()
match()是否有匹配结果if (html.links().match()){ xxx; }


4.4 使用Pipeline保存结果

(1)  WebMagic用于保存结果的组件叫做Pipeline。
例如我们通过“控制台输出结果”这件事也是通过一个内置的Pipeline完成的,它叫做ConsolePipeline。如果想要把结果用Json的格式保存下来,只需要将Pipeline的实现换成“JsonFilePipeline”就可以了。
例:

public static void main(String[] args) {
    Spider.create(new GithubRepoPageProcessor())
            //从"https://github.com/code4craft"开始抓
            .addUrl("https://github.com/code4craft")
            .addPipeline(new JsonFilePipeline("D:\\webmagic\\"))
            //开启5个线程抓取
            .thread(5)
            //启动爬虫
            .run();
}


}这样子下载下来的文件就会保存在D盘的webmagic目录中了。
(2)将爬取的数据放到数据库中
例:
@Component
public class CityBankPipeline implements Pipeline {

    @Autowired
    private CityBankDao cityBankDao


    @Override
    public void process(ResultItems resultItems, Task task) {
        ViaCouponCrawler spiderData = resultItems.get(SpiderDataEnum.spiderData.toString());
       if (null != spiderData) {
          cityBankDao.save(spiderData);
       }
    }
}

4.5 Jsoup和Xsoup

WebMagic的抽取主要用到了Jsoup和Xsoup

4.5.1 Jsoup

Jsoup是一个简单的HTML解析器,同时它支持使用CSS选择器的方式查找元素。Jsoup笔记

4.5.2 Xsoup

Xsoup是我基于Jsoup开发的一款XPath解析器。
语法对照表:

NameExpressionSupport
nodenamenodenameyes
immediate parent/yes
parent//yes
attribute[@key=value]yes
nth childtag[n]yes
attribute/@keyyes
wildcard in tagname/*yes
wildcard in attribute/[@*]yes
functionfunction()part
ora | byes since 0.2.0
parent in path. or ..no
predicatesprice>35no
predicates logic@class=a or @class=byes since 0.2.0

以下这些函数式标准XPath是没有的,但是在WebMagic中可以使用。

Expression

Description

XPath1.0

text(n)第n个直接文本子节点,为0表示所有text() only
allText()所有的直接和间接文本子节点not support
tidyText()所有的直接和间接文本子节点,并将一些标签替换为换行,使纯文本显示更整洁not support
html()内部html,不包括标签的html本身not support
outerHtml()内部html,包括标签的html本身not support
regex(@attr,expr,group)这里@attr和group均可选,默认是group0not support


4.5.3 Saxon

Saxon是一个强大的XPath解析器,支持XPath 2.0语法。webmagic-saxon是对Saxon尝试性的一个整合,但是目前看来,XPath 2.0的高级语法,似乎在爬虫开发中使用者并不多。

4.6 爬虫的监控

目前项目暂未用到,待补充。

4.7 配置代理

从0.7.1版本开始,WebMagic开始使用了新的代理APIProxyProvider。因为相对于Site的“配置”,ProxyProvider定位更多是一个“组件”,所以代理不再从Site设置,而是由HttpClientDownloader设置。

API

说明

HttpClientDownloader.setProxyProvider(ProxyProvider proxyProvider)设置代理

ProxyProvider有一个默认实现:SimpleProxyProvider。它是一个基于简单Round-Robin的、没有失败检查的ProxyProvider。可以配置任意个候选代理,每次会按顺序挑选一个代理使用。它适合用在自己搭建的比较稳定的代理的场景。
代理示例:
(1)设置单一的普通HTTP代理为101.101.101.101的8888端口,并设置密码为"username","password"
       HttpClientDownloader httpClientDownloader = new HttpClientDownloader();

       httpClientDownloader.setProxyProvider(SimpleProxyProvider.from(new Proxy("101.101.101.101",8888,"username","password")));

       spider.setDownloader(httpClientDownloader);


(2)设置代理池,其中包括101.101.101.101和102.102.102.102两个IP,没有密码
         HttpClientDownloader httpClientDownloader = new HttpClientDownloader();

         httpClientDownloader.setProxyProvider(SimpleProxyProvider.from(

          new Proxy("101.101.101.101",8888)

         ,new Proxy("102.102.102.102",8888)));

4.8 处理非HTTP GET请求

一般来说,爬虫只会抓取信息展示类的页面,所以基本只会处理HTTP GET方法的数据。但是对于某些场景,模拟POST等方法也是需要的。
0.7.1版本之后,废弃了老的nameValuePair的写法,采用在Request对象上添加Method和requestBody来实现。

Request request = new Request("http://xxx/path");
request.setMethod(HttpConstant.Method.POST);
request.setRequestBody(HttpRequestBody.json("{'id':1}","utf-8"));


HttpRequestBody内置了几种初始化方式,支持最常见的表单提交、json提交等方式。

API

说明

HttpRequestBody.form(Map\<string,object> params, String encoding)</string,object>使用表单提交的方式
HttpRequestBody.json(String json, String encoding)使用JSON的方式,json是序列化后的结果
HttpRequestBody.xml(String xml, String encoding)设置xml的方式,xml是序列化后的结果
HttpRequestBody.custom(byte[] body, String contentType, String encoding)设置自定义的requestBody

POST的去重:

从0.7.1版本开始,POST默认不会去重,详情见:Issue 484。如果想要去重可以自己继承DuplicateRemovedScheduler,重写push方法。

5.组件的使用和定制

5.1 使用和定制Pipeline

Pileline是抽取结束后,进行处理的部分,它主要用于抽取结果的保存,也可以定制Pileline可以实现一些通用的功能。

5.1.1 Pipeline介绍

接口定义:

public interface Pipeline {

    // ResultItems保存了抽取结果,它是一个Map结构,
    // 在page.putField(key,value)中保存的数据,可以通过ResultItems.get(key)获取
    public void process(ResultItems resultItems, Task task);

}

Pipeline其实就是将PageProcessor抽取的结果,继续进行了处理的,其实在Pipeline中完成的功能,基本上也可以直接在PageProcessor实现,那么使用Pipeline有以下几个原因:
(1)为了模块分离。“页面抽取”和“后处理、持久化”是爬虫的两个阶段,将其分离开来,一个是代码结构比较清晰,另一个是以后也可能将其处理过程分开,分开在独立的线程以至于不同的机器执行。
(2)Pipeline的功能比较固定,更容易做成通用组件。每个页面的抽取方式千变万化,但是后续处理方式则比较固定,例如保存到文件、保存到数据库这种操作,这些对所有页面都是通用的。WebMagic中             就已经提供了控制台输出、保存到文件、保存为JSON格式的文件几种通用的Pipeline。 在WebMagic里,一个Spider可以有多个Pipeline,使用Spider.addPipeline()即可增加一个Pipeline。

         这些Pipeline都会得到处理,例如你可以使用 spider.addPipeline(new ConsolePipeline()).addPipeline(new FilePipeline())实现输出结果到控制台,并且保存到文件的目                         标。

5.1.2 WebMagic提供的几个Pipeline

说明

备注

ConsolePipeline输出结果到控制台抽取结果需要实现toString方法
FilePipeline保存结果到文件抽取结果需要实现toString方法
JsonFilePipelineJSON格式保存结果到文件 
ConsolePageModelPipeline(注解模式)输出结果到控制台 
FilePageModelPipeline(注解模式)保存结果到文件 
JsonFilePageModelPipeline(注解模式)JSON格式保存结果到文件想要持久化的字段需要有getter方法


5.2 使用和定制Downloader

WebMagic的默认Downloader基于HttpClient。一般来说,无须自己实现Downloader。
另外,如果想通过其他方式来实现页面下载,例如使用SeleniumDownloader来渲染动态页面。

5.3 使用和定制Scheduler

Scheduler是WebMagic中进行URL管理的组件。一般来说,Scheduler包括两个作用:
对待抓取的URL队列进行管理。
对已抓取的URL进行去重。
WebMagic内置了几个常用的Scheduler。如果只是在本地执行规模比较小的爬虫,那么基本无需定制Scheduler。

说明

备注

DuplicateRemovedScheduler抽象基类,提供一些模板方法继承它可以实现自己的功能
QueueScheduler使用内存队列保存待抓取URL 
PriorityScheduler使用带有优先级的内存队列保存待抓取URL耗费内存较QueueScheduler更大,但是当设置了request.priority之后,只能使用PriorityScheduler才可使优先级生效
FileCacheQueueScheduler使用文件保存抓取URL,可以在关闭程序并下次启动时,从之前抓取到的URL继续抓取需指定路径,会建立.urls.txt和.cursor.txt两个文件
RedisScheduler使用Redis保存抓取队列,可进行多台机器同时合作抓取需要安装并启动redis

在0.5.1版本里,我对Scheduler的内部实现进行了重构,去重部分被单独抽象成了一个接口:DuplicateRemover,从而可以为同一个Scheduler选择不同的去重方式,以适应不同的需要,目前提供了两种去重方式。

说明

HashSetDuplicateRemover使用HashSet来进行去重,占用内存较大
BloomFilterDuplicateRemover使用BloomFilter来进行去重,占用内存较小,但是可能漏抓页面

所有默认的Scheduler都使用HashSetDuplicateRemover来进行去重,(除开RedisScheduler是使用Redis的set进行去重)。如果你的URL较多,使用HashSetDuplicateRemover会比较占用内存,所以也可以尝试以下BloomFilterDuplicateRemover1,使用方式:

spider.setScheduler(new QueueScheduler()
.setDuplicateRemover(new BloomFilterDuplicateRemover(10000000)) //10000000是估计的页面数量
)


 0.6.0版本后,如果使用BloomFilterDuplicateRemover,需要单独引入Guava依赖包。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值