magic文档:
爬虫技术的原理在网上有很多大神有详细的讲解,我主要根据自己的学习做些总结。爬虫技术宏观上看主要分为两个部分:页面发现、页面解析
页面发现: 通过请求页面获得响应,将相应页面中的链接地址通过一定的规则(包括正则表达式、Xpath、Css选择器等等)筛选,找出自己需要的页面,并添加到一个未访问链接队列中,在处理完成页面后,从未访问队列取出链接访问后添加到一个已访问队列,通过比对两个队列中的链接保证页面访问的去重。
页面解析:页面解析通常就是通过种种工具定位到具体的标签,将所需要的内容抽取出来,可以说不同的网站的处理方式是有很大差距。
magic提供了pageProcessor接口定义获取页面的方式和参数设置
public class GetText implements PageProcessor { @Override public void process(Page page) { // 获取所有url ListurlaLL = page.getHtml() .links() // 获取页面下所有链接 .regex("http://www\\.kanunu8\\.com/(?:files|book[3-4]{1})/[^\\s]*") /* * 通过正则筛选: 只用当前网站的书籍页面加入队列 */ .all(); // 将所有符合条件的链接加入等待请求的队列 page.addTargetRequests(urlaLL); /* * 筛选元素: 通过xpath语法筛选指定标签下的元素 div/table[2]/tbody/tr/td/a/text(): * div元素下的第二个table中的tbody元素的tr-td-a标签中的文本内容 */ Selectable xpath = page.getHtml().xpath( "div/table[2]/tbody/tr/td/a/text()"); List all = xpath.all(); String dirname = null; System.out.println(page.getUrl()); //去掉了获取内容中的书名号和空格,用于生成目录 if (all != null && all.size() >= 2) { if (all.size() == 2) { dirname = all.get(0).trim() + "\\" + all.get(1).trim().replaceAll("《|》", ""); } else { dirname = all.get(1).trim() + "\\" + all.get(2).trim().replaceAll("《|》", ""); } } //通过css选择器,获取页面中的p标签内容,并将所有的html标签替换为回车,同时转换空格 List list = page.getHtml().$("p").replace("<[^>]*>", "\r\n") .replace(" ", " ").all(); String text = null; if (list != null && list.size() > 0) { text = list.get(0); } String filename = null; //css选择器获取H2标签下的font标签的文本内容 String subhead = page.getHtml().$("h2>font", "text").get(); if (subhead != null) { filename = subhead.trim(); } page.putField("dirname", dirname); page.putField("filename", filename); page.putField("text", text); if (page.getResultItems().get("dirname") == null || page.getResultItems().get("filename") == null || page.getResultItems().get("text") == null) { page.setSkip(true);//skip用于设置该页面是否需进行处理true则跳过处理 } } @Override public Site getSite() { //用于设置请求的参数,如请求间隔,重试间隔等 return Site.me().setRetryTimes(3).setSleepTime(1000); } public static void main(String[] args) { //核心类通过创建执行器开始爬虫 Spider.create(new GetText()) .addUrl("http://www.kanunu8.com/files/writer/8675.html") .thread(20) //添加了一个自定义的页面处理类 .addPipeline(new MyPipline()) .run(); }
在magic框架中将爬虫需要的功能都进行了抽取封装,使我们可以只关注爬虫逻辑即可
通过自定义的Pingline类完成页面写入本地
public class MyPipline implements Pipeline { private static String basepath = "E:\\text\\"; @Override public void process(ResultItems resultItems, Task task) { try { //ResultItems用于存储解析后的网页信息,以供后续操作 if (resultItems.isSkip()) { return; } //ResultItems本身维护了一个map可以通过封装的get方法获取值 String dir = resultItems.get("dirname"); String filename = resultItems.get("filename"); String text = resultItems.get("text"); System.out.println("作品:" + dir); // 创建文件写入路径 File dirfile = new File(basepath + dir + "\\"); if (!dirfile.isDirectory()) { dirfile.mkdirs(); } File file = new File(dirfile, filename + ".txt"); if (!file.isFile()) { file.createNewFile(); } try (FileWriter fw = new FileWriter(file, true)) { fw.write(text); } } catch (IOException e) { e.printStackTrace(); } } }
在这样的解析下回将每本书生成文件夹,每个章节生成文档。曾经希望直接将整本书整合为一个文件夹,但因为无法控制所爬虫爬取的网页顺序而作罢。
对于将爬取的数据写入单个文件的方案有两个想法:
1.增加执行序列用于多线程之间协调工作,预先爬取书籍的所有页面并生成目录,再按指定顺序完成数据写入,难点是可能出现爬取数据不全,线程无限期休眠问题
2.先写入数据库,再数据库中通过整理排列完成读取