這幾天對JS動態網頁的爬取作了一下研究,主要涉及到的對象有HtmlUnit、HttpUnit、Selenium WebDriver、CasperJs和HttpClient模擬等操做。咱們能夠經過對天貓商品的促銷價爬取來對他們進行下對比。html
測試網頁以下:前端
http://detail.tmall.com/item.htm?spm=0.0.0.0.HkxFxe&id=520129049356java
促銷價在天貓的網站上是動態生成的,在源碼中是看不到的。 web
1 HtmlUnitajax
HtmlUnit是一款開源的Java 頁面分析工具,讀取頁面後,能夠有效的使用HtmlUnit分析頁面上的內容。項目能夠模擬瀏覽器運行,被譽爲java瀏覽器的開源實現。它對於動態生成的數據提供了支持,使咱們可以獲得ajax執行後的源代碼。並且它操做簡單隻需將相關的jar包引入即可使用。瀏覽器
測試代碼:cookie
public class HtmlUnitTest {網絡 String url = "http://detail.tmall.com/item.htm?spm=0.0.0.0.HkxFxe&id=520129049356";app static WebClient webClient;函數
@BeforeClass public static void init() { // 指定瀏覽器,並指定瀏覽器模擬的版本; webClient = new WebClient(BrowserVersion.CHROME); // webclient參數載體 WebClientOptions clientOptions = webClient.getOptions(); ProxyConfig proxyConfig = new ProxyConfig(); proxyConfig.setProxyHost("proxy.asiainfo.com"); proxyConfig.setProxyPort(8080); // 設置webClient的相關參數 clientOptions.setJavaScriptEnabled(true); clientOptions.setCssEnabled(false); clientOptions.setTimeout(10000); clientOptions.setThrowExceptionOnScriptError(false); clientOptions.setProxyConfig(proxyConfig); webClient.setAjaxController(new NicelyResynchronizingAjaxController()); }
public void test() { long start = System.currentTimeMillis(); // 模擬瀏覽器打開一個目標網址 HtmlPage rootPage; try { rootPage = webClient.getPage(url); HtmlElement htmlElement = rootPage.getBody(); String xmlContent = htmlElement.asXml(); long end = System.currentTimeMillis(); System.out.println("time:" + (end - start)); System.out.println(xmlContent); // 測試js生成的部分是否加載成功 Document doc = Jsoup.parse(xmlContent); Elements select = doc.select("#J_PromoPrice .tm-price"); if (select != null && select.size() > 0) { String text = select.get(0).text(); System.out.println(text); } } catch (FailingHttpStatusCodeException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } |
執行結果:
經過代碼及測試咱們會發現,它簡單易用,可是在抓取時間上耗時仍是不少的。
2 HttpUnit
上面談到HtmlUnit咱們還要提一下這個HttpUnit,名字類似、功能類似,若是不注意每每會混淆。網上搜索HttpUnit的官方文檔,你會發現它的版本已經有好幾年沒有更新了,並且對ajax動態數據的解析不支持,沒法解決咱們現有的問題,放棄。
3 Selenium WebDriver
如今的網絡訪問不了官方網站,只能從其餘地方收集一些資料,它包含了多種Driver,以下圖所示:
1)HtmlUnitDriver
從這些子類中咱們能夠看出Selenium包含了HtmlUnitDriver,經過查看源碼以及使用發現它實際上就是調用的HtmlUnit的部分方法,不過與HtmlUnit不一樣的是它並無提供Ajax支持,所以對於天貓這類的網頁它是解析不了促銷價格的。
2)各類瀏覽器Driver
咱們在使用時,不一樣的瀏覽器須要配置不一樣的參數來指定瀏覽器的安裝路徑。每次初始化操做都會打開一個瀏覽器。下面讓咱們以火狐瀏覽器爲例:
測試代碼:
public class SelenimuWebDriverTest { WebDriver webDriver; String url = "http://detail.tmall.com/item.htm?spm=0.0.0.0.HkxFxe&id=520129049356";
@Before public void setUp() { DesiredCapabilities capability = DesiredCapabilities.firefox(); capability.setCapability("firefox_binary", "E:\\Program Files\\Mozilla Firefox\\firefox.exe"); webDriver = new FirefoxDriver(capability);// 初始化一個火狐瀏覽器 webDriver.manage().window().maximize(); // 最大化瀏覽器 }
@Test public void test1() throws Exception { long start = System.currentTimeMillis(); webDriver.get(url); String pageSource = webDriver.getPageSource(); long end = System.currentTimeMillis(); System.out.println("time:" + (end - start)); Document doc = Jsoup.parse(pageSource); Elements select = doc.select("#J_PromoPrice .tm-price"); if (select != null && select.size() > 0) { String result = select.get(0).text(); System.out.println(result); } else { System.out.println("no result"); } webDriver.close(); } } |
執行結果:
從網頁的抓取時間上來講,它的效率仍是能夠的,初始化瀏覽器的過程咱們須要注意防止瀏覽器打開過多,以避免耗時且耗資源。而且這個在使用的時候有時候打開瀏覽器的時候會提示導入嚮導,這種還須要調整瀏覽器的配置參數。
3)PhantomJSDriver
PhantomJSDriver的使用很是簡單、接近真實瀏覽器且不須要打開瀏覽器,所以,直接建立一個PhantomJSDriver對象就能夠用了。前提是須要安裝phantomjs而且配置好環境變量,不過這一步是很是簡單的。
測試代碼:
public void setUp() { webDriver = new PhantomJSDriver(); } //其餘同上 |
結果:
這個用起來仍是很方便的,不知道是不是網絡的緣由,測試過程當中抓取時間的變化在2s-7s範圍波動,從抓取來看時間也不是最慢的,簡單易用。
4 CasperJs
因爲最早研究的是這個東西,在此仍是要先簡單說一下吧。
CasperJS[1] 是一個開源的導航腳本處理和測試工具,基於PhantomJS(前端自動化測試工具)編寫。CasperJS簡化了完整的導航場景的過程定義,提供了用於完成常見任務的實用的高級函數、方法和語法。它的寫法與JS、Jquery的方法相似,咱們能夠在他的腳本中使用js或者Jquery的函數。
因爲官方文檔寫的不是很詳細,所以對於初學者來講也是有必定難度的。且它的抓取效率也是秒級的。因爲它不是Java編寫的,所以咱們須要經過相關的方法來調用腳本。它是基於PhantomJS的,所以咱們也須要安裝PhantomJS,配置相關的環境變量。
官方地址http://docs.casperjs.org/en/latest/installation.html
調用CasperJs的腳本,來獲得頁面源代碼,示例代碼以下:
public static void useSperjs() { String url = "http://detail.tmall.com/item.htm?spm=a220m.1000858.1000725.1.MbFIev&id=45844061725&skuId=93180315832&areaId=110000&cat_id=52810004&rn=21d1496d974e4f9984611fad013f1696&user_id=728628560&is_b=1"; long start = System.currentTimeMillis(); try { Process exec = Runtime.getRuntime().exec("casperjs D:/jade/casperjs/jadeFile/test.js --url=" + url); InputStream in = exec.getInputStream(); InputStreamReader isr = new InputStreamReader(in, "gbk"); BufferedReader br = new BufferedReader(isr); String line = null; StringBuilder sb = new StringBuilder(); while ((line = br.readLine()) != null) { sb.append(line + "\n\r"); } System.out.println(sb.toString()); } catch (IOException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("casperjs time:" + (end - start) / 1000); } |
示例腳本以下:
//test.js var casper = require('casper').create({ verbose: false, logLevel: 'debug', waitTimeout: 10000, pageSettings:{ loadImages: false, // The WebPage instance used by Casper will loadPlugins: false // use these settings } }); phantom.outputEncoding = "gbk";//解決亂碼問題 phantom.cookiesEnabled = true; var url =casper.cli.get('url');
casper.start(url, function() { this.scrollToBottom(); casper.GetDetailUrl(url); });
casper.GetDetailUrl = function(detailUrl) { casper.thenOpen(detailUrl, function() { console.log(this.getCurrentUrl()); }); };
casper.then(function getPic() { return this.echo(this.getHTML()); }); casper.run(); |
咱們能夠經過上述的方式來獲得AJax加載後的源代碼,而後經過流的方式取得頁面的源碼,不過這個須要咱們掌握CasperJs腳本的編寫規則。在性能方面也是秒級的。
5 HttpClient模擬請求
這種狀況相對於前面全部的方法來講就比較複雜了,由於它須要咱們對網頁進行分析,因爲不一樣的網站結構不同,所以採用這種方式須要咱們對網頁請求進行分析。通常都須要藉助一些專業的工具如httpwatch等。
好比咱們要提取價格,咱們須要找到價格對應的請求連接,而後再去請求數據,這種狀況在提取多個屬性信息的時候也是不方便的,由於涉及到屢次請求,並且通常不具備通用性。好處是httpclient抓取性能較高,抓取效率是毫秒級的。
6 總結
從簡單的測試結果來看,我認爲HtmlUnit、Selenium WebDriver的PhantomJSDriver都是不錯的選擇,模擬瀏覽器簡單易用且都能知足咱們目前的需求。
以上是我這幾天的調查結果,有不少的不足之處,對於本人粗陋的總結,對於後期的工做就當是拋磚引玉吧。