1. 程式人生 > 實用技巧 >Python爬蟲技術--基礎篇--內建模組XML和HTMLParser

Python爬蟲技術--基礎篇--內建模組XML和HTMLParser

1.XML

XML雖然比JSON複雜,在Web中應用也不如以前多了,不過仍有很多地方在用,所以,有必要了解如何操作XML。

DOM vs SAX

操作XML有兩種方法:DOM和SAXDOM會把整個XML讀入記憶體,解析為樹,因此佔用記憶體大,解析慢,優點是可以任意遍歷樹的節點。SAX是流模式,邊讀邊解析,佔用記憶體小,解析快,缺點是我們需要自己處理事件。

正常情況下,優先考慮SAX,因為DOM實在太佔記憶體。

在Python中使用SAX解析XML非常簡潔,通常我們關心的事件是start_elementend_elementchar_data,準備好這3個函式,然後就可以解析xml了。

舉個例子,當SAX解析器讀到一個節點時:

<a href="/">python</a>

會產生3個事件:

  1. start_element事件,在讀取<a href="/">時;

  2. char_data事件,在讀取python時;

  3. end_element事件,在讀取</a>時。

用程式碼實驗一下:

from xml.parsers.expat import ParserCreate

class DefaultSaxHandler(object):
    def start_element(self, name, attrs):
        print('sax:start_element: %s, attrs: %s' % (name, str(attrs)))

    def end_element(self, name):
        print('sax:end_element: %s' % name)

    def char_data(self, text):
        print('sax:char_data: %s' % text)

xml = r'''<?xml version="1.0"?>
<ol>
    <li><a href="/python">Python</a></li>
    <li><a href="/ruby">Ruby</a></li>
</ol>
'''

handler = DefaultSaxHandler()
parser = ParserCreate()
parser.StartElementHandler = handler.start_element
parser.EndElementHandler = handler.end_element
parser.CharacterDataHandler = handler.char_data
parser.Parse(xml)

需要注意的是讀取一大段字串時,CharacterDataHandler可能被多次呼叫,所以需要自己儲存起來,在EndElementHandler裡面再合併。

除了解析XML外,如何生成XML呢?99%的情況下需要生成的XML結構都是非常簡單的,因此,最簡單也是最有效的生成XML的方法是拼接字串:

L = []
L.append(r'<?xml version="1.0"?>')
L.append(r'<root>')
L.append(encode('some & data'))
L.append(r'</root>')
return ''.join(L)

如果要生成複雜的XML呢?建議你不要用XML,改成JSON。

小結

解析XML時,注意找出自己感興趣的節點,響應事件時,把節點資料儲存起來。解析完畢後,就可以處理資料。

2.HTMLParser

如果我們要編寫一個搜尋引擎,第一步是用爬蟲把目標網站的頁面抓下來,第二步就是解析該HTML頁面,看看裡面的內容到底是新聞、圖片還是視訊。

假設第一步已經完成了,第二步應該如何解析HTML呢?

HTML本質上是XML的子集,但是HTML的語法沒有XML那麼嚴格,所以不能用標準的DOM或SAX來解析HTML。

好在Python提供了HTMLParser來非常方便地解析HTML,只需簡單幾行程式碼:

from html.parser import HTMLParser
from html.entities import name2codepoint

class MyHTMLParser(HTMLParser):

    def handle_starttag(self, tag, attrs):
        print('<%s>' % tag)

    def handle_endtag(self, tag):
        print('</%s>' % tag)

    def handle_startendtag(self, tag, attrs):
        print('<%s/>' % tag)

    def handle_data(self, data):
        print(data)

    def handle_comment(self, data):
        print('<!--', data, '-->')

    def handle_entityref(self, name):
        print('&%s;' % name)

    def handle_charref(self, name):
        print('&#%s;' % name)

parser = MyHTMLParser()
parser.feed('''<html>
<head></head>
<body>
<!-- test html parser -->
    <p>Some <a href=\"#\">html</a> HTML&nbsp;tutorial...<br>END</p>
</body></html>''')

feed()方法可以多次呼叫,也就是不一定一次把整個HTML字串都塞進去,可以一部分一部分塞進去

特殊字元有兩種,一種是英文表示的&nbsp;,一種是數字表示的&#1234;,這兩種字元都可以通過Parser解析出來

小結

利用HTMLParser,可以把網頁中的文字、影象等解析出來。