您好,登錄后才能下訂單哦!
狹義上講,爬蟲只負責抓取,也就是下載網頁。而實際上,爬蟲還要負責從下載的網頁中提取我們想要的數據,即對非結構化的數據(網頁)進行解析提取出結構化的數據(有用數據)。比如,我們要抓取了一個新聞頁面的網頁(html)下來,但我們想要的是這個網頁中關于新聞的結構化數據:新聞的標題、新聞的發布時間、新聞的正文等。
所以說,網頁下載下來只是第一步,還有重要的一步就是數據提取。不同的爬蟲想要的數據不一樣,提取的數據也就不一樣,但提取方法都是類似的。
最簡單的提取數據的方法,就是使用正則表達式,此種方法簡單,提取的邏輯也不能復雜,不然寫出的正則表達式就晦澀難懂,甚至不能提取復雜的數據結構。
經過多年的使用經驗,選擇了lxml和xpath來解析網頁提取結構化數據。順便說一下 BeautifulSoup,它也是一個很棒的解析HTML的工具,可以使用多個解析器,比如Python標準庫的parser,但是速度比較慢,也可以使用lxml作為解析器,但是它的使用方法、API跟lxml不太一樣。使用下來,還是lxml的API更舒服。
lxml 對C語言庫 libxml2和 libxslt進行綁定,提供了Pythonic的API,它有一些主要特點:
總結為一句話就是,C語言的速度和Python的簡易相結合的神器。
lxml有兩大部分,分別支持XML和HTML的解析:
lxml.etree可以用來解析RSS feed,它就是一個XML格式的文檔。然而爬蟲抓取的絕大部分都是html網頁,所以,我們這里主要講述lxml.html解析網頁的方法。
我們下載得到的網頁就是一串html字符串,如何把它輸入給lxml.html模塊,從而生成html文檔的樹結構呢?
該模塊提供了幾種不同的方法:
document_fromstring(string)
,如果是一個單一片段,則返回
fragment_fromstring(string)
。
下面我們通過具體示例來說明上面幾個方法的不同。
In [1]: import lxml.html as lh In [2]: z = lh.document_fromstring('<span>abc</span><span>xyz</span>') # 可以看到,它自動加了根節點<html> In [3]: z Out[3]: <Element html at 0x7fc410667b88> In [4]: z.tag Out[4]: 'html' # 還加了<body>節點 In [5]: z.getchildren() Out[5]: [<Element body at 0x7fc4101a3ae8>] # 把字符串的兩個節點放在了<body>里面 In [6]: z.getchildren()[0].getchildren() Out[6]: [<Element span at 0x7fc410092bd8>, <Element span at 0x7fc410667c28>]
In [11]: z = lh.fragment_fromstring(‘<div>abc</div><div>xyz</div>’) --------------------------------------------------------------------------- ParserError Traceback (most recent call last) <ipython-input-11-a11f9a0f71d1> in <module>() ----> 1 z = lh.fragment_fromstring(‘<div>abc</div><div>xyz</div>’) ~/.virtualenvs/py3.6/lib/python3.6/site-packages/lxml/html/__init__.py in fragment_fromstring(html, create_parent, base_url, parser, **kw) 850 raise etree.ParserError( 851 “Multiple elements found (%s)” --> 852 % ‘, ‘.join([_element_name(e) for e in elements])) 853 el = elements[0] 854 if el.tail and el.tail.strip(): ParserError: Multiple elements found (div, div) # 可以看到,輸入是兩個節點(element)時就會報錯 # 如果加上 create_parent 參數,就沒問題了 In [12]: z = lh.fragment_fromstring('<div>abc</div><div>xyz</div>', create_parent='p') In [13]: z.tag Out[13]: 'p' In [14]: z.getchildren() Out[14]: [<Element div at 0x7fc40a41a818>, <Element div at 0x7fc40a41aea8>]
# 輸入字符串含有一個節點,則返回包含這一個節點的列表 In [17]: lh.fragments_fromstring('<div>abc</div>') Out[17]: [<Element div at 0x7fc40a124ea8>] # 輸入字符串含有多個節點,則返回包含這多個節點的列表 In [18]: lh.fragments_fromstring('<div>abc</div><div>xyz</div>') Out[18]: [<Element div at 0x7fc40a124b88>, <Element div at 0x7fc40a124f98>]
In [27]: z = lh.fromstring('<div>abc</div><div>xyz</div>') In [28]: z Out[28]: <Element div at 0x7fc40a0eb368> In [29]: z.getchildren() Out[29]: [<Element div at 0x7fc410135548>, <Element div at 0x7fc40a0eb2c8>] In [30]: type(z) Out[30]: lxml.html.HtmlElement
這里,fromstring輸入的如果是多個節點,它會給加一個父節點并返回。但是像html網頁都是從節點開始的,我們使用fromstring() 和 document_fromstring() 都可以得到完整的網頁結構。
從上面代碼中我們可以看到,那幾個函數返回的都是HtmlElement對象,也就是說,我們已經學會了如何從html字符串得到HtmlElement的對象,下一節我們將學習如何操作HtmlElement對象,從中提取我們感興趣的數據。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。