使用Selenium來抓取動態加載的頁面

通常的爬蟲都是直接使用http協議,下載指定url的html內容,並對內容進行分析和抽取。在我寫的爬蟲框架webmagic裏也使用了HttpClient來完成這樣的任務。html

可是有些頁面是經過js以及ajax動態加載的,例如:花瓣網。這時若是咱們直接分析原始頁面的html,是得不到有效的信息的。固然,由於不管怎樣動態加載,基礎信息總歸是包含在初始頁面中得,因此咱們能夠用爬蟲代碼來模擬js代碼,js讀取頁面元素值,咱們也讀取頁面元素值;js發送ajax,咱們就拼湊參數、發送ajax並解析返回的json。這樣總歸是能作的,可是比較麻煩,有沒有比較省力的方法呢?比較好的方法大概是內嵌一個瀏覽器了。java

Selenium是一個模擬瀏覽器,進行自動化測試的工具,它提供一組API能夠與真實的瀏覽器內核交互。Selenium是跨語言的,有Java、C#、python等版本,而且支持多種瀏覽器,chrome、firefox以及IE都支持。python

在Java項目中使用Selenium,須要作兩件事:git

  • 在項目中引入Selenium的Java模塊,以Maven爲例:github

    <dependency>
          <groupId>org.seleniumhq.selenium</groupId>
          <artifactId>selenium-java</artifactId>
          <version>2.33.0</version>
      </dependency>
  • 下載對應的driver,以chrome爲例:http://code.google.com/p/chromedriver/downloads/listweb

    下載後,須要將driver的位置寫到Java的環境變量裏,例如我在mac下將其下載到了/Users/yihua/Downloads/chromedriver,則須要在程序裏添加如下代碼(固然在JVM參數裏寫-Dxxx=xxx也是能夠的):ajax

    <!-- lang: java --> System.getProperties().setProperty("webdriver.chrome.driver","/Users/yihua/Downloads/chromedriver");chrome

Selenium的API挺簡單的,核心是WebDriver,下面是動態渲染頁面,並獲取最終html的代碼:json

<!-- lang: java -->
	 @Test
    public void testSelenium() {
        System.getProperties().setProperty("webdriver.chrome.driver", "/Users/yihua/Downloads/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();
    }

值得注意的是,每次new ChromeDriver(),Selenium都會創建一個Chrome進程,並使用一個隨機端口在Java中與chrome進程進行通訊來交互。因而可知有兩個問題:瀏覽器

  • 所以若是直接關閉Java程序,Chrome進程多是沒法關閉的。這裏須要顯示的調用webDriver.close()來關閉進程。

  • 建立進程的開銷仍是比較大的,儘可能對webDriver進行復用會比較好。惋惜根據官方的文檔,webDriver不是線程安全的,因此咱們須要創建一個webDriver池來保存它們。不清楚Selenium是否有這樣的接口,反正我是本身寫了一個WebDriverPool來完成這個任務。

我已經將Selenium整合到了個人爬蟲框架webmagic中,目前仍是試用版本,有興趣的能夠一塊兒學習交流。

最後說說效率問題。嵌入瀏覽器以後,不但要多花CPU去渲染頁面,還要下載頁面附加的資源。彷佛單個webDriver中的靜態資源是有緩存的,初始化以後,訪問速度會加快。我試用ChromeDriver加載了100次花瓣的首頁(http://huaban.com/),共耗時263秒,平均每一個頁面2.6秒。

爲了測試效果,我寫了一個花瓣抽取器,抽取花瓣網的分享圖片url,用了咱本身的webmagic框架,集成了Selenium。

<!-- lang: java -->
    /**
 * 花瓣網抽取器。<br>
 * 使用Selenium作頁面動態渲染。<br>
 */
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='pin_img']/img/@src").toString());
        } else {
            page.getResultItems().setSkip(true);
        }
    }

    @Override
    public Site getSite() {
        if (site == null) {
            site = Site.me().setDomain("huaban.com").addStartUrl("http://huaban.com/").setSleepTime(1000);
        }
        return site;
    }

    public static void main(String[] args) {
        Spider.create(new HuabanProcessor()).thread(5)
                .scheduler(new RedisScheduler("localhost"))
                .pipeline(new FilePipeline("/data/webmagic/test/"))
                .downloader(new SeleniumDownloader("/Users/yihua/Downloads/chromedriver"))
                .run();
    }
}

sample地址:HuabanProcessor.java

相關文章
相關標籤/搜索