1. 程式人生 > >Python爬蟲解析html:lxml的HtmlElement物件獲取和設定inner html

Python爬蟲解析html:lxml的HtmlElement物件獲取和設定inner html

開發十年,就只剩下這套架構體系了! >>>   

我在我的個人部落格“猿人學網站”和公眾號“猿人學Python”上寫Python教程,有興趣的可以關注公眾號和網站。

Python的lxml是一個相當強悍的解析html、XML的模組,最新版本支援的python版本從2.6到3.6,是寫爬蟲的必備利器。它基於C語言庫libxml2 和 libxslt,進行了Python範兒(Pythonic)的繫結,成為一個具有豐富特性又容易使用的Python模組。雖然特性豐富,但是它在修改數節點時又缺少了些介面,比如本文講到的獲取 inner html 和 設定(修改)inner html功能。

解析網頁的html一般使用lxml.html模組,步驟很簡單分三步走:

(1) 匯入模組:

import lxml.html

(2) 把html轉換為html document 樹,根節點就是<html>標籤:

doc = lxml.html.fromstring(html)

(3) 使用xpath查詢要提取的節點:

nodes = doc.xpath('//div[@class, 'the']/div[@id, 'xpath']')

以上三步分成簡潔,實際使用中,可能要反覆第三部,通過不同的xpath獲得不同的節點進行資料提取。

可以說,lxml解析(只讀模式)html的功能又強大又方便。但是,如果需要修改(寫模式)某些節點的html就有點困難了,它在這方面提供的API很少,只有修改節點tag屬性的API,比如修改節點的class,id,href等屬性是可以的。

那麼如何操作節點的實際html字串呢?

1. 獲取節點的inner html

那麼,什麼是inner html呢?首先,我們來看一段html程式碼示例:

<div class=”text”>這是div<a href=”/node”>節點</a>內容</div>

對於div 這個html標籤節點,它的inner html就是:

這是div<a href=”/node”>節點</a>內容

即該標籤包含的所有內容;而包含div標籤在內的全部示例程式碼就是div的outer html。

明白了inner html 和 outer html的概念,我們就著手獲取它們。

lxml.html.tostring(html_element) 介面的作用是把一個節點及其子節點形成的樹轉換成html,也就是該節點的outer html,由此我們來獲得inner html,並實現為以下函式:


def get_inner_html(node):                                                                                                                                                  
    html = lxml.html.tostring(node, encoding="utf8").decode('utf8')            
    p_begin = html.find('>') + 1                                               
    p_end = html.rfind('<')                                                    
    return html[p_begin: p_end]

2. 設定節點的inner html

設定inner html相較於獲取更復雜一些,我們還是以上面那段html程式碼為例:

<div class=”text”>這是div<a href=”/node”>節點</a>內容</div>

假設我們要把它的inner html 改成如下字串:

this is div<a href=”/node”>node</a>text

則操作步驟是:

  1.  清空節點div裡面的內容:包括它的text和子節點
  2.  把新的inner html轉變成fragments
  3.  把fragments加到清空後的div節點

把以上步驟寫出Python函式就是:


def set_inner_html(node, html):
    node.text = ''
    for child in node.getchildren():
        node.remove(child)                                                                                                                                           4     
    fragments = lxml.html.fragments_fromstring(html)
    if type(fragments[0]) == str:
        node.text = fragments.pop(0)
    node.extend(fragments)

通過以上函式就可以成功把node裡面的內容設定成想要的html內容,適合在動態修改網頁結