poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标。如果对课程感兴趣,请大家咨询qq:908821478,咨询电话010-84505200。
一. 设计思路
(1)一个收集所需网页全站或者指定子域名的链接队列
(2)一个存放将要访问的URL队列(跟上述有点重复, 用空间换时间, 提升爬取速度)
(3)一个保存已访问过URL的数据结构
数据结构有了, 接下来就是算法了, 一般推荐采取广度优先的爬取算法, 免得中了反爬虫的某些循环无限深度的陷阱。
使用了 jsoup (一个解析HTML元素的Lib)和 httpclient (网络请求包)来简化代码实现。
二. 代码实现
上述三种数据结构:
// 已爬取URL <URL, isAccess>
final static ConcurrentHashMap<String, Boolean> urlQueue = new ConcurrentHashMap<String, Boolean>();
// 待爬取URL
final static ConcurrentLinkedDeque<String> urlWaitingQueue = new ConcurrentLinkedDeque<String>();
// 待扫描网页URL队列
final static ConcurrentLinkedDeque<String> urlWaitingScanQueue = new ConcurrentLinkedDeque<String>();
入队等待:
/**
* url store in the waiting queue
* @param originalUrl
* @throws Exception
*/
private static void enterWaitingQueue(final String originalUrl) throws Exception{
String url = urlWaitingScanQueue.poll();
// if accessed, ignore the url
/*while (urlQueue.containsKey(url)) {
url = urlWaitingQueue.poll();
}*/
final String finalUrl = url;
Thread.sleep(600);
new Thread(new Runnable() {
public void run() {
try{
if (finalUrl != null) {
Connection conn = Jsoup.connect(finalUrl);
Document doc = conn.get();
//urlQueue.putIfAbsent(finalUrl, Boolean.TRUE); // accessed
logger.info("扫描网页URL: " + finalUrl);
Elements links = doc.select("a[href]");
for (int linkNum = 0; linkNum < links.size(); linkNum++) {
Element element = links.get(linkNum);
String suburl = element.attr("href");
// 某条件下, 并且原来没访问过
if (!urlQueue.containsKey(suburl)) {
urlWaitingScanQueue.offer(suburl);
urlWaitingQueue.offer(suburl);
logger.info("URL入队等待" + linkNum + ": " + suburl);
}
}
}
}
} catch (Exception ee) {
logger.error("muti thread executing error, url: " + finalUrl, ee);
}
}
}).start();
}