Java爬蟲系列四:使用selenium-java爬取js非同步請求的資料

JAVA開發老菜鳥發表於2021-10-17

在之前的系列文章中介紹了如何使用httpclient抓取頁面html以及如何用jsoup分析html原始檔內容得到我們想要的資料,但是有時候通過這兩種方式不能正常抓取到我們想要的資料,比如看如下例子。

1.需求場景:

想要抓取股票的最新價格,頁面F12資訊如下:

按照前面的方式,爬取的程式碼如下:

/**
 * @description: 爬取股票的最新股價
 * @author: JAVA開發老菜鳥
 * @date: 2021-10-16 21:47
 */
public class StockPriceSpider {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    public static void main(String[] args) {

        StockPriceSpider stockPriceSpider = new StockPriceSpider();
        String html = stockPriceSpider.httpClientProcess();
        stockPriceSpider.jsoupProcess(html);
    }

    private String httpClientProcess() {
        String html = "";
        String uri = "http://quote.eastmoney.com/sh600036.html";
        //1.生成httpclient,相當於該開啟一個瀏覽器
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        //2.建立get請求,相當於在瀏覽器位址列輸入 網址
        HttpGet request = new HttpGet(uri);
        try {
            request.setHeader("user-agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36");
            request.setHeader("accept", "application/json, text/javascript, */*; q=0.01");

//            HttpHost proxy = new HttpHost("3.211.17.212", 80);
//            RequestConfig config = RequestConfig.custom().setProxy(proxy).build();
//            request.setConfig(config);

            //3.執行get請求,相當於在輸入位址列後敲Enter鍵
            response = httpClient.execute(request);

            //4.判斷響應狀態為200,進行處理
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                //5.獲取響應內容
                HttpEntity httpEntity = response.getEntity();
                html = EntityUtils.toString(httpEntity, "utf-8");
                logger.info("訪問{} 成功,返回頁面資料{}", uri, html);
            } else {
                //如果返回狀態不是200,比如404(頁面不存在)等,根據情況做處理,這裡略
                logger.info("訪問{},返回狀態不是200", uri);
                logger.info(EntityUtils.toString(response.getEntity(), "utf-8"));
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //6.關閉
            HttpClientUtils.closeQuietly(response);
            HttpClientUtils.closeQuietly(httpClient);
        }
        return html;
    }

    private void jsoupProcess(String html) {
        Document document = Jsoup.parse(html);
        Element price = document.getElementById("price9");
        logger.info("股價為:>>> {}", price.text());
    }

}

執行結果:

納尼,股價為"-" ?不可能。
之所以爬不到正確的結果,是因為這個值在網站上是通過非同步載入渲染的,因此不能正常獲取。

2.java爬取非同步載入的資料的方法

那如何爬取非同步載入的資料呢?通常有兩種做法:

2.1內建瀏覽器核心

內建瀏覽器就是在抓取的程式中啟動一個瀏覽器核心,使我們獲取到 js 渲染後的頁面就和靜態頁面一樣。常用的核心有

  • Selenium
  • PhantomJs
  • HtmlUnit

這裡我選了Selenium,它是一個模擬瀏覽器,是進行自動化測試的工具,它提供一組 API 可以與真實的瀏覽器核心互動。當然,爬蟲也可以用它。
具體做法如下:

  • 引入pom依賴
<dependency>
   <groupId>org.seleniumhq.selenium</groupId>
   <artifactId>selenium-java</artifactId>
   <version>3.141.59</version>
</dependency>
  • 配置對應瀏覽器的驅動
    要使用selenium,需要下載瀏覽器的驅動,根據不同的瀏覽器要下載的驅動程式也不一樣,下載地址為:https://npm.taobao.org/mirrors/chromedriver/
    我用的是谷歌瀏覽器,因此下載了對應版本的windows和linux驅動。

    下載後需要配置進java環境變數裡面,指定驅動的目錄:

    System.getProperties().setProperty("webdriver.chrome.driver", "F:/download/chromedriver_win32_1/chromedriver.exe");

  • 程式碼實現:

    Logger logger = LoggerFactory.getLogger(this.getClass());
    
      public static void main(String[] args) {
    
          StockPriceSpider stockPriceSpider = new StockPriceSpider();
          stockPriceSpider.seleniumProcess();
      }
    
      private void seleniumProcess() {
    
          String uri = "http://quote.eastmoney.com/sh600036.html";
    
          // 設定 chromedirver 的存放位置
          System.getProperties().setProperty("webdriver.chrome.driver", "F:/download/chromedriver_win32_1/chromedriver.exe");
    
          // 設定瀏覽器引數
          ChromeOptions chromeOptions = new ChromeOptions();
          chromeOptions.addArguments("--no-sandbox");//禁用沙箱
          chromeOptions.addArguments("--disable-dev-shm-usage");//禁用開發者shm
          chromeOptions.addArguments("--headless"); //無頭瀏覽器,這樣不會開啟瀏覽器視窗
          WebDriver webDriver = new ChromeDriver(chromeOptions);
    
          webDriver.get(uri);
          WebElement webElements = webDriver.findElement(By.id("price9"));
          String stockPrice = webElements.getText();
          logger.info("最新股價為 >>> {}", stockPrice);
          webDriver.close();
      }
    

    執行結果:

    爬取成功!

2.2反向解析法

反向解析法就是通過F12查詢到 Ajax 非同步獲取資料的連結,直接呼叫該連結得到json結果,然後直接解析json結果獲取想要的資料。
這個方法的關鍵就在於找到這個Ajax連結。這種方式我沒有去研究,感興趣的可以百度下。這裡略。

3.結束語

以上即為如何通過selenium-java爬取非同步載入的資料的方法。通過本方法,我寫了一個小工具:
持倉市值通知系統,他會每日根據自己的持倉配置,自動計算賬戶總市值,並郵件通知到指定郵箱。
用到的技術如下:

相關程式碼已經上傳到我的碼雲,感興趣可以看下。

相關文章