nodejs cheerio模塊提取html頁面內容


nodejs cheerio模塊提取html頁面內容

本文給出使用一個用cheerio模塊提取html文件中指定內容的例子,並說明具體步驟、涉及到的API、以及其它模塊。 cheerio模塊是一個類似jquery的模塊,具有相似的API、功能,能夠將一個網頁解析為DOM,以及通過selector選擇元素,設置、獲取元素屬性。

以下為我們待解析網頁截圖:

 

目標是將task1-5中的所有題目、以及答案提取出來,以文本形式保存。最終提取出的效果如下。 這是題目文本:

Task 1: You will be given 10 minutes to read the text for the first time and then choose an appropriate answer for each of the following questions.

1. What’s the passage mainly about?
A. How to learn online successfully.
B. How to set up a learning goal.
C. The future of online learning.
D. The benefits of online learning.

...

這是答案文本:

Task 1:
1. D
2. C
3. C
4. D
5. A

注:其中答案保存在網頁中,但在網頁中沒有顯示出來。

1.1 找到目標元素

提取問題文本的整體思路:先找到包含題目的所有元素,然后再獲取這些元素的內容即可。 通過chrome的devtool(或者firefox的firebug)看出,所有的目標元素為:hr元素的所有兄弟結點。cheerio的nextAll函數滿足需求,這個函數獲取當前結點的所有后續的兄弟結點。程序如下:

 
var fs = require('fs');
var cheerio = require('cheerio');

var myHtml = fs.readFileSync("a.html");
var $ = cheerio.load(myHtml);
var t = $('html').find('hr');
var t2 = t.nextAll();

t2.each(function(i, elem) {
    getContent($(this));
    console.log($(this).text());
});

 

首先將網頁讀取為一個字符串,傳給cheerio.load函數,返回值即是一個cheerio對象(類似於一個jquery對象)。然后使用find函數,通過selector查找hr元素。再調用 nextAll函數得到hr元素的所有兄弟結點。 最后在each函數中, 通過text函數將所有包含問題的元素的見容打印出來。

結果中有亂碼,問題原因是fs模塊不支持中文。通過iconv-lite先解碼為中文解決。修改后代碼如下:

var fs = require('fs');
var cheerio = require('cheerio');
var iconv = require('iconv-lite');  

var myHtml = fs.readFileSync("a.html");
var $ = cheerio.load(iconv.decode(myHtml, 'gbk'));
var t = $('html').find('hr');
var t2 = t.nextAll();

t2.each(function(i, elem) {
    getContent($(this));
    console.log($(this).text());
});

 

最終結果如下:

           Task  1: You will be given 10 minutes to read the text for the first time and then 
                                         choose  an appropriate answer for each of the following questions.

    
    
       1. What does the “true gratitude” mean?
          
 
         A. A way of life.
            
          B. A joyous occasion.
            
...
    

以上結果有多余的空格、換行符,輸出文本看起來很散亂,但至少內容是獲取正確了。再在task2-5的html文件驗證一下,也獲取到了正確的內容,證明方法可行。接下來我們可以集中精力解決格式散亂的問題。

1.2 美化文本輸出

最主要的問題是有多余的空格、換行符。想到的一個辦法是:將所有結點的內容(包括文本結果)trim,即去年前后的所有空白字符,並對於br元素,加入一個換行符。也即模擬了一下html文檔的render效果(因為在瀏覽器中顯示是正確的,所以采用同樣的方法,也能得到相同的結果)。 要實現這個方法,要獲取一個元素的所有的子結果,使用cheerio的contents函數,這個函數獲取一個元素的所有子元素(包括文本元素)。然后調用字符串的trim函數去除首尾的空白文本。由於子元素又有子元素,因此使用遞歸函數。代碼如下:

function getContent(node){
    var a = node.contents();
    if (a.length == 0) {
        if (node.is('br')){
            RST+='\n';
        } else {
            RST+=node.text().trim();;
        }
    } else {
        node.contents().each(function(i, elem){
            getContent($(this));
        });

        if (node.is('p') || node.is('tr')){
            RST+='\n';
        }
    }
}

 

getContent函數用於獲取一個元素的文本內容,輸入參數為元素,這個函數會被遞歸調用。首先調用contents函數獲取所有子元素。如果子元素數目為0,表示這個元素是葉結點,則首先判斷如果是br元素,則在結果中加入一個換行符,否則,調用text函數獲取這個元素的文本內容。 如果子元素大於0,則遞歸地處理所有子元素。如果當前元素為p或tr元素,則在結果中加入一個換行。 其中RST為一個全局變量,用於保存結果文本。在調用函數前需要設置為空字符串。 這樣處理后,結果如下:

Task  1: You will be given 10 minutes to read the text for the first time and then
choose  an appropriate answer for each of the following questions.
1. What does the “true gratitude” mean?
A.A way of life.
B.A joyous occasion.
C.A much deeper level of gratitude.
D.The improvement of the quality of life.
2. Who has so many things to be grateful for?
...

看起來漂亮多了。 問題文本提取成功,接下來再提取答案文本。

1.3 提取答案文本

在html源文件中搜索answer,可以看出,答案是保存在script中的,如下:

<SCRIPT LANGUAGE="JavaScript">
  var StandardAnswer = new Array() 
  StandardAnswer =["C","D","D","D","D"]
</SCRIPT>

 

選擇題的答案保存在StandardAnswer的數組中。則獲取答案文本的方法為:先獲取script元素中的代碼文本,再通過eval函數得到這個數組值,最后生成答案文本。代碼如下:

var t = $('html').find('script');
var A = undefined;
t.each(function(i, elem) {
    var text = $(this).text();
    if (text.match('StandardAnswer')){
        var a = eval(text);
        console.log("standardanswer: "+ a);
        A= a;
    }
});

 

變量A保存答案數組。通過判斷文本中是否包含'StandardAnswer'來判斷是否是目標代碼。然后將這個代碼傳給eval,返回值即為‘["C","D","D","D","D"]‘這個數組。 有了答案數組,生成答案文本就比較容易了。

1.4 最終代碼

可以在這查看最終代碼。 最終的代碼還解決了一些小問題,如問題文本中包含了多余的文本(對於task4),task4的答案也會被顯示在問題文本中,沒有留下空白填寫答案等。 整個分析、編碼過程大致3個小時。其中文件a.js用於生成問題文本,b.js用於生成答案文本。a.js與b.js有很大的重復(b.js是直接復制了一份a.js修改而來)。這個代碼基本上是解決一次性問題,沒有什么重用性(在編寫的過程中也沒有考慮這些)。但是最重要的是:它解決了問題,它能夠工作。它不需要那么好!最后,這個程序被用於處理幾十個文件,成功正確地生成了問題文本及答案文本。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM