WebCollector爬取百度搜索引擎例子

使用WebCollector来爬取百度搜索引擎按照关键字搜索的结果页面,解析规则可能会随百度搜索的改版而失效。

代码如下:

package com.wjd.baidukey.crawler;

import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.TimeZone;

import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import cn.edu.hfut.dmic.contentextractor.ContentExtractor;
import cn.edu.hfut.dmic.webcollector.model.CrawlDatum;
import cn.edu.hfut.dmic.webcollector.model.CrawlDatums;
import cn.edu.hfut.dmic.webcollector.model.Page;
import cn.edu.hfut.dmic.webcollector.plugin.ram.RamCrawler;

public class BdiduKeywordCrawler extends RamCrawler{

	private Connection connection;  
	private PreparedStatement pstatement; 
	// 连接MySql数据库,用户名root,密码mahao
	String url = "jdbc:mysql://localhost:3306/wjd";
	String username = "root";
	String password = "mahao";
	//保存抽取到的数据
	StringBuilder result = new StringBuilder();
	public BdiduKeywordCrawler(String keyword, int maxPageNum) throws Exception {
		for (int pageNum = 1; pageNum <= maxPageNum; pageNum++) {
			String url = createUrl(keyword, pageNum);
			CrawlDatum datum = new CrawlDatum(url)
					.putMetaData("keyword", keyword)
					.putMetaData("pageNum", pageNum + "")
					.putMetaData("pageType", "searchEngine")
					.putMetaData("depth", "1");
			addSeed(datum);
		}
	}
	@Override
	public void visit(Page page, CrawlDatums next) {
		String keyword = page.getMetaData("keyword");
		String pageType = page.getMetaData("pageType");
		int depth = Integer.valueOf(page.getMetaData("depth"));
		if (pageType.equals("searchEngine")) {
			int pageNum = Integer.valueOf(page.getMetaData("pageNum"));
			System.out.println("成功抓取关键词" + keyword + "的第" + pageNum + "页搜索结果");
			// || div[class=result-op c-container xpath-log ]>h3>a
			Elements results = page.select("div[class=result c-container ]>h3>a");
		//	Elements results1 = page.select("div[class=result-op c-container xpath-log]>h3>a");//,div[id=result-op c-container xpath-log]>h3>a
			//System.out.println(results1.get(0));
			//results.add(results1.get(0));
			for (int rank = 0; rank < results.size(); rank++) {
				Element result = results.get(rank);
				/*
				 * 我们希望继续爬取每条搜索结果指向的网页,这里统称为外链。
				 * 我们希望在访问外链时仍然能够知道外链处于搜索引擎的第几页、第几条,
				 * 所以将页号和排序信息放入后续的CrawlDatum中,为了能够区分外链和
				 * 搜索引擎结果页面,我们将其pageType设置为outlink,这里的值完全由 用户定义,可以设置一个任意的值
				 * 在经典爬虫中,每个网页都有一个refer信息,表示当前网页的链接来源。
				 * 例如我们首先访问新浪首页,然后从新浪首页中解析出了新的新闻链接,
				 * 则这些网页的refer值都是新浪首页。WebCollector不直接保存refer值,
				 * 但我们可以通过下面的方式,将refer信息保存在metaData中,达到同样的效果。
				 * 经典爬虫中锚文本的存储也可以通过下面方式实现。
				 * 在一些需求中,希望得到当前页面在遍历树中的深度,利用metaData很容易实现
				 * 这个功能,在将CrawlDatum添加到next中时,将其depth设置为当前访问页面 的depth+1即可。
				 */
				CrawlDatum datum = new CrawlDatum(result.attr("abs:href"))
						.putMetaData("keyword", keyword)
						.putMetaData("pageNum", pageNum + "")
						.putMetaData("rank", rank + "")
						.putMetaData("pageType", "outlink")
						.putMetaData("depth", (depth + 1) + "")
						.putMetaData("refer", page.getUrl());
				next.add(datum);
			}
		} else if (pageType.equals("outlink")) {
			/*int pageNum = Integer.valueOf(page.getMetaData("pageNum"));
			int rank = Integer.valueOf(page.getMetaData("rank"));
			String refer = page.getMetaData("refer");*/
			try {
				String content = ContentExtractor.getContentByUrl(page.getUrl());
				/*String line = String.format(
						"第%s页第%s个结果:标题:%s(%s字节)\tdepth=%s\trefer=%s", pageNum,
						rank + 1, page.getDoc().title(), content,
						depth, refer);*/
				String line = String.format("标题:%s\n来源:%s\n正文:%s", page.getDoc().title(),page.getUrl(),content);
				HashMap<String, String> data = new HashMap<String,String>();
				Date currentDate = new java.util.Date();
				SimpleDateFormat myFmt = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
				TimeZone timeZoneChina = TimeZone.getTimeZone("Asia/Shanghai");// 获取中国的时区
				myFmt.setTimeZone(timeZoneChina);// 设置系统时区
				String grabTime = myFmt.format(currentDate);// new Date()为获取当前系统时间
				data.put("title", page.getDoc().title());
				data.put("from", page.getUrl());
				data.put("content", content);
				data.put("grabTime", grabTime);
				//String line = String.format("标题:%s\n", page.getDoc().title());
				//持久化到word文档中
				//是否为线程安全???
				//synchronized(this) {
					String destFile = "D:\\"+"Result"+keyword+".doc";
					result.append(line);
					//将result写到doc文件中
					write2File(destFile,result.toString());
					//添加到数据库中
					addResultData(data);
				//}
				System.out.println(line);
			} catch (Exception e) {
				//e.printStackTrace();
				System.out.println("链接"+page.getUrl()+"失效");
			}
		}
	}
       //将数据保存到mysql数据库中
	private void addResultData(HashMap<String, String> data) {
		
		String title = data.get("title");
		String source_url = data.get("from");
		String content = data.get("content").replaceAll("\\?{2,}", "");//去掉字符串中出现的多个连续问号。
		//抓取时间
		String grabTime = data.get("grabTime");
		/*SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
		Date date = null;
		try {
			date = format.parse(grabTime);
		} catch (Exception e) {
			e.printStackTrace();
		}*/
		//System.out.println("抓取时间"+grabTime);
		try {
			connection = DriverManager.getConnection(url, username,	password);
			String sql = "INSERT INTO wjd_keyword_search_table(TITLE,GRAP_TIME,CONTENT,SOURCE_URL) VALUES(?,?,?,?)";
			String checkSql = "select 1 from wjd_keyword_search_table where TITLE='" + title + "'";
			Statement statement = connection.prepareStatement(checkSql);
			ResultSet result = statement.executeQuery(checkSql);
			if (!result.next()) {
				// 如果数据库中不存在该记录,则添加到数据库中
				pstatement = connection.prepareStatement(sql);
				pstatement.setString(1, title);
				//pstatement.setString(2, date);
				pstatement.setString(2,grabTime);
				pstatement.setString(3, content);
				pstatement.setString(4, source_url);
				pstatement.executeUpdate();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	/**
	 * 将数据持久化到本地doc文件中
	 * @param destFile
	 * @param line
	 */
	private void write2File(String destFile, String line) {
		try {
            //doc content
            ByteArrayInputStream bais = new ByteArrayInputStream(line.getBytes());
            POIFSFileSystem fs = new POIFSFileSystem();
            DirectoryEntry directory = fs.getRoot(); 
            directory.createDocument("WordDocument", bais);
            FileOutputStream ostream = new FileOutputStream(destFile);
            fs.writeFilesystem(ostream);
            bais.close();
            ostream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
	}
	public static void main(String[] args) throws Exception {
		String[] keywordsList = {"网络爬虫","搜索引擎"};
		int pageToal =5;
		for (String keyword : keywordsList) {
			BdiduKeywordCrawler crawler = new BdiduKeywordCrawler(keyword, pageToal);
			crawler.start();
		}
	}
	
	/**
	 * 根据关键词和页号拼接百度搜索对应的URL
	 */
	public static String createUrl(String keyword, int pageNum)
			throws Exception {
		int first = (pageNum-1) * 10;
		keyword = URLEncoder.encode(keyword, "utf-8");
		return String.format("https://www.baidu.com/s?wd=%s&pn=%s",
				keyword, first);
	}
}


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值