2012年9月7日 星期五

解析 XML - Node.js

這篇文章我們會來解析一份 HTML 文件,因為一份寫得完整的 HTML 就是一份 XML 文件,而 HTML 文件比 XML 文件取得容易,因為網頁的原始碼就是 HTML 文件。不過話說在前頭,有些寫的很差的 HTML 是沒辦法當成 XML 來解析的,雖然 Browser 看得懂,至少要是 一個  Well Formed 的 HTML。通常來說如果不是手寫的,用程式產生出來的 HTML 符合 XML Well Fromed 規則的機會是很高的。那我們就先來看一下,今天要被解析的主角
 
<html>
    <body>
        <table>
            <tr>
                <td>Apples</td>
                <td>44%</td>
            </tr>
            <tr>
                <td>Bananas</td>
                <td>23%</td>
            </tr>
            <tr>
                <td>Oranges</td>
                <td>13%</td>
            </tr>
            <tr>
                <td>Other</td>
                <td>10%</td>
            </tr>
    </body>
</html>
 
把這個檔案命名為 hello.html 存在資料夾中,這份 HTML 執行出來是這樣的結果
不過結果不重要,我們要用 Node.js 來解析它,Node.js 需要一個 module 名為 libxmljs,用 npm install 它,如下
npm install -g libxmljs
然後產生一個新的 js 檔名為 parseXML.js,內容如下
 
var libxmljs = require("libxmljs");
var fs = require('fs');
fs.readFile("hello.html", 'utf8', function(err, data) { 
  if (err) throw err;
  var xmlDoc = libxmljs.parseXmlString(data);
  // xpath queries
  var gchild = xmlDoc.find('//td');
  for (var i = 0; i < gchild.length; i++){
      console.log(gchild[i].text());
  };
});  
一開始需要 libxmljs 這個 module ,接下來還需要 fs 這個 module,然後就用 fs 的 readFile 指定第一個參數就是我們要解析的 hello.html,第二個參數是這個檔案的文字編碼為 utf8 ,第三個參數就是處理從 disk 讀到的 hello.html,放在 data 這個變數裡。
data 被 parseXMLString 這個 function 解析完成之後會把整個 XML 文件架構放在 xmlDoc裡,需要去尋找某一個 tag 的時候,利用 find() 這個 function,裡面的參數是用 XPath 來描述的,在這個例子是 //td,代表的意思就是整份文件裡的 td tag,然後把結果放到 gchild 這個變數。 find() 這個 function 會回傳一個 array 筆者就用 for-loop 把它的結果用 console.log() show
在 Terminal 上面,會看到
Apples
44%
Bananas
23%
Oranges
13%
Other
10%
的結果,是這樣沒錯,寫到這讀者應該會查覺,要找到任一個 tag 就要用 XPath 去描述,有關 XPath 的說明可以參考這邊。接下來我們再試試一個例子。
需求是這樣的。想要找到包含 Apple 這個 tag 之後的第一個 tag 的值。在我們的例子就是想要找到下圖框起來的部分。
我們要這樣想,先找到一個 tag 的值是 Apple ,然後再去找這個 tag 的下一個 tag 的值。這樣的 XPath 寫法會是。
var xpath = '//tr[td = "Apples"]';
 這個寫法的意思是先找到 <td> 的 text 中為 "Apples" 的 <tr>,然後再用 XML Parser 執行,
var tr = xmlDoc.get(xpath);
然後我們用 console.log() 印出來。

 console.log("Up node is : " + tr.text());
 
 console.log("Child nodes : "+tr.childNodes());

這樣會在 Terminal 看到

Up node is : Apples44%
           
Child nodes : <td>Apples</td>,<td>44%</td>,
第一個是用 text(),直接印出,第二個是用 childNodes 其輸出是個 array ,然後被 Node.js 當成 string 就會看到
<td>Apples</td>,<td>44%</td>,
的結果,接著我們可再從 tr.childNodes() 找出我們要的 tag,Apples 是第一個,那下一個就是第二個,就是我們要的,就用
console.log("Next nodes : "+tr.childNodes()[1].text());
就會看到
Next nodes : 44%
不過這個前提是 XML 檔或是 HTML 檔中 tag 和 tag 中沒有其他的文字,比如上方一開始的 HTML 要改成如下,才會解析成功。
<html>
    <body>
        <table>
            <tr><td>Apples</td><td>44%</td>
            </tr>
            <tr>
                <td>Bananas</td>
                <td>23%</td>
            </tr>
            <tr>
                <td>Oranges</td>
                <td>13%</td>
            </tr>
            <tr>
                <td>Other</td>
                <td>10%</td>
            </tr>
        </table>
    </body>
</html>
筆者刻意把看到第一個 <tr> 和 <td> 中間沒有任何的空間,也許這樣對人眼是不好看的,但是它還是一個合法的 XML 而且更適合解析。
今天解析 XML 就到這,咱們下次見,檔案一樣放在 GitHub

沒有留言:

張貼留言