最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java爬蟲實(shí)戰(zhàn)抓取一個(gè)網(wǎng)站上的全部鏈接

 更新時(shí)間:2016年10月19日 11:35:49   作者:pangfc  
這篇文章主要介紹了JAVA使用爬蟲抓取網(wǎng)站網(wǎng)頁內(nèi)容的方法,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧。

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

一 算法簡(jiǎn)介

程序在思路上采用了廣度優(yōu)先算法,對(duì)未遍歷過的鏈接逐次發(fā)起GET請(qǐng)求,然后對(duì)返回來的頁面用正則表達(dá)式進(jìn)行解析,取出其中未被發(fā)現(xiàn)的新鏈接,加入集合中,待下一次循環(huán)時(shí)遍歷。

具體實(shí)現(xiàn)上使用了Map<String, Boolean>,鍵值對(duì)分別是鏈接和是否被遍歷標(biāo)志。程序中使用了兩個(gè)Map集合,分別是:oldMap和newMap,初始的鏈接在oldMap中,然后對(duì)oldMap里面的標(biāo)志為false的鏈接發(fā)起請(qǐng)求,解析頁面,用正則取出<a>標(biāo)簽下的鏈接,如果這個(gè)鏈接未在oldMap和newMap中,則說明這是一條新的鏈接,同時(shí)要是這條鏈接是我們需要獲取的目標(biāo)網(wǎng)站的鏈接的話,我們就將這條鏈接放入newMap中,一直解析下去,等這個(gè)頁面解析完成,把oldMap中當(dāng)前頁面的那條鏈接的值設(shè)為true,表示已經(jīng)遍歷過了。

最后是當(dāng)整個(gè)oldMap未遍歷過的鏈接都遍歷結(jié)束后,如果發(fā)現(xiàn)newMap不為空,則說明這一次循環(huán)有新的鏈接產(chǎn)生,因此將這些新的鏈接加入oldMap中,繼續(xù)遞歸遍歷,反之則說明這次循環(huán)沒有產(chǎn)生新的鏈接,繼續(xù)循環(huán)下去已經(jīng)不能產(chǎn)生新鏈接了,因?yàn)槿蝿?wù)結(jié)束,返回鏈接集合oldMap

二 程序?qū)崿F(xiàn)

上面相關(guān)思路已經(jīng)說得很清楚了,并且代碼中關(guān)鍵地方有注釋,因此這里就不多說了,代碼如下:

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>(); // 存儲(chǔ)鏈接-是否被遍歷
                                      // 鍵值對(duì)
    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());
 
    }
 
  }
 
  /**
   * 抓取一個(gè)網(wǎng)站所有可以抓取的網(wǎng)頁鏈接,在思路上使用了廣度優(yōu)先算法
   * 對(duì)未遍歷過的新鏈接不斷發(fā)起GET請(qǐng)求,一直到遍歷完整個(gè)集合都沒能發(fā)現(xiàn)新的鏈接
   * 則表示不能發(fā)現(xiàn)新的鏈接了,任務(wù)結(jié)束
   * 
   * @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();
        // 發(fā)起GET請(qǐng)求
        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(); //標(biāo)題
                // 判斷獲取到的鏈接是否以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);
                //去重,并且丟棄其他網(wǎng)站的鏈接
                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);
      }
    }
    //有新鏈接,繼續(xù)遍歷
    if (!newMap.isEmpty()) {
      oldMap.putAll(newMap);
      oldMap.putAll(crawlLinks(oldLinkHost, oldMap)); //由于Map的特性,不會(huì)導(dǎo)致出現(xiàn)重復(fù)的鍵值對(duì)
    }
    return oldMap;
  }
}

三 最后的測(cè)試效果

PS:其實(shí)用遞歸這種方式不是太好,因?yàn)橐蔷W(wǎng)站頁面比較多的話,程序運(yùn)行時(shí)間長(zhǎng)了對(duì)內(nèi)存的消耗會(huì)非常大

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

相關(guān)文章

  • Spring Boot外部化配置實(shí)戰(zhàn)解析

    Spring Boot外部化配置實(shí)戰(zhàn)解析

    這篇文章主要介紹了Spring Boot外部化配置實(shí)戰(zhàn)解析,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-06-06
  • spring的data派生查詢機(jī)制的實(shí)現(xiàn)

    spring的data派生查詢機(jī)制的實(shí)現(xiàn)

    SpringData的派生查詢是一種通過方法名約定自動(dòng)生成數(shù)據(jù)庫查詢的機(jī)制,無需手動(dòng)編寫SQL或JPQL,下面就來介紹一下spring data派生查詢的實(shí)現(xiàn),感興趣的可以了解一下
    2025-03-03
  • java -jar啟動(dòng)項(xiàng)目以及日志輸出的相關(guān)問題

    java -jar啟動(dòng)項(xiàng)目以及日志輸出的相關(guān)問題

    這篇文章主要介紹了java -jar啟動(dòng)項(xiàng)目以及日志輸出的相關(guān)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • 淺談Java反射與代理

    淺談Java反射與代理

    下面小編就為大家?guī)硪黄獪\談Java反射與代理。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-07-07
  • mybatis 實(shí)現(xiàn)多條update同時(shí)執(zhí)行

    mybatis 實(shí)現(xiàn)多條update同時(shí)執(zhí)行

    這篇文章主要介紹了mybatis 實(shí)現(xiàn)多條update同時(shí)執(zhí)行,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • SpringBoot系列教程JPA之基礎(chǔ)環(huán)境搭建的方法

    SpringBoot系列教程JPA之基礎(chǔ)環(huán)境搭建的方法

    這篇文章主要介紹了SpringBoot系列教程JPA之基礎(chǔ)環(huán)境搭建的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Java之SM4加密解密的實(shí)現(xiàn)

    Java之SM4加密解密的實(shí)現(xiàn)

    這篇文章主要介紹了Java之SM4加密解密的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • maven的pom.xml中profiles的作用詳解

    maven的pom.xml中profiles的作用詳解

    這篇文章主要介紹了maven的pom.xml中profiles的作用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java單元測(cè)試Mockito的使用詳解

    Java單元測(cè)試Mockito的使用詳解

    Mockito是一個(gè)強(qiáng)大的mock工具,本文將重點(diǎn)講述Mockito的基本使用及注意事項(xiàng),以及Controller測(cè)試用例,本文通過示例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-07-07
  • IDEA啟動(dòng)報(bào)錯(cuò)Internal?error.?Please?refer?to?https://jb.gg/ide/critical-startup-errors解決辦法

    IDEA啟動(dòng)報(bào)錯(cuò)Internal?error.?Please?refer?to?https://jb.gg/i

    這篇文章主要介紹了IDEA啟動(dòng)報(bào)錯(cuò)Internal?error.?Please?refer?to?https://jb.gg/ide/critical-startup-errors解決辦法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04

最新評(píng)論

宁安市| 昆山市| 类乌齐县| 饶平县| 甘德县| 贵溪市| 延庆县| 辽源市| 西乌珠穆沁旗| 襄城县| 大田县| 潮州市| 建水县| 汉阴县| 积石山| 裕民县| 叙永县| 鄢陵县| 常州市| 阿鲁科尔沁旗| 高安市| 江陵县| 抚远县| SHOW| 巨鹿县| 兴安盟| 镇安县| 招远市| 呼和浩特市| 彭阳县| 株洲市| 浮梁县| 大同市| 九寨沟县| 新野县| 乐亭县| 贵阳市| 旌德县| 景泰县| 南岸区| 祁连县|