日期:2014-05-16  浏览次数:20362 次

JSOUP实现简单爬虫

转截请注明出处: fair-jm.iteye.com

?

额 有段时间不写文了

这个说是简单爬虫 其实连个爬虫也算不上吧 功能太精简了...

?

流程很简单: 输入几个初始的网页 然后通过JSOUP获取网页中的a标签的href的值

接着把新得到的地址放入任务队列中

?

实现中的worker是一个单线程的派发器 用于产生Parser

Parser用于完成网页的保存 网页的解析 以及入队列操作

?

内容很简单 也没有使用数据库

任务队列直接用了一个Queue

已完成地址和正在处理的地址的保存用了List

具体代码如下:

package com.cc.crawler.infrastructure;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class Worker {

	//保存地址 这里保存在G盘的html文件夹内
	public static final String SAVED_FOLDER = "G:\\html\\";

	private BlockingQueue<String> taskQueue = new LinkedBlockingQueue<String>(
			10000); // 任务队列 最大100000
	private List<String> finished = Collections
			.synchronizedList(new ArrayList<String>()); // 存放已经完成处理的地址的列表
	private List<String> processing = Collections
			.synchronizedList(new ArrayList<String>()); // 存放正在处理中的地址的列表

	private ExecutorService savedExector = Executors.newFixedThreadPool(100); // 100个文件保存队列
	private ExecutorService parserExector = Executors.newFixedThreadPool(100); // 最大100的线程池
																				// 用来做解析工作

	private volatile boolean stop = false;

	public Worker() {
	}

	public void addStartAddress(String address) {
		try {
			taskQueue.put(address); // 使用阻塞的put方式
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 启动 这边是一个单线程的派发任务 内容很简单 不断地从任务队列里取值 判断是否处理过 没有的话就处理
	 */
	public void start() {
		while (!stop) {
			String task;
			try {
				task = taskQueue.take();
				if (filter(task)) { // 这边是过路任务的 过滤条件自己写
					continue;
				}
				// System.out.println("start():"+task);
				
				processing.add(task); // 正在处理的任务
				parserExector.execute(new Parser(task));
				

			} catch (InterruptedException e) {
				e.printStackTrace();
			}

		}
		// 立即关闭写和读的任务
		parserExector.shutdownNow();
		savedExector.shutdownNow();

	}

	public void stop() {
		stop = true;
	}

	/**
	 * 
	 * @param task 是否过滤的网址
	 * @return true 表示过滤     false 表示不过滤
	 */
	public boolean filter(String task) {

		
		if (finished.contains(task) || processing.contains(task)) {
			return true;
		}

		if (finished.contains(task + "/") || processing.contains(task + "/")) {
			return true;
		}
		
		
		if(task.contains("#")){
			String uri=task.substring(0,task.indexOf("#"));
			if (finished.contains(uri) || processing.contains(uri)) {
				return true;
			}
		}
		
		return false;
		

		// else {
		// int in = task.indexOf("?");
		// if (in > 0)
		// contains = finished.contains(task.substring(0, in));
		// }
	}

	/**
	 * 进行解析的工具
	 * 
	 * @author cc fair-jm
	 * 
	 */
	class Parser implements Runnable {

		private final String url;

		public Parser(String url) {
			if (!url.toLowerCase().startsWith("http")) {
				url = "http://" + url;
			}
			this.url = url;
		}

		@Override
		public void run() {
			try {
				Document doc = Jsoup.connect(url).get();
				String uri = doc.baseUri();

				try {
					savedExector.execute(new Saver(doc.html(), uri)); // 先进行存储
				} catch (RejectedExecutionException ex) { // 产生了这个异常说明保存线程池已经关掉了
															// 那么后续的工作就不要做了
															// 这边可以再保存一下状态
					return;
				}

				Elements es = doc.select("a[href]");
				for (Element e : es) {
					String href = e.attr("href");
					// System.out.println("worker run():"+href);

					if (href.length() > 1) {
						if (href.star