如何时间停止系列链接webmagic的链接管理的去重

阅读 19065
刚刚开始学,很多东西可能理解错了,还请各位指教
一些基本类:
Request:包含要爬行的url和一些附加信息,是Page的一个成员变量
&& 主要成员变量
String &url
Map&String, Object& &extras 存储附加信息
long &priority& 优先级 值越大越优先
Request(String url) { this.url = }构造函数
Request &setPriority(long &priority)& 设置优先级需要PriorityScheduler管理
Object &getExtra(String key)获取想要的附加信息 通过key在map里寻找
Request &putExtra(String key, Object value) 添加一组附加信息
ResultItems:包含抓取的结果,是Page类的一个成员变量,并且要在pipeline中处理
主要成员变量
Map&String, Object& fields& 是HashMap
boolean &skip 是否要跳过这条结果,跳过了就不会被pipeline处理
&T& T get(String key) &通过key获取相应值
Map&String, Object& &getAll() { }
ResultItems &put(String key, T value) 给当前对象添加一对值就是field添加记录
Request &getRequest() { }
Page:包含页面抽取的结果和要去取的URL
&& 主要成员变量
Request && 主要就是url对象
ResultItems &resultItems& 抓取的结果,用于持久化
String &rawT
Selectable &
Int &statusC
List&Request& &targetRequests
&& 主要方法
Selectable &getUrl(){ &} 获取当前页url&&&&&&&&&&&&&&&&
Html &getHtml()获取当前页内容&&&&&&&&&&&&&&&
ResultItems &getResultItems(){return resultI }获得抓取的结果以在Pipeline持久化里使用
void& putField(String, Object)保存抓取的结果
void &addTargetRequests(List&String& &requests) 添加url 去抓取
void &addTargetRequests(List&String& &requests, long priority) 添加url 去抓取
void &addTargetRequest(String &requestString) 添加url 去抓取
void &addTargetRequest(Request request) 添加request 去抓取
Site:包含爬虫的一些配置
&& 主要成员变量
List&Request& &startRequests = new ArrayList&Request&();& 爬虫开始的url集
Map&String, String& &cookies = new LinkedHashMap&String, String&();
Map&String, String& &headers = new HashMap&String, String&()
String &userA
int &sleepTime = 5000;
int &retryTimes = 0;
int &cycleRetryTimes = 0;
int &timeOut = 5000;
HttpHost &httpProxy
Site me() { return new Site(); } 新生成一个Site
Site &addCookie(String name, String value) &说明Add a cookie with domain
Site &setDomain(String domain) 设置Site的domain
Site &addStartRequest(Request &startRequest) 添加一条url到开始集里
Site &addHeader(String key, String value) 为下载器设置一个http头
Task &toTask()转换为一个Task
Task:标识不同任务的接口(是个interface),没有成员变量
& 主要方法
String &getUUID() 获取一个task的唯一ID
Site &&&getSite()& 获取一个task的Site
Spider:爬虫入口,包含Downloader&& Scheduler& PageProcessor &and &Pipeline
&& 主要成员变量
Downloader &downloader;
List&Pipeline& &pipelines = new ArrayList&Pipeline&();
Scheduler &scheduler = new QueueScheduler();
PageProcessor &pageP
List&Request& &startRequests;
Site &site;
String &uuid;
Logger logger = Logger.getLogger(getClass());
int &threadNum = 1;
& 主要方法
Spider &create(PageProcessor pageProcessor) 创建一个爬虫
Spider &startUrls(List&String& startUrls)添加开始集合 也就是给startRequests赋值
Spider &startRequest(List&Request& &startRequests) 添加开始集合,给startRequests赋值
Spider &scheduler(Scheduler scheduler)& 设置scheduler(url管理器),给scheduler赋值
Spider &setScheduler(Scheduler scheduler) 设置scheduler(url管理器) ,给scheduler赋值
Spider &pipeline(Pipeline pipeline) 设置pipeline(持久化),给pipelines队列添加一条
Spider &addPipeline(Pipeline pipeline) 设置pipeline(持久化),给pipelines队列添加一条
Spider &clearPipeline() 清空pipeline
Spider &downloader(Downloader downloader) 设置downloader(下载器),给downloader赋值
Spider &setDownloader(Downloader downloader)设置downloader,给downloader赋值
void& test(String... urls) 该方法只抓取一个单独的页面,用于测试抽取效果
void &processRequest(Request request){& //核心处理流程
&&&&& Page page = downloader.download(request, this);
&&&&& pageProcessor.process(page);
&&&&& extractAndAddRequests(page);
&&&&& for (Pipeline pipeline : pipelines)
&&&&&&& pipeline.process(page.getResultItems(), this);
一个简单例子
Spider.create(new SimplePageProcessor("http://my.oschina.net/",
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &"http://my.oschina.net/*blog/*")).run()
如果想把结果存到文件里怎么办呢?
Spider.create(new &SimplePageProcessor("http://my.oschina.net/",
&&"http://my.oschina.net/*blog/*")) .pipeline(new FilePipeline("/data/temp/webmagic/")).run();
使用FileCacheQueueScheduler在文件中存取url和游标以便关闭后重新继续开始
Spider.create(new SimplePageProcessor("http://my.oschina.net/",
&&"http://my.oschina.net/*blog/*"))
&.scheduler(new FileCacheQueueScheduler("/data/temp/webmagic/cache/")).run();
webmagic四大模块代码解析
downloader文件件夹& ,download一般不用管
Downloader是一个接口
Page &download(Request request, Task task);下载页面并存到Page对象
void &setThread(int threadNum); 设置线程数
HttpClientDownloader 继承Downloader接口
主要成员变量
Logger &logger = Logger.getLogger(getClass());
Map&String, CloseableHttpClient& &httpClients = new HashMap&String, CloseableHttpClient&();
HttpClientGenerator &httpClientGenerator = new HttpClientGenerator();
&& 主要方法
Html &download(String url)& 下载html
Html &download(String url, String charset)
Page &download(Request request, Task task) 实现接口方法
HttpClientGenerator 这个类是HttpClient产生器,就是工厂
主要成员变量
PoolingHttpClientConnectionManager &connectionM
CloseableHttpClient &&generateClient(Site site)
CloseableHttpClient& &getClient(Site site)
HttpClientGenerator &setPoolSize(int poolSize)
void &generateCookie(HttpClientBuilder httpClientBuilder, Site site)
目前有几个Downloader的实现:
&HttpClientDownloader
集成了Apache HttpClient的Downloader。Apache HttpClient(4.0后整合到HttpCompenent项目中)是强大的Java http下载器,它支持自定义HTTP头(对于爬虫比较有用的就是User-agent、cookie等)、自动redirect、连接复用、cookie保留、设置代理等诸多强大的功能。
SeleniumDownloader(这个文件里没有)
对于一些Javascript动态加载的网页,仅仅使用http模拟下载工具,并不能取到页面的内容。这方面的思路有两种:一种是抽丝剥茧,分析js的逻辑,再用爬虫去重现它;另一种就是:内置一个浏览器,直接获取最后加载完的页面。webmagic-selenium包中整合了Selenium到SeleniumDownloader,可以直接进行动态载页面的抓取。使用selenium需要安装一些native的工具,具体步骤可以参考作者的博文使用Selenium来抓取动态加载的页面
PageProcessor文件夹,页面分析及链接抽取,是我们使用的关键
PageProcessor是一个接口,用于定制爬虫,可定制启动url和一些设置,如何发现要抓取的url,数据要如何提取和存储
&& 主要方法
void &process(Page page); 处理Page对象,提取URL,提取数据并存储
Site &getSite() 获取site 的一些设置信息
通过编写一个实现PageProcessor接口的类,就可以定制一个自己的爬虫,比如
public &class &GithubRepoPageProcesser implements &PageProcessor {
private &Site &site = Site.me().setRetryTimes(3).setSleepTime(100); //一些设置信息
&@Override
&public void &process(Page &page) { //通过正则表达式和Xpath来抓取
&& //要抓取的链接需要显示地添加
&& page.addTargetRequests(page.getHtml().links().regex("(https://github\\.com/\\w+/\\w+)").all());
&& //抓取规则
&& page.putField("author", page.getUrl().regex("https://github\\.com/(\\w+)/.*").toString());
&& page.putField("name", page.getHtml().xpath("//h1[@class='entry-title public']/strong/a/text()").toString());
& &if (page.getResultItems().get("name")==null){& page.setSkip(true);& } //skip this page
&& page.putField("readme", page.getHtml().xpath("//div[@id='readme']/tidyText()"));
&public Site &getSite() {
public &static &void &main(String[] &args) {
&&&& &Spider.create(new GithubRepoPageProcesser()).addUrl("https://github.com/code4craft").thread(5).run();
几种抓取方式
1.java正则表达式:这个不多讲 .& []& ()& 等
2.Jsoup支持jquery中css selector的方式选取元素 :
& &String content = "blabla"; Document doc = JSoup.parse(content);& Elements links = doc.select("a[href]");
3. HtmlCleaner支持XPath的方式选取元素:
4.Xpath: tagNode.evaluateXPath("//a/ ")
5.CSS选择:$("a[href]").attr("href")
webmagic主要实现了CSS Selector、XPath和正则表达式三种抓取方式,其实现放在Selector文件下
Scheduler文件夹,URL管理 主要有两个实现QueueScheduler和FileCacheQueueScheduler
Scheduler是一个接口,实现它可以管理要抓取的URL,还可以去重复
& 主要方法
void push(Request request, Task task)& 添加一个要抓取的url
Request poll(Task task)& 取出一个URL来爬行
QueueScheduler继承Scheduler接口,一个简单的内存队列,速度较快,并且是线程安全的
主要成员变量
Logger &logger = Logger.getLogger(getClass());
BlockingQueue&Request& &queue = new LinkedBlockingQueue&Request&();
Set&String& &urls = new HashSet&String&();
&& 主要方法
void &push(Request request, Task task)& 往queue里边添加一个request
Request &poll(Task task)& 从queue里边弹出一个request
PriorityScheduler继承Scheduler接口,是一个文件队列,它可以用于耗时较长的下载任务,在任务中途停止后,下次执行仍然从中止的URL开始继续爬取。
主要成员变量
Logger &logger = Logger.getLogger(getClass());
BlockingQueue&Request& &noPriorityQueue = new &LinkedBlockingQueue&Request&();
Set&String& &urls = new HashSet&String&();
void &push(Request request, Task task) 添加一个request
Request &poll(Task task) 弹出一个request
Pipeline文件夹,用于后续处理和持久化 主要有四个实现
Pipeline是一个接口,用于持久化 和 离线处理& 只有一个方法
& 主要方法
void &process(ResultItems&resultItems, Task &task) 从resultItems通过get(key)得到数据
CollectorPipeline&T&也是一个接口,且继承了Pipeline,新增了一个方法
&&& 主要方法
List&T& &getCollected(); 获取收集到的所有信息
ConsolePipeline 实现了Pipeline接口,把结果输出到控制台
&public void process(ResultItems resultItems, Task task) {
&&&& System.out.println("get page: " + resultItems.getRequest().getUrl());
&&&& for (Map.Entry&String, Object& entry : resultItems.getAll().entrySet()) {
&&&&&&&&&&& System.out.println(entry.getKey() + ":\t" + entry.getValue());
ResultItemsCollectorPipeline实现了CollectorPipeline&T&接口
主要成员变量
List&ResultItems& &collector = new &ArrayList&ResultItems&();
&&&& 主要方法
public &synchronized &void &process(ResultItems resultItems, Task task) {
&&&&&&& collector.add(resultItems);
public &List&ResultItems& &getCollected() {
FilePipeline 实现了FilePersistentBase,Pipeline接口,把数据存到文件里,每个URL单独保存到一个页面,以URL的MD5结果作为文件名
&& 主要成员变量
Logger &logger = Logger.getLogger(getClass());
&& 主要方法
FilePipeline() {& setPath("/data/webmagic/");& } 默认构造函数 设置了存储路径
FilePipeline(String &path) {& setPath(path);& } 自定义存储路径
void &process(ResultItems resultItems, Task task)
selector文件夹,算是PageProcessor的扩展吧,因为要集合很多选取方式
Selectable:接口 可选的文本& & & & & & & & & & & & & & & & & & & & & & &
Selectable &xpath(String xpath);使用xpath选择
Selectable &$(String selector);& 使用CSS选择
Selectable &$(String selector, String attrName); 使用CSS选择
Selectable &css(String selector); 使用CSS选择
Selectable &css(String selector, String attrName); 使用CSS选择
Selectable &regex(String regex);使用正则表达式选择group默认为1
Selectable &regex(String regex, int group); 使用正则表达式选择
Selectable &replace(String regex, String replacement);
Selectable &smartContent(); 选取smart content
Selectable &links(); 选取所有链接
PlainText:实现了Selectable接口
PlainText(List&String& strings)
PlainText(String text)
PlainText &create(String text)
Selectable &select(Selector selector, List&String& strings)
Selectable &selectList(Selector selector, List&String& strings)
Html:继承了PlainText
主要成员变量
Logger &logger = Logger.getLogger(getClass());
Document & 存储解析过的文档
boolean &init =
主要方法:给出新增的方法
Html(List&String& strings)
Html(String text)
Html(Document document)
void &&initDocument() 简单的初始化方法
Html &create(String text)
String &selectDocument(Selector selector)
List&String& &selectDocumentForList(Selector selector)
String &getText()
Document &getDocument()
Selectors:一个抽象类
RegexSelector &regex(String expr)&&&&&&& RegexSelector &regex(String expr, int group)
CssSelector &$(String expr)&&&&&&&&&&&&& CssSelector &$(String expr, String attrName)
XpathSelector &xpath(String expr)&&&&&&& XsoupSelector &xsoup(String expr)
AndSelector &and(Selector... selectors)&&& OrSelector &or(Selector... selectors)
SmartContentSelector &smartContent()&
Selector:接口,文本抽取器 就两个方法
String &select(String text) 抽取单条结果,若有多个,只有第一个被选择
List&String& &selectList(String text) 抽取所有结果
ElementSelector:接口&& 针对html elements
String select(Element element);
List&String& selectList(Element element);
RegexSelector:实现Selector接口,使用正则表达式抽取
主要成员变量
String &regexS
int &group = 1;
RegexSelector(String regexStr, int group)构造
RegexSelector(String regexStr)构造
String &select(String text)重写
List&String& &selectList(String text)重写
RegexResult &selectGroup(String text)
List&RegexResult& &selectGroupList(String text)
XpathSelector:实现Selector接口,使用Xpath抽取
主要成员变量
String &xpathS
主要成员方法
XpathSelector(String xpathStr)
String &select(String text)重写
List&String& &selectList(String text)重写
ReplaceSelector:实现Selector接口,重置选择器
主要成员变量
String &regexS
主要成员方法
ReplaceSelector(String regexStr, String replacement)
String &select(String text)重写
List&String& &selectList(String text)重写
&AndSelector:实现Selector接口,所有选择器以管道式安排,后一个用前一个的结果
主要成员变量
List&Selector& &selectors = new ArrayList&Selector&()
主要成员方法
AndSelector(Selector... selectors)
AndSelector(List&Selector& selectors)
String &select(String text)重写
List&String& &selectList(String text)重写
OrSelector:实现Selector接口,所有提取器独自工作,提取的结果将会整合起来
主要成员变量
List&Selector& &selectors = new ArrayList&Selector&()
主要成员方法
OrSelector(Selector... selectors)
OrSelector(List&Selector& selectors)
String &select(String text)重写
List&String& &selectList(String text)重写
OrSelector:实现Selector接口,所有提取器独自工作,提取的结果将会整合起来
主要成员变量
List&Selector& &selectors = new ArrayList&Selector&()
主要成员方法
OrSelector(Selector... selectors)
OrSelector(List&Selector& selectors)
String &select(String text)重写
List&String& &selectList(String text)重写
BaseElementSelector:抽象类 继承于Selector, ElementSelector
String &select(String text)重写
List&String& &selectList(String text)重写
CssSelector:继承BaseElementSelector&& CSS selector. Based on Jsoup
主要成员变量
String &selectorT
String &attrN
主要成员方法
CssSelector(String selectorText)
CssSelector(String selectorText, String attrName)
String getValue(Element element)
String &select(String text)重写
List&String& &selectList(String text)重写
XsoupSelector:继承BaseElementSelector&& XPath selector based on Xsoup
主要成员变量
XPathEvaluator xPathE
主要成员方法
XsoupSelector(String xpathStr)
String &select(String text)重写
List&String& &selectList(String text)重写
例如,我已经下载了一个页面,现在要抽取某个区域的所有包含"blog"的链接,我可以这样写:
String content = "blabla"; //content是用别的爬虫工具抽取到的正文
List&String& &links = Html.create(content)
.$("div.title")& //css 选择,Java里虽然很少有$符号出现,不过貌似$作为方法名是合法的
.xpath("//@href")& //提取链接
.regex(".*blog.*") //正则匹配过滤
.all(); //转换为string列表
utils文件夹,一些实用工具
UrlUtils:url和html的工具
&&Pattern patternForProtocal = Pattern.compile("[\\w]+://")
Pattern patternForCharset = Pattern.compile("charset\\s*=\\s*['\"]*([^\\s;'\"]*)");
& Pattern patternForHref = Pattern.compile("(&a[^&&]*href=)[\"']{0,1}([^\"'&&", Pattern.CASE_INSENSITIVE);
String canonicalizeUrl(String url, String refer)
& String getHost(String url)
& String removeProtocol(String url)
& String getDomain(String url)
String fixAllRelativeHrefs(String html, String url)
List&Request& convertToRequests(Collection&String& urls)
List&String& convertToUrls(Collection&Request& requests)
String getCharset(String contentType)
ThreadUtils:
& ExecutorService newFixedThreadPool(int threadSize)
NumberUtils:抽象类
& int compareLong(long o1, long o2)
FilePersistentBase:文件持久化的基本对象
& String path
String PATH_SEPERATOR = "/"
& String getPath()
& void checkAndMakeParentDirecotry(String fullName)
File getFile(String fullName)
void setPath(String path)
EnvironmentUtil
&& String USE_XSOUP = "xsoup"
&& boolean useXsoup()
&& void &setUseXsoup(boolean useXsoup)
& 著作权归作者所有
人打赏支持
码字总数 5947
总结的不错。不过:一切不排版的代码都是耍流氓!
我觉得对于开发者来说,能脚本化编写爬虫是一件挺开心的事情( ̄▽ ̄)&。所以我们团队开发了一个专门让开发者用简单的几行 javascript 就能在云上编写和运行复杂爬虫的系统,叫神箭手云爬虫开发平台: http://www.shenjianshou.cn 。欢迎同行们来试用拍砖,尽情给俺们提意见。有想法的可以加群讨论:
评论删除后,数据将无法恢复
webmagic使用selenium爬取知乎,遇到点击图片动静图交替的页面要怎么处理? https://zhuanlan.zhihu.com/p/ 是爬取网址,只能获取到静图图片,点击没反应。...
Nicll ? 06/15 ?
webmagic selenium+JAVA 如何自动判断,页面加载完再执行之后的代码
Nicll ? 今天 ?
Gather Platform 数据抓取平台是一套基于 Webmagic 内核的,具有 Web 任务配置和任务管理界面的数据采集平台,一个轻量级的搜索引擎系统。具有以下功能 根据配置的模板进行数据采集 对采集的...
gsh199449 ?
今年的 Google I/O 大会上,Google 是这样评价 Kotlin:在过去一年里,有35%的专业 Android 开发者在使用 Kotlin,其中95%的开发者都对 Kotlin 非常满意。虽然 Kotlin 正处于发展的初始阶段,...
码云Gitee ? 05/28 ?
以上代码抓取的结果全是 .com,我用demo测试抓取正常,求指教
开源中国酱油部主任 ? 01/17 ?
当获取的网页内容含有评论时,获取的第二页评论里,又有三个跳转页,这种怎么处理比较好,每跳转一次,链接地址就在当前地址上叠加了,要怎么才能固定成直接的页面地址?
Nicll ? 01/17 ?
原文出处:拓海 介绍 大家好!我们从今天开始学习开源爬虫框架Scrapy,如果你看过《手把手》系列的前两篇,那么今天的内容就非常容易理解了。细心的读者也许会有疑问,为什么不学出身名门的A...
拓海 ? 04/28 ?
@大白痴 你好,想跟你请教个问题: 我的webmagic为什吗存储不到本地,就是使用pipeline
城花祭泪 ?
此文章是webmagic 0.1.0版的设计手册,后续版本的入门及用户手册请看这里:https://github.com/code4craft/webmagic/blob/master/user-manual.md 之前就有网友在博客里留言,觉得webmagic的实...
官方网站http://webmagic.io/ webmagic是一个开源的Java垂直爬虫框架,目标是简化爬虫的开发流程,让开发者专注于逻辑功能的开发。webmagic的核心非常简单,但是覆盖爬虫的整个流程,也是很好...
没有更多内容
加载失败,请刷新页面
PE 的意思就是 Portable Executable(可移植的执行体)。PE文件结构的总体层次分布图: -------------- |DOS MZ Header | |--------------| |DOS Stub | |--------------| |PE Header | |--......
simpower ? 18分钟前 ?
最近发现自己的项目放到centos上,启动的时候非常慢,遂打算深入看看这个问题,好在最后解决了,过程如下。 参考大神:https://www.tuicool.com/articles/uaiURzF https://www.cnblogs.com/...
zyx870805 ? 18分钟前 ?
摘要:2017年,ofo向市场投入了一千多万辆单车,这些单车的投放、运营和调度需要大量数据的支持。本文将从ofo选择MaxCompute的理由以及数据完整性、任务调度、Proxy服务三个方面的实战应用,...
猫耳m ? 22分钟前 ?
1、比较简单的设计模式,在项目中使用的场景非常多 通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象 2、原型模式有两种表现形式: (...
职业搬砖20年 ? 25分钟前 ?
package com.proxy. import java.lang.annotation.A import java.lang.reflect.M import net.sf.cglib.proxy.E import net.sf.cglib.proxy.MethodInterceptor......
徐志 ? 27分钟前 ?
摘要:2017年,ofo向市场投入了一千多万辆单车,这些单车的投放、运营和调度需要大量数据的支持。本文将从ofo选择MaxCompute的理由以及数据完整性、任务调度、Proxy服务三个方面的实战应用,...
阿里云云栖社区 ? 31分钟前 ?
var screenHeight = document.body.clientH
// 获取原先屏幕高度
var u = navigator.userA // 获取厂商
var isAndroid = u.indexOf('Android') & -1 || u.indexOf('Adr') & ......
不负好时光 ? 36分钟前 ?
kettle国内镜像下载 http://mirror.bit.edu.cn/pentaho/Data%20Integration/
gulf ? 37分钟前 ?
ip查询:http://ip.taobao.com/accurancy.html
evil_01 ? 38分钟前 ?
话说北京有个地儿叫大栅栏,在前门前,天桥下,过了天桥就到了天坛。这个大栅栏标准读音是da zha lan,但如果你非得这么念可能要遭到本地人笑话的,正确的土话读法是:da she lan er。 言归正...
大大枣 ? 42分钟前 ?
没有更多内容
加载失败,请刷新页面
文章删除后无法恢复,确定取消删除此文章吗?
亲,自荐的博客将通过私信方式通知管理员,优秀的博客文章审核通过后将在博客推荐列表中显示
确定推荐此文章吗?
确定推荐此博主吗?
聚合全网技术文章,根据你的阅读喜好进行个性推荐
指定官方社区
深圳市奥思网络科技有限公司版权所有阅读 63670
此文章是webmagic 0.1.0版的设计手册,后续版本的入门及用户手册请看这里:
之前就有网友在博客里留言,觉得webmagic的实现比较有意思,想要借此研究一下爬虫。最近终于集中精力,花了三天时间,终于写完了这篇文章。之前垂直爬虫写了一年多,webmagic框架写了一个多月,这方面倒是有一些心得,希望对读者有帮助。
webmagic的目标
一般来说,一个爬虫包括几个部分:
页面下载是一个爬虫的基础。下载页面之后才能进行其他后续操作。
一般爬虫都会有一些初始的种子URL,但是这些URL对于爬虫是远远不够的。爬虫在爬页面的时候,需要不断发现新的链接。
最基础的URL管理,就是对已经爬过的URL和没有爬的URL做区分,防止重复爬取。
内容分析和持久化
一般来说,我们最终需要的都不是原始的HTML页面。我们需要对爬到的页面进行分析,转化成结构化的数据,并存储下来。
不同的爬虫,对这几部分的要求是不一样的。
&!--more--&
对于通用型的爬虫,例如搜索引擎蜘蛛,需要指对互联网大部分网页无差别进行抓取。这时候难点就在于页面下载和链接管理上--如果要高效的抓取更多页面,就必须进行更快的下载;同时随着链接数量的增多,需要考虑如果对大规模的链接进行去重和调度,就成了一个很大的问题。一般这些问题都会在大公司有专门的团队去解决,比如这里有一篇来自淘宝的。对Java来说,如果你要研究通用爬虫,那么可以看一下或者。
而垂直类型的爬虫要解决的问题则不一样,比如想要爬取一些网站的新闻、博客信息,一般抓取数量要求不是很大,难点则在于如何高效的定制一个爬虫,可以精确的抽取出网页的内容,并保存成结构化的数据。这方面需求很多,webmagic就是为了解决这个目的而开发的。
使用Java语言开发爬虫是比较复杂的。虽然Java有很强大的页面下载、HTML分析工具,但是每个都有不小的学习成本,而且这些工具本身都不是专门为爬虫而生,使用起来也没有那么顺手。我曾经有一年的时间都在开发爬虫,重复的开发让人头痛。Java还有一个比较成熟的框架,但是它是为通用爬虫而设计的,扩展性差一些,满足不了我的业务需要。我也有过自己开发框架的念头,但是终归觉得抽象的不是很好。直到发现python的爬虫框架,它将爬虫的生命周期拆分的非常清晰,我参照它进行了模块划分,并用Java的方式去实现了它,于是就有了webmagic。
代码已经托管到github,地址是,Javadoc:
webmagic的实现还参考了另一个Java爬虫。SpiderMan是一个全栈式的Java爬虫,它的设计思想跟webmagic稍有不同,它希望将Java语言的实现隔离,仅仅让用户通过配置就完成一个垂直爬虫。理论上,SpiderMan功能更强大,很多功能已经内置,而webmagic则比较灵活,适合熟悉Java语法的开发者,可以比较非常方便的进行扩展和二次开发。
webmagic的模块划分
webmagic目前的核心代码都在webmagic-core中,webmagic-samples里有一些定制爬虫的例子,可以作为参考。而webmagic-plugin目前还不完善,后期准备加入一些常用的功能。下面主要介绍webmagic-core的内容。
前面说到,webmagic参考了scrapy的模块划分,分为Spider(整个爬虫的调度框架)、Downloader(页面下载)、PageProcessor(链接提取和页面分析)、Scheduler(URL管理)、Pipeline(离线分析和持久化)几部分。只不过scrapy通过middleware实现扩展,而webmagic则通过定义这几个接口,并将其不同的实现注入主框架类Spider来实现扩展。
Spider类-核心调度
Spider是爬虫的入口类,Spider的接口调用采用了链式的API设计,其他功能全部通过接口注入Spider实现,下面是启动一个比较复杂的Spider的例子。
&!-- lang: java --&
Spider.create(sinaBlogProcessor)
.scheduler(new FileCacheQueueScheduler(&/data/temp/webmagic/cache/&))
.pipeline(new FilePipeline())
.thread(10).run();
Spider的核心处理流程非常简单,代码如下:
&!-- lang: java --&
private void processRequest(Request request) {
Page page = downloader.download(request, this);
if (page == null) {
sleep(site.getSleepTime());
pageProcessor.process(page);
addRequest(page);
for (Pipeline pipeline : pipelines) {
pipeline.process(page, this);
sleep(site.getSleepTime());
Downloader-页面下载
页面下载是一切爬虫的开始。
大部分爬虫都是通过模拟http请求,接收并分析响应来完成。这方面,JDK自带的HttpURLConnection可以满足最简单的需要,而Apache HttpClient(4.0后整合到HttpCompenent项目中)则是开发复杂爬虫的不二之选。它支持自定义HTTP头(对于爬虫比较有用的就是User-agent、cookie等)、自动redirect、连接复用、cookie保留、设置代理等诸多强大的功能。
webmagic使用了HttpClient 4.2,并封装到了HttpClientDownloader。学习HttpClient的使用对于构建高性能爬虫是非常有帮助的,官方的就是很好的学习资料。目前webmagic对HttpClient的使用仍在初步阶段,不过对于一般抓取任务,已经够用了。
下面是一个使用HttpClient最简单的例子:
&!-- lang: java --&
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(&http://youhost/xxx&);
HttpResponse httpResponse = httpClient.execute(httpGet);
System.out.println(EntityUtils.toString(httpResponse.getEntity().getContent()));
对于一些Javascript动态加载的网页,仅仅使用http模拟下载工具,并不能取到页面的内容。这方面的思路有两种:一种是抽丝剥茧,分析js的逻辑,再用爬虫去重现它(比如在网页中提取关键数据,再用这些数据去构造Ajax请求,最后直接从响应体获取想要的数据);
另一种就是:内置一个浏览器,直接获取最后加载完的页面。这方面,js可以使用PhantomJS,它内部集成了webkit。而Java可以使用Selenium,这是一个非常强大的浏览器模拟工具。考虑以后将它整理成一个独立的Downloader,集成到webmagic中去。
一般没有必要去扩展Downloader。
PageProcessor-页面分析及链接抽取
这里说的页面分析主要指HTML页面的分析。页面分析可以说是垂直爬虫最复杂的一部分,在webmagic里,PageProcessor是定制爬虫的核心。通过编写一个实现PageProcessor接口的类,就可以定制一个自己的爬虫。
页面抽取最基本的方式是使用正则表达式。正则表达式好处是非常通用,解析文本的功能也很强大。但是正则表达式最大的问题是,不能真正对HTML进行语法级别的解析,没有办法处理关系到HTML结构的情况(例如处理标签嵌套)。例如,我想要抽取一个&div&里的内容,可以这样写:&&div&(.*?)&/div&&。但是如果这个div内部还包含几个子div,这个时候使用正则表达式就会将子div的&&/div&&作为终止符截取。为了解决这个问题,我们就需要进行HTML的分析。
HTML分析是一个比较复杂的工作,Java世界主要有几款比较方便的分析工具:
Jsoup是一个集强大和便利于一体的HTML解析工具。它方便的地方是,可以用于支持用jquery中css selector的方式选取元素,这对于熟悉js的开发者来说基本没有学习成本。
&!-- lang: java --&
String content = &blabla&;
Document doc = JSoup.parse(content);
Elements links = doc.select(&a[href]&);
Jsoup还支持白名单过滤机制,对于网站防止XSS攻击也是很好的。
####HtmlParser
HtmlParser的功能比较完备,也挺灵活,但谈不上方便。这个项目很久没有维护了,最新版本是2.1。HtmlParser的核心元素是Node,对应一个HTML标签,支持getChildren()等树状遍历方式。HtmlParser另外一个核心元素是NodeFilter,通过实现NodeFilter接口,可以对页面元素进行筛选。这里有一篇HtmlParser的使用文章:。
####Apache tika
tika是专为抽取而生的工具,还支持PDF、Zip甚至是Java Class。使用tika分析HTML,需要自己定义一个抽取内容的Handler并继承org.xml.sax.helpers.DefaultHandler,解析方式就是xml标准的方式。crawler4j中就使用了tika作为解析工具。SAX这种流式的解析方式对于分析大文件很有用,我个人倒是认为对于解析html意义不是很大。
&!-- lang: java --&
InputStream inputStream =
HtmlParser htmlParser = new HtmlParser();
htmlParser.parse(new ByteArrayInputStream(page.getContentData()),
contentHandler, metadata, new ParseContext());
####HtmlCleaner与XPath
HtmlCleaner最大的优点是:支持XPath的方式选取元素。XPath是一门在XML中查找信息的语言,也可以用于抽取HTML元素。XPath与CSS Selector大部分功能都是重合的,但是CSS Selector专门针对HTML,写法更简洁,而XPath则是通用的标准,可以精确到属性值。XPath有一定的学习成本,但是对经常需要编写爬虫的人来说,这点投入绝对是值得的。
学习XPath可以参考w3school的。下面是使用HtmlCleaner和xpath进行抽取的一段代码:
&!-- lang: java --&
HtmlCleaner htmlCleaner = new HtmlCleaner();
TagNode tagNode = htmlCleaner.clean(text);
Object[] objects = tagNode.evaluateXPath(&xpathStr&);
几个工具的对比
在这里评价这些工具的主要标准是“方便”。就拿抽取页面所有链接这一基本任务来说,几种代码分别如下:
&!-- lang: java --&
tagNode.evaluateXPath(&//a/@href&)
CSS Selector:
&!-- lang: java --&
//使用类似js的实现
$(&a[href]&).attr(&href&)
HtmlParser:
&!-- lang: java --&
Parser p = new Parser(value);
NodeFilter aFilter = new TagNameFilter(&a&);
NodeList nodes = p.extractAllNodesThatMatch(aFilter);
for (int i = 0; i & nodes.size(); i++) {
Node eachNode = nodes.elementAt(i);
if (eachNode instanceof LinkTag) {
LinkTag linkTag = (LinkTag) eachN
System.out.println(linkTag.extractLink());
XPath是最简单的,可以精确选取到href属性值;而CSS Selector则次之,可以选取到HTML标签,属性值需要调用函数去获取;而HtmlParser和SAX则需要手动写程序去处理标签了,比较麻烦。
webmagic的Selector
Selector是webmagic为了简化页面抽取开发的独立模块,是整个项目中我最得意的部分。这里整合了CSS Selector、XPath和正则表达式,并可以进行链式的抽取,很容易就实现强大的功能。即使你使用自己开发的爬虫工具,webmagic的Selector仍然值得一试。
例如,我已经下载了一个页面,现在要抽取某个区域的所有包含&blog&的链接,我可以这样写:
&!-- lang: java --&
//content是用别的爬虫工具抽取到的正文
String content = &blabla&;
List&String& links = Html.create(content)
.$(&div.title&)
//css 选择,Java里虽然很少有$符号出现,不过貌似$作为方法名是合法的
.xpath(&//@href&)
//提取链接
.regex(&.*blog.*&) //正则匹配过滤
.all(); //转换为string列表
另外,webmagic的抓取链接需要显示的调用Page.addTargetRequests()去添加,这也是为了灵活性考虑的(很多时候,下一步的URL不是单纯的页面href链接,可能会根据页面模块进行抽取,甚至可能是自己拼凑出来的)。
补充一个有意思的话题,就是对于页面正文的自动抽取。相信用过Evernote Clearly都会对其自动抽取正文的技术印象深刻。这个技术又叫Readability,webmagic对readability有一个粗略的实现SmartContentSelector,用的是P标签密度计算的方法,在测试oschina博客时有不错的效果。
Scheduler-URL管理
URL管理的问题可大可小。对于小规模的抓取,URL管理是很简单的。我们只需要将待抓取URL和已抓取URL分开保存,并进行去重即可。使用JDK内置的集合类型Set、List或者Queue都可以满足需要。如果我们要进行多线程抓取,则可以选择线程安全的容器,例如LinkedBlockingQueue以及ConcurrentHashMap。
因为小规模的URL管理非常简单,很多框架都并不将其抽象为一个模块,而是直接融入到代码中。但是实际上,抽象出Scheduler模块,会使得框架的解耦程度上升一个档次,并非常容易进行横向扩展,这也是我从scrapy中学到的。
在webmagic的设计中,除了Scheduler模块,其他的处理-从下载、解析到持久化,每个任务都是互相独立的,因此可以通过多个Spider共用一个Scheduler来进行扩展。排除去重的因素,URL管理天生就是一个队列,我们可以很方便的用分布式的队列工具去扩展它,也可以基于mysql、redis或者mongodb这样的存储工具来构造一个队列,这样构建一个多线程乃至分布式的爬虫就轻而易举了。
URL去重也是一个比较复杂的问题。如果数据量较少,则使用hash的方式就能很好解决。数据量较大的情况下,可以使用Bloom Filter或者更复杂的方式。
webmagic目前有两个Scheduler的实现,QueueScheduler是一个简单的内存队列,速度较快,并且是线程安全的,FileCacheQueueScheduler则是一个文件队列,它可以用于耗时较长的下载任务,在任务中途停止后,下次执行仍然从中止的URL开始继续爬取。
Pipeline-离线处理和持久化
Pipeline其实也是容易被忽略的一部分。大家都知道持久化的重要性,但是很多框架都选择直接在页面抽取的时候将持久化一起完成,例如crawer4j。但是Pipeline真正的好处是,将页面的在线分析和离线处理拆分开来,可以在一些线程里进行下载,另一些线程里进行处理和持久化。
你可以扩展Pipeline来实现抽取结果的持久化,将其保存到你想要保存的地方-本地文件、数据库、mongodb等等。Pipeline的处理目前还是在线的,但是修改为离线的也并不困难。
webmagic目前只支持控制台输出和文件持久化,但是持久化到数据库也是很容易的。
webmagic确实是一个山寨的框架,本身也没有太多创新的东西,但是确实对Java爬虫的实现有了一些简化。在强大便利的功能和较高的灵活性中间,webmagic选择了后者,目标就是要打造一个熟练的Java开发者也用的比较顺手的工具,并且可以集成到自己的业务系统中,这一点我自己开发了不少这样的业务,对其灵活性还是比较有信心的。webmagic目前的代码实现还比较简单(不到2000行),如果有兴趣的阅读代码可能也会有一些收获,也非常欢迎建议和指正。
最后再次附上代码地址:
& 著作权归作者所有
人打赏支持
开源马克杯是开源中国定制的“高大上”Coders 喝水利器!
领取条件:购买或拥有开源马克杯的OSCer可领取
开源项目作者
领取时间:
作为一个开源项目作者,是时候站出来拯救世界了!
领取条件:开源项目被开源中国收录的开发者可领取
参与源创会
领取时间:
“”在线下联结了各位 OSCer,推广开源项目和理念,很荣幸有你的参与~
领取条件:参与过开源中国“源创会”的 OSCer 可以领取
码字总数 116344
你好,我看了你一些文档,我想知道,把你的项目在我的Java下eclipse下运行爬虫,我首先是从downloader下的httpclientdownloader开始爬虫,不知道步骤是否正确,同时需要咨询的是那个moudule_webmagic-core.xml也出现下面错误:D:\Eclipse-file\dotry\webmagic-core\module_webmagic-core.xml:83: Reference ignored.files not found.&?xml version=&1.0& encoding=&UTF-8&?&&project name=&module_webmagic-core& default=&compile.module.webmagic-core&&
&dirname property=&module.webmagic-core.basedir& file=&${ant.file.module_webmagic-core}&/&
&property name=&module.jdk.home.webmagic-core& value=&${project.jdk.home}&/&
&property name=&module.jdk.bin.webmagic-core& value=&${project.jdk.bin}&/&
&property name=&module.jdk.classpath.webmagic-core& value=&${project.jdk.classpath}&/&
&property name=&compiler.args.webmagic-core& value=&${compiler.args}&/&
&property name=&webmagic-core.output.dir& value=&${module.webmagic-core.basedir}/target/classes&/&
&property name=&webmagic-core.testoutput.dir& value=&${module.webmagic-core.basedir}/target/test-classes&/&
&path id=&
引用来自“muyu114”的评论关于@ExtractBy这种解释性描述代码,有没有相关的讲解的https://github.com/code4craft/webmagic/blob/master/user-manual.md 文档中有
关于@ExtractBy这种解释性描述代码,有没有相关的讲解的
引用来自“温佐镜”的评论能抓取javascript生成的页面吗你可以用Selenium来抓动态页面,我写过一篇文章:不过推荐做法还是直接抓ajax...
能抓取javascript生成的页面吗
It&s really an awesome job. thx
引用来自“黄亿华”的评论引用来自“glamey”的评论今天浏览了下您的代码,发现了几个问题,能否解答下,我看的比较快,中间可能会忽略一些东西。现在有疑问:1、是否可以设置采集的深度。2、遍历每个页面找到符合规则的URL能否不放在arrayList,没有去重的表现(虽然最终还是放在队列中去重了)。3、是否可以第一次采集完毕后,后续是否可以读取DB,进行数据的再次采集更新(这个我可以单独调用内部的方法也可以实现)。多谢!就需要这种有建设性的意见,嗯嗯。1. 目前没有提供api,可以借助request里嵌入层数并定制scheduler实现,我写过一篇博客说明:2. 最初设计没有考虑到单页大量重复URL,最终把去重都放在了scheduler里,对于重复链接多的页面,先去重确实可以省掉一部分时间,考虑在scheduler(特别是RedisScheduler)里预先做一遍。3. 目前没有这个功能,因为没法限定持久化的方式。这个功能其实很多人问到了,但是没有通用的解决方案,之前的做法是从外部定期启动spider,并把链接放进去。多谢你的回复。最喜欢webmagic的整体架构,清晰、合理、任务独立,支持下。
引用来自“glamey”的评论今天浏览了下您的代码,发现了几个问题,能否解答下,我看的比较快,中间可能会忽略一些东西。现在有疑问:1、是否可以设置采集的深度。2、遍历每个页面找到符合规则的URL能否不放在arrayList,没有去重的表现(虽然最终还是放在队列中去重了)。3、是否可以第一次采集完毕后,后续是否可以读取DB,进行数据的再次采集更新(这个我可以单独调用内部的方法也可以实现)。多谢!就需要这种有建设性的意见,嗯嗯。1. 目前没有提供api,可以借助request里嵌入层数并定制scheduler实现,我写过一篇博客说明:2. 最初设计没有考虑到单页大量重复URL,最终把去重都放在了scheduler里,对于重复链接多的页面,先去重确实可以省掉一部分时间,考虑在scheduler(特别是RedisScheduler)里预先做一遍。3. 目前没有这个功能,因为没法限定持久化的方式。这个功能其实很多人问到了,但是没有通用的解决方案,之前的做法是从外部定期启动spider,并把链接放进去。
今天浏览了下您的代码,发现了几个问题,能否解答下,我看的比较快,中间可能会忽略一些东西。现在有疑问:1、是否可以设置采集的深度。2、遍历每个页面找到符合规则的URL能否不放在arrayList,没有去重的表现(虽然最终还是放在队列中去重了)。3、是否可以第一次采集完毕后,后续是否可以读取DB,进行数据的再次采集更新(这个我可以单独调用内部的方法也可以实现)。
引用来自“oschinaid”的评论另外,我想用于论文中,我打算写上作者,并标明出处为开源中国,再附上链接,可以吗没问题
评论删除后,数据将无法恢复
本文来自作者 追梦 在 GitChat 上分享 「Java 编程之美:并发编程高级篇之一」 编辑 | 工藤 前言 借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了。 ...
gitchat ? 05/24 ?
诞生于1991年的Java如今已经成为世界范围内应用最为广泛的编程语言之一。在今天的文章中,我们将共同了解Java所拥有的七大关键新特性,展望其如何在未来的超级计算、大数据以及物联网等领域继...
Java大数据处理 ? 04/22 ?
伴随着IT的火热,越来越多的人进入了IT领域,这在进一步推动着IT发展的同时也极大增加了就业压力。伴随着激烈的岗位竞争,越来越多的人开始感叹工作难找,越火的行业越是如此,Java自是首当其...
糖宝_d864 ? 06/08 ?
使用struts框架的好处之一就是所有action类继承一个基类,将访问控制在基类中处理.2.所有的action类都继承自baseaction,一个资源对应一个action类. 1.实现一个继承自struts的action的baseact...
thinkyoung ?
本人三年java工作经验,下面是这两个月的面试历程,分享给大家! 1、新东方 04_19 技术面试,两轮,地点:中关村地铁站E口,鼎好大厦6层, 面试官:龙彦俊,李经理 面试时间::3...
java高级架构牛人 ? 05/13 ?
一、简历 简历里面需要包含的内容应该是学历,自己的技术栈,然后自己做过的一些项目。简历不需要太长,两页纸即可。里面应该重点写你使用过的一些框架,自己做的一些项目,以及自己的收获,...
java高级架构牛人 ? 05/29 ?
JVM 1、请介绍一下JVM内存模型??用过什么垃圾回收器都说说呗 2、线上发送频繁full gc如何处理? CPU 使用率过高怎么办? 如何定位问题?如何解决说一下解决思路和处理方法 3、知道字节码吗?字节...
j4love ? 04/14 ?
少年,我看你骨骼精奇,将是未来万中无一的IT精英,很是适合学JAVA。维护世界和平就看你的了,我这里有能让你成为IT精英的办法!还不来看看! 基础类 1、《Thinking in Java》,入门第一位是...
启示录是真的 ? 05/25 ?
前言 对于 ThreadLocal 的使用,并不难。但要深入理解 ThreadLocal 的实现方式,需要细细揣摩。写本文前,我在网上看了很多关于 ThreadLocal 的分析,但却感到遗憾,因为很多文章存在着一定误...
徐志毅 ? 04/11 ?
原文作者:阿飞Javaer 原文链接:https://www.jianshu.com/p/da8 接下来对sharding-jdbc源码的分析基于tag为源码,根据sharding-jdbc Features深入学习sharding-jdbc的几个主要特性...
飞哥-Javaer ? 05/03 ?
没有更多内容
加载失败,请刷新页面
Hive是一个数据仓库基础工具在Hadoop中用来处理结构化数据,提供简单的sql查询功能,可以将sql语句转换为MapReduce任务进行运行(具体的Hive架构大家自行搜索)。接下来主要讲下Hadoop集群下...
海岸线的曙光 ? 今天 ?
裸机通过iso安装CoreOS,个人趟了很多坑,以下就是完整的从零开始部署和配置的过程,希望对大家有用。 一、安装CoreOS到硬盘 1. 准备Live iso镜像,制作好usb启动盘 Live iso下载地址 2. 搭建...
ykbj ? 今天 ?
表格已经完成后新加的需求,要实现锁表格的第一列。很多带这种效果的都是js封装的框架或者具体某种框架的组件,不适用解决当前问题。作为后端开发又实在不熟样式,搜到了一个可以用的,虽然样...
刘昌鑫 ? 今天 ?
Trafodion是Apache基金会的一个开源项目,提供了一个成熟的企业级SQL-on-HBase解决方案。Trafodion的主要设计思想是处理operational类型的工作负载,或者是传统的OLTP应用。此外,对于需要保...
Mr_zebra ? 今天 ?
系统磁盘优化——"/var/spool/postfix/maildrop" 文件清理 最近某服务器磁盘空间告警,在排查过程中发现"/var/spool/postfix/maildrop"目录下堆积了很多小文件,起初想直接删除,但是使用rm删...
阿dai ? 今天 ?
使用以太坊钱包开发实现经典的HelloWord智能合约类。本文中,我们将看到如何编写简单的合约并将其部署到区块链上。我们还将通过发送和读取数据来了解如何与我们的智能合约进行交互。 Solidi...
笔阁 ? 今天 ?
转载自: http://liuyieyer.iteye.com/blog/2214722?utm_source=tuicool&utm_medium=referral 由于网站使用nginx做的反向代理负载均衡。在没有默认的系统TCP参数情况下回导致大量的TIME_WAIT...
lxzh504 ? 今天 ?
转载:https://blog.csdn.net/myan/article/details/647511 前不久chensh出于不可告人的目的,要充当老师,教别人线性代数。于是我被揪住就线性代数中一些务虚性的问题与他讨论了几次。很明显...
亭子happy ? 今天 ?
Ubuntu在执行apt-get upgrade时出现了错误: dpkg: 处理软件包 libc6:i386 (--configure)时出错: installed libc6:i386 package post-installation script subprocess returned error ex......
雪饼 ? 今天 ?
https://tool.lu/js/
LM_Mike ? 今天 ?
没有更多内容
加载失败,请刷新页面
文章删除后无法恢复,确定取消删除此文章吗?
亲,自荐的博客将通过私信方式通知管理员,优秀的博客文章审核通过后将在博客推荐列表中显示
确定推荐此文章吗?
确定推荐此博主吗?
聚合全网技术文章,根据你的阅读喜好进行个性推荐
指定官方社区
深圳市奥思网络科技有限公司版权所有}

我要回帖

更多关于 时间停止系列链接 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信