WebMagic
一、WebMagic介绍
爬虫框架WebMagic,底层是基于HttpClient和Jsoup,让我们能够更方便的开发爬虫。
项目结构:
webmagic主要包括两个包:
webmagic-core
webmagic核心部分,只包含爬虫基本模块和基本抽取器。webmagic-core的目标是成为网页爬虫的一个教科书般的实现。
webmagic-extension
webmagic的扩展模块,提供一些更方便的编写爬虫的工具。包括注解格式定义爬虫、JSON、分布式等支持。
webmagic还包含两个可用的扩展包,因为这两个包都依赖了比较重量级的工具,所以从主要包中抽离出来,这些包需要下载源码后自己编译::
webmagic-saxon
webmagic与Saxon结合的模块。Saxon是一个XPath、XSLT的解析工具,webmagic依赖Saxon来进行XPath2.0语法解析支持。
webmagic-selenium
webmagic与Selenium结合的模块。Selenium是一个模拟浏览器进行页面渲染的工具,webmagic依赖Selenium进行动态页面的抓取。
在项目中,你可以根据需要依赖不同的包。
不使用maven
在项目的lib目录下,有依赖的所有jar包,直接在IDE里import即可。
1.1 设计思想
参考官方文档:http://webmagic.io/docs/zh/
1. 一个框架,一个领域
一个好的框架必然凝聚了领域知识。WebMagic的设计参考了业界最优秀的爬虫Scrapy,而实现则应用了HttpClient、Jsoup等Java世界最成熟的工具,目标就是做一个Java语言Web爬虫的教科书般的实现。
如果你是爬虫开发老手,那么WebMagic会非常容易上手,它几乎使用Java原生的开发方式,只不过提供了一些模块化的约束,封装一些繁琐的操作,并且提供了一些便捷的功能。
如果你是爬虫开发新手,那么使用并了解WebMagic会让你了解爬虫开发的常用模式、工具链、以及一些问题的处理方式。熟练使用之后,相信自己从头开发一个爬虫也不是什么难事。
因为这个目标,WebMagic的核心非常简单——在这里,功能性是要给简单性让步的。
2. 微内核和高可扩展性
WebMagic由四个组件(Downloader、PageProcessor、Scheduler、Pipeline)构成,核心代码非常简单,主要是将这些组件结合并完成多线程的任务。这意味着,在WebMagic中,你基本上可以对爬虫的功能做任何定制。
WebMagic的核心在webmagic-core包中,其他的包你可以理解为对WebMagic的一个扩展——这和作为用户编写一个扩展是没有什么区别的。
3. 注重实用性
虽然核心需要足够简单,但是WebMagic也以扩展的方式,实现了很多可以帮助开发的便捷功能。例如基于注解模式的爬虫开发,以及扩展了XPath语法的Xsoup等。这些功能在WebMagic中是可选的,它们的开发目标,就是让使用者开发爬虫尽可能的简单,尽可能的易维护。
1.2 总体架构
WebMagic的结构分为Downloader、PageProcessor、Scheduler、Pipeline四大组件,并由Spider将它们彼此组织起来。这四大组件对应爬虫生命周期中的下载、处理、管理和持久化等功能。WebMagic的设计参考了Scapy,但是实现方式更Java化一些。
而Spider则将这几个组件组织起来,让它们可以互相交互,流程化的执行,可以认为Spider是一个大的容器,它也是WebMagic逻辑的核心。
WebMagic总体架构图如下:
1.2.1 WebMagic的四个组件
1.Downloader
Downloader负责从互联网上下载页面,以便后续处理。WebMagic默认使用了Apache HttpClient作为下载工具。
2.PageProcessor
PageProcessor负责解析页面,抽取有用信息,以及发现新的链接。WebMagic使用Jsoup作为HTML解析工具,并基于其开发了解析XPath的工具Xsoup。
在这四个组件中,PageProcessor对于每个站点每个页面都不一样,是需要使用者定制的部分。
3.Scheduler
Scheduler负责管理待抓取的URL,以及一些去重的工作。WebMagic默认提供了JDK的内存队列来管理URL,并用集合来进行去重。也支持使用Redis进行分布式管理。
除非项目有一些特殊的分布式需求,否则无需自己定制Scheduler。
4.Pipeline
Pipeline负责抽取结果的处理,包括计算、持久化到文件、数据库等。WebMagic默认提供了“输出到控制台”和“保存到文件”两种结果处理方案。
Pipeline定义了结果保存的方式,如果你要保存到指定数据库,则需要编写对应的Pipeline。对于一类需求一般只需编写一个Pipeline。
1.2.2 用于数据流转的对象
1. Request
Request是对URL地址的一层封装,一个Request对应一个URL地址。
它是PageProcessor与Downloader交互的载体,也是PageProcessor控制Downloader唯一方式。
除了URL本身外,它还包含一个Key-Value结构的字段extra。你可以在extra中保存一些特殊的属性,然后在其他地方读取,以完成不同的功能。例如附加上一个页面的一些信息等。
2. Page
Page代表了从Downloader下载到的一个页面——可能是HTML,也可能是JSON或者其他文本格式的内容。
Page是WebMagic抽取过程的核心对象,它提供一些方法可供抽取、结果保存等。在第四章的例子中,我们会详细介绍它的使用。
3. ResultItems
ResultItems相当于一个Map,它保存PageProcessor处理的结果,供Pipeline使用。它的API与Map很类似,值得注意的是它有一个字段skip,若设置为true,则不应被Pipeline处理。
1.2.3 控制爬虫运转的引擎–Spider
Spider是WebMagic内部流程的核心。Downloader、PageProcessor、Scheduler、Pipeline都是Spider的一个属性,这些属性是可以自由设置的,通过设置这个属性可以实现不同的功能。Spider也是WebMagic操作的入口,它封装了爬虫的创建、启动、停止、多线程等功能。下面是一个设置各个组件,并且设置多线程和启动的例子。详细的Spider设置请看第四章——爬虫的配置、启动和终止。
public static void main(String[] args) {
Spider.create(new GithubRepoPageProcessor())
//从https://github.com/code4craft开始抓
.addUrl(“https://github.com/code4craft”)
//设置Scheduler,使用Redis来管理URL队列
.setScheduler(new RedisScheduler(“localhost”))
//设置Pipeline,将结果以json方式保存到文件
.addPipeline(new JsonFilePipeline(“D:\data\webmagic”))
//开启5个线程同时执行
.thread(5)
//启动爬虫
.run();
}
1.2.4 快速上手
上面介绍了很多组件,但是其实使用者需要关心的没有那么多,因为大部分模块WebMagic已经提供了默认实现。
一般来说,对于编写一个爬虫,PageProcessor是需要编写的部分,而Spider则是创建和控制爬虫的入口。在第四章中,我们会介绍如何通过定制PageProcessor来编写一个爬虫,并通过Spider来启动。
1.3 项目组成
WebMagic项目代码包括几个部分,在根目录下以不同目录名分开。它们都是独立的Maven项目。
1.3.1 主要部分
WebMagic主要包括两个包,这两个包经过广泛实用,已经比较成熟:
webmagic-core
webmagic-core是WebMagic核心部分,只包含爬虫基本模块和基本抽取器。WebMagic-core的目标是成为网页爬虫的一个教科书般的实现。
webmagic-extension
webmagic-extension是WebMagic的主要扩展模块,提供一些更方便的编写爬虫的工具。包括注解格式定义爬虫、JSON、分布式等支持。
1.3.2 外围功能
除此之外,WebMagic项目里还有几个包,这些都是一些实验性的功能,目的只是提供一些与外围工具整合的样例。因为精力有限,这些包没有经过广泛的使用和测试,推荐使用方式是自行下载源码,遇到问题后再修改。
webmagic-samples
这里是作者早期编写的一些爬虫的例子。因为时间有限,这些例子有些使用的仍然是老版本的API,也可能有一些因为目标页面的结构变化不再可用了。最新的、精选过的例子,请看webmaigc-core的us.codecraft.webmagic.processor.example包和webmaigc-core的us.codecraft.webmagic.example包。
webmagic-scripts
WebMagic对于爬虫规则脚本化的一些尝试,目标是让开发者脱离Java语言,来进行简单、快速的开发。同时强调脚本的共享。
目前项目因为感兴趣的用户不多,处于搁置状态,对脚本化感兴趣的可以看这里:webmagic-scripts简单文档
webmagic-selenium
WebmMgic与Selenium结合的模块。Selenium是一个模拟浏览器进行页面渲染的工具,WebMagic依赖Selenium进行动态页面的抓取。
webmagic-saxon
WebMagic与Saxon结合的模块。Saxon是一个XPath、XSLT的解析工具,webmagic依赖Saxon来进行XPath2.0语法解析支持。
1.3.3 webmagic-avalon
webmagic-avalon是一个特殊的项目,它想基于WebMagic实现一个产品化的工具,涵盖爬虫的创建、爬虫的管理等后台工具。Avalon是亚瑟王传说中的“理想之岛”,webmagic-avalon的目标是提供一个通用的爬虫产品,达到这个目标绝非易事,所以取名也有一点“理想”的意味,但是作者一直在朝这个目标努力。
对这个项目感兴趣的可以看这里WebMagic-Avalon项目计划。
二、快速开始
WebMagic主要包含两个jar包:webmagic-core-{version}.jar和webmagic-extension-{version}.jar。在项目中添加这两个包的依赖,即可使用WebMagic。
WebMagic默认使用Maven管理依赖,但是你也可以不依赖Maven进行使用。
2.1 添加依赖
WebMagic基于Maven进行构建,推荐使用Maven来安装WebMagic。在你自己的项目(已有项目或者新建一个)中添加以下坐标即可:
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-core</artifactId>
<version>0.7.4</version>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-extension</artifactId>
<version>0.7.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.2</version>
</dependency>
WebMagic使用slf4j-log4j12作为slf4j的实现.如果你自己定制了slf4j的实现,请在项目中去掉此依赖。
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-extension</artifactId>
<version>0.7.4</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
2.2 加入配置文件
log4j2.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration status="warn" monitorInterval="5">
<properties>
<property name="LOG_HOME">D:/logs</property>
</properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
</Console>
<File name="file" fileName="${LOG_HOME}/myfile.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" />
</File>
<RandomAccessFile name="accessFile" fileName="${LOG_HOME}/myAcclog.log">
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %m%n" />
</RandomAccessFile>
<RollingFile name="rollingFile" fileName="${LOG_HOME}/myrollog.log"
filePattern="D:/logs/$${date:yyyy-MM-dd}/myrollog-%d{yyyy-MM-dd-HH-mm}-%i.log">
<ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY" />
<PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%-5level] %l %c{36} - %msg%n" />
<Policies>
<OnStartupTriggeringPolicy />
<SizeBasedTriggeringPolicy size="10 MB" />
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="30" />
</RollingFile>
</Appenders>
<Loggers>
<Root level="error">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</configuration>
2.3 案例实现
/**
* @Description: 入门案例
* @Author: SunJie
* @Date: 2021/7/15
* @Version: V1.0
*/
public class JbProcessor implements PageProcessor {
private final Site site = Site.me().setRetryTimes(3).setSleepTime(100);
/**
* 解析页面
*
* @param page
*/
@Override
public void process(Page page) {
//解析返回数据page,并把结果放入ResultItems中
page.putField("div", page.getHtml().css("div.w > ul.fr > li#ttbar-login"));
}
@Override
public Site getSite() {
return site;
}
/**
* 主函数执行爬虫
* @param args
*/
public static void main(String[] args) {
Spider.create(new JbProcessor())
.addUrl("https://kuaibao.jd.com/?ids=244061871,256782966,250487368,262017895")//设置爬取数据页面
.run();//执行爬虫
}
}
三、WebMagic功能
WebMagic主要使用了三种抽取技术:XPath、正则表达式和CSS选择器。另外,对于JSON格式的内容,可使用JsonPath进行解析。
1. XPath
XPath本来是用于XML中获取元素的一种查询语言,但是用于Html也是比较方便的。例如:
page.getHtml().xpath("//h1[@class='entry-title public']/strong/a/text()")
这段代码使用了XPath,它的意思是“查找所有class属性为’entry-title public’的h1元素,并找到他的strong子节点的a子节点,并提取a节点的文本信息”。 对应的Html是这样子的:
xpath-html
2.CSS选择器
CSS选择器是与XPath类似的语言。如果大家做过前端开发,肯定知道$(‘h1.entry-title’)这种写法的含义。客观的说,它比XPath写起来要简单一些,但是如果写复杂一点的抽取规则,就相对要麻烦一点。
page.putField("div", page.getHtml().css("div.w > ul.fr > li a").all());
3. 正则表达式
正则表达式则是一种通用的文本抽取语言。
page.putField("div", page.getHtml().css("div.w > ul.fr > li a").regex(".*京东.*").all());
这段代码就用到了正则表达式,它表示匹配所有"https://github.com/code4craft/webmagic"这样的链接。
4. JsonPath
JsonPath是与XPath很类似的一个语言,它用于从Json中快速定位一条内容。WebMagic中使用的JsonPath格式可以参考这里:https://code.google.com/p/json-path/
5. 获取链接
//获取链接
page.addTargetRequests(page.getHtml().css("div.w > ul.fr > li a").links().all());
page.putField("div5",page.getHtml().css("div.w > ul.fr > li a").regex(".*/b.j.*").all());
6. 爬虫的配置、启动和终止
- Spider
Spider是爬虫启动的入口。在启动爬虫之前,我们需要使用一个PageProcessor创建一个Spider对象,然后使用run()进行启动。同时Spider的其他组件(Downloader、Scheduler、Pipeline)都可以通过set方法来进行设置。
- Site
对站点本身的一些配置信息,例如编码、HTTP头、超时时间、重试策略等、代理等,都可以通过设置Site对象来进行配置。
其中循环重试cycleRetry是0.3.0版本加入的机制。
该机制会将下载失败的url重新放入队列尾部重试,直到达到重试次数,以保证不因为某些网络原因漏抓页面。
四、爬虫的分类
网络爬虫按照系统结构和实现技术,大致可以分为以下几种类型:
通用网络爬虫、聚焦网络爬虫、增量式网络爬虫、深层网络爬虫、实际的网络爬虫系统通常是集中爬虫技术结合到一起实现的。
4.1 通用网络爬虫(General Purpose Web Crawler)
爬取目标资源在全互联网中,爬取目标数据巨大。对爬取性能要求非常高。应用于大型搜索引擎中,有非常高的应用价值。
通用网络爬虫的基本构成:初始URL集合,URL队列,页面爬行模块,页面分析模块,页面数据库,链接过滤模块等构成。
通用网络爬虫的爬行策略:主要有深度优先爬行策略和广度优先爬行策略。
4.2 聚焦网络爬虫(Focused Crawler)
将爬取目标定位在与主题相关的页面中。主要应用在对特定信息的爬取中,主要为某一类特定的人群提供服务。
聚焦网络爬虫的基本构成:初始URL,URL队列,页面爬行模块,页面分析模块,页面数据库,连接过滤模块,内容评价模块,链接评价模块等构成。
聚焦网络爬虫的爬行策略
基于内容评价的爬行策略
基于链接评价的爬行策略
基于增强学习的爬行策略
基于语境图的爬行策略
关于聚焦网络爬虫具体的爬行策略
4.3 增量式网络爬虫(Incremental Web Crawler)
增量式更新指的是在更新的时候只更新改变的地方,而未改变的地方则不更新只爬取内容发生变化的网页或者新产生的网页,一定程度上能保证所爬取的网页,尽可能是新网页
4.4 深层网络爬虫(Deep Web Crawler)
表层网页:不需要提交表单,使用静态的链接就能够到达的静态网页
深层网页:隐藏在表单后面,不能通过静态链接直接获得,是需要提交一定的关键词之后才能够获取得到的网页;深层网络爬虫最重要的部分即为表单填写部分。
深层网络爬虫的基本构成:URL列表,LVS列表(LVS指的是标签/数值集合,即填充表单的数据源)爬行控制器,解析器,LVS控制器,表单分析器,表单处理器,响应分析器等
深层网络爬虫表单填写有两种类型:
-
基于领域知识的表单填写(建立一个填写表单的关键词库,在需要的时候,根据语义分析选择对应的关键词进行填写)
-
基于网页结构分析的表单填写(一般是领域只是有限的情况下使用,这种方式会根据网页结构进行分析,并自动的进行表单填写)
五、案例开发分析
实现一个聚焦网络爬虫,爬取招聘相关的数据。
5.1 业务分析
关键字:
- 计算机软件
- 互联网电子商务
数据库:
CREATE TABLE `job_info` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`company_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '公司名称',
`company_addr` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '公司联系方式',
`company_info` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '公司信息',
`job_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '职位名称',
`job_addr` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '工作地点',
`job_info` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '职位信息',
`pay_min` int(10) NULL DEFAULT NULL COMMENT '最小薪资',
`pay_max` int(10) NULL DEFAULT NULL COMMENT '最大薪资',
`details_url` varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '招聘详情页地址',
`time` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '发布时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '招聘信息' ROW_FORMAT = Dynamic;