概览
设计思想 :功能性让步简单性,微内核,高可拓展性,注重实用性
总体架构:
分为四层:Downloader,PageProcessor,Scheduler,Pipeline四个组件,对应爬虫生命周期中的下载,处理,管理和持久化等功能
Downloader负责从互联网上下载页面,以便后续处理,默认使用Apache HttpClient作为下载工具
PageProcessor负责解析页面,抽取有用信息,以及发现新的链接,使用Jsoup作为HTML解析工具 并基于他开发了解析Xpath的工具Xsoup
Scheduler负责管理待抓取的URL,以及去重的工作,默认提供了JDK的内存队列来管理URL,并且用集合来去重
Pipeline负责抽取结果的处理,包括计算,持久化到文件,数据库等,默认提供了输出到控制台和保存到文件两种结果处理方案
控制爬虫运转的引擎--Spider:Spider是WebMagic内部流程的核心。Downloader、PageProcessor、Scheduler、Pipeline都是Spider的一个属性,这些属性是可以自由设置的,通过设置这个属性可以实现不同的功能。Spider也是WebMagic操作的入口,它封装了爬虫的创建、启动、停止、多线程等功能。
PageProcessor
是需要编写的部分,而Spider
则是创建和控制爬虫的入口。第一个爬虫程序
package com.lzj.pojo;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.pipeline.ConsolePipeline;
import us.codecraft.webmagic.processor.PageProcessor;
import java.util.List;
public class GithubRepoPageProcessor implements PageProcessor {
//抓取网站的相关配置
private Site site=Site.me().setRetryTimes(3).setSleepTime(100).setDomain("my.oschina.net");
@Override
public void process(Page page) {
List<String > links= page.getHtml().links().regex("http://my\\\\.oschina\\\\.net/flashsword/blog/\\\\d+").all();
page.addTargetRequests(links);
//xpath是一种html网页的访问方式,类似于css
page.putField("title", page.getHtml().xpath("//div").toString());
page.putField("context",page.getHtml().$("div.content").toString());
page.putField("tags",page.getHtml().xpath("//div/a").all());
}
@Override
public Site getSite() {
return site;
}
public static void main(String[] args) {
//这里采用控制台输出,使用.addPipeline(new JsonFilePipeline("D:\\webmagic\\")),可以以Json格式存放到D盘下的WebMagic目录下,一个Spider可以有多个Pipeline
Spider.create(new GithubRepoPageProcessor()).addUrl("https://www.bilibili.com/").addPipeline(new ConsolePipeline()).run();
}
}
Spider的一些具体方法
Site的具体方法
爬虫的监控
功能通过JMX实现,可以使用Jconsole等JMX工具查看本地或者远程的爬虫信息
配置代理
//设置代理
HttpClientDownloader.setProxyProvider(ProxyProvider proxyProvider)
ProxyProvider
有一个默认实现:SimpleProxyProvider
。它是一个基于简单Round-Robin的、没有失败检查的ProxyProvider。可以配置任意个候选代理,每次会按顺序挑选一个代理使用。它适合用在自己搭建的比较稳定的代理的场景。
实例
单一代理,设置用户名和密码
HttpClientDownloader httpClientDownloader = new HttpClientDownloader();
httpClientDownloader.setProxyProvider(SimpleProxyProvider.from(new Proxy("101.101.101.101",8888,"username","password")));
spider.setDownloader(httpClientDownloader);
代理池
HttpClientDownloader httpClientDownloader = new HttpClientDownloader();
httpClientDownloader.setProxyProvider(SimpleProxyProvider.from(
new Proxy("101.101.101.101",8888)
,new Proxy("102.102.102.102",8888)));
注解编写爬虫(友情提醒:注解一时爽,运维火葬场)
@HelpUrl与TargetUrl
HelpUrl/TargetUrl
是一个非常有效的爬虫开发模式,TargetUrl是我们最终要抓取的URL,最终想要的数据都来自这里;而HelpUrl则是为了发现这个最终URL,我们需要访问的页面
@TargetUrl("GitHub: Where the world builds software · GitHub\w+/\w+")
括号里的内容使用正则表达式,但做了改动
将URL中常用的 “ .” 做了转义,变成了 .
将“ *” 替换成了“.”,直接使用可表示通配符
支持定义sourceRegion,这个参数是XPath表达式
@ExtractBy
@ExtractBy注解主要作用于字段,它表示“使用这个抽取规则,将抽取到的结果保存到这个字段中”。支持使用多个抽取方式,但是需要声明类型,且里面还有一个属性,notnull,将它设置为true,可以过滤掉无用的页面
当一个页面上有多条数据,在类上使用这个标签,可以只扫描这一块区域,当然,类里面如果再次使用这个注解则也是在当前区域选择,如果想要在整个页面选择,需要设置source=RawHtml
@ExtractBy("//h1[@name='ls']/text()")
private String title;
@ExtractByUrl
@ExtractByUrl`是一个单独的注解,它的意思是“从URL中进行抽取”。它只支持正则表达式作为抽取规则
@Formatter
可以将结果类型转换成指定类型例如
@Formatter("yyyy-MM-dd HH:mm")
@ExtractBy("//div[@class='BlogStat']/regex('\\d+-\\d+-\\d+\\s+\\d+:\\d+')")
private Date date;
一般情况下,Formatter会根据字段类型进行转换,但是特殊情况下,我们会需要手动指定类型。这主要发生在字段是List
类型的时候。
@Formatter(value = "",subClazz = Integer.class)
@ExtractBy(value = "//div[@class='id']/text()", multi = true)
private List<Integer> ids;
支持自定义结果
注解爬虫的创建与启动
注解模式的入口是OOSpider
,它继承了Spider
类,提供了特殊的创建方法,其他的方法是类似的。创建一个注解模式的爬虫需要一个或者多个Model
类,以及一个或者多个PageModelPipeline
——定义处理结果的方式。
public static OOSpider create(Site site, PageModelPipeline pageModelPipeline, Class... pageModels);
AfterExtractor接口
当解无法满足要求时,重写这个类的方法,完成操作,这个方法会在抽取结束,字段都初始化完毕之后执行
Pipeline
Scheduler
DuplicateRemover接口
实现去重的功能
所有默认的Scheduler都使用HashSetDuplicateRemover来进行去重,(除开RedisScheduler是使用Redis的set进行去重)。如果你的URL较多,使用HashSetDuplicateRemover会比较占用内存,所以也可以尝试以下BloomFilterDuplicateRemover。