天天看點

爬山的蝸牛旅程:爬蟲 Jsoup+(HtmlUnit或HttpClient)實作具體實作

學習的旅程,就像蝸牛爬山,一點點的往上爬,一點點的欣賞旅途的風景

Jsoup:解析和操作Html的技術(将html解析成document),通過操作document節點來解析元素屬性和文本的技術(類似jQuery)

  • 1 Jsoup自身自持通過url請求靜态html的
  • 2 Jsoup不支援解析執行js
  • 3 Jsoup有類似(jQuery操作document節點的)–此處網上很多教程

HttpClient:用于請求靜态html的技術(擷取請求傳回的最原始的html)–不執行js

HtmlUnit:類似一個模拟浏覽器的技術(通過模拟浏覽器請求url,可觸發請求傳回的html所包含的js腳本)

具體實作

  • 第一步:pom.mxl配置
    <!-- 爬蟲 -->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.10.2</version>
        </dependency>
        <!-- 爬蟲 适用于包含動态js的html請求 -->
        <dependency>
            <groupId>net.sourceforge.htmlunit</groupId>
            <artifactId>htmlunit</artifactId>
            <version>2.27</version>
        </dependency>
        <!-- 爬蟲 适用于純靜态html請求  -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.6</version>
        </dependency>
               
  • 基于HttpClient的爬蟲例子
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;

/**
 * 基于org.apache.http.impl.client.HttpClients;方式去擷取html資料(靜态html,不執行js的純靜态html)
 * 1-為啥不用Jsoup請求html:因為功能,性能,效率都不如專業的org.apache.http.impl.client.HttpClients
 * 2-Jsoup是可以自己通過url去請求html(但是功能效率效果都不如上面的)
 * 3-Jsoup是解析操作html的工具
 * @author hxz
 */
public class HttpClient_Jsoup_Dome {
    /**
     *  通過HttpClient+Jsoup方式爬取資料
     * @param a
     */
    public static void main(String[] a)throws Exception{
        //通過HttpClient去請求html
        String Context=HttpClientHtml("http://www.baidu.com");
        //Jsoup解析html并擷取圖檔路徑
        doJsoup(Context,"http://www.baidu.com");
    }

    /**
     * HttpClient 請求靜态html資料
     * @param URL
     * @return
     */
    public static String HttpClientHtml(String URL){
        //String URL="http://www.baidu.com";
        String Context="";

        //建立一個新的請求用戶端
        CloseableHttpClient httpClient= HttpClients.createDefault();

        //使用HttpGet的方式請求網址
        HttpGet httpGet = new HttpGet(URL);

        //擷取網址的傳回結果
        CloseableHttpResponse response=null;
        try {
            response=httpClient.execute(httpGet);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //擷取傳回結果中的實體
        HttpEntity entity = response.getEntity();

        //将傳回的實體輸出
        try {
            Context=EntityUtils.toString(entity, "utf-8");
            System.out.println(Context);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Context;
    }

    /**
     * 通過Jsoup解析html
     * @param html
     */
    public static void doJsoup(String html,String url) throws Exception{
        Document document = Jsoup.parse(html);
        //若HTML文檔包含相對URLs路徑,需要将這些相對路徑轉換成絕對路徑的URLs
        document.setBaseUri(url);//指定base URI

        //擷取所有的img元素
        Elements elements = document.select("img");
        int i=1;
        for (Element e : elements) {
            //擷取每個src的絕對路徑
            String src = e.absUrl("src");
            System.out.println(src);

            //擷取圖檔
            //URL urlSource = new URL(src);
            //URLConnection urlConnection = urlSource.openConnection();

            //設定圖檔名字
            String imageName = src.substring(src.lastIndexOf("/") + 1,src.length());

            //控制台輸出圖檔的src
            System.out.println(imageName);

            //通過URLConnection得到一個流,将圖檔寫到流中,并且建立檔案儲存
            /*InputStream in = urlConnection.getInputStream();
            OutputStream out = new FileOutputStream(new File("E:\\IDEA\\imgs\\", imageName));
            byte[] buf = new byte[1024];
            int l = 0;
            while ((l = in.read(buf)) != -1) {
                out.write(buf, 0, l);
            }*/
        }
    }
}
           
  • 基于HtmlUnit的爬蟲例子
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

import java.util.List;

/**
 * 基于com.gargoylesoftware.htmlunit.WebClient;方式去擷取html資料(包含js建構的動态html)
 * 1-為啥不用Jsoup請求html:因為功能,性能,效率都不如專業的com.gargoylesoftware.htmlunit.WebClient;【最重要的是不支援執行js】
 * 2-Jsoup是可以自己通過url去請求html(但是功能效率效果都不如上面的)
 * 3-Jsoup是解析操作html的工具
 * @author hxz
 */
public class HtmlUnit_Jsoup_Dome {
    /**
     * 通過Htmlunit+Jsoup方式爬取資料
     * @param
     */
   public static void main(String[] a){
       WebClient webClient = new WebClient(BrowserVersion.CHROME);//建立一個模拟谷歌Chrome浏覽器的浏覽器用戶端對象

       webClient.getOptions().setThrowExceptionOnScriptError(false);//當JS執行出錯的時候是否抛出異常, 這裡選擇不需要
       webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);//當HTTP的狀态非200時是否抛出異常, 這裡選擇不需要
       webClient.getOptions().setActiveXNative(false);
       webClient.getOptions().setCssEnabled(false);//是否啟用CSS, 因為不需要展現頁面, 是以不需要啟用
       webClient.getOptions().setJavaScriptEnabled(true); //很重要,啟用JS
       webClient.setAjaxController(new NicelyResynchronizingAjaxController());//很重要,設定支援AJAX

       HtmlPage page = null;
       try {
           page = webClient.getPage("http://ent.sina.com.cn/film/");//嘗試加載上面圖檔例子給出的網頁
       } catch (Exception e) {
           e.printStackTrace();
       }finally {
           webClient.close();
       }

       webClient.waitForBackgroundJavaScript(30000);//異步JS執行需要耗時,是以這裡線程要阻塞30秒,等待異步JS執行結束

       String pageXml = page.asXml();//直接将加載完成的頁面轉換成xml格式的字元串

       //TODO 下面的代碼就是對字元串的操作了,正常的爬蟲操作,用到了比較好用的Jsoup庫

       Document document = Jsoup.parse(pageXml);//擷取html文檔
       List<Element> infoListEle = document.getElementById("feedCardContent").getElementsByAttributeValue("class", "feed-card-item");//擷取元素節點等
       infoListEle.forEach(element -> {
           System.out.println(element.getElementsByTag("h2").first().getElementsByTag("a").text());
           System.out.println(element.getElementsByTag("h2").first().getElementsByTag("a").attr("href"));
       });
   }
}