Java爬虫进阶-Selenium+PhantomJs的运用

来源:互联网 发布:windows内部运行机制 编辑:程序博客网 时间:2024/06/06 00:15

原文:http://blog.csdn.net/smile_miracle/article/details/70817088

selenium
Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE、Mozilla Firefox、Mozilla Suite等。这个工具的主要功能包括:测试与浏览器的兼容性——测试你的应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创建衰退测试检验软件功能和用户需求。支持自动录制动作和自动生成。Net、Java、Perl等不同语言的测试脚本。Selenium 是ThoughtWorks专门为Web应用程序编写的一个验收测试工具。

phantomjs

一、phantomjs介绍
(1)一个基于webkit内核的无头浏览器,即没有UI界面,即它就是一个浏览器,只是其内的点击、翻页等人为相关操作需要程序设计实现。
(2)提供JavaScript API接口,即通过编写js程序可以直接与webkit内核交互,在此之上可以结合Java语言等,通过java调用js等相关操作,从而解决了以前c/c++才能比较好的基于webkit开发优质采集器的限制。
(3)提供windows、Linux、mac等不同os的安装使用包,也就是说可以在不同平台上二次开发采集项目或是自动项目测试等工作。
二、phantomjs常用API介绍
近几天的学习找了不少资料,也包括官网的,但其相关学习资料还是相对较少的,很多问题都是进行N多测试才搞清楚,在此耗费了不少时间。在学习时,结合官网和本篇博文效果会更好。
(1)常用内置几大对象
1 var system=require(‘system’); //获得系统操作对象,包括命令行参数、phantomjs系统设置等信息

2 var page = require(‘webpage’); //获取操作dom或web网页的对象,通过它可以打开网页、接收网页内 容、request、response参数,其为最核心对象。

3 var fs = require(‘fs’); //获取文件系统对象,通过它可以操作操作系统的文件操作,包括read、write、 move、copy、delete等。

