前言:寫這篇文章之前,主要是我看了幾篇類似的爬蟲寫法,有的是用的隊列來寫,感覺不是很直觀,還有的只有一個請求然后進行頁面解析,根本就沒有自動爬起來這也叫爬蟲?因此我結合自己的思路寫了一下簡單的爬蟲。
一 算法簡介
程序在思路上采用了廣度優先算法,對未遍歷過的鏈接逐次發起GET請求,然后對返回來的頁面用正則表達式進行解析,取出其中未被發現的新鏈接,加入集合中,待下一次循環時遍歷。
具體實現上使用了Map<String, Boolean>,鍵值對分別是鏈接和是否被遍歷標志。程序中使用了兩個Map集合,分別是:oldMap和newMap,初始的鏈接在oldMap中,然后對oldMap里面的標志為false的鏈接發起請求,解析頁面,用正則取出<a>標簽下的鏈接,如果這個鏈接未在oldMap和newMap中,則說明這是一條新的鏈接,同時要是這條鏈接是我們需要獲取的目標網站的鏈接的話,我們就將這條鏈接放入newMap中,一直解析下去,等這個頁面解析完成,把oldMap中當前頁面的那條鏈接的值設為true,表示已經遍歷過了。
最后是當整個oldMap未遍歷過的鏈接都遍歷結束后,如果發現newMap不為空,則說明這一次循環有新的鏈接產生,因此將這些新的鏈接加入oldMap中,繼續遞歸遍歷,反之則說明這次循環沒有產生新的鏈接,繼續循環下去已經不能產生新鏈接了,因為任務結束,返回鏈接集合oldMap
二 程序實現
上面相關思路已經說得很清楚了,并且代碼中關鍵地方有注釋,因此這里就不多說了,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
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 id="codetool">
三 最后的測試效果
PS:其實用遞歸這種方式不是太好,因為要是網站頁面比較多的話,程序運行時間長了對內存的消耗會非常大 感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持! 延伸 · 閱讀
精彩推薦
|