使用Jsoup實作簡單的頁面資訊爬取
首先,簡單說一下個人對爬蟲的了解(比較簡單,以後再完善):爬蟲,簡單點就是從網站上來爬取你所需要的資訊。從這個功能考慮的話,也就簡單的分為了一下幾步(本篇主要就此展開讨論):1、你需要的網址;2、你所需要的資訊;3、擷取資訊并存儲或進行相關處理。其次,簡單說幾個工具,最開始了解到一個httpclient的神奇東東可以實作功能強大的爬蟲功能(個人了解:用java程式來封裝資料包,進行網絡通路,然後對通路結果進行判斷,正确判斷的話,再對擷取到的網頁資訊進行解析,可以利用封裝好的方法,也可以自己編寫比對過濾規則,進而擷取有用的資訊),後來又看到一個Jsoup的網頁解析便器(比較友善的解析工具)。接下來,詳細看了下 官方文檔(http://www.open-open.com/jsoup/)(請認真看),又在論壇裡看到了幾個執行個體,感覺确實很友善,接下來便開始了一段Jsoup的實踐之旅.
第一,Jsoup的相關操作:
1. 連結打開方式
一、 File input = new File("D:/test.html");
Document doc = Jsoup.parse(input,"UTF-8","http://baidu.com/");//第三個參數是b //aseURL,當有相對位址時,有該位址補全
二、Document doc = Jsoup.parse("D:/test.html");//直接加載網頁檔案
三、Connect conn = Jsoup.connect(url);
Document doc = conn.data("query","Java").//請求參數
userAgent("I'm Jsoup").//設定User-Agent
cookie("auth","token").//設定cookie
timeout(3000).//設定連接配接逾時時間
post();//使用post方法通路URL
執行個體:
public class First {
//public boolean append;
//網頁打開方法
public Document openURL(String url){
Document document = null;
try {
document = Jsoup.connect(url).timeout(40000).get();
System.out.println("網頁"+url+"通路成功");
} catch (IOException e) {
System.out.println("網頁"+url+"打開失敗");
}
}
2. 解析規則
(1)jsoup中主要有三種操作對象:Document、Elements及Element。其中:
- Document繼承自Element類,它包含頁面中的全部資料,通過Jsoup類的靜态方法獲得;
- Elements是Element的集合類;
- Element是頁面元素的實體類,包含了諸多操作頁面元素的方法,其中除了類似于jQuery選擇器的select方法外,還有大量類似于JS和jQuery的對DOM元素進行操作的方法,如getElementById,text,addClass,等等。
(2)Select方法将傳回一個Elements集合,并提供一組方法來抽取和處理結果。
Selector選擇器概述
· tagname:通過标簽查找元素,比如:a
· ns|tag:通過标簽在命名空間查找元素,比如:可以用 fb|name 文法來查找 <fb:name> 元素
· #id:通過ID查找元素,比如:#logo
· .class:通過class名稱查找元素,比如:.masthead
· [attribute]:利用屬性查找元素,比如:[href]
· [^attr]:利用屬性名字首來查找元素,比如:可以用[^data-] 來查找帶有HTML5 Dataset屬性的元素
· [attr=value]:利用屬性值來查找元素,比如:[width=500]
· [attr^=value], [attr$=value], [attr*=value]:利用比對屬性值開頭、結尾或包含屬性值來查找元素,比如:[href*=/path/]
· [attr~=regex]:利用屬性值比對正規表達式來查找元素,比如: img[src~=(?i)\.(png|jpe?g)]
· *:這個符号将比對所有元素
Selector選擇器組合使用
· el#id:元素+ID,比如: div#logo
· el.class:元素+class,比如: div.masthead
· el[attr]:元素+class,比如: a[href]
· 任意組合,比如:a[href].highlight
· ancestorchild:查找某個元素下子元素,比如:可以用.body p 查找在"body"元素下的所有 p元素
· parent> child:查找某個父元素下的直接子元素,比如:可以用div.content > p 查找 p 元素,也可以用body > * 查找body标簽下所有直接子元素
· siblingA+ siblingB:查找在A元素之前第一個同級元素B,比如:div.head + div
· siblingA~ siblingX:查找A元素之前的同級X元素,比如:h1 ~ p
· el,el, el:多個選擇器組合,查找比對任一選擇器的唯一進制素,例如:div.masthead, div.logo
僞選擇器selectors
· :lt(n):查找哪些元素的同級索引值(它的位置在DOM樹中是相對于它的父節點)小于n,比如:td:lt(3) 表示小于三列的元素
· :gt(n):查找哪些元素的同級索引值大于n,比如: div p:gt(2)表示哪些div中有包含2個以上的p元素
· :eq(n):查找哪些元素的同級索引值與n相等,比如:form input:eq(1)表示包含一個input标簽的Form元素
· :has(seletor):查找比對選擇器包含元素的元素,比如:div:has(p)表示哪些div包含了p元素
· :not(selector):查找與選擇器不比對的元素,比如: div:not(.logo) 表示不包含 class=logo 元素的所有 div清單
· :contains(text):查找包含給定文本的元素,搜尋不區分大不寫,比如: p:contains(jsoup)
· :containsOwn(text):查找直接包含給定文本的元素
· :matches(regex):查找哪些元素的文本比對指定的正規表達式,比如:div:matches((?i)login)
· :matchesOwn(regex):查找自身包含文本比對指定正規表達式的元素
· 注意:上述僞選擇器索引是從0開始的,也就是說第一個元素索引值為0,第二個元素index為1等
可以檢視Selector API參考來了解更詳細的内容
第二,Jsoup執行個體
1. 爬取一個源URL(http://apk.hiapk.com/)下的連結(安卓遊戲),并擷取新連結(安卓遊戲)的内容
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class TestJsoup {
public Document getDocument (String url){
try {
return Jsoup.connect(url).get();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
TestJsoup t = new TestJsoup();
Document doc= t.getDocument("http://apk.hiapk.com/");
//Document doc = t.getDocument("http://www.weather.com.cn/html/weather/101280101.shtml");
//Document doc = t.getDocument("http://www.cnblogs.com/xiaoMzjm/p/3899366.html");//zijichangshi
// 擷取目标HTML代碼
//找到遊戲連結,并跳轉到遊戲界面
Elements elements1 = doc.select("a[title = 安卓遊戲]");
String href = elements1.attr("href");//獲得連結
System.out.println(href);//控制台檢視
Document docgame = t.getDocument(href);//打開擷取的新連結
System.out.println(docgame.select("title").text());
Elements elements2 = docgame.select("a[class = s2]");
String href2 = elements2.attr("href");
String txt = elements2.text();
System.out.println(href2);
Document docgame2 = t.getDocument(href2);
System.out.println(docgame2.select("title").text());
}
}
2 . 擷取源URL下所有遊戲的連結及其相關遊戲詳細資訊
步驟:
1)源URL |
2)遊戲頁面 |
3) 遊戲分類頁面 |
4) 單個遊戲的連結 |
5) 單個遊戲的詳細資訊 |
1) 源URL : http://apk.hiapk.com/
2) 遊戲頁:
//擷取 遊戲 連結
Elements gameLink = doc.select("a[title = 遊戲-安卓市場]");
String url2 = gameLink.attr("abs:href");
Document doc2 = first.openURL(url2);
3) 遊戲分類頁
//擷取 遊戲分類 連結
Elements gameLink2 = doc2.select("div.cate_list_box");
Elements gameLinkType = gameLink2.select("a");
int length = gameLinkType.size();
4) 單個遊戲的連結:可能遇到多頁的情況,這時候要進行分頁處理
簡單思路:擷取所有分頁的URL , 求并集便是一類遊戲下的所有遊戲
//擷取遊戲分類下的遊戲清單
public void GameTypeFetch(String url, String table){
First gtfetch = new First();
Document doc = gtfetch.openURL(url);
Elements gameLink = doc.select("ul#appSoftListBox").select("li");
int length = gameLink.size();
String urlGame[] = new String[length];
String nameGame[] = new String[length];
int temp = 0;
/*for(int i=0; i<length; i++){
urlGame[i] = gameLink.get(2*i+1).attr("abs:href");
nameGame[i] = gameLink.get(2*i+1).text();
System.out.println(i+" : "+urlGame[i]+" : "+nameGame[i]);
}*/
System.out.println(" "+length);
for(Element e : gameLink){//單個遊戲的連結
urlGame[temp] = e.select("a").get(1).attr("abs:href");
nameGame[temp] = e.select("a").get(1).text();
System.out.println(" "+temp+" : "+urlGame[temp]+" : "+nameGame[temp]);
gtfetch.gameItems(nameGame[temp],urlGame[temp],false,0,table);
temp++;
}
}
5)單個遊戲的詳細資訊
注意對其它版本的處理
//擷取遊戲的具體詳細資訊
public void gameItems(String gameName, String gameLink, boolean otherVersion, int version, String table){
//int tableindex = 0;
//變量聲明,存儲遊戲相關資訊
String name = gameName;
String link = gameLink;
//屬性1
int starsize = 5;
String star[] =new String[starsize];
String starNUM = null;
int numEVAL = 0;//有多少人評分
//屬性2
String des = null;
String updes = null;
//屬性3
String author = null;
String down = null;
String size = null;
String type = null;
String language = null;
String time = null;
//String name = null;
//屬性4
int VSNUM = 0;
String otherVS[];
//擷取目前遊戲資訊
Elements details;
First gtfetch = new First();
Document doc = gtfetch.openURL(link);
boolean valid = gtfetch.isValid();
if(valid==true){
//System.out.println(eee);
//擷取屬性3
details = doc.select("div.line_content");
if(!details.isEmpty()){
System.out.println(details.size());
author = details.get(0).select("span").get(1).text();
down = details.get(1).select("span").get(1).text();
size = details.get(2).select("span").get(1).text();
type = details.get(3).select("span").get(1).text();
language = details.get(4).select("span").get(1).text();
time = details.get(7).select("span").get(1).text();
//擷取屬性2
des = doc.select("pre#softIntroduce").text();
updes = doc.select("pre.soft_imprint_font").text();
//擷取屬性1
Elements eval = doc.select("div.star_line");
for(Element e : eval){
star[--starsize] = e.text();
}
starNUM = doc.select("div.star_num").text();
numEVAL = Integer.parseInt(doc.select("span#startCount").text());//有多少人評分
if(!otherVersion){
//typelength++;
//tableindex = typelength;
//擷取屬性4
Elements otherversions = doc.select("div#otherSoftBox");
Elements versions = otherversions.select("dl.soft_item_dl");
int versionlength = versions.size();
VSNUM = versionlength + 1;
//otherVS = new String[otherVSNUM];
//System.out.println(otherVSNUM);
for(int ind=(versions.size()-1);ind>=0;ind--){
String otherurl = versions.get(ind).select("a[href]").first().attr("abs:href");
gameItems(name,otherurl,true,(versions.size()-ind+1),table);
}
}else{
typelength++;
System.out.println(name+" 沒有其他版本!");
VSNUM = version;
tableindex = typelength;
}
System.out.println(name+" "+link+" "+author+" "+down+" "+size+" "+type+" "+" "+time+" "+starNUM+" "+numEVAL+" "+VSNUM);
}
}
}
綜上,使用Jsoup也可以實作簡單的爬蟲程式,但更适合于網頁的解析,爬蟲程式,一般都有一個基本的架構,通過一系列的種子URL,擷取所需連結(往往數量較多),這時,便遇到了爬取到的大量連結的通路問題,一般,将連結分為兩大部分,以通路的連結和帶通路的連結(用隊列或者堆棧存儲,友善操作),有時,會将采集到的資訊用資料庫進行存儲,以友善對采集的資訊進行進一步的處理(實作了一個簡單的mysql資料庫的執行個體)。該文主要是以一個簡單執行個體來介紹Jsoup的使用,進而對Jsoup有一個更深刻的認識,同時,對爬蟲也有一個初步的了解,針對于爬蟲的複雜程度,所采用的爬蟲結構也會有所變化!
望對剛剛入門的你,會有所幫助!