java网络爬虫如何控制爬取的深度_Java 动手写爬虫: 二、 深度爬取

第二篇

前面实现了一个最基础的爬取单网页的爬虫,这一篇则着手解决深度爬取的问题

简单来讲,就是爬了一个网页之后,继续爬这个网页中的链接

1. 需求背景

背景比较简单和明确,当爬了一个网页之后,目标是不要就此打住,扫描这个网页中的链接,继续爬,所以有几个点需要考虑:

哪些链接可以继续爬 ?

是否要一直爬下去,要不要给一个终止符?

新的链接中,提取内容的规则和当前网页的规则不一致可以怎么办?

2. 设计

针对上面的几点,结合之前的实现结构,在执行 doFetchPage 方法获取网页之后,还得做一些其他的操作

扫描网页中的链接,根据过滤规则匹配满足要求的链接

记录一个depth,用于表示爬取的深度,即从最原始的网页出发,到当前页面中间转了几次(讲到这里就有个循环爬取的问题,后面说)

不同的页面提取内容规则不一样,因此可以考虑留一个接口出来,让适用方自己来实现解析网页内容

基本实现

开始依然是先把功能点实现,然后再考虑具体的优化细节

先加一个配置项,表示爬取页面深度; 其次就是保存的结果,得有个容器来暂存, 所以在 SimpleCrawlJob 会新增两个属性

/**

* 批量查询的结果

*/

private List crawlResults = new ArrayList<>();

/**

* 爬网页的深度, 默认为0, 即只爬取当前网页

*/

private int depth = 0;

因为有深度爬取的过程,所以需要修改一下爬取网页的代码,新增一个 doFetchNetxtPage方法,进行迭代爬取网页,这时,结果匹配处理方法也不能如之前的直接赋值了,稍微改一下即可, 改成返回一个接过实例

/**

* 执行抓取网页

*/

public void doFetchPage() throws Exception {

doFetchNextPage(0, this.crawlMeta.getUrl());

this.crawlResult = this.crawlResults.get(0);

}

private void doFetchNextPage(int currentDepth, String url) throws Exception {

HttpResponse response = HttpUtils.request(new CrawlMeta(url, this.crawlMeta.getSelectorRules()), httpConf);

String res = EntityUtils.toString(response.getEntity());

CrawlResult result;

if (response.getStatusLine().getStatusCode() != 200) { // 请求成功

result = new CrawlResult();

result.setStatus(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase());

result.setUrl(crawlMeta.getUrl());

this.crawlResults.add(result);

return;

}

result = doParse(res);

// 超过最大深度, 不继续爬

if (currentDepth > depth) {

return;

}

Elements elements = result.getHtmlDoc().select("a[href]");

for(Element element: elements) {

doFetchNextPage(currentDepth + 1, element.attr("href"));

}

}

private CrawlResult doParse(String html) {

Document doc = Jsoup.parse(html);

Map> map = new HashMap<>(crawlMeta.getSelectorRules().size());

for (String rule : crawlMeta.getSelectorRules()) {

List list = new ArrayList<>();

for (Element element : doc.select(rule)) {

list.add(element.text());

}

map.put(rule, list);

}

CrawlResult result = new CrawlResult();

result.setHtmlDoc(doc);

result.setUrl(crawlMeta.getUrl());

result.setResult(map);

result.setStatus(CrawlResult.SUCCESS);

return result;

}

说明

主要的关键代码在 doFetchNextPage 中,这里有两个参数,第一个表示当前url属于爬取的第几层,爬完之后,判断是否超过最大深度,如果没有,则获取出网页中的所有链接,迭代调用一遍

下面主要是获取网页中的跳转链接,直接从jsoup的源码中的example中获取,获取网页中链接的方法

// 未超过最大深度, 继续爬网页中的所有链接

result = doParse(res);

Elements elements = result.getHtmlDoc().select("a[href]");

for(Element element: elements) {

doFetchNextPage(currentDepth + 1, element.attr("href"));

}

测试case

测试代码和之前的差不多,唯一的区别就是指定了爬取的深度,返回结果就不截图了,实在是有点多

/**

* 深度爬

* @throws InterruptedException

*/

@Test

public void testDepthFetch() throws InterruptedException {

String url = "https://my.oschina.net/u/566591/blog/1031575";

CrawlMeta crawlMeta = new CrawlMeta();

crawlMeta.setUrl(url);

SimpleCrawlJob job = new SimpleCrawlJob(1);

job.setCrawlMeta(crawlMeta);

Thread thread = new Thread(job, "crawlerDepth-test");

thread.start();

thread.join();

List result = job.getCrawlResults();

System.out.println(result);

}

3. 改进

问题

上面虽然是实现了目标,但问题却有点多:

就比如上面的测试case,发现有122个跳转链接,顺序爬速度有点慢

59b58dcfeb20778f5c0c8c682e2b0841.png

链接中存在重复、页面内锚点、js等各种情况,并不是都满足需求

最后的结果塞到List中,深度较多时,链接较多时,list可能被撑暴

- 添加链接的过滤

过滤规则,可以划分为两种,正向的匹配,和逆向的排除

首先是修改配置类 CrawlMeta, 新增两个配置

/**

* 正向的过滤规则

*/

@Setter

@Getter

private Set positiveRegex = new HashSet<>();

/**

* 逆向的过滤规则

*/

&#

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值