一个简易的Java多页面队列爬虫程序
之前写过很多单页面python爬虫,感觉python还是很好用的,这里用java总结一个多页面的爬虫,迭代爬取种子页面的所有链接的页面,全部保存在tmp路径下。
一、 序言
实现这个爬虫需要两个数据结构支持,unvisited队列(priorityqueue:可以适用pagerank等算法计算出url重要度)和visited表(hashset:可以快速查找url是否存在);队列用于实现宽度优先爬取,visited表用于记录爬取过的url,不再重复爬取,避免了环。java爬虫需要的工具包有httpclient和htmlparser1.5,可以在maven repo中查看具体版本的下载。
1、目标网站:新浪 http://www.sina.com.cn/
2、结果截图:
下面说说爬虫的实现,后期源码会上传到github中,需要的朋友可以留言:
二、爬虫编程
1、创建种子页面的url
mycrawler crawler = new mycrawler();
crawler.crawling(new string[]{"http://www.sina.com.cn/"});
2、初始化unvisited表为上面的种子url
linkqueue.addunvisitedurl(seeds[i]);
3、最主要的逻辑实现部分:在队列中取出没有visit过的url,进行下载,然后加入visited的表,并解析改url页面上的其它url,把未读取的加入到unvisited队列;迭代到队列为空停止,所以这个url网络还是很庞大的。注意,这里的页面下载和页面解析需要java的工具包实现,下面具体说明下工具包的使用。
while(!linkqueue.unvisitedurlsempty()&&linkqueue.getvisitedurlnum()<=1000) { //队头url出队列 string visiturl=(string)linkqueue.unvisitedurldequeue(); if(visiturl==null) continue; downloadfile downloader=new downloadfile(); //下载网页 downloader.downloadfile(visiturl); //该 url 放入到已访问的 url 中 linkqueue.addvisitedurl(visiturl); //提取出下载网页中的 url set<string> links=htmlparsertool.extraclinks(visiturl,filter); //新的未访问的 url 入队 for(string link:links) { linkqueue.addunvisitedurl(link); } }
4、下面html页面的download工具包
public string downloadfile(string url) { string filepath = null; /* 1.生成 httpclinet 对象并设置参数 */ httpclient httpclient = new httpclient(); // 设置 http 连接超时 5s httpclient.gethttpconnectionmanager().getparams().setconnectiontimeout( 5000); /* 2.生成 getmethod 对象并设置参数 */ getmethod getmethod = new getmethod(url); // 设置 get 请求超时 5s getmethod.getparams().setparameter(httpmethodparams.so_timeout, 5000); // 设置请求重试处理 getmethod.getparams().setparameter(httpmethodparams.retry_handler, new defaulthttpmethodretryhandler()); /* 3.执行 http get 请求 */ try { int statuscode = httpclient.executemethod(getmethod); // 判断访问的状态码 if (statuscode != httpstatus.sc_ok) { system.err.println("method failed: " + getmethod.getstatusline()); filepath = null; } /* 4.处理 http 响应内容 */ byte[] responsebody = getmethod.getresponsebody();// 读取为字节数组 // 根据网页 url 生成保存时的文件名 filepath = "temp\\" + getfilenamebyurl(url, getmethod.getresponseheader( "content-type").getvalue()); savetolocal(responsebody, filepath); } catch (httpexception e) { // 发生致命的异常,可能是协议不对或者返回的内容有问题 system.out.println("please check your provided http address!"); e.printstacktrace(); } catch (ioexception e) { // 发生网络异常 e.printstacktrace(); } finally { // 释放连接 getmethod.releaseconnection(); } return filepath; }
5、html页面的解析工具包:
public static set<string> extraclinks(string url, linkfilter filter) { set<string> links = new hashset<string>(); try { parser parser = new parser(url); parser.setencoding("gb2312"); // 过滤 <frame >标签的 filter,用来提取 frame 标签里的 src 属性所表示的链接 nodefilter framefilter = new nodefilter() { public boolean accept(node node) { if (node.gettext().startswith("frame src=")) { return true; } else { return false; } } }; // orfilter 来设置过滤 <a> 标签,和 <frame> 标签 orfilter linkfilter = new orfilter(new nodeclassfilter( linktag.class), framefilter); // 得到所有经过过滤的标签 nodelist list = parser.extractallnodesthatmatch(linkfilter); for (int i = 0; i < list.size(); i++) { node tag = list.elementat(i); if (tag instanceof linktag)// <a> 标签 { linktag link = (linktag) tag; string linkurl = link.getlink();// url if (filter.accept(linkurl)) links.add(linkurl); } else// <frame> 标签 { // 提取 frame 里 src 属性的链接如 <frame src="test.html"/> string frame = tag.gettext(); int start = frame.indexof("src="); frame = frame.substring(start); int end = frame.indexof(" "); if (end == -1) end = frame.indexof(">"); string frameurl = frame.substring(5, end - 1); if (filter.accept(frameurl)) links.add(frameurl); } } } catch (parserexception e) { e.printstacktrace(); } return links; }
6、未访问页面使用priorityqueue带偏好的队列保存,主要是为了适用于pagerank等算法,有的url忠诚度更高一些;visited表采用hashset实现,注意可以快速查找是否存在;
public class linkqueue { //已访问的 url 集合 private static set visitedurl = new hashset(); //待访问的 url 集合 private static queue unvisitedurl = new priorityqueue(); //获得url队列 public static queue getunvisitedurl() { return unvisitedurl; } //添加到访问过的url队列中 public static void addvisitedurl(string url) { visitedurl.add(url); } //移除访问过的url public static void removevisitedurl(string url) { visitedurl.remove(url); } //未访问的url出队列 public static object unvisitedurldequeue() { return unvisitedurl.poll(); } // 保证每个 url 只被访问一次 public static void addunvisitedurl(string url) { if (url != null && !url.trim().equals("") && !visitedurl.contains(url) && !unvisitedurl.contains(url)) unvisitedurl.add(url); } //获得已经访问的url数目 public static int getvisitedurlnum() { return visitedurl.size(); } //判断未访问的url队列中是否为空 public static boolean unvisitedurlsempty() { return unvisitedurl.isempty(); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。