亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

利用lxml從網頁HTML/XML中提取數據

發布時間:2020-09-24 11:14:24 來源:億速云 閱讀:243 作者:Leah 欄目:編程語言

今天就跟大家聊聊有關利用lxml從網頁HTML/XML中提取數據,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

Python 的 lxml 模塊是一個非常好用且性能高的HTML、XML解析工具,通過它解析網頁,爬蟲就可以輕松的從網頁中提取想要的數據。lxml是基于C語言的libxml2和libxslt庫開發的,所以速度是相當的快。

使用lxml提取網頁數據的流程

要從網頁里面提取數據,使用lxml需要兩步:

第一步,用lxml把網頁(或xml)解析成一個DOM樹。這個過程,我們可以選擇etree、etree.HTML 和 lxml.html 這三種來實現,它們基本類似但又有些許差別,后面我們會詳細講到。

第二步,使用xpath遍歷這棵DOM 樹,找到你想要的數據所在的節點并提取。這一步要求我們對xpath規則比較熟練,xpath規則很多,但別怕,我來總結一些常用的套路。

生成DOM樹

上面我們說了,可以有三種方法來把網頁解析成DOM樹,有選擇困難癥的同學要犯難了,選擇那種好呢?別急,我們逐一探究一下。下面我通過實例來解析一下下面這段html代碼:

<div class="1">
    <p class="p_1 item">item_1</p>
    <p class="p_2 item">item_2</p>
</div>
<div class="2">
    <p id="p3"><a href="/go-p3">item_3</a></p>
</div>

使用etree.fromstring()函數

先看看這個函數的說明(docstring):

In [3]: etree.fromstring?
Signature:      etree.fromstring(text, parser=None, *, base_url=None)
Call signature: etree.fromstring(*args, **kwargs)
Type:           cython_function_or_method
String form:    <cyfunction fromstring at 0x7fe538822df0>
Docstring:
fromstring(text, parser=None, base_url=None)

Parses an XML document or fragment from a string.  Returns the
root node (or the result returned by a parser target).

To override the default parser with a different parser you can pass it to
the ``parser`` keyword argument.

The ``base_url`` keyword argument allows to set the original base URL of
the document to support relative Paths when looking up external entities
(DTD, XInclude, ...).

這個函數就是把輸入的html解析成一棵DOM樹,并返回根節點。它對輸入的字符串text有什么要求嗎?首先,必須是合法的html字符串,然后我們看看下面的例子:

In [19]: html = ''' 
...: <div class="1"> 
...:     <p class="p_1 item">item_1</p> 
...:     <p class="p_2 item">item_2</p> 
...: </div> 
...: <div class="2"> 
...:     <p id="p3"><a href="/go-p3">item_3</a></p> 
...: </div> 
...: '''

In [20]: etree.fromstring(html)
Traceback (most recent call last):

    File "/home/veelion/.virtualenvs/py3.6/lib/python3.6/site-packages/IPython/core/interactiveshell.py", 
    line 3267, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)

    File "<ipython-input-20-aea2e2c2317e>", line 1, in <module>
etree.fromstring(html)

    File "src/lxml/etree.pyx", line 3213, in lxml.etree.fromstring

    File "src/lxml/parser.pxi", line 1877, in lxml.etree._parseMemoryDocument

    File "src/lxml/parser.pxi", line 1758, in lxml.etree._parseDoc

    File "src/lxml/parser.pxi", line 1068, in lxml.etree._BaseParser._parseUnicodeDoc

    File "src/lxml/parser.pxi", line 601, in lxml.etree._ParserContext._handleParseResultDoc

    File "src/lxml/parser.pxi", line 711, in lxml.etree._handleParseResult

    File "src/lxml/parser.pxi", line 640, in lxml.etree._raiseParseError

    File "<string>", line 6
    XMLSyntaxError: Extra content at the end of the document, line 6, column 1

竟然報錯了!究其原因,我們的html是兩個并列的<div>標簽,沒有一個單獨的root節點。那么給這個html再加一個最外層的<div>標簽呢?

In [22]: etree.fromstring('<div>' + html + '</div>')
Out[22]: <Element div at 0x7fe53aa978c8>

這樣就可以了,返回了root節點,它是一個Element對象,tag是div。

總結一下,etree.fromstring()需要最外層是一個單獨的節點,否則會出錯。這個方法也適用于生成 XML 的DOM樹。

使用etree.HTML()函數

這個函數更像是針對 HTML 的,看看它的docstring:

In [23]: etree.HTML?
Signature:      etree.HTML(text, parser=None, *, base_url=None)
Call signature: etree.HTML(*args, **kwargs)
Type:           cython_function_or_method
String form:    <cyfunction HTML at 0x7fe538822c80>
Docstring:     
HTML(text, parser=None, base_url=None)

Parses an HTML document from a string constant.  Returns the root
node (or the result returned by a parser target).  This function
can be used to embed "HTML literals" in Python code.

To override the parser with a different ``HTMLParser`` you can pass it to
the ``parser`` keyword argument.

