1. 程式人生 > >java 動態載入的頁面資料的抓取

java 動態載入的頁面資料的抓取

動態載入的頁面資料的抓取

動態載入頁面資料有兩種方法可以選擇:

1模擬頁面中的請求,直接獲取介面返回的資料
2內建瀏覽器渲染頁面,然後獲取渲染後的資料
分析
在頁面中通過拼湊引數等方法來模擬網路請求,最終獲取介面資料,這種方法是可以行的通的,問題是比較麻煩。本文主要通過內建瀏覽器渲染這種簡單粗暴的方法來實現資料的抓取。

問題來了,如何內建瀏覽器呢?

熟悉自動化測試同學應該都知道 Selenium ,這個模擬瀏覽器進行自動化測試的工具。Selenium 提供一組 API 可以與真實的瀏覽器核心互動。Selenium 是跨語言的,有 Java、C#、python 等版本,並且支援多種瀏覽器,chrome、firefox 以及 IE 都支援。

實現
我們用 Java 來寫 Demo。

新增依賴

新增 Selenium 依賴,以 Maven 為例:

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.0.1</version>
</dependency>
<dependency>
    <groupId>org.seleniumhq.selenium</groupId
>
<artifactId>selenium-chrome-driver</artifactId> <version>3.0.1</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-server</artifactId> <version>2.18.0</version> </dependency
>

下載 driver

以 chrome 為例: https://sites.google.com/a/chromium.org/chromedriver/

下載後,最好新增環境變數。當然,也可以在呼叫前設定環境:

System.getProperties().setProperty("webdriver.chrome.driver",
    "/Users/zhenguo/Documents/chrome/chromedriver");

注意:Mac環境下需要確認 chromedriver 是可執行的。

安裝 Chrome 瀏覽器

測試 selenium ,程式碼如下:

@Ignore("need chrome driver")
@Test
public void testSelenium() {
    System.getProperties().setProperty("webdriver.chrome.driver",
        "/Users/zhenguo/Documents/chrome/chromedriver");
    WebDriver webDriver = new ChromeDriver();
    webDriver.get("http://huaban.com/");
    WebElement webElement = webDriver.findElement(By.xpath("/html"));
    System.out.println(webElement.getAttribute("outerHTML"));
    webDriver.close();
}

如果出現類似以下結果,就說明 webdriver 配置好了:

Starting ChromeDriver 2.25.426935 (820a95b0b81d33e42712f9198c215f703412e1a1) on port 2052
Only local connections are allowed.
Nov 07, 2016 12:35:11 AM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Attempting bi-dialect session, assuming Postel's Law holds true on the remote end
Nov 07, 2016 12:35:13 AM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: OSS

PS:每次 new ChromeDriver() ,Selenium都會建立一個Chrome程序,並使用一個隨機埠在Java中與chrome程序進行通訊來互動。我們需要呼叫 webDriver.close() 關閉程序。如果是網路爬蟲抓取資料的話,最好用執行緒池來處理。

實現爬蟲

上面步驟都設定好了,基於 webmagic 的爬蟲實現就比較簡單了,程式碼如下:

public class HuabanProcessor implements PageProcessor {

    private Site site;

    @Override
    public void process(Page page) {
           page.addTargetRequests(
             page.getHtml().links().regex("http://huaban\\.com/.*").all());
        if (page.getUrl().toString().contains("pins")) {
            page.putField("img", page.getHtml().
                          xpath("//div[@id='baidu_image_holder']/img/@src").toString());
        } else {
            page.getResultItems().setSkip(true);
        }
    }

    @Override
    public Site getSite() {
        if (null == site) {
            site = Site.me().setDomain("huaban.com").setSleepTime(0);
        }
        return site;
    }

    public static void main(String[] args) {
        Spider.create(new HuabanProcessor()).thread(5)
                .addPipeline(new FilePipeline("/Users/zhenguo/Documents/chrome/webmagic/test/"))
                .setDownloader(new SeleniumDownloader("/Users/zhenguo/Documents/chrome/chromedriver"))
                .addUrl("http://huaban.com/explore/gufenghaibao/")
                .runAsync();
    }
}

上面 HuabanProcessor 使用到 SeleniumDownloader ,程式碼如下:

