王超 閭陳莉 吳迪 項英杰
摘 要: 移動設(shè)備的普及和移動互聯(lián)網(wǎng)的高速發(fā)展讓人們開始依賴通過移動設(shè)備獲取信息,但普通頁面在移動端的體驗很難滿足用戶的需求。為此研究了HttpClient和Jsoup等技術(shù),分析了一般網(wǎng)站的登錄原理,成功實現(xiàn)了對一般網(wǎng)站的模擬登錄和頁面信息的提取,在此基礎(chǔ)上實現(xiàn)的系統(tǒng)可以在目標(biāo)網(wǎng)站中獲取用戶所需的信息,并將這些信息整合應(yīng)用到Android平臺,從而設(shè)計出用戶體驗更佳的網(wǎng)站Android客戶端。
關(guān)鍵詞: HttpClient; Jsoup; Android; 模擬登錄
中圖分類號:TP311 文獻(xiàn)標(biāo)志碼:A 文章編號:1006-8228(2014)03-30-03
0 引言
客戶端(Client)或稱為用戶端,是指與服務(wù)器相對應(yīng),為客戶提供本地服務(wù)的程序。而Android系統(tǒng)上90%的客戶端軟件具有一個共性:為了改善網(wǎng)頁在Android系統(tǒng)上體驗不佳而生。這類軟件最突出的特點是:先有網(wǎng)站再有客戶端。由于網(wǎng)絡(luò)技術(shù)發(fā)展的多樣性,手機(jī)瀏覽器往往無法跟隨它的步伐,為改善用戶體驗,網(wǎng)站客戶端軟件應(yīng)運(yùn)而生。
由于一般網(wǎng)站不提供服務(wù)端的開放平臺,我們無法調(diào)用所提供的API接口來開發(fā),所以需要對頁面信息進(jìn)行處理分析。在對網(wǎng)頁信息進(jìn)行處理分析時,不可能采用瀏覽器人工閱覽的方法,因此需要自己編寫分析處理程序,提取出有用的信息,為進(jìn)一步分析做準(zhǔn)備。由于原始HTML頁面的數(shù)據(jù)格式并不適合進(jìn)行處理[1],本研究將結(jié)合HttpClient與Jsoup對HTML頁面進(jìn)行抓取解析。
1 開發(fā)工具與環(huán)境的介紹
1.1 HttpClient
網(wǎng)頁抓取,就是把URL地址中指定的網(wǎng)絡(luò)資源從網(wǎng)絡(luò)流中讀取出來,保存到本地。類似于使用程序模擬IE瀏覽器的功能,把URL作為HTTP請求的內(nèi)容發(fā)送到服務(wù)器端,然后讀取服務(wù)器端的相應(yīng)資源[2]。雖然java.net包中已經(jīng)提供了訪問HTTP協(xié)議的基本功能,但是對于大部分應(yīng)用程序來說還不夠豐富和靈活。
HttpClient[3]是Apache Jakarta Common下的子項目,用來提供高效的、最新的、功能豐富的、支持HTTP協(xié)議的客戶端編程工具包,并且支持HTTP協(xié)議最新的版本和建議。HttpClient能夠處理HTTP連接中的各種問題,使用十分方便,只需導(dǎo)入HttpClient.jar包,就可以模擬IE瀏覽器來獲取網(wǎng)頁內(nèi)容。
1.2 Jsoup
Jsoup分析網(wǎng)頁結(jié)構(gòu)和內(nèi)容的功能遠(yuǎn)遠(yuǎn)強(qiáng)大于HtmlParser,無論是獲取整個頁面的文本,還是分析特定內(nèi)容的網(wǎng)頁結(jié)構(gòu),都十分方便。Jsoup數(shù)據(jù)獲取有兩大方法:①通過分析dom模型的網(wǎng)頁標(biāo)簽和元素;②select元素選擇器,類似Jquery方式。
1.3 Android
Android是一種基于Linux的自由及開放源代碼的操作系統(tǒng),主要適用于移動設(shè)備,如智能手機(jī)和平板電腦,由Google公司和開放手機(jī)聯(lián)盟領(lǐng)導(dǎo)及開發(fā)。2011年第一季度,Android在全球的市場份額首次超過塞班系統(tǒng),躍居全球第一。2012年11月的數(shù)據(jù)顯示,Android占據(jù)全球智能手機(jī)操作系統(tǒng)市場76%的份額,中國市場占有率為90%。在2013年09月24日Android操作系統(tǒng)迎來5歲生日時,全球采用這款系統(tǒng)現(xiàn)有設(shè)備數(shù)量已經(jīng)達(dá)到10億臺。
2 網(wǎng)站客戶端開發(fā)的設(shè)計
2.1 獲取數(shù)據(jù)的原理
圖1是一個簡單的示意圖,告訴我們,數(shù)據(jù)是由網(wǎng)頁從數(shù)據(jù)庫中取出,要為這個系統(tǒng)做客戶端,就應(yīng)該按照該數(shù)據(jù)獲取的原理去改造它,如圖2所示。
[網(wǎng)頁] [數(shù)據(jù)]
圖1 網(wǎng)頁從數(shù)據(jù)庫中提取數(shù)據(jù)
[安卓客戶端][網(wǎng)頁] [數(shù)據(jù)]
圖2 客戶端間接訪問數(shù)據(jù)庫
通過這樣間接的方法來訪問數(shù)據(jù)庫,只要網(wǎng)頁能看到的內(nèi)容,我們的客戶端都能獲取到,而UI是由用戶自行制作,可以使用戶體驗上一個臺階。
2.2 HttpClient模擬登錄原理
現(xiàn)如今不論是網(wǎng)站還是論壇,又或是一般的信息管理系統(tǒng),登錄功能都是必不可少的。因此我們?nèi)粝胍L問所需要的網(wǎng)站并從中獲取數(shù)據(jù),登錄是無法跳過的一個環(huán)節(jié)[4]。
因為HTTP協(xié)議是無狀態(tài)的,也就是連接的有效期只限于當(dāng)前請求,請求內(nèi)容結(jié)束后連接就關(guān)閉了。在這種情況下為了保存用戶的登錄信息必須使用Cookie機(jī)制[5]。以JSP/Servlet為例,當(dāng)瀏覽器請求一個JSP或者是Servlet頁面時,應(yīng)用服務(wù)器會返回一個參數(shù),名為jsessionid(因不同應(yīng)用服務(wù)器而異),其值是一個較長的惟一字符串的Cookie,這個字符串也就是當(dāng)前訪問該站點的會話標(biāo)識。瀏覽器每在訪問該站點的其他頁面時都要帶上jsessionid這樣的Cookie信息,應(yīng)用服務(wù)器根據(jù)這個會話標(biāo)識來獲取對應(yīng)的會話信息。
對于需要用戶登錄的網(wǎng)站,一般在用戶登錄成功后會將用戶資料保存在服務(wù)器的會話中,當(dāng)訪問其他的頁面時,應(yīng)用服務(wù)器根據(jù)瀏覽器送上的Cookie讀取當(dāng)前請求對應(yīng)的會話標(biāo)識以獲得對應(yīng)的會話信息,然后就可以判斷用戶資料是否存在于會話信息中,如果存在則允許訪問頁面,否則跳轉(zhuǎn)到登錄頁面中要求用戶輸入帳號和口令進(jìn)行登錄。這就是一般使用JSP開發(fā)的網(wǎng)站在處理用戶登錄時比較通用的方法。
對于HTTP的客戶端而言,如果要訪問一個受保護(hù)的頁面,就必須模擬瀏覽器所做的工作,首先是請求登錄頁面,讀取Cookie值;其次是請求登錄頁面并加入登錄頁所需的每個參數(shù);最后是請求最終所需的頁面。當(dāng)然,除第一次請求之外,其他的請求都需要附帶上Cookie信息以便服務(wù)器能判斷當(dāng)前請求是否已經(jīng)通過驗證。
在本研究中,通過HttpClient,可以方便地模擬出登錄的http請求,并獲取已經(jīng)通過驗證的Cookie,之后再通過該Cookie來進(jìn)行所需頁面的http請求,最終獲取其數(shù)據(jù)。雖然HttpClient自身具有自動調(diào)去Cookie的機(jī)制,但仍建議能夠自行調(diào)用代碼傳送Cookie。
3 網(wǎng)站客戶端的實現(xiàn)
本次模擬登錄和頁面信息獲取是在Java項目中完成的,由于Java項目可以完美移植到Android項目中,方便被調(diào)試,而且模擬Http請求這一過程并沒有用到任何Android功能。運(yùn)用HttpClient和 Jsoup技術(shù)的版本是HttpClient 3.x和Jsoup 1.6。
3.1 模擬登錄實現(xiàn)
/*對HttpClient 的初始化*/
private HttpClient client;
client=new HttpClient();
client.getParams().setHttpElementCharset("GBK");
client.getHostConfiguration().setHost(host_url, 80, "http");
/*設(shè)置PostMethod */
PostMethod post=new PostMethod(login_url);
post.addRequestHeader("Accept", "*/*");
post.addRequestHeader("Accept-Encoding", "gzip, deflate");
post.addRequestHeader("Host", host_url);
post.addRequestHeader("Connection", "Keep-Alive");
/*設(shè)置http request的body值*/
post.setRequestBody(new NameValuePair[]{
new NameValuePair("__VIEWSTATE",viewstate),
new NameValuePair("textBox1",xh),
new NameValuePair("textBox2",pwd),
new NameValuePair("Button1",""),
new NameValuePair("lbLanguage","")
});
/*獲取cookie*/
client.executeMethod(post);
Cookie cookies[]=client.getState().getCookies(); //得到cookies
for(int i=0; i if(i==0) { cookie=cookies[i].toString(); } else { cookie+=cookies[i]+";"; } } 3.2 中文亂碼問題的解決 在實際開發(fā)中,以上代碼獲得的content可能會出現(xiàn)中文亂碼。在開發(fā)這類客戶端時,中文編碼往往是個很具難度的問題。由于中文網(wǎng)頁的編碼可能是UTF-8或GB2312格式的,如果沒有采用對應(yīng)的編碼格式進(jìn)行解析,則會產(chǎn)生亂碼的問題。 據(jù)調(diào)查,80%以上的網(wǎng)頁都沒有設(shè)置response Header的charset,所以直接通過HttpClient獲取charset是不可靠的;但是99%的網(wǎng)頁設(shè)置了meta元素的charset,所以可以先根據(jù)一個默認(rèn)編碼(ISO-8859-1)獲得HTML文檔,再根據(jù)Jsoup或正則表達(dá)式獲得該文檔的charset。 因此以上代碼中若有中文參數(shù),可將 new NameValuePair("value_name" ,”中文”); 修改為 new NameValuePair("value_name", new String(“中文”.getBytes(), "ISO-8859-1")); 3.3 訪問所需頁面 //設(shè)置所需訪問的url String mainUrl="/content.aspx; //設(shè)置GetMethod GetMethod get=new GetMethod(mainUrl); //設(shè)置http Header,主重cookie信息 get.setRequestHeader("Accept-Language", "zh-CN"); get.setRequestHeader("Connection", "Keep-Alive"); get.setRequestHeader("Cache-Control", "no-cache"); get.setRequestHeader("Cookie", cookie); //獲取所需頁面的html client.executeMethod(get); content=get.getResponseBodyAsString(); 3.4 Android平臺的移植 這一模塊的實現(xiàn)需要運(yùn)用到Jsoup技術(shù),解析已經(jīng)得到的html,從中獲取需要的數(shù)據(jù)。以下代碼是從上文的content中提取相關(guān)的通知標(biāo)題,標(biāo)題由標(biāo)簽 //將剛才的html轉(zhuǎn)化成Document Document document=Jsoup.parse(content);等包含。
//一個Document由elements組成
//選擇”tr”開頭的標(biāo)簽,存入 trs元素群中
Elements trs=document.select("tr");
for(int i=0; i //觀察HTML,從第i+2個tr開始,包含的才是我們要的信息 //從每個tr中選出td標(biāo)簽元素群 Elements tds=trs.get(i+2).select("td"); //得到每個tr中td的個數(shù) int totalTds=tds.size(); //一個臨時的HashMap,里面是String-Object鍵值對 Map //j是一個標(biāo)識數(shù) for(int j=0; j switch (j) { //0表示第一個,即標(biāo)題 //put方法即向map加入一條鍵值對 //html()方法就得到標(biāo)簽括起來的內(nèi)容 case 0: map.put("title", tds.get(j).html().toString()); break; case 1: //1表示第二個,即作者 map.put("author", tds.get(j).html().toString()); break; default: break; } } list.add(map); } //list就是所需要的ArrayList 上面所有代碼調(diào)試通過后,只需一些簡單的復(fù)制、粘貼操作,就可以放在Android工程中,再加上一段簡單的代碼就可以讓ListView顯示這個ArrayList。 4 結(jié)束語 本文基于對HttpClint和Jsoup等技術(shù)的研究,成功實現(xiàn)了模擬登錄網(wǎng)站,并從所需頁面中提取有價值的信息,運(yùn)用到Android設(shè)備中。通過本文所研究的方法,可以有效改善一般網(wǎng)站在移動設(shè)備上的體驗不佳現(xiàn)狀,從而提升其用戶體驗。雖然本文方法能夠有效地應(yīng)對目前大多數(shù)網(wǎng)站,但對于使用https協(xié)議的網(wǎng)站或者存在其他特殊驗證的站點,還有一定的局限性。因此今后的研究方向是提高對多種網(wǎng)頁結(jié)構(gòu)的適應(yīng)性,以及感知信息和網(wǎng)頁結(jié)構(gòu)的更新變化,同時減少其復(fù)雜性,提高其自動化和智能性。 參考文獻(xiàn): [1] 洪輝,劉子敏.智能Web信息提取系統(tǒng)的研究和設(shè)計[J].微計算機(jī)信 息,2005.21(11). [2] 羅剛,王振東.自己動手寫網(wǎng)絡(luò)爬蟲[M].清華大學(xué)出版社,2010. [3] http://hc.apache.org/httpclient-3.x [4] 付聰.基于Smack庫與HttpClient登陸的實現(xiàn).中國科技論文在線, 2009. [5] http://www.blogjava.net/Alpha/archive/2007/01/22/95216.html