文章目录
- 前言
- 一、WebMagic
- 二、使用步骤
- 1. 搭建 Maven 项目
- 2. 引入依赖
- 三、入门案例
- 四、核心对象&组件
- 1. 核心对象
- Sipder
- Request
- Site
- Page
- ResultItems
- Html(Selectable)
- 2. 四大组件
- Downloader
- PageProcessor
- Scheduler
- Pipeline
上篇:Java-网络爬虫(一)
前言
之前有介绍过传统实现爬虫的技术 HttpClient
和 Jsoup
,并提供了一些案例,但是作为企业级的应用,还是远远不够的,竟然如此就需要一些更深入的技术 WebMagic
。
一、WebMagic
官网:https://webmagic.io/
WebMagic
是一款基于 Java
的开源网络爬虫框架,底层是 HttpClient
和 Jsoup
,它提供了简单、灵活、强大的爬取功能,可以用于抓取网页数据、图片、文件等。WebMagic
的设计参考了 Scapy
,但是实现方式更 Java
化一些。
该框架分为核心和扩展两个部分,核心部分是一个精简、模块化的爬虫实现,而扩展部分则包括一些便利的、实用性的功能。
- 核心部分(
webmagic core
):提供非常简单、灵活的API
,在基本不改变开发模式的情况下,编写一个爬虫 - 扩展部分(
webmagic extension
):提供一些便捷的功能,例如注解模式编写爬虫等,同时内置了一些常用的组件,便于开发
优点:
- 多语言支持:提供了
Python
和Scala
等语言的版本,能够适应不同开发者的需求 - 任务调度:能够与
Quartz
等任务调度框架结合使用,实现定时爬取数据的功能 - 多线程支持:能够利用多核
CPU
提高爬虫效率 - 功能强大:支持
Cookie
、代理等功能,能够模拟登录、避免反爬等操作 - 数据存储:支持多种格式的数据存储,如
MySQL
、Redis
、Elasticsearch
等,方便后续数据处理 - 常处理:能够处理一些异常情况,如页面
404
、解析错误等,提高爬虫的健壮性 - 易于学习和使用:
WebMagic
的核心非常简单,但是覆盖了爬虫的整个流程,也是很好的学习爬虫开发的材料。它提供简单灵活的API
,只需少量代码即可实现一个爬虫 - 模块化和可扩展性:采用完全模块化的设计,功能覆盖整个爬虫的生命周期(链接提取、页面下载、内容抽取、持久化),支持多线程抓取,分布式抓取,并支持自动重试、自定义
UA/cookie
等功能 - 支持爬取动态渲染的页面:支持爬取
js
动态渲染的页面 - 无框架依赖:无框架依赖,可以灵活地嵌入到项目中
架构介绍:
WebMagic
的结构分为 Downloader
、PageProcessor
、Scheduler
、Pipeline
四大组件,这四大组件对应爬虫生命周期中的下载、处理、管理和持久化等功能。
Downloader
:- Downloader 负责从互联网上下载页面,以便后续处理,WebMagic 默认使用了 Apache HttpClient 作为下载工具
PageProcessor
:- PageProcessor 负责解析页面,抽取有用信息,以及发现新的链接。WebMagic 使用 Jsoup 作为 HTML 解析工具,并基于其开发了解析 XPath 的工具 Xsoup
Scheduler
:- Scheduler 负责管理待抓取的 URL,以及一些去重的工作。WebMagic 默认提供了 JDK 的内存队列来管理 URL,并用集合来进行去重。也支持使用 Redis 进行分布式管理
Pipeline
:- Pipeline 负责抽取结果的处理,包括计算、持久化到文件、数据库等。WebMagic 默认提供了 “输出到控制台” 和 “保存到文件” 两种结果处理方案
用于数据流转的对象:
Request
:- Request 是对 URL 地址的一层封装,一个 Request 对应一个 URL 地址,它是 PageProcessor 于 Downloader 交互的载体,也是 PageProcessor 控制 Downloader 的唯一方式。除了 URL 本身,它还包含一个 Key-Value 结构的字段 extra,可以在 extra 中保存一些特殊的属性,然后在其它地方读取,以完成不同的功能
Page
:- Page 代表了从 Downloader 下载到的一个页面 — 可能是 HTML,也可能是 JSON 或者其它文本格式的内容。Page 是 WebMagic 抽取过程的核心对象,它提供了一些方法可供抽取、结果保存等
ResultItems
:- ResultItems 相当于一个 Map,它保存 PageProcessor 处理的结果,供 Pipeline 使用,它的 API 与 Map 很类似,值得注意的是它有一个字段 skip,若设置为 true 则不应被 Pipeline 处理
而 Spider
则将这几个组件组织起来,让它们可以互相交互,流程化的执行,可以认为 Spider
是一个大的容器,它也是 WebMagic
逻辑的核心。
工作原理:
从架构图中可以得知:
- ① 一个
http
请求(其实http
请求之后也会转换为Request
)进入到Downloader
之后会进行页面下载,输出Page
- ②
Page
经过PageProcessor
之后开始解析页面,会有两种输出类型:Request
和ResultItems
,对应两种情况- 情况一:输出的是
Request
- ③ 根据解析的逻辑如果发现需要进一步爬取的
url
地址,则产生一些新的Request
进入Scheduler
等待进一步抓取 - ④
Downloader
会从Scheduler
拉取待处理的Request
- ⑤ 执行 ①
- ③ 根据解析的逻辑如果发现需要进一步爬取的
- 情况二:输出的是
ResultItems
- ③ 需要抽取的数据会封装到
ResultItems
中,再流转至Pipeline
- ④
Pipeline
对抽取的结果进行处理
- ③ 需要抽取的数据会封装到
- 情况一:输出的是
二、使用步骤
1. 搭建 Maven 项目
如果有一个 Maven
工程的项目,可跳过
打开 IDEA
工具,点击 File
-> New
-> Project...
创建一个项目
选择 Maven
设置项目保存地址,点击 Finish
创建完成
不过使用 WebMagic
一般会将爬取到的结果数据持久到数据中,所以这里建议是搭建 SpringBoot
或者 SpringCloud
项目,但是搭建这些项目不是本文的重点,如果想要搭建简单的 SpringBoot
项目可参见 SpringBoot - 快速搭建
2. 引入依赖
WebMagic
分为两个部分:核心和扩展,可在 Maven 仓库 中查询这这两个依赖
<!-- webMagic -->
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-core</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-extension</artifactId>
<version>0.10.0</version>
</dependency>
三、入门案例
还是使用 WebMagic
爬取一个网站为例作为入门,在上篇博客中我们爬取了 https://www.rgbku.com/chaxun.html(rgb颜色查询器) 这个网站的表格信息,现在我们使用 WebMagic
获取底部链接的信息
html
源码:
查看 html
源码可知只需要获取到<div>
为类 .zh
-> <div>
-> <span>
-> <a>
,然后再拿到 <a>
标签的 href
属性内容即可
代码编写:
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.selector.Html;
import java.util.ArrayList;
import java.util.List;
public class TestProcessorDemo implements PageProcessor {
@Override
public void process(Page page) {
// 通过 page 获取 Html 对象
Html html = page.getHtml();
// 通过 Html 可以获取到 Document
Document document = html.getDocument();
// 有了 Document 就能够通过 Jsoup 的一些操作进行解析,比如说获取 <a> 标签元素
Elements aElements = document.select("div.zh > div > span > a");
// 创建 List 集合用于存放 <a> 标签的超链接信息
List<String> links = new ArrayList<>();
for (Element aElement : aElements) {
// 获取 <a> 标签中的链接
String href = aElement.attr("href");
// 添加到集合中
links.add(href);
}
System.err.println("links = " + links);
/*
* 不过一般也不会用 Jsoup 的方式来解析 Html
* WebMagic 有一些解析器可以较为方便的拿到这些元素,比如使用 css 解析器
*/
List<String> href = html.css("div.zh > div > span > a", "href").all();
// 在将结果封装到 ResultItems(默认设置下会打印在控制台上)
page.putField("href", href);
}
@Override
public Site getSite() {
return PageProcessor.super.getSite();
}
public static void main(String[] args) {
Spider.create(new TestProcessorDemo())
// 设置起始的 URL
.addUrl("https://www.rgbku.com/chaxun.html")
// 在当前线程中执行爬虫
//.run();
// 在新线程中执行爬虫
.start();
}
}
运行结果如下:
可以看到是能够成功获取得到想要的数据。
如果查看过 WebMagic
的核心包中的源码,可以发现其实有几个现成的 demo
案例
四、核心对象&组件
在上述案例中有使用过 PageProcessor
、Page
、Site
、ResultItems
、Spider
等对象,以下内容我会结合源码对这些对象和组件的一些概要说明
1. 核心对象
Sipder
可以认为 Spider
是一个大的容器,它也是 WebMagic
逻辑的核心。它的作用是:将各个组件组织起来,使它们能够相互协作,形成一个完整的爬虫系统。它负责管理和调度各个组件的运行,以确保整个爬虫过程的顺利进行。
获取 Spider
对象的方法有两个,要么通过构造方法 new
出来,要么使用静态方法 create(PageProcessor pageProcessor)
创建出来:
public class Spider implements Runnable, Task {
...
public static Spider create(PageProcessor pageProcessor) {
return new Spider(pageProcessor);
}
public Spider(PageProcessor pageProcessor) {
this.newUrlCondition = this.newUrlLock.newCondition();
this.pageCount = new AtomicLong(0L);
this.emptySleepTime = 30000L;
this.pageProcessor = pageProcessor;
this.site = pageProcessor.getSite();
}
...
}
例如上述入门案例中就是使用 create
的方式
Spider spider = Spider.create(new TestProcessorDemo());
从源码上也可以看到 Spider
中包含了 webMaigc
的四大组件的影子
所以才说它是一个大的容器,同时 Spider
中有各个组件的配置以及线程相关的方法,是 webMagic
的核心
Request
Request
是对 URL
地址的一层封装,一个 Request
对象对应一个 URL
地址。
从源码上可以看到 Request
对象中包含了发送一个 http
请求的所有要素,包括 url
地址、请求类型、请求参数、cookie
和 header
等等,还包含一个 Key-Value
结构的字段 extra
。可以在 extra
中保存一些特殊的属性,然后在其他地方读取,以完成不同的功能。
之前说 http
在进入到下载组件 Download
是便会转化成 Request
对象,可以通过入门案例中的代码
spider.addUrl("https://www.rgbku.com/chaxun.html")
进入到 addUrl(String... urls)
方法中看到
public class Spider implements Runnable, Task {
...
public Spider addUrl(String... urls) {
String[] var2 = urls;
int var3 = urls.length;
for(int var4 = 0; var4 < var3; ++var4) {
String url = var2[var4];
// 将 url 转化成 Request 对象,加载到调度器 Scheduler 中
this.addRequest(new Request(url));
}
this.signalNewUrl();
return this;
}
...
}
Site
Site
对象用于配置站点本身的一些配置信息,例如编码、HTTP
头、超时时间、重试策略等、代理等。其中待解析的域名存放在 domain
这个属性中
通过设置 Site
对象,可以对爬虫的行为进行详细配置,以满足不同的需求。具体来说,Site
对象可以设置以下配置信息:
- 编码:用于指定站点的字符编码方式,以确保爬虫能够正确解析页面内容
HTTP
头:用于设置请求头信息,以模拟浏览器行为,增加爬虫的隐蔽性- 超时时间:用于设置爬虫请求的超时时间,以避免因网络延迟等原因导致请求等待过长时间
- 重试策略:用于设置爬虫在遇到请求失败时是否进行重试以及重试的次数和间隔等
- 代理:用于设置代理服务器地址和端口等信息,以便通过代理访问目标站点
Site
对象提供的配置方法如下:
方法 | 说明 |
---|---|
setCharset(String charset) | 设置字符编码方式,以确保爬虫能够正确解析页面内容 |
setUserAgent(String userAgent) | 设置用户代理,用于标识发送请求的客户端应用或设备,更好的模拟游览器发送请求 |
Site setDomain(String domain) | 设置域名,需要设置域名后,addCookie() 才会生效 |
setSleepTime(int sleepTime) | 设置爬虫在抓取下一个页面之前等待的时间,以避免过于频繁的请求导致被目标站点封禁 |
setTimeOut(int timeOut) | 设置爬虫请求的超时时间,以避免因网络延迟等原因导致请求等待过长时间 |
setRetrySleepTime(int retrySleepTime) | 设置爬虫在遇到请求失败时的重试间隔时间 |
setRetryTimes(int retryTimes) | 设置爬虫在遇到请求失败时的重试次数 |
addHeader(String key, String value) | 添加请求头信息,以模拟浏览器行为,增加爬虫的隐蔽性 |
addCookie(String key, String value) | 添加 Cookie 信息,以模拟浏览器会话信息,增加爬虫的隐蔽性 |
通过合理配置 Site
对象的参数,可以优化爬虫的性能,提高爬虫的效率和成功率,增加爬虫的隐蔽性。
之前提到过 domian
存放的是待解析域名,从入门案例中的 addUrl(String... urls)
-> addRequest(Request request)
可以看到这条逻辑,当然不止这一处
public class Spider implements Runnable, Task {
...
private void addRequest(Request request) {
if (this.site.getDomain() == null && request != null && request.getUrl() != null) {
// 将清洗过的 url 存放到 Site 的 domain 属性中
this.site.setDomain(UrlUtils.getDomain(request.getUrl()));
}
this.scheduler.push(request, this);
}
...
}
在 上篇:Java-网络爬虫(一) 博客中有通过 HttpClient
的 execute(HttpUriRequest var1)
方法来发送请求(HttpGet、HttpPost...
均实现了 HttpUriRequest
)
// 创建 httpClient 对象
CloseableHttpClient httpClient = HttpClients.createDefault();
// 创建 httpGet 对象,设置访问 URL
HttpGet httpGet = new HttpGet("https://www.rgbku.com/chaxun.html");
// 发送请求
response = httpClient.execute(httpGet);
在 webMagic
中底层也是使用 HttpClient
来发送请求的,不够首先要通过 Site
和 Request
获取到 HttpUriRequest
和 HttpContext
对象,进而就可以执行HttpClient
的 execute(HttpUriRequest var1, HttpContext var2)
方法发送请求获取响应信息了
Page
Page
对象是用于处理和封装从目标网站下载得到的 HTML
页面内容的一种对象
该对象最重要的两个作用就是获取 Html
对象,而 Html
对象是解析网页十分重要的对象,其次是该 Page
对象能够能够将封装好的 ResultItems
对象传送给存储器 Pipeline
做持久化处理
常用方法:
方法 | 说明 |
---|---|
getHtml() | 获取当前页面的 Html 对象 |
String getRawText() | 获取当前页面的文本内容 |
putField(String key, Object field) | 以 key-value 的形式封装信息到 ResultItems 中 |
ResultItems
ResultItems
是一个 Map
对象,用于保存 PageProcessor
处理的结果,供Pipeline使用。它的 API
与 Map
类似,可以保存各种类型的数据,包括字符串、列表、字典等
ResultItems
的主要作用是作为 PageProcessor
和 Pipeline
之间的数据传输媒介。当 PageProcessor
处理完一个页面后,可以将处理结果保存到 ResultItems
中,然后由 Pipeline
进行处理。这样可以方便地实现数据的提取、清洗、过滤等操作,并将结果持久化到文件、数据库等地方
ResultItems
还提供了一些额外的方法来控制结果的输出和处理。例如,可以通过 setSkip(true)
方法来跳过当前的结果,不进行后续的处理和输出。此外,ResultItems
还提供了 getExtra()
方法,用于获取一些自定义的数据和属性
总的来说,WebMagic
中的 ResultItems
是一个重要的组件,它充当了 PageProcessor
和 Pipeline
之间的桥梁,使得数据的处理和输出更加灵活和方便。
Html(Selectable)
通过入门案例中使用 page.getHtml()
能够获取到一个 Html
对象,解析相关的操作都与该对象有关,而这个对象实现了 Selectable
在 WebMagic
中,Selectable
是一个重要的接口,它定义了一系列链式 API
调用方式
抽取元素的四种方式:
Selectable
里主要支持了四种抽取技术:XPath
、正则表达式和 CSS
选择器。另外,对于 JSON
格式的内容,可使用 JsonPath
进行解析。
- 方式一:XPath 选择器
XPath 的教程可参考:https://www.w3cschool.cn/xpath/
例如:入门案例中获取<a>
标签中的链接的代码可改写为:
List<String> href = html.xpath("//div[@class=zh]/div/span/a/@href").all();
- 方式二:regex(正则表达式)选择器
正则表达式-基础教程:https://blog.csdn.net/xhmico/article/details/126729869
正则表达式是一种通用的文本抽取语言,一般会用这种方式获取 url
地址等
例如:入门案例中获取<a>
标签中的链接的代码可改写为:
page.putField("links", page.getHtml().regex("[a-zA-z]+://[^\\s][^\" ]*").all());
- CSS 选择器
入门案例中使用的就是这种方式获取元素,CSS
选择器是与 XPath
类似的语言,但相对于 XPath
而言简单一点,只要对 css 语法熟悉,写起来应该是比较简单的。比方说入门案例<a>
标签中的链接的代码还可以改成:
List<String> href = html.css(".zh a", "href").all();
- JsonPath 选择器
JsonPath
是于 XPath
很类似的一个语言,它用于从 Json
中快速定位一条内容
关于它的使用可参考:JsonPath完全介绍及详细使用教程
API:
Selectable
提供了一系列链式 API
调用方式,支持多种选择器,包括 xpath
、css
、regex
和 jsonPath
等。用户可以使用这些选择器方便地获取所需元素的信息,同时还提供了诸如获取链接等便利方法。简单来说就是根据特定的方法抽取 html
页面的信息。使用 Selectable
接口,可以直接完成页面元素的链式抽取,也无需去关心抽取的细节。
public interface Selectable {
Selectable xpath(String var1);
Selectable $(String var1);
Selectable $(String var1, String var2);
Selectable css(String var1);
Selectable css(String var1, String var2);
Selectable smartContent();
Selectable links();
Selectable regex(String var1);
Selectable regex(String var1, int var2);
Selectable replace(String var1, String var2);
String toString();
String get();
boolean match();
List<String> all();
Selectable jsonPath(String var1);
Selectable select(Selector var1);
Selectable selectList(Selector var1);
List<Selectable> nodes();
}
以下是 Selectable
常用 API
的介绍
方法 | 说明 | 示例 |
---|---|---|
Selectable xpath(String var1) | 使用 XPath 选择器 | html.xpath(“//div[@class=‘title’]”) |
Selectable regex(String var1) | 使用正则表达式抽取 | html.regex(“(.*?)”) |
Selectable regex(String var1, int var2) | 使用正则表达式抽取,并指定捕获组 | html.regex(“(.*?)”,1) |
Selectable $(String var1) | 使用 Css 选择器选择 | html.$(“div.title”) |
Selectable $(String var1, String var2) | 使用 Css 选择器选择 | html.$(“div.title”,“text”) |
Selectable css(String var1) | 功能同$() ,使用 Css 选择器选择 | html.css(“div.title”) |
Selectable css(String var1, String var2) | 功能同$() ,使用Css选择器选择 | html.css(“div.title”,“text”) |
Selectable jsonPath(String var1) | 使用 JsonPath 选择器选择 | html.jsonPath(“$.*”) |
Selectable links() | 获取所有链接,如果链接为相对地址会自动拼接 | html.links() |
Selectable replace(String regex, String replacement) | 替换内容 | html.replace(“”,“”) |
String get() | 返回一条String类型的结果 | String link= html.links().get() |
String toString() | 功能同 get() ,返回一条 String 类型的结果 | String link= html.links().toString() |
List all() | 返回所有抽取结果 | List links= html.links().all() |
boolean match() | 是否有匹配结果 | boolean result = html.links().match() |
Html
除了 Selectable
这些常用的 API
外还有如下几个方法用得也比较多
方法 | 说明 |
---|---|
Document getDocument() | 获取 Document 对象 |
static Html create(String text) | 通过文本获取 Html 对象 |
从这些 API
中可以看出,那些抽取元素的方法返回的都是 Selectable
对象,也就是说抽取是支持链式调用的,
例如:入门案例中获取<a>
标签中的链接的代码可改写为:
List<String> href = html
// css 选择类为 zh 的标签
.css(".zh")
// 获取其下所有的链接
.links() // 使用这种方式获取链接,如果链接是相对地址的形式会自动进行拼接
.all();
输出:
2. 四大组件
Downloader
前面就有提到过 Downloader
的作用就是负责从互联网上下载页面
Downloader
的输入是 Request
输出是 Pag
,可见 Downloader.java
源码:
public interface Downloader {
Page download(Request var1, Task var2);
void setThread(int var1);
}
默认情况下 Spider
配置的 Downloader
为 HttpClientDownloader
,可以从 initComponent()
方法中得出
查看 HttpClientDownloader.java
源码中的 download()
方法
public class HttpClientDownloader extends AbstractDownloader {
...
public Page download(Request request, Task task) {
if (task != null && task.getSite() != null) {
CloseableHttpResponse httpResponse = null;
// 获取 HttpClient 对象
CloseableHttpClient httpClient = this.getHttpClient(task.getSite());
Proxy proxy = this.proxyProvider != null ? this.proxyProvider.getProxy(request, task) : null;
// 通过 Request 和 Site 得到 HttpUriRequest、HttpContext
HttpClientRequestContext requestContext = this.httpUriRequestConverter.convert(request, task.getSite(), proxy);
Page page = Page.fail(request);
Page var9;
try {
// 发起请求
httpResponse = httpClient.execute(requestContext.getHttpUriRequest(), requestContext.getHttpClientContext());
// 解析响应体并封装成 Page 返回
page = this.handleResponse(request, request.getCharset() != null ? request.getCharset() : task.getSite().getCharset(), httpResponse, task);
this.onSuccess(page, task);
this.logger.info("downloading page success {}", request.getUrl());
Page var8 = page;
return var8;
} catch (IOException var13) {
this.onError(page, task, var13);
this.logger.info("download page {} error", request.getUrl(), var13);
var9 = page;
} finally {
if (httpResponse != null) {
EntityUtils.consumeQuietly(httpResponse.getEntity());
}
if (this.proxyProvider != null && proxy != null) {
this.proxyProvider.returnProxy(proxy, page, task);
}
}
return var9;
} else {
throw new NullPointerException("task or site can not be null");
}
}
...
}
如果希望下载页面时进行一些其它的操作,可以自定义 Downloader
,要么实现 Downloader
接口,要么继承实现了 Downloader
接口的子类,比如:HttpClientDownloader
如果想要自定义的 Downloader
生效,就需要在 spider.setDownloader()
方法中进行设置,比如:
Spider.create(new TestProcessorDemo())
// 设置起始的 URL
.addUrl("https://www.rgbku.com/chaxun.html")
// 设置自定义 Downloader
.setDownloader(new MyDownload())
// 在当前线程中执行爬虫
.run();
// 在新线程中执行爬虫
//.start();
PageProcessor
PageProcessor 的作用就是负责解析页面的
它的输入是 Page
对象,process() 方法中实现页面解析的逻辑,PageProcessor.java
源码如下:
public interface PageProcessor {
void process(Page var1);
default Site getSite() {
return Site.me();
}
}
而页面的解析是开发者根据需求去编写,也就是说要开发者去实现,webMaic
也没办法提供默认的 PageProcessor
, 所以使用 webMaigc
编写爬虫的时候都需要去实现 PageProcessor
接口或者继承实现了 PageProcessor
接口的类,比如:SimplePageProcessor
。而解析的结果最好是封装到 ResultItems
中交给 Pipeline
进行处理,Page 对象中可以通过 putField(String key, Object field)
方法直接将对象封装到 ResultItems
中
public class Page {
...
public void putField(String key, Object field) {
this.resultItems.put(key, field);
}
...
}
Scheduler
Scheduler
负责管理待抓取的 URL
Scheduler.java
源码:
public interface Scheduler {
void push(Request var1, Task var2);
Request poll(Task var1);
}
Scheduler
默认是 QueueScheduler
,可查看 Spider.java
的源码得出
而 QueueScheduler
的底层就如同它的类名一样,是个队列
之前说过 Downloader
下载页面时需要 Request
对象,而这些 Request
对象都是从 Scheduler
中拉取而来的,包括起始的 url
也是会先放到 Scheduler
,可以从入门案例中的 spider.addUrl()
方法去追溯源码证实
也可以从进入入门案例 sipder.run()
方法中看到传入 Downloader
的 Request
是从 Scheduler
中获取的
同时 Scheduler
也可以自定义,只需要实现 Scheduler
接口或者继承其实现类,比如:QueueScheduler
即可,然后通过 spider.setScheduler()
方法去设置,例如:
Spider.create(new TestProcessorDemo())
// 设置起始的 URL
.addUrl("https://www.rgbku.com/chaxun.html")
// 设置自定义 Scheduler
.setScheduler(new MyScheduler())
// 在当前线程中执行爬虫
.run();
// 在新线程中执行爬虫
//.start();
Pipeline
Pipeline
负责抽取结果的处理,包括计算、持久化到文件、数据库等
Pipeline
.java 源码:
public interface Pipeline {
void process(ResultItems var1, Task var2);
}
默认情况下,webMaigc
的使用的 Pipeline
是 ConsolePipeline
,可查看 Spider.initComponent()
的方法源码得知:
ConsolePipeline
的处理就是将封存在 ResultItems
里的内容打印到控制台上
ConsolePipeline.java
源码:
public class ConsolePipeline implements Pipeline {
public ConsolePipeline() {
}
public void process(ResultItems resultItems, Task task) {
System.out.println("get page: " + resultItems.getRequest().getUrl());
Iterator var3 = resultItems.getAll().entrySet().iterator();
while(var3.hasNext()) {
Entry<String, Object> entry = (Entry)var3.next();
System.out.println((String)entry.getKey() + ":\t" + entry.getValue());
}
}
}
Spider
中可以配置多个 Pipeline
通常情况下开发者会自定义 Pipeline
,将爬取的数据存放在数据库中,自定义的方式就是实现 Pipeline
接口重写 process()
方法,通过 spider.setPipelines()
来设置,例如:
List<Pipeline> pipelines = new ArrayList<>();
// 添加输出到控制台的 Pipeline:ConsolePipeline
pipelines.add(new ConsolePipeline());
// 添加保存到文本的 Pipeline:FilePipeline
pipelines.add(new FilePipeline());
// 添加自定义自定义 Pipeline
pipelines.add(new MyPipeline());
Spider.create(new TestProcessorDemo())
// 设置起始的 URL
.addUrl("https://www.rgbku.com/chaxun.html")
// 设置 Pipeline
.setPipelines(pipelines)
// 在当前线程中执行爬虫
.run();
// 在新线程中执行爬虫
//.start();
上篇:Java-网络爬虫(一)
参考博客:
WebMagic:https://blog.csdn.net/weixin_40055163/article/details/123541437
JsonPath完全介绍及详细使用教程:https://blog.csdn.net/software_test010/article/details/125427926