在解析页面的时候,很可能会解析出相同的url地址(例如商品标题和商品图片超链接,而且url一样),如果不进行处理,同样的url会解析处理多次,浪费资源。所以我们需要有一个url去重的功能。
URL保存
WebMagic提供了Scheduler可以帮助我们解决以上问题。
Scheduler是WebMagic中进行URL管理的组件。一般来说,Scheduler包括两个作用:
1)对待抓取的URL队列进行管理。
2)对已抓取的URL进行去重。
WebMagic内置了几个常用的Scheduler。如果你只是在本地执行规模比较小的爬虫,那么基本无需定制Scheduler,但是了解一下已经提供的几个Scheduler还是有意义的。
DuplicateRemovedScheduler
抽象基类,提供一些模板方法,继承它可以实现自己的功能
QueueScheduler
使用内存队列保存待抓取URL
PriorityScheduler
使用带有优先级的内存队列保存待抓取URL,耗费内存较QueueScheduler更大,但是当设置了request.priority之后,只能使用PriorityScheduler才可使优先级生效
FileCacheQueueScheduler
使用文件保存抓取URL,可以在关闭程序并下次启动时,从之前抓取到的URL继续抓取,需指定路径,会建立.urls.txt和.cursor.txt两个文件
RedisScheduler(开发最常用)
使用Redis保存抓取队列,可进行多台机器同时合作抓取(分布式),需要安装并启动redis
URL去重
去重部分被单独抽象成了一个接口:DuplicateRemover,从而可以为同一个Scheduler选择不同的去重方式,以适应不同的需要,目前提供了两种去重方式。
HashSetDuplicateRemover
使用HashSet来进行去重,占用内存较大
BloomFilterDuplicateRemover
使用BloomFilter来进行去重,占用内存较小,但是可能漏抓页面。
RedisScheduler是使用Redis的set进行去重,其他的Scheduler默认都使用HashSetDuplicateRemover来进行去重。
如果要使用BloomFilter,必须要加入以下依赖:
us.codecraft
webmagic-core
0.7.3
us.codecraft
webmagic-extension
0.7.3
com.google.guava
guava
16.0
修改代码,添加布隆过滤器public static void main(String[] args) {
Spider.create(new JobProcessor())
//初始访问url地址
.addUrl("https://www.jd.com/moreSubject.aspx")
.addPipeline(new FilePipeline("D:/webmagic/"))
.setScheduler(new QueueScheduler()
.setDuplicateRemover(new BloomFilterDuplicateRemover(10000000))) //参数设置需要对多少条数据去重
.thread(1)//设置线程数
.run();
}
修改public void process(Page page)方法,添加一下代码//每次加入相同的url,测试去重
page.addTargetRequest("https://www.jd.com/news.html?id=36480");
打开布隆过滤器BloomFilterDuplicateRemover,在下图处打断点测试
完整代码:package com.jiagou1216.crawler.test;
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.scheduler.BloomFilterDuplicateRemover;
import us.codecraft.webmagic.scheduler.QueueScheduler;
import us.codecraft.webmagic.scheduler.Scheduler;
/**
* @author 架构师小跟班
* @Description: https://www.jiagou1216.com
* @date 2020/7/16 15:38
*/
public class JobProcessor implements PageProcessor {
//解析页面
public void process(Page page) {
//解析返回的数据page,并且把解析的结果放到ResultItems中
//css选择器
page.putField("div", page.getHtml().css("div.mt h2").all());
//XPath
page.putField("div2", page.getHtml().xpath("//div[@id=news_div]/ul/li/div/a"));
//正则表达式
page.putField("div3", page.getHtml().css("div#news_div a").regex(".*江苏.*").all());
//处理结果API
page.putField("div4", page.getHtml().css("div#news_div a").regex(".*江苏.*").get());
page.putField("div5", page.getHtml().css("div#news_div a").regex(".*江苏.*").toString());
//获取链接
//page.addTargetRequests(page.getHtml().css("div#news_div").links().regex(".*9$").all());
//page.putField("url",page.getHtml().css("div.mt h1").all());
page.addTargetRequest("https://www.jd.com/news.html?id=37319");
page.addTargetRequest("https://www.jd.com/news.html?id=37319");
page.addTargetRequest("https://www.jd.com/news.html?id=37319");
}
private Site site = Site.me()
.setCharset("utf8") //设置编码
.setTimeOut(10000) //设置超时时间,单位是ms毫秒
.setRetrySleepTime(3000) //设置重试的间隔时间
.setSleepTime(3); //设置重试次数
public Site getSite() {
return site;
}
//主函数,执行爬虫
public static void main(String[] args) {
Spider spider = Spider.create(new JobProcessor())
.addUrl("https://www.jd.com/moreSubject.aspx") //设置爬取数据的页面
//.addPipeline(new FilePipeline("C:\\Users\\tree\\Desktop\\result"))
.thread(5)
//设置布隆去重过滤器,指定最多对1000万数据进行去重操作
.setScheduler(new QueueScheduler().setDuplicateRemover(new BloomFilterDuplicateRemover(10000000)));
Scheduler scheduler = spider.getScheduler();
//执行爬虫
spider.run();
}
}