二、常用API
1 page.open(url,function (status) {} //通过page对象打开url链接,并可以回调其声明的回调函数,其回调发生的时机为该URL被彻底打开完毕,即该URL所引发的请求项被全部加载完,但ajax请求是与它的加载完成与否没有关系

2 page.onLoadStarted = function() {}//当page.open调用时,回首先执行该函数,在此可以预置一些参数或函数,用于后边的回调函数中

3 page.onResourceError = function(resourceError) {} //page的所要加载的资源在加载过程中,出现了各种失败,则在此回调处理

4 page.onResourceRequested = function(requestData, networkRequest) {} //page的所要加载的资源在发起请求时,都可以回调该函数

5 page.onResourceReceived = function(response) {} //page的所要加载的资源在加载过程中,每加载一个相关资源,都会在此先做出响应,它相当于http头部分, 其核心回调对象为response,可以在此获取本次请求的cookies、userAgent等

6 page.onConsoleMessage = function (msg) {}//欲在执行web网页时,打印一些输出信息到控制台,则可以在此回调显示。

7 page.onAlert = function(msg) {} //phantomjs是没有界面的,所以对alert也是无法直接弹出的,故phantomjs以该函数回调在page在执行过程中的alert事件

8 page.onError = function(msg, trace) {} //当page.open中的url,它自己(不包括所引起的其它的加载资源)出现了异常,如404、no route to web site等,都会在此回调显示。

9 page.onUrlChanged = function(targetUrl) {} // 当page.open打开的url或是该url在打开过程中基于该URL进行了跳转,则可在此函数中回调。

10 page.onLoadFinished = function(status){} // 当page.open的目标URL被真正打开后,会在调用open的回调函数前调用该函数,在此可以进行内部的翻页等操作

11 page.evaluate(function(){});// 在所加载的web page内部执行该函数,像翻页、点击、滑动等,均可在此中执行

12 page.render(“”);//将当前page的现状渲染成图片,输出到指定的文件中去。

三、注意事项

1、区分phantomjs的对象和打开的web page的对象,如document、window等,两者都有,在调用page.evaluate和不调用的时候,注意区分二者的范围,容易在调试时出现很多的问题,且不好发现。
2、page.injectJs和page.includeJs的区别,前者侧重本地的js文件,与libraryPath挂购,后者侧重网络js文件,尤其在引入jQuery等第三方库时,会经常遇到。
3、编码问题,两个重要参数,–output-encoding,–script-encoding,前者为输出编码,后者为所使用js、参数配置文件的编码,为方便起鉴,建议均采用utf-8编码,并注所应用到的目标文件的编码,以免引起很不可思议的异常,又无从查起。

四、百度元搜索采集器

主要是JavaSE+js+phantomjs的应用,
(1) 编写好js脚文程序,预留出所有可配置参数,并提供json文件传输相关参数。
(2) 通过java程序,定义相关参数并生成对应的json文件。
(3) 通过java命令行调用API,调用phantomjs命令,并传入js、配置文件路径,从而开启爬虫。
(4) 首先采集关键词的搜索页的链接集合,最后统一去遍历采集具体的对象网页。

五、应用小结

经过上述的项目实测应用,它将非常方便的应用于模拟登陆,如微博、电商类,或是小米、火车票抢票等项目中,下一步计划将其与上述项目结合,开发更有意思的项目。

上面是这两个技术的简单介绍即用法和注意事项,下面来说说phantomjs的具体安装和使用,它其实就是一个无UI界面的浏览器,有自己的内核的浏览器,对css,js等的支持很强大,而且它配合selenium可以模拟你页面上的登陆和其他点击操作获取下级页面的内容,对于写爬虫帮助很大。

phantomjs的安装

如果爬虫只是在本地运行那么很简单,直接去官网卸载一个window版的phantomjs.exe文件,放入到自己定义好的本地文件夹就好,文件夹要记住,后面爬虫要用到;但是如果你写的爬虫需要在服务器上运行那么就有一点小复杂,详情跳转:phantomjs安装linux版,安装完毕后我们就可以来进行我们的爬虫程序的编写,下面就说一下我写的一个根据QQ号码爬取此QQ的第一条动态的爬虫程序,前提是此QQ没设权限,设了权限如何突破没深入研究。

爬虫技术的使用

由于csdn限制了文件上传大小,这里不能把jar包全放上去,那么我就截图给大家看看用到哪些包,其中有些包是多余的没时间去剔除:

这里写图片描述

这里写图片描述

打红色的jar包都很重要,先贴上整体代码:

package com.lyitong.crawler.QQ;import java.io.IOException;import java.net.MalformedURLException;import java.util.concurrent.TimeUnit;import org.openqa.selenium.By;import org.openqa.selenium.By.ByXPath;import org.openqa.selenium.WebElement;import org.openqa.selenium.phantomjs.PhantomJSDriver;import org.openqa.selenium.phantomjs.PhantomJSDriverService;import org.openqa.selenium.remote.DesiredCapabilities;import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;public class GetUserInformationFromQQ {    /**     * @Title: main     * @Description: 通过QQ号码查询用户最新动态 ,只针对没设权限空间,设了权限此方法无用    * @param @param args     * @return void    * @throws IOException     * @throws MalformedURLException     * @throws FailingHttpStatusCodeException     * @throws     * @date 2016年9月27日下午5:30:46    */    public static void main(String[] args) throws Exception {            //设置必要参数                DesiredCapabilities dcaps = new DesiredCapabilities();                //ssl证书支持                dcaps.setCapability("acceptSslCerts", true);                //截屏支持                dcaps.setCapability("takesScreenshot", true);                //css搜索支持                dcaps.setCapability("cssSelectorsEnabled", true);                //js支持                dcaps.setJavascriptEnabled(true);             //设置代理        Proxy proxy = new Proxy();        proxy.setHttpProxy("ip:port").setFtpProxy("ip:port").setSslProxy("ip:port");        dcaps.setCapability(CapabilityType.ForSeleniumServer.AVOIDING_PROXY, true);        dcaps.setCapability(CapabilityType.ForSeleniumServer.ONLY_PROXYING_SELENIUM_TRAFFIC, true);        System.setProperty("http.nonProxyHosts", "localhost");        dcaps.setCapability(CapabilityType.PROXY, proxy);                              //设置请求头        dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_PAGE_CUSTOMHEADERS_PREFIX + "Accept-Language", "en-US");        dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_PAGE_CUSTOMHEADERS_PREFIX + "Accept", "*/*");        dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_PAGE_CUSTOMHEADERS_PREFIX + "User-Agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)");        dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_PAGE_CUSTOMHEADERS_PREFIX + "Connection", "keep-alive");                            //驱动支持                dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,"C:\\Python34\\Scripts\\phantomjs.exe");                //创建无界面浏览器对象                PhantomJSDriver driver = new PhantomJSDriver(dcaps);        try {               // 让浏览器访问空间主页                driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);                driver.get("http://xui.ptlogin2.qq.com/cgi-bin/xlogin?proxy_url=http%3A//qzs.qq.com/qzone/v6/portal/proxy.html&daid=5&&hide_title_bar=1&low_login=0&qlogin_auto_login=1&no_verifyimg=1&link_target=blank&appid=549000912&style=22&target=self&s_url=http%3A%2F%2Fqzs.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone&pt_qr_app=%E6%89%8B%E6%9C%BAQQ%E7%A9%BA%E9%97%B4&pt_qr_link=http%3A//z.qzone.com/download.html&self_regurl=http%3A//qzs.qq.com/qzone/v6/reg/index.html&pt_qr_help_link=http%3A//z.qzone.com/download.html");                driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);                Thread.sleep(1000L);                WebElement pwdLoginbutton = driver.findElement(By.id("bottom_qlogin")).findElement(By.id("switcher_plogin"));                pwdLoginbutton.click();                //获取账号密码输入框的节点                WebElement userNameElement = driver.findElement(By.id("u"));                WebElement pwdElement = driver.findElement(By.id("p"));                userNameElement.sendKeys("2437801435");                pwdElement.sendKeys("lyt123456");                //获取登录按钮                driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);                WebElement loginButton = driver.findElement(By.id("login_button"));                loginButton.click();                        //设置线程休眠时间等待页面加载完成                        Thread.sleep(1000L);                //获取新页面窗口句柄并跳转,模拟登陆完成                    String windowHandle = driver.getWindowHandle();                driver.switchTo().window(windowHandle);                //设置说说详情数据页面的加载时间并跳转                driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);                driver.get("http://ic2.s21.qzone.qq.com/cgi-bin/feeds/feeds_html_module?i_uin=564227332&i_login_uin=2437801435&mode=4&previewV8=1&style=25&version=8&needDelOpr=true&transparence=true&hideExtend=false&showcount=5&MORE_FEEDS_CGI=http%3A%2F%2Fic2.s21.qzone.qq.com%2Fcgi-bin%2Ffeeds%2Ffeeds_html_act_all&refer=2&paramstring=os-winxp|100");                //获取要抓取的元素,并设置等待时间,超出抛异常                driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);                //设置设置线程休眠时间等待页面加载完成                Thread.sleep(1000L);                WebElement firstTalk = driver.findElement(ByXPath.xpath("/html/body/div[1]/div[1]/ul/li[1]/div[2]/div/div[1]"));                WebElement talkTime = driver.findElement(ByXPath.xpath("/html/body/div[1]/div[1]/ul/li[1]/div[1]/div[2]/div[2]/span[1]"));                String content = firstTalk.getText();                String time = talkTime.getText();                System.out.println("content="+content+"========="+"time="+time);                } catch (Exception e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }finally{                    //关闭并退出浏览器                    driver.close();                    driver.quit();                }            }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

说完JAR包问题那么我们来看看程序到底如何写:

①设置请求头

//设置必要参数DesiredCapabilities dcaps = new DesiredCapabilities();//ssl证书支持dcaps.setCapability("acceptSslCerts", true);//截屏支持dcaps.setCapability("takesScreenshot", true);//css搜索支持dcaps.setCapability("cssSelectorsEnabled", true);//js支持dcaps.setJavascriptEnabled(true);//驱动支持                dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,"C:\\Python34\\Scripts\\phantomjs.exe");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这是一些基本配置,像css支持,js支持截屏还有证书支持等等,也许你会问为什么我不直接把CHROME.EXE浏览器设为默认浏览器?你想想因为谷歌是有界面的啊,你可以设置一个啊,其结果就是你程序一跑马上本地的谷歌被自动打开了,然后他会在谷歌里面找到搜索框自动输入数据做搜索什么的,如果你要看效果可以试试,但是爬虫的话这样干就行不通,所以我们选型这个phantomjs无UI界面的浏览器。要说一句的是如果你的程序要在linux上跑,那么这个路径你就得换成你安装phantomjs时服务器路径。

②创建phantomjs浏览器对象

//创建无界面浏览器对象PhantomJSDriver driver = new PhantomJSDriver(dcaps);
  • 1
  • 2
  • 1
  • 2

③开始链接页面

这里要说一下:driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);

他是隐式等待, 此处的隐式等待是针对Driver 每次执行命令的 最长执行时间也可以理解为超时时间, 一些人对此处有误解,认为是让Driver等一段时间, 确实某些时候能让Driver等一段时间, 但是影响是全局的,每次Driver执行 找不到元素都会等待此处设置的时间, 假设某处将此值设置的太长,必须在执行完成之后还原回来,否则判断一个元素是否存在的时候,就会遇到很坑爹的问题。 改进后的方法如下:
WebDriver会进行一个隐式等待,但参数只有时间,这就导致我需要什么元素出现,我不一定能等到它

 driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);  driver.findElement(by); 
  • 1
  • 2
  • 1
  • 2

得不到某个元素,我们就延迟一下…

写到这里显得讲解一下,这个爬虫实现的原理,你拿到最终的空间链接修改参数是没有用的,你必须要从头到尾模拟一次登陆,这个登陆账号你如果不想自己号被封那么就去腾讯申请,然后用这个号开通空间,等这个号登陆后获取下级页面句柄跳过去检查元素看存不存在,存在登陆成功你就可以基于这个号跳转到别人的QQ空间,前提是没设权限,因为phantomjsdriver跟谷歌,火狐一样的,它会自动记录下你的操作,就像你在浏览器上操作一样。

重要操作

phantomjsdriver提供了非常健全的页面元素选择器,这里就不多说,你通过定位你需要的元素可以做很多事,比如说我定位到了一个按钮或者超链接,你就可以直接模拟点击,也就是click()方法,基于浏览器有加载时间,一旦你模拟点击一个操作后,做好让线程休眠,具体时间可以自己调试,因为如果你不设置程序会直接跑下去而driver里面的数据还没加载过来;还有就是如果你模拟了任何跳转其他页面的操作的话,那么有一段代码必不可少:

//获取新页面窗口句柄并跳转,模拟登陆完成String windowHandle = driver.getWindowHandle();driver.switchTo().window(windowHandle);
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

为什么不能丢了这段代码的重点在于你浏览器打开了这么多页面,每个页面都有句柄,我们通过这个句柄跳转具体页面,如果没有句柄,他会默认你还在当前这个句柄的页面上,那么你是永远也获取不到跳转页面的数据的。

总结

写爬虫是一个很枯燥很需要耐心的工作,调试要花很长的时间,selenium+phantomjs是一款非常棒的爬虫技术,他相比于htmlunit而言要复杂不假,但是人家对js,css还有页面的模拟操作都比前者更好,当获取不到数据时不要急,多断点多设线程休眠,看是不是资源加载没成功。