webmagic 增量爬取
webmagic 是一个很好并且很简单的爬虫框架,其教程网址:http://my.oschina.net/flashsword/blog/180623
webmagic参考了scrapy的模块划分,分为Spider(整个爬虫的调度框架)、Downloader(页面下载)、PageProcessor(链接提取和页面分析)、Scheduler(URL管理)、Pipeline(离线分析和持久化)几部分。只不过scrapy通过middleware实现扩展,而webmagic则通过定义这几个接口,并将其不同的实现注入主框架类Spider来实现扩展。
关于Scheduler(URL管理) 最基本的功能是实现对已经爬取的URL进行标示。
目前scheduler有三种实现方式:
1)内存队列
2)文件队列
3)redis队列
文件队列保存URL,能实现中断后,继续爬取时,实现增量爬取。
如果我只有一个主页的URL,比如:http://www.cndzys.com/yundong/。如果直接引用webmagic的FileCacheQueueScheduler的话,你会发现第二次启动的时候,什么也爬不到。可以说第二次启动基本不爬取数据了。因为FileCacheQueueScheduler 把http://www.cndzys.com/yundong/ 记录了,然后不再进行新的爬取。虽然是第二次增量爬取,但还是需要保留某些URL重新爬取,以保证爬取结果是我们想要的。我们可以重写FileCacheQueueScheduler里的比较方法。
package com.fortunedr.crawler.expertadvice; import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.math.NumberUtils; import us.codecraft.webmagic.Request;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.scheduler.DuplicateRemovedScheduler;
import us.codecraft.webmagic.scheduler.MonitorableScheduler;
import us.codecraft.webmagic.scheduler.component.DuplicateRemover; /**
* Store urls and cursor in files so that a Spider can resume the status when shutdown.<br>
*增加去重的校验,对需要重复爬取的网址进行正则过滤
* @author code4crafter@gmail.com <br>
* @since 0.2.0
*/
public class SpikeFileCacheQueueScheduler extends DuplicateRemovedScheduler implements MonitorableScheduler,Closeable { private String filePath = System.getProperty("java.io.tmpdir"); private String fileUrlAllName = ".urls.txt"; private Task task; private String fileCursor = ".cursor.txt"; private PrintWriter fileUrlWriter; private PrintWriter fileCursorWriter; private AtomicInteger cursor = new AtomicInteger(); private AtomicBoolean inited = new AtomicBoolean(false); private BlockingQueue<Request> queue; private Set<String> urls; private ScheduledExecutorService flushThreadPool; private String regx; public SpikeFileCacheQueueScheduler(String filePath) {
if (!filePath.endsWith("/") && !filePath.endsWith("\\")) {
filePath += "/";
}
this.filePath = filePath;
initDuplicateRemover();
} private void flush() {
fileUrlWriter.flush();
fileCursorWriter.flush();
} private void init(Task task) {
this.task = task;
File file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
readFile();
initWriter();
initFlushThread();
inited.set(true);
logger.info("init cache scheduler success");
} private void initDuplicateRemover() {
setDuplicateRemover(
new DuplicateRemover() {
@Override
public boolean isDuplicate(Request request, Task task) {
if (!inited.get()) {
init(task);
}
boolean temp=false;
String url=request.getUrl();
temp=!urls.add(url);//原来验证URL是否存在
//正则匹配
if(url.matches(regx)){//二次校验,如果符合我们需要重新爬取的,返回false。可以重新爬取
temp=false;
}
return temp;
} @Override
public void resetDuplicateCheck(Task task) {
urls.clear();
} @Override
public int getTotalRequestsCount(Task task) {
return urls.size();
}
});
} private void initFlushThread() {
flushThreadPool = Executors.newScheduledThreadPool(1);
flushThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
flush();
}
}, 10, 10, TimeUnit.SECONDS);
} private void initWriter() {
try {
fileUrlWriter = new PrintWriter(new FileWriter(getFileName(fileUrlAllName), true));
fileCursorWriter = new PrintWriter(new FileWriter(getFileName(fileCursor), false));
} catch (IOException e) {
throw new RuntimeException("init cache scheduler error", e);
}
} private void readFile() {
try {
queue = new LinkedBlockingQueue<Request>();
urls = new LinkedHashSet<String>();
readCursorFile();
readUrlFile();
// initDuplicateRemover();
} catch (FileNotFoundException e) {
//init
logger.info("init cache file " + getFileName(fileUrlAllName));
} catch (IOException e) {
logger.error("init file error", e);
}
} private void readUrlFile() throws IOException {
String line;
BufferedReader fileUrlReader = null;
try {
fileUrlReader = new BufferedReader(new FileReader(getFileName(fileUrlAllName)));
int lineReaded = 0;
while ((line = fileUrlReader.readLine()) != null) {
urls.add(line.trim());
lineReaded++;
if (lineReaded > cursor.get()) {
queue.add(new Request(line));
}
}
} finally {
if (fileUrlReader != null) {
IOUtils.closeQuietly(fileUrlReader);
}
}
} private void readCursorFile() throws IOException {
BufferedReader fileCursorReader = null;
try {
fileCursorReader = new BufferedReader(new FileReader(getFileName(fileCursor)));
String line;
//read the last number
while ((line = fileCursorReader.readLine()) != null) {
cursor = new AtomicInteger(NumberUtils.toInt(line));
}
} finally {
if (fileCursorReader != null) {
IOUtils.closeQuietly(fileCursorReader);
}
}
} public void close() throws IOException {
flushThreadPool.shutdown();
fileUrlWriter.close();
fileCursorWriter.close();
} private String getFileName(String filename) {
return filePath + task.getUUID() + filename;
} @Override
protected void pushWhenNoDuplicate(Request request, Task task) {
queue.add(request);
fileUrlWriter.println(request.getUrl());
} @Override
public synchronized Request poll(Task task) {
if (!inited.get()) {
init(task);
}
fileCursorWriter.println(cursor.incrementAndGet());
return queue.poll();
} @Override
public int getLeftRequestsCount(Task task) {
return queue.size();
} @Override
public int getTotalRequestsCount(Task task) {
return getDuplicateRemover().getTotalRequestsCount(task);
} public String getRegx() {
return regx;
}
/**
* 设置保留需要重复爬取url的正则表达式
* @param regx
*/
public void setRegx(String regx) {
this.regx = regx;
} }
那么在爬虫时就引用自己特定的FileCacheQueueScheduler就可以
spider.addRequest(requests);
SpikeFileCacheQueueScheduler file=new SpikeFileCacheQueueScheduler(filePath);
file.setRegx(regx);//http://www.cndzys.com/yundong/(index)?[0-9]*(.html)?
spider.setScheduler(file );
这样就实现了增量爬取。
优化的想法:一般某个网站的内容列表都是首页是最新内容。上面的方式是可以实现增量爬取,但是还是需要爬取很多“无用的”列表页面。
能不能实现,当爬取到上次"最新"URL之后就不再爬取。就是不用爬取其他多余的leib
webmagic 增量爬取的更多相关文章
- scrapy增量爬取
开始接触爬虫的时候还是初学Python的那会,用的还是request.bs4.pandas,再后面接触scrapy做个一两个爬虫,觉得还是框架好,可惜都没有记录都忘记了,现在做推荐系统需要爬取一定的 ...
- scrapy过滤重复数据和增量爬取
原文链接 前言 这篇笔记基于上上篇笔记的---<scrapy电影天堂实战(二)创建爬虫项目>,而这篇又涉及redis,所以又先熟悉了下redis,记录了下<redis基础笔记> ...
- 使用scrapy实现去重,使用Redis实现增量爬取
面试场景: 要求对正在爬取的内容与mysql数据库中的数据进行比较去重 解决方式: 通过Redis来作为中间件,通过url来确保爬过的数据不会再爬,做到增量爬取. Redis数据库其实就是一个中间件, ...
- nutch的定时增量爬取
译文来着: http://wiki.apache.org/nutch/Crawl 介绍(Introduction) 注意:脚本中没有直接使用Nutch的爬去命令(bin/nutch crawl或者是& ...
- Java爬虫框架WebMagic入门——爬取列表类网站文章
初学爬虫,WebMagic作为一个Java开发的爬虫框架很容易上手,下面就通过一个简单的小例子来看一下. WebMagic框架简介 WebMagic框架包含四个组件,PageProcessor.Sch ...
- 爬虫——爬取Ajax动态加载网页
常见的反爬机制及处理方式 1.Headers反爬虫 :Cookie.Referer.User-Agent 解决方案: 通过F12获取headers,传给requests.get()方法 2.IP限制 ...
- python爬取智联招聘职位信息(单进程)
我们先通过百度搜索智联招聘,进入智联招聘官网,一看,傻眼了,需要登录才能查看招聘信息 没办法,用账号登录进去,登录后的网页如下: 输入职位名称点击搜索,显示如下网页: 把这个URL:https://s ...
- 基于webmagic的爬虫小应用--爬取知乎用户信息
听到“爬虫”,是不是第一时间想到Python/php ? 多少想玩爬虫的Java学习者就因为语言不通而止步.Java是真的不能做爬虫吗? 当然不是. 只不过python的3行代码能解决的问题,而Jav ...
- webmagic爬取渲染网站
最近突然得知之后的工作有很多数据采集的任务,有朋友推荐webmagic这个项目,就上手玩了下.发现这个爬虫项目还是挺好用,爬取静态网站几乎不用自己写什么代码(当然是小型爬虫了~~|). 好了,废话少说 ...
随机推荐
- MS SQL 2008 发布订阅配置错误总结
最近在配置SQL 2008的发布订阅功能时,遇到了几个小错误,顺便归纳总结一下(以后碰到各类关于发布订阅的错误都将收录.更新到这篇文章),方便自己在以后碰到这类问题时,能够迅速解决问题.毕 ...
- ORACLE参数max_shared_servers空值与零的区别
ORACLE数据库中的参数max_shared_servers,这是一个DBA很熟悉的参数,但是这个参数max_shared_servers为空值与为0有区别吗?这个细节可能很多人都没有注意过.如下所 ...
- native2ascii.exe 字符转码与反转码
本人最近在做OAF的二次开发,在看别人写的代码时,发现总有类似这样的语句:”\u65e0\u6548\u7684GP\u9879\u76ee\u7f16\u53f7“,这些语句其实是用Java_hom ...
- python time和datetime的常用转换处理
一.time 1.获取当前时间和时区 >>> now = time.time() # 当前时间 float类型 >>> time.strftime("%Y ...
- OpenStack中Keystone的基本概念理解
原文http://www.kankanews.com/ICkengine/archives/10788.shtml Keystone简介 Keystone(OpenStack Identity Ser ...
- 使用ab进行压力测试详解
ab是apache自带的压力测试工具,非常好用.转载几篇对ab工具的详细使用的博文.猛击下面的链接地址 http://www.365mini.com/page/apache-benchmark.htm ...
- 浅谈Linux中的信号处理机制(一)
有好些日子没有写博客了,自己想想还是不要荒废了时间,写点儿东西记录自己的成长还是百利无一害的.今天是9月17号,暑假在某家游戏公司实习了一段时间,做的事情是在Windows上用c++写一些游戏英雄技能 ...
- [django]django+datatable简单运用于表格中
使用datatable首先需要下载datatable文件,文件主要包括三个文件夹css,img,js相关文件,在django中如何配置呢? 首先需要在模板中引入datatable文件,格式如下: &l ...
- 洛谷P1328 生活大爆炸版石头剪刀布——S.B.S.
题目描述 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头.如果两个人出拳一样,则不分胜负.在<生活大爆炸>第二季第8 集中出现了一种石头剪刀布的升级版游戏. 升级版游戏在传统的 ...
- Hibernate 分组查询 子查询 原生SQL
分组查询: 使用group by关键字对数据分组,使用having关键字对分组数据设定约束条件,从而完成对数据分组和统计 1.1 聚合函数:常被用来实现数据统计功能 ① count() 统计记录条数 ...