这几天对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都是不错的选择,模拟浏览器简单易用且都能知足咱们目前的需求。
以上是我这几天的调查结果,有不少的不足之处,对于本人粗陋的总结,对于后期的工做就当是抛砖引玉吧。