Java爬蟲進階-Selenium+PhantomJs的運用

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編碼,並注所應用到的目標文件的編碼,以免引起很不可思議的異常,又無從查起。

四、百度元搜索採集器

主要是Java SE+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);
                //驅動支持
                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();
                }

            }
}

說完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");

這是一些基本配置,像css支持,js支持截屏還有證書支持等等,也許你會問爲什麼我不直接把CHROME.EXE瀏覽器設爲默認瀏覽器?你想想因爲谷歌是有界面的啊,你可以設置一個啊,其結果就是你程序一跑馬上本地的谷歌被自動打開了,然後他會在谷歌裏面找到搜索框自動輸入數據做搜索什麼的,如果你要看效果可以試試,但是爬蟲的話這樣幹就行不通,所以我們選型這個phantomjs無UI界面的瀏覽器。要說一句的是如果你的程序要在linux上跑,那麼這個路徑你就得換成你安裝phantomjs時服務器路徑。

②創建phantomjs瀏覽器對象

//創建無界面瀏覽器對象
PhantomJSDriver driver = new PhantomJSDriver(dcaps);

③開始鏈接頁面

這裏要說一下:driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);

他是隱式等待, 此處的隱式等待是針對Driver 每次執行命令的 最長執行時間也可以理解爲超時時間, 一些人對此處有誤解,認爲是讓Driver等一段時間, 確實某些時候能讓Driver等一段時間, 但是影響是全局的,每次Driver執行 找不到元素都會等待此處設置的時間, 假設某處將此值設置的太長,必須在執行完成之後還原回來,否則判斷一個元素是否存在的時候,就會遇到很坑爹的問題。 改進後的方法如下:
WebDriver會進行一個隱式等待,但參數只有時間,這就導致我需要什麼元素出現,我不一定能等到它

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

得不到某個元素,我們就延遲一下…

寫到這裏顯得講解一下,這個爬蟲實現的原理,你拿到最終的空間鏈接修改參數是沒有用的,你必須要從頭到尾模擬一次登陸,這個登陸賬號你如果不想自己號被封那麼就去騰訊申請,然後用這個號開通空間,等這個號登陸後獲取下級頁面句柄跳過去檢查元素看存不存在,存在登陸成功你就可以基於這個號跳轉到別人的QQ空間,前提是沒設權限,因爲phantomjsdriver跟谷歌,火狐一樣的,它會自動記錄下你的操作,就像你在瀏覽器上操作一樣。

重要操作

phantomjsdriver提供了非常健全的頁面元素選擇器,這裏就不多說,你通過定位你需要的元素可以做很多事,比如說我定位到了一個按鈕或者超鏈接,你就可以直接模擬點擊,也就是click()方法,基於瀏覽器有加載時間,一旦你模擬點擊一個操作後,做好讓線程休眠,具體時間可以自己調試,因爲如果你不設置程序會直接跑下去而driver裏面的數據還沒加載過來;還有就是如果你模擬了任何跳轉其他頁面的操作的話,那麼有一段代碼必不可少:

//獲取新頁面窗口句柄並跳轉,模擬登陸完成
String windowHandle = driver.getWindowHandle();
driver.switchTo().window(windowHandle);

爲什麼不能丟了這段代碼的重點在於你瀏覽器打開了這麼多頁面,每個頁面都有句柄,我們通過這個句柄跳轉具體頁面,如果沒有句柄,他會默認你還在當前這個句柄的頁面上,那麼你是永遠也獲取不到跳轉頁面的數據的。

總結

寫爬蟲是一個很枯燥很需要耐心的工作,調試要花很長的時間,selenium+phantomjs是一款非常棒的爬蟲技術,他相比於htmlunit而言要複雜不假,但是人家對js,css還有頁面的模擬操作都比前者更好,當獲取不到數據時不要急,多斷點多設線程休眠,看是不是資源加載沒成功。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章