最近在作项目的时候有一个需求:从网页面抓取数据,要求是首先抓取整个网页的html源码(后期更新要使用到)。刚开始一看这个简单,而后就稀里哗啦的敲起了代码(在这以前使用过Hadoop平台的分布式爬虫框架Nutch,使用起来是很方便,可是最后由于速度的缘由放弃了,但生成的统计信息在后来的抓取中使用到了),很快holder.html和finance.html页面成功下载完成,而后解析完holder.html页面以后再解析finance.html,而后很沮丧的发如今这个页面中我须要的数据并无在html源码中,再去浏览器查看源码果真是这样的,在源码中确实没有我须要的数据,看来不是我程序写错了,接下来让人身心疲惫的事情来了---获取包含动态内容的html页面。html
在所谓的中国最强搜索引擎---百度上面行走了好长的时间,发现大部分的人都在将使用WebDriver和HttpUnit(其实前者已经包含了后者),这个高兴,终于找到了解决办法。怀着万分的激动使用WebDriver,我要想骂人了。java
下面是关于WebDriver的吐槽web
WebDriver是一个测试框架,本来设计的时候就不是用来服务爬虫的,可是我想说的是:八字就差一撇了,你就不能多往前作一步吗?为何网上还有那么多的人推荐WebDriver呢?我想这些人没有从实际出发,甚至还有的人狂言WebDriver能够解析完成后的页面返回给想要爬去整个页面的人(包含动态生成的内容),对,WebDriver能够完成这个任务,可是看到做者写的代码,我想说的是:哥们,你的代码局限性太大了,解析本身写的js代码,并且js代码简单,这样WebDriver固然是毫无压力的完成任务。WebDriver在解析动态内容是要看js代码的复杂性和多样性。浏览器
什么是复杂性?框架
先贴一段代码jvm
WebDriver driver = newInternetExplorerDriver (); HtmlPage page = driver.get(url); System.out.println(page.asXml());
这一段代码的意思是相信你们都看懂,上面使用的IE内核,固然还有FirefoxDriver, ChromeDriver,HtmlUnitDriver,这些driver的使用原理都是同样的,先开启浏览器(这个要时间的),而后加载url并完成动态解析,而后经过page.asXml()就能够获得完成的html页面,其中HtmlUnitDriver模拟无界面浏览器,java中有执行js的引擎rhino,HtmlUnitDriver使用的就是rhino来解析js的,因为不会去启动有界面的浏览器,因此HtmlUnitDriver的速度比前面的三者都快。不管是什么Driver,避免不了的是解析js,这是须要时间的,并且不用的内核对js的支持程序又是不一样,好比说HtmlUnitDriver对于带有滚动的js代码支持不好,在执行时会报错(亲自体验了)。js代码的复杂的意思就是:对于不一样的内核他们支持的js是不彻底相同的,这个应该根据具体状况来定,鄙人很久没有研究js了,因此关于各内核对js的支持就不说了。分布式
什么是多样性工具
前面说了,浏览器解析js是须要时间的。对于只嵌入少数的js代码的页面来讲,经过page.asXml()来获取完整的页面时没有问题的。可是对于嵌入比较多的js代码的页面,解析js是须要不少时间的(对于jvm来讲),那么此时经过page.asXml()来获取的页面中大多数时候是不包含有动态生成的内容的。问题就来了,这样的话为何还说WebDriver能够得到包含有动态内容的html页面呢?网上有人说在driver.get(url)以后须要是当前线程等待一下才能获取完成的页面,也就是相似于下面的形式oop
WebDriver driver = new InternetExplorerDriver(); HtmlPage page = dirver.get(url); Thread.sleep(2000); System.output.println(page.asXml());
我按照这个想法去尝试如下,呀,真的是能够。可是问题不正好也摆在那里了么?怎么样去肯定等待时间?相似于数据挖掘中肯定阀值时的凭经验的方法?,仍是尽量的是时间长一点。我以为这些都不是很好的办法,时间代价比较大。我就想在driver应该能够捕获解析js完成后的状态,因而我去找啊,找啊,但是根本就没有这个方法,因此我说WebDriver的设计者为何再也不往前走一步,让咱们能够在程序中获取到driver解析js完成后的状态,这样的话就不用使用Thread.sleep(2000)这样的不肯定性代码了,惋惜的是怎么也找不到,真是让我心痛了一场。FirefoxDriver, ChromeDriver,HtmlUnitDriver也有一样的问题,能够说使用WebDriver来辅助爬去动态生成的网页所获得的结果是很不稳定的。这一点我是深有体会,使用IEDriver的时候,同一个页面两次爬取的结果会出现不同,并且甚至有时候IE直接挂掉,你说这样的东西大家敢用在爬虫程序中吗?我是不敢的。post
另外还有就是有人推荐使用HttpUnit,其实WebDirver中HtmlUnitDriver在内部使用的就是httpUnit,因此使用HttpUnit也会遇到一样的问题,我也作了实验,确实是这样。经过Thread.sleep(2000)来等待js的解析完成,我以为不可取的办法。不肯定性太大了,特别是在大型的抓取工做中。
总结一下,WebDriver是为测试而设计的框架,虽然按照其原理理论上能够用来辅助爬虫获取包含有动态内容的html页面,可是在实际的应用中是不取的,不肯定性太大了,稳定性太差,速度太慢,咱们仍是让框架各尽其值吧,不要折煞了他们的优势。
个人工做没有完成,因此继续去网上需找办法,此次找到了一个稳定的,肯定性高的辅助工具---phantomjs,目前我还不彻底了解这个东西。可是目前已经用它来实现了我想要的功能。在java中经过runtime.exec(arg)来调用phantomjs获得解析js后的页面。我仍是把代码贴出来吧
phantomjs端要执行的代码
system = require('system') address = system.args[1];//得到命令行第二个参数 接下来会用到 //console.log('Loading a web page'); var page = require('webpage').create(); var url = address; //console.log(url); page.open(url, function (status) { //Page is loaded! if (status !== 'success') { console.log('Unable to post!'); } else { //此处的打印,是将结果一流的形式output到java中,java经过InputStream能够获取该输出内容 console.log(page.content); } phantom.exit(); });
java端执行的代码
public void getParseredHtml(){ String url = "www.bai.com"; Runtime runtime = Runtime.getRuntime(); runtime.exec("F:/phantomjs/phantomjs/phantomjs.exe F:/js/parser.js "+url); InputStream in = runtime.getInputStream(); //后面的代码省略,获得了InputStream就好说了 }
这样的话在java端就能够得到解析完成后的html页面了,而不是像WebDriver中须要使用Thread.sleep()这样的不肯定性的代码来获取可能完成的代码。有一点须要说明:在phantomjs端的js代码千万不要要语法错误,不然js代码编译不一样的话,java端就一直等待着,并不会抛异常。再就是因为在使用phantomjs.exe的时候,java端每次都要去开启一个phantomjs进程,时间上消耗仍是比较大的。可是最起码来讲结果是稳定的。固然最后我尚未使用phantomjs,我直接download须要的数据,并无去抓取整个完整的页面,主要是速度方面的问题(其实,我不敢用是由于phantomjs不熟悉,因此我慎用)。
折腾了几天,虽然没有解决个人问题,可是见识长了很多,后期的工做是熟悉phantomjs,看能不能再速度方面提高,要是能打破速度的框框,之后再爬去网页的时候就驾轻就熟了,再者就是Nutch这个框架,我佩服着哥们在使用的时候方便性,全部后期颇有必要研究下如何优化Nutch在Hadoop上的抓取速度,另外,Nutch原始的功能中也不会抓取动态生成的页面内容,可是可使用Nutch和WebDirver结合,说不定抓取的结果稳定了,哈哈,这些只是构想,可是不尝试怎么知道呢?
若是园友对于使用WebDriver辅助爬虫所获得的结果的稳定性方面有要说的,欢迎各位啊,由于我确实没有找相关的资料来稳定爬去结果。