maven
工程,加入依赖:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>cn.ysgroupId>
<artifactId>crawler-webmagicartifactId>
<version>1.0-SNAPSHOTversion>
<dependencies>
<dependency>
<groupId>us.codecraftgroupId>
<artifactId>webmagic-coreartifactId>
<version>0.7.3version>
dependency>
<dependency>
<groupId>us.codecraftgroupId>
<artifactId>webmagic-extensionartifactId>
<version>0.7.3version>
dependency>
dependencies>
project>
注意
:0.7.3 版本对 SSL 的并不完全,如果是直接从 Maven 中央仓库下载依赖,在爬取只支持 SSL v1.2 的网站时会有 SSL 的异常抛出。
解决方案
:1、等作者的 0.7.4 的版本发布;2、直接从 github 上下载最新代码,安装到本地仓库;3、也可以参考以下资料自己修复:https://github.com/code4craft/webmagic/issues/701
WebMagic
使用 slf4j-log4j12
作为 slf4j
的实现。
添加 log4j.properties
:
log4j.rootLogger = DEBUG,A1
log4j.appender.A1 = org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout = org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
需求
:编写爬虫程序,爬取 CSDN 中博客 —— 人工智能 的内容。
网址
:https://blog.csdn.net/nav/ai
package cn.ys.webmagic.test;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
public class JobProcessor implements PageProcessor {
//解析页面
public void process(Page page) {
//解析返回的数据page,并且把解析的结果放到ResultItems中
page.putField("div", page.getHtml().css("div.unin_reason_dialog h3").all());
}
private Site site = Site.me();
public Site getSite() {
return site;
}
//主函数,执行爬虫
public static void main(String[] args) {
Spider.create(new JobProcessor())
.addUrl("https://blog.csdn.net/nav/ai") //设置爬取数据的页面
.run(); //执行爬虫
}
}
控制台打印:
get page: https://blog.csdn.net/nav/ai
div: [<h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>]
2019-07-31 15:55:18,138 [main] [us.codecraft.webmagic.Spider]-[INFO] Spider blog.csdn.net closed! 1 pages downloaded.
注意
:Spider 是爬虫启动的入口。在启动爬虫之前,我们需要使用一个 PageProcessor 创建一个 Spider 对象,然后使用 run() 进行启动。
PageProcessor
Selectable
WebMagic
里面主要使用类三种抽取技术:XPath、正则表达式和CSS选择器
。另外,对于 JSON 格式的内容,可使用 JsonPath
进行解析。
XPath
XPath,即为XML路径语言(XMLPathLanguage),它是一种用来确定XML文档中某部分位置的语言。XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
以上是获取属性 class=mt 的 div 标签,里面的 h1 标签的内容。
page.putField("div2",page.getHtml().xpath("//div[@class=title]/h2/a"));
CSS
选择器CSS 选择器是与 XPath 类似的语言。它比 XPath 写起来要简单一些,但是如果写复杂一点的抽取规则,就相对麻烦一些。
div.mt > h1
:表示 class 为 mt 的 div 标签下的直接子元素 h1 标签。
package cn.ys.webmagic.test;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
public class JobProcessor implements PageProcessor {
//解析页面
public void process(Page page) {
//解析返回的数据page,并且把解析的结果放到ResultItems中
page.putField("div", page.getHtml().css("div.unin_reason_dialog h3").all());
//XPath
page.putField("div2",page.getHtml().xpath("//div[@class=title]/h2/a"));
//正则表达式
page.putField("div3",page.getHtml().css("div.title h2 a").regex(".*数学.*").all());
}
private Site site = Site.me();
public Site getSite() {
return site;
}
//主函数,执行爬虫
public static void main(String[] args) {
Spider.create(new JobProcessor())
.addUrl("https://blog.csdn.net/nav/ai") //设置爬取数据的页面
.run(); //执行爬虫
}
}
控制台打印:
get page: https://blog.csdn.net/nav/ai
div: [<h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>, <h3>选择理由,精准屏蔽</h3>]
div2: <a href="https://blog.csdn.net/qq_40798435/article/details/95489469" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/qq_40798435/article/details/95489469","strategy":"none","index":"0"}"> 建筑施工借助物联网卡保障高楼安全 </a>
div3: [<a href="https://blog.csdn.net/qq_40798435/article/details/95489469" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/qq_40798435/article/details/95489469","strategy":"none","index":"0"}"> 建筑施工借助物联网卡保障高楼安全 </a>, <a href="https://blog.csdn.net/yyykj/article/details/95322311" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/yyykj/article/details/95322311","strategy":"none","index":"1"}"> 转战物联网·基础篇02-物联网中的角儿 </a>, <a href="https://blog.csdn.net/weixin_43532158/article/details/95323836" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/weixin_43532158/article/details/95323836","strategy":"none","index":"2"}"> 基于物联网的智能家居 </a>, <a href="https://blog.csdn.net/weixin_44452342/article/details/95049283" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/weixin_44452342/article/details/95049283","strategy":"none","index":"3"}"> ZT交易所即将上线BRC(贝尔链) </a>, <a href="https://blog.csdn.net/ss5214423/article/details/96136274" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/ss5214423/article/details/96136274","strategy":"none","index":"4"}"> Unity3D游戏开发笔记-2【优化】 </a>, <a href="https://blog.csdn.net/qq_40798435/article/details/95628250" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/qq_40798435/article/details/95628250","strategy":"none","index":"5"}"> 医疗行业需要物联网卡能做什么? </a>, <a href="https://blog.csdn.net/weixin_43830887/article/details/96835533" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/weixin_43830887/article/details/96835533","strategy":"none","index":"6"}"> 在新加坡成立基金会做ICO交易所合规白皮书你应该知道的几个要点 </a>, <a href="https://blog.csdn.net/qq_36075612/article/details/97110898" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/qq_36075612/article/details/97110898","strategy":"none","index":"7"}"> 关注物联网、关注NB-IoT </a>, <a href="https://blog.csdn.net/bemfa/article/details/96564875" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/bemfa/article/details/96564875","strategy":"none","index":"8"}"> MQTT物联网通信协议概论 </a>, <a href="https://blog.csdn.net/COMC1DU/article/details/94866252" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/COMC1DU/article/details/94866252","strategy":"none","index":"9"}"> 社链生态全球发布会圆满成功,共建社链梦! </a>, <a href="https://blog.csdn.net/qq_34229678/article/details/96156404" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/qq_34229678/article/details/96156404","strategy":"none","index":"10"}"> Unity3D Network如何限制玩家人数 </a>, <a href="https://blog.csdn.net/qq_42992919/article/details/95343296" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/qq_42992919/article/details/95343296","strategy":"none","index":"11"}"> Python图形用户界面和游戏开发 </a>, <a href="https://blog.csdn.net/weixin_42411153/article/details/94840046" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/weixin_42411153/article/details/94840046","strategy":"none","index":"12"}"> 物联网协议--MQTT整理 </a>, <a href="https://blog.csdn.net/kaihuiguoji/article/details/90671898" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/kaihuiguoji/article/details/90671898","strategy":"none","index":"13"}"> 区块链对区块链金融有什么作用? </a>, <a href="https://blog.csdn.net/weixin_43272781/article/details/94891083" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/weixin_43272781/article/details/94891083","strategy":"none","index":"14"}"> 物联网的发展与应用 </a>, <a href="https://blog.csdn.net/weixin_44452342/article/details/96572844" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/weixin_44452342/article/details/96572844","strategy":"none","index":"15"}"> ZT交易所OTC商家火热招募中 </a>, <a href="https://blog.csdn.net/qq_34229678/article/details/96970866" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/qq_34229678/article/details/96970866","strategy":"none","index":"16"}"> Unity3D (新)SteamVR 2.0手柄输入与震动 </a>, <a href="https://blog.csdn.net/qq_31243065/article/details/97119523" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/qq_31243065/article/details/97119523","strategy":"none","index":"17"}"> Win32 游戏开发:贪吃蛇 上篇 </a>, <a href="https://blog.csdn.net/LEON1741/article/details/96888960" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/LEON1741/article/details/96888960","strategy":"none","index":"18"}"> 【三分钟讲清区块链/比特币】之一:区块链入门教程 </a>, <a href="https://blog.csdn.net/PyhtonChen/article/details/96149214" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/PyhtonChen/article/details/96149214","strategy":"none","index":"19"}"> Python图形界面游戏开发 </a>, <a href="https://blog.csdn.net/weixin_36628778/article/details/96507085" target="_blank" data-report-click="{"mod":"popu_459","dest":"https://blog.csdn.net/weixin_36628778/article/details/96507085","strategy":"none","index":"20"}"> day-012--图形用户界面和游戏开发 </a>]
2019-07-31 16:02:53,547 [main] [us.codecraft.webmagic.Spider]-[INFO] Spider blog.csdn.net closed! 1 pages downloaded.
API
Selectable 相关的抽取元素链式 API 是 WebMagic 的一个核心功能。使用 Selectable 接口,可以直接完成页面元素的链式抽取,也无需关心抽取的细节。
上例中可以看到,page.getHtml()
返回的是一个 html 对象,它实现类 Selectable 接口。这个接口包含的方法分为两类:抽取部分
和 获取结果部分
。
方法 | 说明 | 示例 |
---|---|---|
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("\(.\*?)\") |
这部分抽取 API 返回的都是一个 Selector 接口,意思是说,是支持链式调用的。
API
下面的方法是选择抽取一个或多个元素:
方法 | 说明 | 示例 |
---|---|---|
get() |
返回一条 String 类型的结果 |
String link = html.links().get() |
toString() |
同 get() ,返回一条 String 类型的结果 |
String link = html.links().toString() |
all() |
返回所有抽取的结果 | List links = html.links().all() |
说明
:当有多条数据时,使用 get() 或 toString() 都是获取第一个 url 地址。
我们可以通过添加目标地址,从种子页面爬取到更多的页面
package cn.ys.webmagic.test;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
public class JobProcessor implements PageProcessor {
//解析页面
public void process(Page page) {
//解析返回的数据page,并且把解析的结果放到ResultItems中
page.putField("div", page.getHtml().css("div.unin_reason_dialog h3").all());
//XPath
page.putField("div2",page.getHtml().xpath("//div[@class=title]/h2/a"));
//正则表达式
page.putField("div3",page.getHtml().css("div.title h2 a").regex(".*数学.*").all());
//获取链接
page.addTargetRequests(page.getHtml().css("div.title").links().regex(".*9$").all());//以9结尾的
page.putField("url",page.getHtml().css("div.article-title-box h1"));
}
private Site site = Site.me();
public Site getSite() {
return site;
}
//主函数,执行爬虫
public static void main(String[] args) {
Spider.create(new JobProcessor())
.addUrl("https://blog.csdn.net/nav/ai") //设置爬取数据的页面
.run(); //执行爬虫
}
}
Pipeline
ConsolePipeline
:控制台输出FilePipeline
:文件保存JsonFilePipeline
:以 JSON 方式保存package cn.ys.webmagic.test;
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.pipeline.FilePipeline;
import us.codecraft.webmagic.pipeline.JsonFilePipeline;
import us.codecraft.webmagic.processor.PageProcessor;
public class JobProcessor implements PageProcessor {
//解析页面
public void process(Page page) {
//解析返回的数据page,并且把解析的结果放到ResultItems中
page.putField("div", page.getHtml().css("div.unin_reason_dialog h3").all());
//XPath
page.putField("div2",page.getHtml().xpath("//div[@class=title]/h2/a"));
//正则表达式
page.putField("div3",page.getHtml().css("div.title h2 a").regex(".*数学.*").all());
//获取链接
page.addTargetRequests(page.getHtml().css("div.title").links().regex(".*9$").all());//以9结尾的
page.putField("url",page.getHtml().css("div.article-title-box h1"));
}
private Site site = Site.me();
public Site getSite() {
return site;
}
//主函数,执行爬虫
public static void main(String[] args) {
Spider.create(new JobProcessor())
.addUrl("https://blog.csdn.net/nav/ai") //设置爬取数据的页面
// .addPipeline(new ConsolePipeline())
// .addPipeline(new FilePipeline("/Users/yangshuo/jd/")) //设置结果输出到文件
.addPipeline(new JsonFilePipeline("/Users/yangshuo/jd/")) //以 JSON 方式保存
.thread(5) //设置5个线程执行
.run(); //执行爬虫
}
}
Pipeline
如果以上 Pipeline 都不能满足你的需要,你可以定制 Pipeline
package cn.ys.demo;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;
public class MyPipeline implements Pipeline {
public void process(ResultItems resultItems, Task task) {
String title = resultItems.get("title");
System.out.println("我的定制的 title:"+title);
}
}
main
方法 //主函数,执行爬虫
public static void main(String[] args) {
Spider.create(new JobProcessor())
.addUrl("https://blog.csdn.net/nav/ai") //设置爬取数据的页面
// .addPipeline(new ConsolePipeline())
// .addPipeline(new FilePipeline("/Users/yangshuo/jd/")) //设置结果输出到文件
.addPipeline(new JsonFilePipeline("/Users/yangshuo/jd/")) //以 JSON 方式保存
.addPipeline(new MyPipeline())//定制化输出
.thread(5) //设置5个线程执行
.run(); //执行爬虫
}
Spider
方法详解方法 | 说明 | 示例 |
---|---|---|
create(PageProcessor) |
创建Spider | Spider.create(new GithubRepoProcessor()) |
addUrl(String…) |
添加初始的URL | spider.addUrl("http://webmagic.io/docs/") |
thread(n) |
开启n个线程 | spider.thread(5) |
run() |
启动,会阻塞,当前线程执行 | spider.run() |
start()/runAsync() |
异步启动,当前线程继续执行 | spider.start() |
stop() |
停止爬虫 | spider.stop() |
addPipeline(Pipeline) |
添加一个Pipeline,一个Spider可以有多个Pipeline | spider .addPipeline(new ConsolePipeline()) |
setScheduler(Scheduler) |
设置Scheduler,一个Spider只能有个一个Scheduler | spider.setScheduler(new RedisScheduler()) |
setDownloader(Downloader) |
设置Downloader,一个Spider只能有个一个Downloader | spider .setDownloader( new SeleniumDownloader()) |
get(String) |
同步调用,并直接取得结果 | ResultItems result = spider.get("http://webmagic.io/docs/") |
getAll(String…) |
同步调用,并直接取得一堆结果 | List results = spider.getAll("http://webmagic.io/docs/","http://webmagic.io/xxx") |
同时Spider的其他组件(Downloader、Scheduler、Pipeline)都可以通过set方法来进行设置。
Page代表了从Downloader下载到的一个页面——可能是HTML,也可能是JSON或者其他文本格式的内容。Page是WebMagic抽取过程的核心对象,它提供一些方法可供抽取、结果保存等。
Site
Site用于定义站点本身的一些配置信息,例如编码、HTTP头、超时时间、重试策略等、代理
等,都可以通过设置Site对象来进行配置。
方法 | 说明 | 示例 |
---|---|---|
setCharset(String) |
设置编码 | site.setCharset("utf-8") |
setUserAgent(String) |
设置UserAgent | site.setUserAgent("Spider") |
setTimeOut(int) |
设置超时时间, 单位是毫秒 | site.setTimeOut(3000) |
setRetryTimes(int) |
设置重试次数 | site.setRetryTimes(3) |
setCycleRetryTimes(int) |
设置循环重试次数 | site.setCycleRetryTimes(3) |
addCookie(String,String) |
添加一条cookie | site.addCookie("dotcomt_user","code4craft") |
setDomain(String) |
设置域名,需设置域名后,addCookie才可生效 | site.setDomain("github.com") |
addHeader(String,String) |
添加一条addHeader | site.addHeader("Referer","https://github.com") |
setHttpProxy(HttpHost) |
设置Http代理 | site.setHttpProxy(new HttpHost("127.0.0.1",8080)) |
Scheduler
我们刚才完成的功能,每次运行可能会爬取重复的页面,这样做是没有任何意义的。
Scheduler(URL管理) 最基本的功能是实现对已经爬取的URL进行标示。可以实现URL的增
量去重。
目前scheduler主要有三种实现方式:
QueueScheduler
FileCacheQueueScheduler
RedisScheduler
使用 setScheduler
来设置Scheduler
//主函数,执行爬虫
public static void main(String[] args) {
Spider.create(new JobProcessor())
.addUrl("https://blog.csdn.net/nav/ai") //设置爬取数据的页面
// .addPipeline(new ConsolePipeline())
// .addPipeline(new FilePipeline("/Users/yangshuo/jd/")) //设置结果输出到文件
.addPipeline(new JsonFilePipeline("/Users/yangshuo/jd/")) //以 JSON 方式保存
.thread(5) //设置5个线程执行
// .setScheduler(new QueueScheduler()) //设置内存队列
// .setScheduler(new FileCacheQueueScheduler("/Users/yangshuo/jd/")) //设置文件队列
.setScheduler(new RedisScheduler("127.0.0.1")) //设置Redis队列
.run(); //执行爬虫
}