地址
gitee地址,GitHub网速较慢,没有上传到GitHub
项目介绍
一个简单的多线程爬虫,具有断点爬取功能,以及自动推测网页编码功能
使用jdk11+,maven构建,我使用jdk11.0.2时会报错,见https://bugs.openjdk.java.net/browse/JDK-8213202,jdk8却没有,jdk本身的bug,所以我换了最新版的jdk11.0.4
解析网页主要使用了Jsoup和正则表达式,所以这两个需要会用
思路
先有一个或多个具体的初始链接,假如说把这些初始链接的页面类型记为seed
定义一个规则,规则主要由Jsoup选择器(类似于css选择器)、正则表达式、页面类型type组成,选择器一般定位到a标签。seed页面通过应用规则就可以自动获取标签中符合正则表达式的链接,并将这些链接存储到h2中,这些链接就是下一层要爬取的链接,假如将这些链接所代表的页面记为page1(type设置成了page1)
重复上一步,也是定义规则,唯一的区别就是应用这些规则的页面不是seed,而是page1页面。也就是说这些规则是有顺序的,除了第一个规则应用于seed页面之外,其余的每一个规则都是应用于上一个规则获取到的页面,以此类推,多少层都可以
结合示例查看源代码能更快的理解
示例及说明
ZongHeng zh = new ZongHeng();
zh.setCacheDirectory("f:/spider");
zh.config().setInterval(50).setBreakPoint(false);
//添加一个初始链接
zh.addSeed("http://www.zongheng.com/");
//添加一个规则,按照这个规则在初始链接页面取链接,并将取到的链接代表的页面记为category
zh.addRule("ul#data-groupdom li a", "http://www.zongheng.com/category/\\d+.html", "category");
//添加规则,在category页面取链接
zh.addRegex("http://book.zongheng.com/book/\\d+.html", "book");
zh.start();
每一个具体的爬虫都继承自Spider类,上面的代码就定义了一个简单的爬虫。
- 第2行:设置缓存目录f:/spider
- 第3行:提交任务间隔最大50毫秒,关闭断点爬取
- 第4行:添加一个初始链接
- 第5行:添加一个规则,按照这个规则在初始链接页面取链接,并将取到的链接代表的页面记为category
- 第6行:添加一个规则,按照这个规则在category页面取链接,并将取到的链接代表的页面记为category
- 第7行:启动爬虫
启动后爬虫就能自己去获取符合规则的网页,但获取网页不是我们的最终目的,我们的最终目的是从网页中提取自己需要的信息,所以继承Spider后还有一个必须重写的方法
@Override
public void parse(Page page) {
if (!page.isSeed()) {
String text = page.urlText();
System.out.println(text + "---" + page.link() + "---" + page.prevLink());
}
if (page.typeEquals("category")) {
//category页面取得的下层链接的标签的文本去掉空白后长度大于1
page.setNextLinksFilter(e -> e.hasText() && e.text().replaceAll("\\s+", "").length() > 1);
}
}
解析主要就是应用Jsoup里面的方法,通过page.getDoc()方法可以获取org.jsoup.nodes.Document对象,或者直接调用page.select(String cssQuery)方法解析,这就不多说了。
要说一下就是Page里面的3个高阶函数,所谓高阶函数就是接收函数作为参数的函数,这3个函数分别是setNextLinksFilter,setNextInfoFunc,setNextLinkUnary,作用分别是筛选符合条件的链接作为下层链接,携带信息到下一层,链接映射。
上面的代码就用到了筛选的方法,传入的函数是这样的:函数的参数是org.jsoup.nodes.Element类型的对象,一般代表a标签,函数返回boolean类型,返回值为true的Element才能作为下一层链接的提供者。
手动获取下层链接
上面说的是自动获取下层链接,即只需要通过规则爬虫就能一层一层的获取链接,但有时候这样可能行不通,需要手动的获取链接然后添加到下一层,怎么做呢?
看一下这个例子,这其实完全可以自动获取下层链接,为了演示改成了手动获取下层链接,addByHand(String type)方法看源码知道这也是一个规则,只不过是一个只有type属性的规则而已。
//添加一个初始链接
zh.addSeed("http://www.zongheng.com/");
zh.addByHand("category");
可以看到初始链接后面紧跟一个手动添加规则,记为category
if (page.isSeed()) {
//这样还是用规则获取的下层链接,所以这没必要用手动,这里仅作为示例
page.addNext(Rule.createRule("ul#data-groupdom li a", "http://www.zongheng.com/category/\\d+.html"));
}
然后在seed页面取链接(这些链接所表示的页面就是category页面)加入到下一层,这就是手动添加了
局限
由于自动缓存的原因,编写好规则并爬取后,如果再次修改规则,如加入筛选条件等,由于从缓存中取数据,此时筛选条件对已经爬取的网页不起作用,要使它起作用,请删除缓存
目前只支持get请求或能转换成get请求的链接
感谢
- elves:参考了该项目思想
- WebCollector:参考了该项目思想
- Jsoup:用于下载和解析网页
- h2 database:提供缓存
- DbUtils:操作数据库