The ``base_url`` keyword argument allows to set the original base URL of
the document to support relative Paths when looking up external entities
(DTD, XInclude, ...).

接口參數跟etree.fromstring()一模一樣,實操一下:

In [24]: etree.HTML(html)
Out[24]: <Element html at 0x7fe53ab03748>

輸入兩個并列節點的html也沒有問題。等等,返回的root節點對象Element的標簽是html?把它用etree.tostring()還原成html代碼看看:

In [26]: print(etree.tostring(etree.HTML(html)).decode())
<html><body><div class="1">
    <p class="p_1 item">item_1</p>
    <p class="p_2 item">item_2</p>
</div>
<div class="2">
    <p id="p3"><a href="/go-p3">item_3</a></p>
</div>
</body></html>

In [27]: print(html)

<div class="1">
    <p class="p_1 item">item_1</p>
    <p class="p_2 item">item_2</p>
</div>
<div class="2">
    <p id="p3"><a href="/go-p3">item_3</a></p>
</div>

也就是說,etree.HTML()函數會補全html代碼片段,給它們加上<html>和<body>標簽。

使用lxml.html函數

lxml.html是lxml的子模塊,它是對etree的封裝,更適合解析html網頁。用這個子模塊生成DOM樹的方法有多個:
lxml.html.document_fromstring()
lxml.html.fragment_fromstring()
lxml.html.fragments_fromstring()
lxml.html.fromstring()

它們的docstring可以在ipython里面查一下,這里就不再列舉。通常,我們解析網頁用最后一個fromstring()即可。這個fromstring()函數也會給我們的樣例html代碼最頂層的兩個并列節點加一個父節點div。

上面三種方法介紹完,相信你自己已經有了選擇,那必須是lxml.html。

因為它針對html做了封裝,所以也多了寫特有的方法:

比如我們要獲得某個節點下包含所有子節點的文本內容時,通過etree得到的節點沒辦法,它的每個節點有個text屬性只是該節點的,不包括子節點,必須要自己遍歷獲得子節點的文本。而lxml.html有一個text_content()方法可以方便的獲取某節點內包含的所有文本。

再比如,好多網頁的鏈接寫的都是相對路徑而不是完整url:<a href="/index.html">,我們提取鏈接后還要自己手動拼接成完整的url。這個時候可以用lxml.html提供的make_links_absolute()方法,這個方法是節點對象Element的方法,etree的Element對象卻沒有。

使用xpath提取數據

我們還以下面這段html代碼為例,來看看如何定位節點提取數據。

<div class="1">
    <p class="p_1 item">item_1</p>
    <p class="p_2 item">item_2</p>
</div>
<div class="2">
    <p id="p3"><a href="/go-p3">item_3</a></p>
</div>

首先導入lxml.html模塊,生成DOM樹:

In [50]: import lxml.html as lh

In [51]: doc = lh.fromstring(html)

(1)通過標簽屬性定位節點
比如我們要獲取<div class="2">這節點:

In [52]: doc.xpath('//div[@class="2"]')
Out[52]: [<Element div at 0x7fe53a492ea8>]

In [53]: print(lh.tostring(doc.xpath('//div[@class="2"]')[0]).decode())
<div class="2">
    <p id="p3"><a href="/go-p3">item_3</a></p>
</div>

(2)contains語法
html中有兩個<p>標簽的class含有item,如果我們要提取這兩個<p>標簽,則:

In [54]: doc.xpath('//p[contains(@class, "item")]')
Out[54]: [<Element p at 0x7fe53a6a3ea8>, <Element p at 0x7fe53a6a3048>]

## 獲取<p>的文本:
In [55]: doc.xpath('//p[contains(@class, "item")]/text()')
Out[55]: ['item_1', 'item_2']

(3)starts-with語法
跟(2)一樣的提取需求,兩個<p>標簽的class都是以p_開頭的,所以:

In [60]: doc.xpath('//p[starts-with(@class, "p_")]')
Out[60]: [<Element p at 0x7fe53a6a3ea8>, <Element p at 0x7fe53a6a3048>]

## 獲取<p>的文本:
In [61]: doc.xpath('//p[starts-with(@class, "p_")]/text()')
Out[61]: ['item_1', 'item_2']

(4)獲取某一屬性的值
比如,我們想提取網頁中所有的鏈接:

In [63]: doc.xpath('//@href')
Out[63]: ['/go-p3']

看完上述內容,你們對利用lxml從網頁HTML/XML中提取數據有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

铜川市| 恩平市| 临漳县| 分宜县| 万源市| 红原县| 闵行区| 昆明市| 仪征市| 通辽市| 全南县| 乐都县| 永吉县| 邵阳县| 思茅市| 共和县| 定州市| 体育| 沁水县| 九江县| 洪江市| 秀山| 玉溪市| 凤山县| 古丈县| 虎林市| 郴州市| 通海县| 华宁县| 阿鲁科尔沁旗| 安龙县| 宁津县| 武宁县| 磴口县| 剑河县| 抚顺县| 延安市| 南安市| 浦北县| 金昌市| 隆安县|