天天看點

使用Jsoup實作簡單的頁面資訊爬取

使用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有一個更深刻的認識,同時,對爬蟲也有一個初步的了解,針對于爬蟲的複雜程度,所采用的爬蟲結構也會有所變化!

望對剛剛入門的你,會有所幫助!