国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

Java爬蟲實戰抓取一個網站上的全部鏈接

2019-11-26 13:41:43
字體:
來源:轉載
供稿:網友

前言:寫這篇文章之前,主要是我看了幾篇類似的爬蟲寫法,有的是用的隊列來寫,感覺不是很直觀,還有的只有一個請求然后進行頁面解析,根本就沒有自動爬起來這也叫爬蟲?因此我結合自己的思路寫了一下簡單的爬蟲。

一 算法簡介

程序在思路上采用了廣度優先算法,對未遍歷過的鏈接逐次發起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:其實用遞歸這種方式不是太好,因為要是網站頁面比較多的話,程序運行時間長了對內存的消耗會非常大

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 如东县| 江门市| 罗定市| 沿河| 梁山县| 山东省| 西乡县| 友谊县| 得荣县| 阿拉尔市| 和龙市| 潮安县| 读书| 阿合奇县| 静海县| 凤冈县| 平南县| 开原市| 桦甸市| 卫辉市| 沽源县| 麟游县| 法库县| 尉氏县| 汕头市| 滕州市| 科技| 滦南县| 且末县| 黑河市| 潼关县| 江川县| 湾仔区| 扶余县| 临汾市| 昭通市| 锡林浩特市| 西乌珠穆沁旗| 衡水市| 富源县| 杭锦旗|