如何用JAVA爬取AJAX加载后的页面(利用phantomjs)【以天眼查为例】

之前

之前利用Jsoup做了个小DEMO爬取百度免费API(* http://apistore.baidu.com/ ),很简单,用Jsoup就可以做到,因为页面加载后的数据填充到html里面,此时查看源代码会看见数据都在源代码里面,这时候可以利用Jsoup爬取,前几天有个需求要爬取天眼查的数据( http://www.tianyancha.com *),自以为和之前做的DEMO一样,不难,就利用原来的DEMO改改,之后居然获取不到想要的。

需求:搜索(条件包括搜索框,注册资本,地区)

以下是我将要爬取的页面:


搜索:有限

习惯性审查一下要爬取的元素:


这部分是主页面

胸有成竹代码就出来:

Document doc = Jsoup.connect("http://www.tianyancha.com/search?key=有限").ignoreHttpErrors(true).timeout(100000).get();
System.out.println(doc.body());

执行--》

<body ng-class="isFromMac ? '':'windows-modal'"> 
 <!-- <body class="b-c13" ng-class="{'windows-modal':!isFromMac, 'bodyOverHidden':bodyHidden, 'bodyOverShow':!bodyHidden}"> --> 
 <div id="loading" class="loading" loading=""></div> 
 <div id="ng-view" ng-view=""></div> 
 <!-- banner --> 
 <div ng-cloak="" ng-controller="bannerCtrl"> 
  <!-- ![](http://static.tianyancha.com/wap/images/element.png?_v=)</img> --> 
  <div ng-cloak="" ng-if="isPCClient" class="bottom-banner"> 
   <!-- <div class="bottom-banner-body"   ng-show="showBanner" >
            ![app下载](http://upload-images.jianshu.io/upload_images/3810137-5c0f1e424921f6e4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
            <div class="bottom-banner-qrcode"  >
                <img class="bottom-banner-qrcode-img" alt="qrcode"
                     src="http://static.tianyancha.com/wap/images/banner-bottom-qrcode.png">
            </div>
            <img class="bottom-banner-close" alt="close"
                 ng-click="ctrlBanner(false);"
                 src="http://static.tianyancha.com/wap/images/banner-bottom-close.png">
        </div> --> 
   <div class="bottom-banner-body2" ng-if="showBanner"> 
    <div class="bottom-banner-box2 company_container"> 
     ![](http://upload-images.jianshu.io/upload_images/3810137-a12c9c8e1c375522.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 
     <div class="code_box"> 
      ![](http://upload-images.jianshu.io/upload_images/3810137-e7840a271fd37e8e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 
     </div> 
     ![](http://upload-images.jianshu.io/upload_images/3810137-2dc1551260095554.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 
     ![](http://upload-images.jianshu.io/upload_images/3810137-2f8c0c671b889cd0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 
    </div> 
   </div> 
   ![app下载](http://upload-images.jianshu.io/upload_images/3810137-41c6fa56e3e5e7e5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 
   <!-- <div ng-show="showAdvertise" advertise-banner></div> --> 
  </div> 
  <div ng-show="!isPCClient&&!isTYCClient&&!isWebAppClient"> 
   <div ng-cloak="" ng-show="!showBannerMobile" style="position: fixed;left: 0;bottom:90px;" ng-click="closeClick(showBannerMobile);"> 
    ![下载天眼查专业版APP](http://upload-images.jianshu.io/upload_images/3810137-93ed2e33708d2d46.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 
   </div> 
   <div class="bottom-banner-mobile" ng-cloak="" ng-show="showBannerMobile"> 
    ![下载天眼查专业版APP](http://upload-images.jianshu.io/upload_images/3810137-0726860681c21931.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 
    ![下载天眼查专业版APP](http://upload-images.jianshu.io/upload_images/3810137-fd3b233ccd8b1c0a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 
    <span> <a href="http://app.tianyancha.com?source=wap" target="_blank"> ![下载天眼查专业版APP](http://upload-images.jianshu.io/upload_images/3810137-d73cd12dbd35be37.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) </a> </span> 
    ![关闭](http://upload-images.jianshu.io/upload_images/3810137-f2a78be96ebe8a57.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 
   </div> 
  </div> 
 </div> 
 <script>var gdt_tracker=gdt_tracker||[];gdt_tracker.push(["set_source_id","33937"]);!function(){var e=navigator.userAgent;if(e.indexOf("ignore")<0){var t=document,r=t.getElementsByTagName("head")[0],a=t.createElement("script");a.async=!0;a.src="http://qzs.qq.com/qzone/biz/res/gt.js";r&&r.insertBefore(a,r.firstChild)}}();</script> 
 <script>var _hmt=_hmt||[];!function(){var e=navigator.userAgent;if(e.indexOf("ignore")<0){var t=document.createElement("script");t.src="//hm.baidu.com/hm.js?e92c8d65d92d534b0fc290df538b4758";var r=document.getElementsByTagName("script")[0];r.parentNode.insertBefore(t,r)}}();</script> 
 <!-- <script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="//hm.baidu.com/hm.js?9c350b9e33b321dffebc9cc49db07828";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}();</script> --> 
 <!-- Piwik --> 
 <script type="text/javascript">var _paq=_paq||[];_paq.push(["trackPageView"]);_paq.push(["enableLinkTracking",!0]);!function(){var e="//www.tianyancha.com/piwik/";_paq.push(["setTrackerUrl",e+"piwik.php"]);_paq.push(["setSiteId",1]);var a=document,p=a.createElement("script"),t=a.getElementsByTagName("script")[0];p.type="text/javascript";p.async=!0;p.defer=!0;p.src=e+"piwik.js";t.parentNode.insertBefore(p,t)}();</script> 
 <noscript>
  <p>[站外图片上传中……(13)]</p>
 </noscript> 
 <!-- End Piwik Code --> 
 <!--<script src="http://api.map.baidu.com/api?v=2.0&ak=E1r6u1CD9SzZ08IxP32bNjgz"></script>-->   
 <!--coolie@1.0.8-->
</body>

WTF?

只有这点东西?明明我审查元素的时候有那么多?为什么抓取出来就这么点?这不科学。

爬取的2个办法:

  • 利用Jsoup爬取
  • 利用Ajax请求返回的数据

其一不行,取其二
通过Ajax请求数据,再填充到body里面,于是研究Ajax请求,通过Chrome的NetWork可以抓到:

NetWork

这个接口返回的就是列表的数据,欣喜若狂,这也太简单了,双击访问出现:

错误

WTF²

这。。到底是咋回事
在搜索了很多文章后发现,这个网站本来就是从各大政府网站爬取过来的数据,哪能那么容易就轻松被别人爬取走。况且:

反爬虫工程师

失望中另辟蹊径

失望中又想到另外的办法,既然不能通过Jsoup去爬取,那么是不是有方法可以模拟人为访问,访问后加载完所有的css和js,等数据都返回,再爬取,这不就可以了。
开启搜索引擎一阵搜:

  1. HttpUnit (http://blog.csdn.net/hfhwfw/article/details/37838615)
  1. HtmlUnit (https://my.oschina.net/MiniBu/blog/140729)
  2. WebDriver (https://my.oschina.net/dyhunter/blog/94090)

以上方法都是做自动化测试用的,根本不是用来爬取数据的,所以兼容性不能不是很好。各种尝试都没有得到想要的效果。
最后发现了phantomjs ( http://phantomjs.org/ )。
下载phantomjs后终端就行,加入环境变量自行百度,此处不再多说。

windows下载对应的phantomjs.exe

运行:

/User/music-man/Downloads/phantomjs/phantomjs /User/music-man/Downloads/phantomjs/code.js http://www.tianyancha.com/search?key=有限

注意中间的空格。第一个是phantomjs,第二个是code.js的路径,第三个是爬取的路径。
code.js

system = require('system')  
address = system.args[1];
var page = require('webpage').create();  
var url = address;  
page.open(url, function (status) {  
    //Page is loaded!  
    if (status !== 'success') {  
        console.log('Unable to post!');  
    } else {  
            console.log(page.content);
            phantom.exit();
    } 
  });   

此时终端执行命令,发现整个页面已经爬取下来。
接下来就是如何与Java结合了。
执行命令Java可以这么做

Runtime rt = Runtime.getRuntime();
String exec = "/Users/music-man/Downloads/phantomjs/phantomjs /Users/music-man/Downloads/phantomjs/code.js " + url;
Process p = rt.exec(exec);
InputStream is = p.getInputStream();

这样就可以获得输入流了,获得输入流之后想要怎么操作就简单了吧。
获取了文件流,想操作dom,如何操作呢?
看了一下Jsoup,发现


输入流转Jsoup
public static Document parse(InputStream in, String charsetName, String baseUri) 

第一个参数是输入流,第二个是字符集,第三个是地址:

Document doc = Jsoup.parse(is, "UTF-8", url);

获取到Document再操作dom元素就很明了了。
最后用JFrame做了个界面

不稳定

执行起来发现不稳定,有时候能爬取到页面,有时候就会失败,让我以为是网站做的限制,后来发现多次重复爬取效率过快的话网站会让输入验证码,导致卡住。

解决

爬取不到我初步怀疑是因为爬取的时候页面还没加载完毕,就进行抓取,有时候网速快,加载好了就能抓取到,有时候没有加载好,爬取失败。看了下phantomjs例子(https://github.com/ariya/phantomjs/blob/master/examples/page_events.js)
发现可以采用js的方法setTimeout
最终code.js改为

system = require('system')  
address = system.args[1];
var page = require('webpage').create();  
var url = address;  
page.open(url, function (status) {  
    //Page is loaded!  
    if (status !== 'success') {  
        console.log('Unable to post!');  
    } else {  
            window.setTimeout(function () {
              page.render("test1.png");  //截图
              console.log(page.content);
              phantom.exit();
          }, 5000);   
    } 
  });   

增加了setTimeout方法后,等待5s差不多执行完页面和js,此时再去抓取页面,发现成功率大大提高。

(2017年12月05日 删除图片,泄漏隐私了)

抓取到后再把需要的写出文件就可以了。

转载请注明出处,谢谢。码了一个多小时

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容