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:其適用遞歸這類方法不是太好,由於如果網站頁面比擬多的話,法式運轉時光長了對內存的消費會異常年夜
感激浏覽,願望能贊助到年夜家,感謝年夜家對本站的支撐!