magic框架完成爬取文字数据

原创
2017/11/06 13:43
阅读数 315

 

magic文档:http://webmagic.io/docs/zh/   

     爬虫技术的原理在网上有很多大神有详细的讲解,我主要根据自己的学习做些总结。爬虫技术宏观上看主要分为两个部分:页面发现、页面解析

    页面发现: 通过请求页面获得响应,将相应页面中的链接地址通过一定的规则(包括正则表达式、Xpath、Css选择器等等)筛选,找出自己需要的页面,并添加到一个未访问链接队列中,在处理完成页面后,从未访问队列取出链接访问后添加到一个已访问队列,通过比对两个队列中的链接保证页面访问的去重。

    页面解析:页面解析通常就是通过种种工具定位到具体的标签,将所需要的内容抽取出来,可以说不同的网站的处理方式是有很大差距。

    magic提供了pageProcessor接口定义获取页面的方式和参数设置

public class GetText implements PageProcessor {

	@Override
	public void process(Page page) {
		// 获取所有url
		List<String> urlaLL = 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<String> 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<String> list = page.getHtml().$("p").replace("<[^>]*>", "\r\n")
				.replace("&nbsp;", " ").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.先写入数据库,再数据库中通过整理排列完成读取

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部