WebMagic爬取网站内容
一、WebMagic介绍
WebMagic是一个开源的Java爬虫框架,目标是简化爬虫的开发流程让开发者专注于逻辑功能的开发
WebMagic采用完全模块化的设计,功能覆盖整个爬虫的生命周期(链接提取、页面下载、内容抽取、持久化),支持多线程抓取,分布式抓取,并支持自动重试、自定义UA/cookie等功能。
1.1、原理介绍
先来看一下WebMagic的原理图
Downloader(下载器)、PageProcesser(解析器)、Schedule(调度器)和Pipeline(管道)四部分组成。 核心代码非常简单,主要是将这些组件结合并完成多线程的任务。这意味着,在WebMagic中,基本上可以对爬虫的功能做任何定制。
- 下载器
webmagic爬取信息首先需要依赖给出的一个初始爬取的地址,下载器会下载这个页面的具体信息: - PageProcesser(解析器)
下载成功后,page信息会传递给解析器,由解析器来定制爬虫模板,通过Xpath、CSS、JSOUP、正则表达式 等解析方法,从页面中提取有用的信息。 - Schedule(调度器)
Scheduler主要负责爬虫的下一步爬取的规划,包括一些去重等功能,如果一个页面爬取完成之后,要进行下一个页面的抓取,就由Schedule来调度。 - Pipeline(管道)
管道,爬取到的网站内容保存的位置由Pipeline来定义
总结:客户通过Internet发起http请求,到达DownLoader,DownLoader负责下载页面信息,下载完成之后将页面信息封装在page对象中交给PageProcesser解析器处理页面内容,(至于如何处理,可以自己制定规则),处理完成之后,将处理结果resultItems交给Pipeline管道,Pipelline负责将内容存储起来,与此同时,PageProcesser发送一个request请求给Schedule,他负责调度下一个html页面
二、WebMagic爬虫的具体实现
2.1、需求:
通过在eclipse创建maven项目,采集http://sousuo.gov.cn/column/30611/0.htm网站的新闻列表页和新闻详细页,完成实验。
Maven介绍:
Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具
Webmagic使用maven管理依赖,在项目中添加对应的依赖即可使用Webmagic
2.2、新建Maven项目,添加依赖
- 打开pom.xml文件,添加Maven依赖
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-core</artifactId>
<version>0.6.1</version>
</dependency>
<dependency>
<groupId>us.codecraft</groupId>
<artifactId>webmagic-extension</artifactId>
<version>0.6.1</version>
</dependency>
选择要刷新的maven项目,右键–>Maven–>Update Project,打开对话框,选择具体要刷新的maven项目,点击OK
2. 代码编写
package com.yxcx.webmagictest;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.pipeline.FilePipeline;
import us.codecraft.webmagic.processor.PageProcessor;
public class WebMagic implements PageProcessor {
public static void main(String[] args) {
Spider.create(new WebMagic())
// 从"http://sousuo.gov.cn/column/30611/0.htm"开始抓
.addUrl("http://sousuo.gov.cn/column/30611/0.htm")
// 抓取页面的存储路径
.addPipeline(new FilePipeline("/data/pachong/govnews"))
// 开启5个线程抓取 ,底层处理了线程同步
.thread(5)
// 启动爬虫
.run();
}
// 第一步:DownLoader加载器下载网页时,抓取网站的相关配置,包括编码、抓取间隔、重试次数等
private Site site = Site.me().setRetryTimes(3).setSleepTime(100);
//第二步: process是定制爬虫逻辑的核心接口,在这里编写抽取逻辑
public void process(Page page) {
//每个html网页的url地址
System.out.println(page.getUrl());
//获取网页的title,该title位于haead标签中,text()方法表示获得文本内容
String title = page.getHtml().xpath("//head/title/text()").toString();
System.out.println(title);
if (title == null) {
//setSkip这个方法是对resultItems的内容进行忽略,默认设置为false,就是在本层逻辑中,爬取到的信息不进入管道进行保存。
page.setSkip(true);
}
page.putField("allhtml", page.getHtml().toString());
// 从页面发现后续的url地址来抓取 采用正则表达式去匹配网页名字,分为两种:1、新闻列表项;2、新闻详细页
// 1、采集该网站新闻列表页 ,
page.addTargetRequests(page.getHtml().links().regex("(http://sousuo.gov.cn/column/30611/\\d+.htm)").all());
// 2、采集该网站每条新闻详细页
page.addTargetRequests(
page.getHtml().links().regex("(http://www.gov.cn/xinwen/2017-\\d+/\\d+/content_\\d+.htm)").all());
}
//此方法在Downloader时底层会调用该方法获得site,包括爬虫时的一些设置信息。
public Site getSite() {
return site;
}
}
大神请看,菜鸟止步!!!
三、WebMagic源码解析
- webmagic官网:http://webmagic.io/download.html
可以去下载jar包和源码包,自行研究
1、webmagic源码结构图:
//1、HttpClientDownloader这个类是下载页面的类,其中的download方法返回page对象,page封装页面的信息。
@Override
public Page download(Request request, Task task) {
Site site = null;
if (task != null) {
site = task.getSite();
}
Set<Integer> acceptStatCode;
String charset = null;
Map<String, String> headers = null;
if (site != null) {
acceptStatCode = site.getAcceptStatCode();
charset = site.getCharset();
headers = site.getHeaders();
} else {
acceptStatCode = Sets.newHashSet(200);
}
logger.info("downloading page {}", request.getUrl());
CloseableHttpResponse httpResponse = null;
int statusCode=0;
try {
HttpUriRequest httpUriRequest = getHttpUriRequest(request, site, headers);
httpResponse = getHttpClient(site).execute(httpUriRequest);
statusCode = httpResponse.getStatusLine().getStatusCode();
request.putExtra(Request.STATUS_CODE, statusCode);
if (statusAccept(acceptStatCode, statusCode)) {
Page page = handleResponse(request, charset, httpResponse, task);
onSuccess(request);
return page;//返回page对象
} else {
logger.warn("code error " + statusCode + "\t" + request.getUrl());
return null;
}
} catch (IOException e) {
logger.warn("download page " + request.getUrl() + " error", e);
if (site.getCycleRetryTimes() > 0) {
return addToCycleRetry(request, site);
}
onError(request);
return null;
} finally {
request.putExtra(Request.STATUS_CODE, statusCode);
try {
if (httpResponse != null) {
//ensure the connection is released back to pool
EntityUtils.consume(httpResponse.getEntity());
}
} catch (IOException e) {
logger.warn("close response fail", e);
}
}
}
2、以上download方法返回page对象后,执行下面的方法process方法
public interface PageProcessor {
/**
* process the page, extract urls to fetch, extract the data and store
*
* @param page
*/
//子类重写该方法,处理页面信息,其中page对象中封装了页面的信息
public void process(Page page);
/**
* get the site settings
*
* @return site
* @see Site
*/
public Site getSite();
}
3、页面信息交个Pieline,存储起来
public class FilePipeline extends FilePersistentBase implements Pipeline {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* create a FilePipeline with default path"/data/webmagic/"
*/
//源码中默认存储在linux目录下的/data/webmagic/
public FilePipeline() {
setPath("/data/webmagic/");
}
//可以自行定义存储目录
public FilePipeline(String path) {
setPath(path);
}
4、WebMagic底层使用线程池处理
public void execute(final Runnable runnable) {
if (threadAlive.get() >= threadNum) {
try {
//底层处理了线程同步 lock锁住
reentrantLock.lock();
while (threadAlive.get() >= threadNum) {
try {
condition.await();
} catch (InterruptedException e) {
}
}
} finally {
//解锁
reentrantLock.unlock();
}
}
threadAlive.incrementAndGet();
executorService.execute(new Runnable() {
@Override
public void run() {
try {
runnable.run();
} finally {
try {
reentrantLock.lock();
threadAlive.decrementAndGet();
condition.signal();
} finally {
reentrantLock.unlock();
}
}
}
});
源码粗略的描述了一遍,感兴趣的同学可以自己查看分析。