Java爬蟲實戰抓取一個網站上的全體鏈接。本站提示廣大學習愛好者:(Java爬蟲實戰抓取一個網站上的全體鏈接)文章只能為提供參考,不一定能成為您想要的結果。以下是Java爬蟲實戰抓取一個網站上的全體鏈接正文
媒介:寫這篇文章之前,重要是我看了幾篇相似的爬蟲寫法,有的是用的隊列來寫,感到不是很直不雅,還有的只要一個要求然落後行頁面解析,基本就沒有主動爬起來這也叫爬蟲?是以我聯合本身的思緒寫了一下簡略的爬蟲。
一 算法簡介
法式在思緒上采取了廣度優先算法,對未遍歷過的鏈接逐次提議GET要求,然後對前往來的頁面用正則表達式停止解析,掏出個中未被發明的新鏈接,參加聚集中,待下一次輪回時遍歷。
詳細完成上應用了Map<String, Boolean>,鍵值對分離是鏈接和能否被遍歷標記。法式中應用了兩個Map聚集,分離是:oldMap和newMap,初始的鏈接在oldMap中,然後對oldMap外面的標記為false的鏈接提議要求,解析頁面,用正則掏出<a>標簽下的鏈接,假如這個鏈接未在oldMap和newMap中,則解釋這是一條新的鏈接,同時如果這條鏈接是我們須要獲得的目的網站的鏈接的話,我們就將這條鏈接放入newMap中,一向解析下去,等這個頁面解析完成,把oldMap中以後頁面的那條鏈接的值設為true,表現曾經遍歷過了。
最初是當全部oldMap未遍歷過的鏈接都遍歷停止後,假如發明newMap不為空,則解釋這一次輪回有新的鏈接發生,是以將這些新的鏈接參加oldMap中,持續遞歸遍歷,反之則解釋此次輪回沒有發生新的鏈接,持續輪回下去曾經不克不及發生新鏈接了,由於義務停止,前往鏈接聚集oldMap
二 法式完成
下面相干思緒曾經說得很清晰了,而且代碼中症結處所有正文,是以這裡就不多說了,代碼以下:
package action;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class WebCrawlerDemo {
public static void main(String[] args) {
WebCrawlerDemo webCrawlerDemo = new WebCrawlerDemo();
webCrawlerDemo.myPrint("http://www.zifangsky.cn");
}
public void myPrint(String baseUrl) {
Map<String, Boolean> oldMap = new LinkedHashMap<String, Boolean>(); // 存儲鏈接-能否被遍歷
// 鍵值對
String oldLinkHost = ""; //host
Pattern p = Pattern.compile("(https?://)?[^/\\s]*"); //好比:http://www.zifangsky.cn
Matcher m = p.matcher(baseUrl);
if (m.find()) {
oldLinkHost = m.group();
}
oldMap.put(baseUrl, false);
oldMap = crawlLinks(oldLinkHost, oldMap);
for (Map.Entry<String, Boolean> mapping : oldMap.entrySet()) {
System.out.println("鏈接:" + mapping.getKey());
}
}
/**
* 抓取一個網站一切可以抓取的網頁鏈接,在思緒上應用了廣度優先算法
* 對未遍歷過的新鏈接赓續提議GET要求,一向到遍歷完全個聚集都沒能發明新的鏈接
* 則表現不克不及發明新的鏈接了,義務停止
*
* @param oldLinkHost 域名,如:http://www.zifangsky.cn
* @param oldMap 待遍歷的鏈接聚集
*
* @return 前往一切抓取到的鏈接聚集
* */
private Map<String, Boolean> crawlLinks(String oldLinkHost,
Map<String, Boolean> oldMap) {
Map<String, Boolean> newMap = new LinkedHashMap<String, Boolean>();
String oldLink = "";
for (Map.Entry<String, Boolean> mapping : oldMap.entrySet()) {
System.out.println("link:" + mapping.getKey() + "--------check:"
+ mapping.getValue());
// 假如沒有被遍歷過
if (!mapping.getValue()) {
oldLink = mapping.getKey();
// 提議GET要求
try {
URL url = new URL(oldLink);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(2000);
connection.setReadTimeout(2000);
if (connection.getResponseCode() == 200) {
InputStream inputStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(
new InputStreamReader(inputStream, "UTF-8"));
String line = "";
Pattern pattern = Pattern
.compile("<a.*?href=[\"']?((https?://)?/?[^\"']+)[\"']?.*?>(.+)</a>");
Matcher matcher = null;
while ((line = reader.readLine()) != null) {
matcher = pattern.matcher(line);
if (matcher.find()) {
String newLink = matcher.group(1).trim(); // 鏈接
// String title = matcher.group(3).trim(); //題目
// 斷定獲得到的鏈接能否以http開首
if (!newLink.startsWith("http")) {
if (newLink.startsWith("/"))
newLink = oldLinkHost + newLink;
else
newLink = oldLinkHost + "/" + newLink;
}
//去除鏈接末尾的 /
if(newLink.endsWith("/"))
newLink = newLink.substring(0, newLink.length() - 1);
//去重,而且拋棄其他網站的鏈接
if (!oldMap.containsKey(newLink)
&& !newMap.containsKey(newLink)
&& newLink.startsWith(oldLinkHost)) {
// System.out.println("temp2: " + newLink);
newMap.put(newLink, false);
}
}
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
oldMap.replace(oldLink, false, true);
}
}
//有新鏈接,持續遍歷
if (!newMap.isEmpty()) {
oldMap.putAll(newMap);
oldMap.putAll(crawlLinks(oldLinkHost, oldMap)); //因為Map的特征,不會招致湧現反復的鍵值對
}
return oldMap;
}
}
三 最初的測試後果
PS:其適用遞歸這類方法不是太好,由於如果網站頁面比擬多的話,法式運轉時光長了對內存的消費會異常年夜
感激浏覽,願望能贊助到年夜家,感謝年夜家對本站的支撐!