package us.codecraft.webmagic.downloader.selenium;

   import org.apache.log4j.Logger;
   import org.openqa.selenium.By;
   import org.openqa.selenium.Cookie;
   import org.openqa.selenium.WebDriver;
   import org.openqa.selenium.WebElement;

   import us.codecraft.webmagic.Page;
   import us.codecraft.webmagic.Request;
   import us.codecraft.webmagic.Site;
   import us.codecraft.webmagic.Task;
   import us.codecraft.webmagic.downloader.Downloader;
   import us.codecraft.webmagic.selector.Html;
   import us.codecraft.webmagic.selector.PlainText;
   import us.codecraft.webmagic.utils.UrlUtils;

   import java.io.Closeable;
   import java.io.IOException;
   import java.util.Map;

/**
 * 使用Selenium呼叫瀏覽器進行渲染。目前僅支援chrome。<br>
 * 需要下載Selenium driver支援。<br>
 *
 * @author [email protected] <br>
 *         Date: 13-7-26 <br>
 *         Time: 下午1:37 <br>
 */
 public class SeleniumDownloader implements Downloader, Closeable {

     private volatile WebDriverPool webDriverPool;

     private Logger logger = Logger.getLogger(getClass());

     private int sleepTime = 0;

     private int poolSize = 1;

     private static final String DRIVER_PHANTOMJS = "phantomjs";

     /**
      * 新建
      *
      * @param chromeDriverPath chromeDriverPath
      */
     public SeleniumDownloader(String chromeDriverPath) {
         System.getProperties().setProperty("webdriver.chrome.driver",
                 chromeDriverPath);
     }

     /**
      * Constructor without any filed. Construct PhantomJS browser
      * 
      * @author [email protected]
      */
     public SeleniumDownloader() {
         // System.setProperty("phantomjs.binary.path",
         // "/Users/Bingo/Downloads/phantomjs-1.9.7-macosx/bin/phantomjs");
     }

     /**
      * set sleep time to wait until load success
      *
      * @param sleepTime sleepTime
      * @return this
      */
     public SeleniumDownloader setSleepTime(int sleepTime) {
         this.sleepTime = sleepTime;
         return this;
     }

     @Override
     public Page download(Request request, Task task) {
         checkInit();
         WebDriver webDriver;
         try {
             webDriver = webDriverPool.get();
         } catch (InterruptedException e) {
             logger.warn("interrupted", e);
             return null;
         }
         logger.info("downloading page " + request.getUrl());
         webDriver.get(request.getUrl());
         try {
             Thread.sleep(sleepTime);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         WebDriver.Options manage = webDriver.manage();
         Site site = task.getSite();
         if (site.getCookies() != null) {
             for (Map.Entry<String, String> cookieEntry : site.getCookies()
                     .entrySet()) {
                 Cookie cookie = new Cookie(cookieEntry.getKey(),
                         cookieEntry.getValue());
                 manage.addCookie(cookie);
             }
         }

         /*
          * TODO You can add mouse event or other processes
          * 
          * @author: [email protected]
          */

         WebElement webElement = webDriver.findElement(By.xpath("/html"));
         String content = webElement.getAttribute("outerHTML");
         Page page = new Page();
         page.setRawText(content);
         page.setHtml(new Html(UrlUtils.fixAllRelativeHrefs(content,
                 request.getUrl())));
         page.setUrl(new PlainText(request.getUrl()));
         page.setRequest(request);
         webDriverPool.returnToPool(webDriver);
         return page;
     }

     private void checkInit() {
         if (webDriverPool == null) {
             synchronized (this) {
                 webDriverPool = new WebDriverPool(poolSize);
             }
         }
     }

     @Override
     public void setThread(int thread) {
         this.poolSize = thread;
     }

     @Override
     public void close() throws IOException {
         webDriverPool.closeAll();
     }
 }
WebDriverPool 程式碼如下:

package us.codecraft.webmagic.downloader.selenium;

import org.apache.log4j.Logger;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;

import java.io.FileReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author [email protected] <br>
 *         Date: 13-7-26 <br>
 *         Time: 下午1:41 <br>
 */
class WebDriverPool {
    private Logger logger = Logger.getLogger(getClass());

    private final static int DEFAULT_CAPACITY = 5;

    private final int capacity;

    private final static int STAT_RUNNING = 1;

    private final static int STAT_CLODED = 2;

    private AtomicInteger stat = new AtomicInteger(STAT_RUNNING);

    /*
     * new fields for configuring phantomJS
     */
    private WebDriver mDriver = null;
    private boolean mAutoQuitDriver = true;

    private static final String CONFIG_FILE = "/Users/zhenguo/Documents/develop/github/webmagic/webmagic-selenium/config.ini";
    private static final String DRIVER_FIREFOX = "firefox";
    private static final String DRIVER_CHROME = "chrome";
    private static final String DRIVER_PHANTOMJS = "phantomjs";

    protected static Properties sConfig;
    protected static DesiredCapabilities sCaps;

    /**
     * Configure the GhostDriver, and initialize a WebDriver instance. This part
     * of code comes from GhostDriver.
     * https://github.com/detro/ghostdriver/tree/master/test/java/src/test/java/ghostdriver
     * 
     * @author [email protected]
     * @throws IOException
     */
    public void configure() throws IOException {
        // Read config file
        sConfig = new Properties();
        sConfig.load(new FileReader(CONFIG_FILE));

        // Prepare capabilities
        sCaps = new DesiredCapabilities();
        sCaps.setJavascriptEnabled(true);
        sCaps.setCapability("takesScreenshot", false);

        String driver = sConfig.getProperty("driver", DRIVER_PHANTOMJS);

        // Fetch PhantomJS-specific configuration parameters
        if (driver.equals(DRIVER_PHANTOMJS)) {
            // "phantomjs_exec_path"
            if (sConfig.getProperty("phantomjs_exec_path") != null) {
                sCaps.setCapability(
                        PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,
                        sConfig.getProperty("phantomjs_exec_path"));
            } else {
                throw new IOException(
                        String.format(
                                "Property '%s' not set!",
                                PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY));
            }
            // "phantomjs_driver_path"
            if (sConfig.getProperty("phantomjs_driver_path") != null) {
                System.out.println("Test will use an external GhostDriver");
                sCaps.setCapability(
                        PhantomJSDriverService.PHANTOMJS_GHOSTDRIVER_PATH_PROPERTY,
                        sConfig.getProperty("phantomjs_driver_path"));
            } else {
                System.out
                        .println("Test will use PhantomJS internal GhostDriver");
            }
        }

        // Disable "web-security", enable all possible "ssl-protocols" and
        // "ignore-ssl-errors" for PhantomJSDriver
        // sCaps.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, new
        // String[] {
        // "--web-security=false",
        // "--ssl-protocol=any",
        // "--ignore-ssl-errors=true"
        // });

        ArrayList<String> cliArgsCap = new ArrayList<String>();
        cliArgsCap.add("--web-security=false");
        cliArgsCap.add("--ssl-protocol=any");
        cliArgsCap.add("--ignore-ssl-errors=true");
        sCaps.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS,
                cliArgsCap);

        // Control LogLevel for GhostDriver, via CLI arguments
        sCaps.setCapability(
                PhantomJSDriverService.PHANTOMJS_GHOSTDRIVER_CLI_ARGS,
                new String[] { "--logLevel="
                        + (sConfig.getProperty("phantomjs_driver_loglevel") != null ? sConfig
                                .getProperty("phantomjs_driver_loglevel")
                                : "INFO") });

        // String driver = sConfig.getProperty("driver", DRIVER_PHANTOMJS);

        // Start appropriate Driver
        if (isUrl(driver)) {
            sCaps.setBrowserName("phantomjs");
            mDriver = new RemoteWebDriver(new URL(driver), sCaps);
        } else if (driver.equals(DRIVER_FIREFOX)) {
            mDriver = new FirefoxDriver(sCaps);
        } else if (driver.equals(DRIVER_CHROME)) {
            mDriver = new ChromeDriver(sCaps);
        } else if (driver.equals(DRIVER_PHANTOMJS)) {
            mDriver = new PhantomJSDriver(sCaps);
        }
    }

    /**
     * check whether input is a valid URL
     * 
     * @author [email protected]
     * @param urlString urlString
     * @return true means yes, otherwise no.
     */
    private boolean isUrl(String urlString) {
        try {
            new URL(urlString);
            return true;
        } catch (MalformedURLException mue) {
            return false;
        }
    }

    /**
     * store webDrivers created
     */
    private List<WebDriver> webDriverList = Collections
            .synchronizedList(new ArrayList<WebDriver>());

    /**
     * store webDrivers available
     */
    private BlockingDeque<WebDriver> innerQueue = new LinkedBlockingDeque<WebDriver>();

    public WebDriverPool(int capacity) {
        this.capacity = capacity;
    }

    public WebDriverPool() {
        this(DEFAULT_CAPACITY);
    }

    /**
     * 
     * @return
     * @throws InterruptedException
     */
    public WebDriver get() throws InterruptedException {
        checkRunning();
        WebDriver poll = innerQueue.poll();
        if (poll != null) {
            return poll;
        }
        if (webDriverList.size() < capacity) {
            synchronized (webDriverList) {
                if (webDriverList.size() < capacity) {

                    // add new WebDriver instance into pool
                    try {
                        configure();
                        innerQueue.add(mDriver);
                        webDriverList.add(mDriver);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    // ChromeDriver e = new ChromeDriver();
                    // WebDriver e = getWebDriver();
                    // innerQueue.add(e);
                    // webDriverList.add(e);
                }
            }

        }
        return innerQueue.take();
    }

    public void returnToPool(WebDriver webDriver) {
        checkRunning();
        innerQueue.add(webDriver);
    }

    protected void checkRunning() {
        if (!stat.compareAndSet(STAT_RUNNING, STAT_RUNNING)) {
            throw new IllegalStateException("Already closed!");
        }
    }

    public void closeAll() {
        boolean b = stat.compareAndSet(STAT_RUNNING, STAT_CLODED);
        if (!b) {
            throw new IllegalStateException("Already closed!");
        }
        for (WebDriver webDriver : webDriverList) {
            logger.info("Quit webDriver" + webDriver);
            webDriver.quit();
            webDriver = null;
        }
    }

}

到此,動態載入的頁面資料抓取就實現了。本文使用 selenium 作為渲染的方法,還有很多其他的方法,例如 phantomjshtmlunit 等。有空了可以嘗試其他的方法,希望本文對你有所幫助。

相關推薦

PhantomJs+MutationObserver實現動態頁面資料

       IT行業,支撐業務的變化需要優秀的大量的資料,我們需要適應資料的動態變化,拿到這些動態變化的資料,分析,然後提供給自己的專案,支撐公司的業務。最近,就碰到這種,需要獲取網頁上不斷變化的資

Python web 動態渲染頁面

通過直接分析ajax資訊,我們仍然可以利用request或者urllib來獲取資訊,但是,JavaScript動態渲染頁面的方式不僅只有ajax一種,也不是傳統的html頁面資訊,運用模擬瀏覽器的執行方式來獲取資訊,只要瀏覽器能接收到,我們就能獲取出來.在 Python 中提供了許多模擬瀏覽器執行的

C# NetCore使用AngleSharp爬周公解夢資料 MySql資料庫的自動建立和頁面資料

這一章詳細講解編碼過程 那麼接下來就是碼程式碼了,GO 新建NetCore WebApi專案 空的就可以    NuGet安裝 Install-Package AngleSharp    或者介面安裝   using。。 預設本地裝有

d3滑鼠拖拽、放大縮小後動態載入頁面資料demo

d3滑鼠拖拽、放大縮小後動態載入頁面資料demo index.html <!DOCTYPE html> <html> <head>     <meta charset="UTF-8">     <style

爬蟲--python3.6+selenium+BeautifulSoup實現動態網頁的資料,適用於對頻率不高的情況

說在前面: 本文主要介紹如何抓取 頁面載入後需要通過JS載入的資料和圖片 本文是通過python中的selenium(pyhton包) + chrome(谷歌瀏覽器) + chromedrive(谷歌瀏覽器驅動) chrome 和chromdrive建議都下最新版本(參考地址:https://blog.c

JAVA HttpClient實現頁面資訊(獲取圖片驗證碼並傳入cookie實現資訊獲取)

有時候我們的程式中需要呼叫第三方介面獲取資料,比如在這裡需要在我的程式裡實現使用者輸入汽車號牌等資訊就可以查到使用者的違章資訊,在沒有其他方法的情況下我就得想辦法在官網獲取資訊。上圖是官網獲取資訊的網站頁面。 傳統的ajax請求不可能實現,光不能跨域這一點就實現不了。

基於Java的阿里媽媽資料技術

基於Java的阿里媽媽資料抓取技術 前言:     對於需要登入的網站爬蟲最大的困難就是需要登入,然後才能獲取到資料,如微博,阿里媽媽,webqq等。之前也有看過使用瀏覽器登入到網站後直接從瀏覽器中獲

java 動態載入頁面資料

動態載入的頁面資料的抓取 動態載入頁面資料有兩種方法可以選擇: 1模擬頁面中的請求,直接獲取介面返回的資料 2內建瀏覽器渲染頁面,然後獲取渲染後的資料 分析 在頁面中通過拼湊引數等方法來模擬網路請求,最終獲取介面資料,這種方法是可以行的通的,問題是比較

爬蟲[1]---頁面分析及資料

頁面分析及資料抓取 anaconda + scrapy 安裝:https://blog.csdn.net/dream_dt/article/details/80187916 用 scrapy 初始化一個爬蟲:https://blog.csdn.net/dream_dt/article

asp.net頁面通過Javascript使用CanvasJS.Chart畫曲線,曲線實現動態載入後臺資料(通過ajax)

頁面程式碼: <html> <head> <script src="jQuery.js" type="text/javascript"></script> <script src="https://canvasjs.com/assets/

Vue音樂--排行榜頁面02_首頁資料

大概步驟: 目標效果 二、抓取排行榜首頁資料 要點 進入QQ音樂移動端,查詢Network中的排行榜首頁資料,在XHR中找到 使用之前寫好的jsonp解析資料的方法,傳入處理好的url jsonp資料 url資料 相關程式碼 import

[記錄]Java網路爬蟲基礎和網站資料的兩個小例項

前段時間在學習爬蟲,並從網路抓取了一些簡單的資料,記錄一下。 抓取分成下面3個部分: 1、網路請求 2、解析抓取下來的頁面,並且處理亂碼或者解壓程式碼的問題 3、拿到指定的資料、資源 完整程式碼如下: 第一個例項: /** * 從某網站查詢所有帖子標題 * 把所有

有哪些好用的網際網路資料資料採集,頁面解析工具?

1、 網際網路剛興起的時候,資料索引是個大問題,當時Yahoo的分類頁面著實火了一陣子。 2、隨著網際網路資料量越來越大,Google,百度等搜尋引擎火了起來。這個階段,幾乎沒有比搜尋引擎更火的技術了,連帶分詞技術都火得一塌糊塗。緊接著, Nutch等開源搜

Java網頁資料例項

在很多行業中,要對行業資料進行分類彙總,及時分析行業資料,對於公司未來的發展,有很好的參照和橫向對比。所以,在實際工作,我們可能要遇到資料採集這個概念,資料採集的最終目的就是要獲得資料,提取有用的資料進行資料提取和資料分類彙總。 很多人在第一次瞭解資料採集的時候,可能無

使用Chrome Headless 快速實現java版數據的

chrome headless java調webkit 參考《使用Chrome快速實現數據的抓取(一)——概述》和《使用Chrome快速實現數據的抓取(二)——協議》。各協議客戶端實現參考:https://github.com/ChromeDevTools/awesome-chrome-devtoo

QueryList免費線上網頁採集資料工具-toolfk.com

     本文要推薦的[ToolFk]是一款程式設計師經常使用的線上免費測試工具箱,ToolFk 特色是專注於程式設計師日常的開發工具,不用安裝任何軟體,只要把內容貼上按一個執行按鈕,就能獲取到想要的內容結果。ToolFk還支援  BarCode條形碼線上

spider資料(第二章)

download最完善的指令碼 import urllib2 import urlparse def download(url, user_agent="wswp", proxy=None, num_retries=2): print "DownLoading", url head

Android 使用jsoup 進行資料

一,身為安卓開發人員,在沒有介面的情況下是很操蛋的。索性就抓點資料測試用了。 準備工作:jsoup.jar 這裡 已經 是 已經實現好 邏輯的方法。 public class MianHuanJsoup { public static final String MH

爬蟲實戰-酷狗音樂資料--XPath,Pyquery,Beautifulsoup資料提取對比實戰

網站: http://www.kugou.com/yy/html/rank.html 爬取目標: 酷酷狗飆升榜的歌手,歌曲名字,歌曲連結等內容,存到Mysql資料庫中 網頁解析: 此次爬取採用三種解析方式: 程式碼如下: import requests from l

poi資料和下載

  網際網路或者企業獲取:直接從一些專業類服務網站上抓取或者購買(例如大眾點評,攜程),或者直接從大家在其公開的地圖服務上的標註中進行篩選和獲取。這就是google,百度,高德自己免費向社會開放其地圖服務所能夠獲得的利益。尤其對於開放API免費企業客戶的使用,這種獲取是很有價值的。