# catclaw **Repository Path**: xiaozheng123/catclaw ## Basic Information - **Project Name**: catclaw - **Description**: 一个可断点爬取的简单多线程爬虫 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2021-01-15 - **Last Updated**: 2021-01-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 项目介绍 1. 一个简单的多线程爬虫,具有断点爬取功能,以及自动推测网页编码功能 2. 使用jdk11+,maven构建,我使用jdk11.0.2时会报错,见[https://bugs.openjdk.java.net/browse/JDK-8213202](https://bugs.openjdk.java.net/browse/JDK-8213202),jdk8却没有,jdk本身的bug,所以我换了最新版的jdk11.0.4 3. 解析网页主要使用了Jsoup和正则表达式,所以这两个需要会用 ## 思路 1. 先有一个或多个具体的初始链接,假如说把这些初始链接的页面类型记为seed 2. 定义一个规则,规则主要由Jsoup选择器(类似于css选择器)、正则表达式、页面类型type组成,选择器一般定位到a标签。seed页面通过应用规则就可以自动获取标签中符合正则表达式的链接,并将这些链接存储到h2中,这些链接就是下一层要爬取的链接,假如将这些链接所代表的页面记为page1(type设置成了page1) 3. 重复上一步,也是定义规则,唯一的区别就是应用这些规则的页面不是seed,而是page1页面。也就是说这些规则是有顺序的,除了第一个规则应用于seed页面之外,其余的每一个规则都是应用于上一个规则获取到的页面,以此类推,多少层都可以 4. 结合示例查看源代码能更快的理解 ## 示例及说明 ``` java 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类,上面的代码就定义了一个简单的爬虫。 1. 第2行:设置缓存目录f:/spider 2. 第3行:提交任务间隔最大50毫秒,关闭断点爬取 3. 第4行:添加一个初始链接 4. 第5行:添加一个规则,按照这个规则在初始链接页面取链接,并将取到的链接代表的页面记为category 5. 第6行:添加一个规则,按照这个规则在category页面取链接,并将取到的链接代表的页面记为book 6. 第7行:启动爬虫 启动后爬虫就能自己去获取符合规则的网页,但获取网页不是我们的最终目的,我们的最终目的是从网页中提取自己需要的信息,所以继承Spider后还有一个必须重写的方法 ``` java @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才能作为下一层链接的提供者。 ## 手动获取下层链接 上面说的是自动获取下层链接,即只需要通过规则爬虫就能一层一层的获取链接,但有时候这样可能行不通,需要手动的获取链接然后添加到下一层,怎么做呢? 看一下这个[例子](https://gitee.com/ospdz/catclaw/blob/master/src/main/java/top/zheng/example/ZongHeng1.java),这其实完全可以自动获取下层链接,为了演示改成了手动获取下层链接,addByHand(String type)方法看源码知道这也是一个规则,只不过是一个只有type属性的规则而已。 ``` java //添加一个初始链接 zh.addSeed("http://www.zongheng.com/"); zh.addByHand("category"); ``` 可以看到初始链接后面紧跟一个手动添加规则,记为category ``` java if (page.isSeed()) { //这样还是用规则获取的下层链接,所以这没必要用手动,这里仅作为示例 page.addNext(Rule.createRule("ul#data-groupdom li a", "http://www.zongheng.com/category/\\d+.html")); } ``` 然后在seed页面取链接(这些链接所表示的页面就是category页面)加入到下一层,这就是手动添加了 ## 局限 * 目前只支持get请求或能转换成get请求的链接 ## 感谢 * [elves](https://github.com/biezhi/elves):参考了该项目思想 * [WebCollector](https://gitee.com/webcollector/WebCollector):参考了该项目思想 * [Jsoup](https://jsoup.org/):用于下载和解析网页 * [h2 database](http://h2database.com/html/main.html):提供缓存 * [DbUtils](https://commons.apache.org/proper/commons-dbutils/):操作数据库