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


原文:http://my.oschina.net/flashsword/blog/147334?p=1

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

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

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

在Java項目中使用Selenium,需要做兩件事:

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

    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>2.33.0</version>
    </dependency>

     

  • 下載對應的driver,以chrome為例:http://www.cnblogs.com/printN/p/7210633.html 

    下載后,需要將driver的位置寫到Java的環境變量里,例如我在mac下將其下載到了/Users/yihua/Downloads/chromedriver,則需要在程序里添加以下代碼(當然在JVM參數里寫-Dxxx=xxx也是可以的):

    System.getProperties().setProperty("webdriver.chrome.driver","/Users/yihua/Downloads/chromedriver");

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

1      @Test
2     public void testSelenium() {
3         System.getProperties().setProperty("webdriver.chrome.driver", "/Users/yihua/Downloads/chromedriver");
4         WebDriver webDriver = new ChromeDriver();
5         webDriver.get("http://huaban.com/");
6         WebElement webElement = webDriver.findElement(By.xpath("/html"));
7         System.out.println(webElement.getAttribute("outerHTML"));
8         webDriver.close();
9     }

 

值得注意的是,每次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。

 1     /**
 2  * 花瓣網抽取器。<br>
 3  * 使用Selenium做頁面動態渲染。<br>
 4  */
 5 public class HuabanProcessor implements PageProcessor {
 6 
 7     private Site site;
 8 
 9     @Override
10     public void process(Page page) {
11         page.addTargetRequests(page.getHtml().links().regex("http://huaban\\.com/.*").all());
12         if (page.getUrl().toString().contains("pins")) {
13             page.putField("img", page.getHtml().xpath("//div[@id='pin_img']/img/@src").toString());
14         } else {
15             page.getResultItems().setSkip(true);
16         }
17     }
18 
19     @Override
20     public Site getSite() {
21         if (site == null) {
22             site = Site.me().setDomain("huaban.com").addStartUrl("http://huaban.com/").setSleepTime(1000);
23         }
24         return site;
25     }
26 
27     public static void main(String[] args) {
28         Spider.create(new HuabanProcessor()).thread(5)
29                 .scheduler(new RedisScheduler("localhost"))
30                 .pipeline(new FilePipeline("/data/webmagic/test/"))
31                 .downloader(new SeleniumDownloader("/Users/yihua/Downloads/chromedriver"))
32                 .run();
33     }
34 }

 

sample地址:HuabanProcessor.java


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM