1. 程式人生 > >爬蟲學習4-HTML和XML資料的分析與解析

爬蟲學習4-HTML和XML資料的分析與解析

目前在 Java 中,解析 HTML 工具主要包含以下幾種:
1,jsoup:強大的 HTML 解析工具,支援以 jQuery 中 CSS Selector 的方式提取 HTML 中的元素,學習成本較低。,
2,HtmlCleaner:另外一款開源的 Java 語言的 HTML 文件解析器,支援以 XPath 的方式提取 HTML 中的元素。另外,在此說明,學習 XPath 語法對於使用另外一款基於 Selenium 的爬蟲工具特別有幫助。
3,Htmlparser:對 HTML 進行有效資訊搜尋和提取的一款 Java 工具,但該工具已長時間不維護了。
我在前面的篇章中,已介紹了 jsoup 工具解析 HTML 的內容,因此,在本文將主要介紹 HtmlCleaner 和 HtmlParser 工具對 HTML 的解析。

針對 XML 資料,Java 也有很多工具進行解析,,主要介紹 jsoup 解析 XML。

HtmlCleaner 解析 HTML

     引入HtmlCleaner的jar包,maven依依賴如下:


		<!--一款開源的 Java 語言的 HTML 文件解析器,支援以 XPath 的方式提取 HTML 中的元素https://mvnrepository.com/artifact/net.sourceforge.htmlcleaner/htmlcleaner -->
		<dependency>
			<groupId>net.sourceforge.htmlcleaner</groupId>
			<artifactId>htmlcleaner</artifactId>
			<version>2.22</version>
		</dependency>

Xpath

     Xpath學習教程:xpath

         XPath 是一門在 XML 文件中查詢資訊的語言,其可用來在 XML 文件中對元素和屬性進行遍歷。在 XPath 中,有七種型別的節點:元素、屬性、文字、名稱空間、處理指令、註釋以及文件節點(或稱為根節點)。其在 HTML 解析中,主要是對節點進行選取,而在選取的過程中,需要路徑進行定位。

       Xpath主要語法:語法

解析HTML

      使用 HtmlCleaner 首先要對其進行初始化,初始化之後,我們便可以使用 Xpath 語法操作節點。以下為 w3school 頁面的解析案例。解析的內容如下:

程式碼如下:

public static void test1() throws IOException, XPatherException {
        //這裡筆者使用jsoup獲取html檔案
        Document doc = Jsoup.connect("http://www.w3school.com.cn/b.asp").timeout(5000).get();
        //轉化成String格式
        String html =doc.html();
        //使用Htmlcleaner解析資料
        //初始化物件
        HtmlCleaner cleaner = new HtmlCleaner();
        //System.out.println(html);
        //解析HTML檔案
        TagNode node = cleaner.clean(html);
        //通過Xpath定位標題的位置,這裡使用//h1和/h1的結果是一樣的
        Object[]  ns = node.evaluateXPath("//div[@id='w3school']//h1");
        System.out.println("HTML中的標題是:\t" + ((TagNode)ns[0]).getText());
        Object[]  ns1 = node.evaluateXPath("//*[@id='w3school']/h1");
        System.out.println("HTML中的標題是:\t" + ((TagNode)ns1[0]).getText());
        //遍歷獲取課程名以及課程地址
        //這裡使用//a表示不考慮位置,如果使用/a獲取不到內容
        Object[]  ns2 = node.evaluateXPath("//*[@id='course']/ul//a");
        for(Object on : ns2) {
            TagNode n = (TagNode) on;
            System.out.println("課程名為:\t" + n.getText() + "\t地址為:\t" + n.getAttributeByName("href"));
        }
        //獲取每個課程名稱以及其對應的簡介
        Object[]  ns3 = node.evaluateXPath("//*[@id='maincontent']//div");
        for (int i = 1; i < ns3.length; i++) {
            TagNode n = (TagNode) ns3[i];
            //獲取課程名稱
            String courseName = n.findElementByName("h2", true).getText().toString();
            //迴圈遍歷所有的p節點獲取課程簡介
            Object[] objarrtr = n.evaluateXPath("//p");
            String summary = "";
            for(Object on : objarrtr) {
                summary += ((TagNode) on).getText().toString();
            }
            System.out.println(courseName + "\t" + summary);
        }
    }

輸出如下:

HTML中的標題是:	瀏覽器指令碼教程
HTML中的標題是:	瀏覽器指令碼教程
課程名為:	JavaScript	地址為:	/js/index.asp
課程名為:	HTML DOM	地址為:	/htmldom/index.asp
課程名為:	jQuery	地址為:	/jquery/index.asp
課程名為:	AJAX	地址為:	/ajax/index.asp
課程名為:	JSON	地址為:	/json/index.asp
課程名為:	DHTML	地址為:	/dhtml/index.asp
課程名為:	E4X	地址為:	/e4x/index.asp
課程名為:	WMLScript	地址為:	/wmlscript/index.asp
Disconnected from the target VM, address: '127.0.0.1:51921', transport: 'socket'
JavaScript	JavaScript 是世界上最流行的指令碼語言。JavaScript 是屬於 web 的語言,它適用於 PC、膝上型電腦、平板電腦和行動電話。JavaScript 被設計為向 HTML 頁面增加互動性。許多 HTML 開發者都不是程式設計師,但是 JavaScript 卻擁有非常簡單的語法。幾乎每個人都有能力將小的 JavaScript 片段新增到網頁中。如果您希望學習更多關於 JavaScript 的知識,請馬上訪問我們的 JavaScript 教程。
HTML DOM	HTML DOM 定義了訪問和操作 HTML 文件的標準方法。DOM 以樹結構表達 HTML 文件。開始學習 HTML DOM !
jQuery 教程	jQuery 是一個 JavaScript 庫。jQuery 極大地簡化了 JavaScript 程式設計。jQuery 很容易學習。開始學習 jQuery !
jQuery Mobile 教程	jQuery Mobile 是一個為觸控優化的框架,用於建立移動 web 應用程式。jQuery 適用於所有流行的智慧手機和平板電腦。jQuery Mobile 構建於 jQuery 庫之上,這使其更易學習,如果您通曉 jQuery 的話。它使用 HTML5、CSS3、JavaScript 和 AJAX 通過儘可能少的程式碼來完成對頁面的佈局。開始學習 jQuery Mobile !
AJAX	AJAX = Asynchronous JavaScript and XML(非同步的 JavaScript 和 XML)。AJAX 不是新的程式語言,而是一種使用現有標準的新方法。AJAX 是與伺服器交換資料並更新部分網頁的藝術,在不重新載入整個頁面的情況下。開始學習 AJAX !
JSON	JSON:JavaScript 物件表示法(JavaScript Object Notation)。JSON 是儲存和交換文字資訊的語法。類似 XML。JSON 比 XML 更小、更快,更易解析。假如您希望學習更多關於 JSON 的知識,請訪問我們的 JSON 教程。
DHTML	DHTML 是一種使 HTML 頁面具有動態特性的藝術。DHTML 是一種建立動態和互動 WEB 站點的技術集。對大多數人來說,DHTML 意味著 HTML、樣式表和 JavaScript 的組合。開始學習 DHTML !
E4X	E4X 是對 JavaScript 的新擴充套件。E4X 向 JavaScript 添加了對 XML 的直接支援。E4X 是正式的 JavaScript 標準。開始學習 E4X !
WMLScript	WMLScript 是用於 WML 頁面的指令碼語言。WML 頁面可以在 WAP 瀏覽器中顯示。WMLScript 用於驗證使用者輸入、生成對話方塊、顯示出錯訊息以及訪問使用者代理裝置等等。開始學習 WMLScript !

       在使用 evaluateXPath(String xPathExpression) 操作 TagNode 時得到的是 Object[] 陣列,通過對該陣列的操作便能夠獲取資料。另外,HtmlCleaner 還提供了很多種用法,例如上述程式中的 findElementByName()、getAttributeByName() 等操作方法

HtmlParser解析HTML

    引入HtmlParser解析HTML的jar包:

<!--對 HTML 進行有效資訊搜尋和提取的一款 Java 工具,但該工具已長時間不維護了 https://mvnrepository.com/artifact/org.htmlparser/htmlparser -->
		<dependency>
			<groupId>org.htmlparser</groupId>
			<artifactId>htmlparser</artifactId>
			<version>2.1</version>
		</dependency>

HtmlParser基礎教程:基礎教程

HTMLParser 的核心模組是 Parser 類,在實際的應用中也是通過該類分析 HTML 檔案。該類中常用的構造方法總結如下:

方法 說明
Parser() 無引數構造。
Parser(Lexer lexer) 通過 Lexer 構造 Parser,在案例程式中我會使用到。
Parser(String resource) 給定一個 URL 或檔案資源,構造 Parser。
Parser(URLConnection connection) 使用 URLConnection 構造 Parser。

對大多數使用這來說,可以通過 URLConnection 或者通過其他工具獲取的 HTML 字串來初始化 Parser。HTMLParser 將解析過的資訊儲存為樹結構,其中重要的是 Node 資料型別。Node 中包含的方法有對樹結構操作的函式以及獲取 Node 節點中包含內容的函式。

方法 說明
NodeList getChildren() 取得子節點的列表
Node getParent () 取得父節點
Node getFirstChild () 取得第一個子節點
Node getLastChild () 取得最後一個子節點
Node getPreviousSibling () 取得上一個兄弟節點
Node getNextSibling () 取得下一個兄弟節點
String getText () 獲取節點中的文字
String toPlainTextString() 獲取純文字資訊
String toHtml () 返回該節點對應的 HTML
Page getPage () 取得這個 Node 對應的 Page 物件
int getStartPosition () 取得這個 Node 在 HTML 頁面中的起始位置
int getEndPosition () 取得這個 Node 在 HTML 頁面中的結束位置

同時在 HTMLParser 提供了 Filter 操作,即對結果進行過濾。常用的過濾器有:

過濾器 說明
TagNameFilter 根據 Tag 的名字進行過濾。
HasChildFilter 返回有符合條件的子節點的節點,需要另外一個 Filter 作為過濾子節點的引數。
HasAttributeFilter 匹配出包含指定名稱的屬性,或者指定屬性的節點。
StringFilter 過濾顯示字串中包含指定內容的標籤節點。
RegexFilter 正則表示式匹配節點。
NodeClassFilter 根據已定義的標籤類獲取節點。
LinkStringFilter 判斷連結中是否包含某個特定的字串,可以用來過濾出指向某個特定網站的連結。
OrFilter 是結合幾種過濾條件的“或”過濾器。
AndFilter 結合幾種過濾條件的“與”過濾器。

使用案例:

           首先我給出第一個使用案例。給定 HTML 字串,使用 Parser(Lexer lexer) 構造,結合過濾器的使用提取網頁中的所有連結(即 href 對應的內容以及連結對應的標題),仍以上面的 w3school 的頁面為案例(http://www.w3school.com.cn/b.asp)

案例1程式碼:

public static void test1() throws ParserException, IOException {
        //這裡筆者使用Jsoup獲取html檔案
        Document doc = Jsoup.connect("http://www.w3school.com.cn/b.asp").timeout(5000).get();
        //轉化成String格式
        String html =doc.html();
         //使用Lexer構造
        Lexer lexer = new Lexer(html);
        Parser parser = new Parser(lexer);
       //過濾頁面中的連結標籤
        NodeFilter filter = new NodeClassFilter(LinkTag.class);
        //獲取匹配到的節點
        NodeList list = parser.extractAllNodesThatMatch(filter);
       //遍歷每一個節點
        for(int i=0; i<list.size();i++){
            Node node = (Node)list.elementAt(i);
            System.out.println("連結為:" + ((LinkTag) node).getLink() + "\t標題為:" + node.toPlainTextString() );
        }
    }

輸出:

連結為:/index.html	標題為:w3school 線上教程
連結為:/h.asp	標題為:HTML 系列教程
連結為:/b.asp	標題為:瀏覽器指令碼
連結為:/s.asp	標題為:伺服器指令碼
連結為:/d.asp	標題為:ASP.NET 教程
連結為:/x.asp	標題為:XML 系列教程
連結為:/ws.asp	標題為:Web Services 系列教程
連結為:/w.asp	標題為:建站手冊
連結為:/js/index.asp	標題為:JavaScript
連結為:/htmldom/index.asp	標題為:HTML DOM
連結為:/jquery/index.asp	標題為:jQuery
連結為:/ajax/index.asp	標題為:AJAX
連結為:/json/index.asp	標題為:JSON
連結為:/dhtml/index.asp	標題為:DHTML
連結為:/e4x/index.asp	標題為:E4X
連結為:/wmlscript/index.asp	標題為:WMLScript
連結為:/js/index.asp	標題為:JavaScript 教程
連結為:/htmldom/index.asp	標題為:開始學習 HTML DOM
連結為:/jquery/index.asp	標題為:開始學習 jQuery
連結為:/jquerymobile/index.asp	標題為:開始學習 jQuery Mobile
連結為:/ajax/index.asp	標題為:開始學習 AJAX
連結為:/json/index.asp	標題為:JSON 教程
連結為:/dhtml/index.asp	標題為:開始學習 DHTML
連結為:/e4x/index.asp	標題為:開始學習 E4X
連結為:/wmlscript/index.asp	標題為:開始學習 WMLScript
連結為:/jsref/index.asp	標題為:JavaScript
連結為:/jquery/jquery_reference.asp	標題為:jQuery
連結為:/vbscript/vbscript_ref_functions.asp	標題為:VBScript
連結為:/jsref/index.asp	標題為:HTML DOM
連結為:/example/jseg_examples.asp	標題為:JavaScript 例項
連結為:/example/jsrf_examples.asp	標題為:JavaScript 物件例項
連結為:/example/hdom_examples.asp	標題為:HTML DOM 例項
連結為:/jquery/jquery_examples.asp	標題為:jQuery 例項
連結為:/jquerymobile/jquerymobile_examples.asp	標題為:jQuery Mobile 例項
連結為:/example/dhtm_examples.asp	標題為:DHTML 例項
連結為:/example/ajax_examples.asp	標題為:AJAX 例項
連結為:/example/vbst_examples.asp	標題為:VBScript 例項
連結為:/js/js_quiz.asp	標題為:JavaScript 測驗
連結為:/jquery/jquery_quiz.asp	標題為:jQuery 測驗
連結為:http://www.ykinvestment.com/	標題為:上海贏科投資有限公司

案例2:

            基於 Filter 層層過濾的方式解析想要的資料,案例程式使用 Parser(String resource) 構造

程式碼:

public static void test2() throws ParserException, IOException{
        //生成一個解析器物件,用網頁的 url 作為引數
        Parser parser = new Parser("http://www.w3school.com.cn/b.asp");
        //設定網頁的編碼(GBK)
        parser.setEncoding("gbk");
        //過濾頁面中的標籤
        NodeFilter filtertag= new TagNameFilter("ul");
        //父節點包含ul
        NodeFilter filterParent = new HasParentFilter(filtertag);
        //選擇的節點為每個li
        NodeFilter filtername = new TagNameFilter("li");
        //並且li節點中包含id屬性
        NodeFilter filterId= new HasAttributeFilter("id");
        //並操作
        NodeFilter filter = new AndFilter(filterParent,filtername);
        //並操作
        NodeFilter filterfinal = new AndFilter(filter,filterId);
        //選擇匹配到的內容
        NodeList list = parser.extractAllNodesThatMatch(filterfinal);  
        //迴圈遍歷
        for(int i=0; i<list.size();i++){
            //獲取li的第一個子節點
            Node node = (Node)list.elementAt(i).getFirstChild();
            System.out.println( "連結為:" + ((LinkTag) node).getLink() +"\t標題為:" + node.toPlainTextString() );
        }
    }

輸出:

連結為:http://www.w3school.com.cn/h.asp	標題為:HTML 系列教程
Disconnected from the target VM, address: '127.0.0.1:52743', transport: 'socket'
連結為:http://www.w3school.com.cn/b.asp	標題為:瀏覽器指令碼
連結為:http://www.w3school.com.cn/s.asp	標題為:伺服器指令碼
連結為:http://www.w3school.com.cn/d.asp	標題為:ASP.NET 教程
連結為:http://www.w3school.com.cn/x.asp	標題為:XML 系列教程
連結為:http://www.w3school.com.cn/ws.asp	標題為:Web Services 系列教程
連結為:http://www.w3school.com.cn/w.asp	標題為:建站手冊

案例3:

  該案例是基於 CSS 選擇器來解析想要的資料

程式碼:

public static void test3() throws ParserException, IOException{
        //使用URLConnection請求資料
        URL url = new URL("http://www.w3school.com.cn/b.asp");
        URLConnection conn = url.openConnection();
        Parser parser = new Parser(conn);
       //通過css選擇器解析內容
        CssSelectorNodeFilter Filter=new CssSelectorNodeFilter ("#course > ul > li");
        //選擇匹配到的內容
        NodeList list = parser.extractAllNodesThatMatch(Filter);
        //迴圈遍歷
        for(int i=0; i<list.size();i++){
            //獲取li的第一個子節點
            Node node = (Node)list.elementAt(i).getFirstChild();
            System.out.println( "連結為:" + ((LinkTag) node).getLink() +"\t標題為:" + node.toPlainTextString() );
        }
    }

jsoup 解析XML

為講解 XML 資料的解析,我選取的案例是爬取網易汽車的銷量資料,例如捷達汽車的銷量,頁面為:

   http://db.auto.sohu.com/cxdata/xml/sales/model/model1001sales.xml

其部分 XML 檔案如下:

利用 jsoup 選擇器,可以快速的解析 XML 檔案中的資料。解析捷達汽車的銷售月份以及該月份的汽車

程式碼:

 //獲取URL對應的HTML內容
        Document doc = Jsoup.connect("http://db.auto.sohu.com/cxdata/xml/sales/model/model1001sales.xml").timeout(5000).get();
       //Jsoup選擇器解析
        Elements sales_ele = doc.select("sales");
        for (Element elem:sales_ele) {
            int salesnum=Integer.valueOf(elem.attr("salesnum"));
            String date = elem.attr("date");
            System.out.println("月份:" + date + "\t銷量:" + salesnum);

        }

輸出:

月份:2007-01-01	銷量:14834
月份:2007-02-01	銷量:9687
月份:2007-03-01	銷量:18173
月份:2007-04-01	銷量:18508
月份:2007-05-01	銷量:19710
月份:2007-06-01	銷量:20311
月份:2007-07-01	銷量:17516
月份:2007-08-01	銷量:17535
月份:2007-09-01	銷量:17743
月份:2007-10-01	銷量:15255
月份:2007-11-01	銷量:17250
月份:2007-12-01	銷量:14609
月份:2008-01-01	銷量:25126
月份:2008-02-01	銷量:9077
月份:2008-03-01	銷量:20396
月份:2008-04-01	銷量:18967
月份:2008-05-01	銷量:18243
月份:2008-06-01	銷量:17738
月份:2008-07-01	銷量:17449
月份:2008-08-01	銷量:18927
月份:2008-09-01	銷量:15586
月份:2008-10-01	銷量:14945
月份:2008-11-01	銷量:17204
月份:2008-12-01	銷量:8645
月份:2009-01-01	銷量:12240
月份:2009-02-01	銷量:12586
月份:2009-03-01	銷量:21139
月份:2009-04-01	銷量:21534
月份:2009-05-01	銷量:20934
月份:2009-06-01	銷量:20722
月份:2009-07-01	銷量:17091
月份:2009-08-01	銷量:21053
月份:2009-09-01	銷量:20667
月份:2009-10-01	銷量:21278
月份:2009-11-01	銷量:20535

原始碼地址:

原始碼