您的位置:首页 > 理论基础 > 计算机网络

webmagic是个神奇的爬虫(二)-- webmagic爬取流程细讲

2017-03-12 18:02 375 查看
    webmagic流程图镇楼:



第一篇笔记讲到了如何创建webmagic项目,这一讲来说一说webmagic爬取的主要流程。

webmagic主要由Downloader(下载器)、PageProcesser(解析器)、Schedule(调度器)和Pipeline(管道)四部分组成。

从流程图上可以看出,webmagic爬取信息首先需要依赖给出的一个初始爬取的地址,下载器会下载这个页面的具体信息:

@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 = WMCollections.newHashSet(200);
}
logger.info("downloading page {}", request.getUrl());
CloseableHttpResponse httpResponse = null;
int statusCode=0;
try {
HttpHost proxyHost = null;
Proxy proxy = null; //TODO
if (site.getHttpProxyPool() != null && site.getHttpProxyPool().isEnable()) {
proxy = site.getHttpProxyFromPool();
proxyHost = proxy.getHttpHost();
} else if(site.getHttpProxy()!= null){
proxyHost = site.getHttpProxy();
}

HttpUriRequest httpUriRequest = getHttpUriRequest(request, site, headers, proxyHost);
httpResponse = getHttpClient(site, proxy).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;
} else {
logger.warn("get page {} error, status code {} ",request.getUrl(),statusCode);
return null;
}
} catch (IOException e) {
logger.warn("download page {} error", request.getUrl(), e);
if (site.getCycleRetryTimes() > 0) {
return addToCycleRetry(request, site);
}
onError(request);
return null;
} finally {
request.putExtra(Request.STATUS_CODE, statusCode);
if (site.getHttpProxyPool()!=null && site.getHttpProxyPool().isEnable()) {
site.returnHttpProxyToPool((HttpHost) request.getExtra(Request.PROXY), (Integer) request
.getExtra(Request.STATUS_CODE));
}
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);
}
}
}
以上是webmagic-core包 0.6.1版本中的下载器主要方法,从代码中可以看出,框架首先会加载程序中预先设置的配置参数site,之后根据页面响应生成page信息。

下载成功后,page信息会传递给解析器,由解析器来定制爬虫模板,通过Xpath、CSS、JSOUP等解析方法,从页面中提取有用的信息,值得一提的是,加入后续处理请求也在解析器中执行。

/**
* add url to fetch
*
* @param requestString requestString
*/
public void addTargetRequest(String requestString) {
if (StringUtils.isBlank(requestString) || requestString.equals("#")) {
return;
}
synchronized (targetRequests) {
requestString = UrlUtils.canonicalizeUrl(requestString, url.toString());
targetRequests.add(new Request(requestString));
}
}
/**
* add requests to fetch
*
* @param request request
*/
public void addTargetRequest(Request request) {
synchronized (targetRequests) {
targetRequests.add(request);
}
}
/**
* add urls to fetch
*
* @param requests requests
* @param priority priority
*/
public void addTargetRequests(List<String> requests, long priority) {
synchronized (targetRequests) {
for (String s : requests) {
if (StringUtils.isBlank(s) || s.equals("#") || s.startsWith("javascript:")) {
continue;
}
s = UrlUtils.canonicalizeUrl(s, url.toString());
targetRequests.add(new Request(s).setPriority(priority));
}
}
}
后续请求可以单独加入,也可以加入一个队列,以上三种方法最为常用。

还有一点值得注意的是Page类中有一个setSkip的方法,刚刚接触webmagic的时候,对这个方法一头雾水,也极少有说明这个方法到底是用途是什么。

public Page setSkip(boolean skip) {
resultItems.setSkip(skip);
return this;

}


在我用webmagic写了无数个爬虫模板之后,再回回过头来看这个方法,才清楚它的用途。
setSkip这个方法是对resultItems的内容进行忽略,默认设置为false,简单说明,就是在本层逻辑中,爬取到的信息不进入管道进行保存。

Html html = page.getHtml();
if (page.getRequest().getUrl().endsWith("&ie=UTF-8")) {
page.setSkip(true);
...此处忽略页面解析逻辑
}
} else if (page.getRequest().getUrl().contains("&pn=")) {
String eqid = StringUtils.substringBetween(page.getHtml().toString(), "bds.comm.eqid = \"", "\";");
...此处忽略页面解析逻辑
page.putField("test",需要保存的内容)
}




这段代码中由于有setSkip的设置,以"&ie=UTF-8"结尾的请求就不需要进行保存,而请求地址中包含"&pn="字样的请求则需要保存。这样的好处就是可以减少一些不必要的资源开销,也能在一定程度上防止程序抛出一些莫名其妙的异常。

信息光是爬取下来并没有多大的价值,只有把爬取到的细信息保存起来信息才能被真正利用起来。webmagic则是通过管道的功能,将爬取到的信息进行保存。框架本身提供了到输出控制台和到文件中两种保存方式。但大多数情况下,爬取下来的内容还是需要输出到数据库,这样的功能还是需要自己定制一个专门的pipeline。

说到现在,还剩最后一个部分,就是调度器。它主要的作用是负责爬取流程的管理。框架本身默认实现QueueScheduler的调度方法,而该方法又是继承了DuplicateRemovedScheduler类,前者是通过阻塞队列的方式保证请求一进一出不会乱,而后者则是相当于Set集合的功能,对队列中的请求进行去重。

。。。至此,webmagic的主要流程及功能部件就讲的差不多了,但是:

作为初级爬虫开发来讲,自己主要需要写的内容,就是解析器的部分。其他的下载器,调度器和管道多数情况下都可以使用框架所提供的。但随着需要爬取的内容和业务逻辑越来越复杂,就需要自己定制这几方面的功能。

最后,建议在github上找一些基于webmagic开发的开源项目,加一些爬虫爱好者的群,不断借鉴,不断交流,才能不断的成长。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息