1. 程式人生 > >python3之lxml、css和xpath

python3之lxml、css和xpath

文章目錄

頁面解析lxml

lxml是python的一個解析庫,支援HTML和XML的解析,支援XPath解析方式,而且解析效率非常高
XPath,全稱XML Path Language,即XML路徑語言,它是一門在XML文件中查詢資訊的語言,它最初是用來搜尋XML文件的,但是它同樣適用於HTML文件的搜尋。
XPath的選擇功能十分強大,它提供了非常簡明的路徑選擇表示式,另外,它還提供了超過100個內建函式,用於字串、數值、時間的匹配以及節點、序列的處理等,幾乎所有我們想要定位的節點,都可以用XPath來選擇。
XPath於1999年11月16日成為W3C標準,它被設計為供XSLT、XPointer以及其他XML解析軟體使用,更多的文件可以訪問其官方網站:

https://www.w3.org/TR/xpath/

lxml自動修復標籤屬性兩側缺失的引號,並閉合標籤。

處理非標準的html

import lxml.html

broben_html = "<ul class=country> <li>Area <li id=aa>Population</ul>"
tree = lxml.html.fromstring(broben_html)
print('tree type:', type(tree))
# pretty_print: 優化輸出,輸出換行符
# fixed_html = lxml.html.tostring(tree, pretty_print=True)
fixed_html = lxml.html.tostring(tree) # 轉換為位元組 print('fixed_html type:', type(fixed_html)) print(fixed_html) 輸出如下: tree type: <class 'lxml.html.HtmlElement'> fixed_html type: <class 'bytes'> b'<ul class="country"> <li>Area </li><li id="aa">Population</li></ul>'
'''
lxml提供如下方式輸入文字:
fromstring():解析字串
HTML():解析HTML物件
XML():解析XML物件
parse():解析檔案型別物件
'''
# 輸出就是前面講的tostring()方法:
>>> root = etree.XML('<root><a><b/></a></root>')
>>> etree.tostring(root)
'<root><a><b/></a></root>'
>>> etree.tostring(root,xml_declaration=True)
"<?xml version='1.0' encoding='ASCII'?>\n<root><a><b/></a></root>"
>>> etree.tostring(root,xml_declaration=True,encoding='utf-8')
"<?xml version='1.0' encoding='utf-8'?>\n<root><a><b/></a></root>"

css

解析完輸入內容之後,進入選擇元素的步驟,此時lxml有幾種不同的方法,比如 XPath 選擇器和類似 Beautiful Soup 的find()方法 。我們將會使用 css 選擇器 , 因為它更加簡沽。
安裝cssselect:pip install cssselect

常用的選擇器示例

選擇所有標籤 :*
選擇<a>標籤:a
選擇所有 class="link"的元素: .link
選擇class="link" 的 <a>標籤: a.link
選擇 id= "home" 的 <a>標籤: a#home
選擇父元素為 <a>標籤的所有 <span>子標籤: a > span
選擇<a>標籤內部的所有 <span>標籤: a span
選擇 title 屬性為 "Home" 的所有<a>標籤: a[title=Home]

在這裡插入圖片描述

栗子

import lxml.html
import requests
from fake_useragent import UserAgent

ua = UserAgent()
header = {
    "User-Agent": ua.random
}
html = requests.get("http://www.baidu.com/", headers=header)
html = html.content.decode("utf8")
print(html)
tree = lxml.html.fromstring(html)
mnav = tree.cssselect('div#head .mnav')

# 輸出文字內容
# area = td.text_content()
for i in mnav:
    print(i.text)

結果如下:

新聞
hao123
地圖
視訊
貼吧
學術

ps: 不知為何使用html.text輸出的是亂碼。

參考css選擇器語法
http://www.runoob.com/cssref/css-selectors.html

xpath

常用規則

表示式 描述
nodename 選取此節點的所有子節點
/ 從當前節點選取直接子節點
// 從當前節點選取子孫節點
. 選取當前節點
選取當前節點的父節點
@ @用來選取屬性
* 萬用字元,匹配元素節點
@* 匹配任何屬性節點
[@attrib] 選取具有給定屬性的所有節點
[@attrib=‘value’] 選取具有給定值屬性的所有節點

謂語

謂語用來查詢某個特定的節點或者包含某個指定的值的節點。謂語被嵌在方括號中。
在這裡插入圖片描述

運算子

在這裡插入圖片描述

常用函式

在這裡插入圖片描述

栗子

  • 解析字串型別的html
from lxml import etree

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
 </div>
'''
html = etree.HTML(text)
result = etree.tostring(html)
print(result)
# 最後一個li標籤不完整,會自動補全
  • 解析檔案型別的html,獲取所有的 li標籤

from lxml import etree
from lxml.etree import HTMLParser
html=etree.parse(‘test.html’, etree.HTMLParser()) # 指定解析器HTMLParser會根據檔案修復HTML檔案中缺失的如宣告資訊
result=etree.tostring(html) # 解析成位元組
result=etree.tostringlist(html) # 解析成列表

from lxml import etree

html = etree.parse('hello.html')
print type(html)
result = html.xpath('//li')
print result
print len(result)
print type(result)
print type(result[0])
# 解釋:etree.parse 的型別是 ElementTree,通過呼叫 xpath 以後,
# 得到了一個列表,包含了 5 個 <li> 元素,每個元素都是 Element 型別
  • 獲取標籤名
# 獲取 class 為 bold 的標籤名
result = html.xpath('//*[@class="bold"]')
print(result[0].tag)

  • 獲取標籤的屬性
from lxml import etree

html = etree.parse('hello.html')
result = html.xpath('//li/@class')
print(result)  # 返回li標籤的class屬性的值
  • 獲取li標籤下 href屬性 為 link1.html 的a標籤
from lxml import etree

html = etree.parse('hello.html')
result = html.xpath('//li/a[@href="link1.html"]')
print(result)
  • 獲取標籤下的子標籤或子孫標籤
from lxml import etree

html = etree.parse('hello.html')
# 注意這麼寫是不對的,因為 / 是用來獲取子元素的,而 <span> 並不是 <li> 的子元素,所以,要用雙斜槓
# result = html.xpath('//li/span') # 獲取li標籤下為span的子節點
result = html.xpath('//li//span')  # 獲取li標籤下為span的子孫節點
print(result)
  • 提取文字內容
from lxml import etree

html = etree.parse('hello.html')
result = html.xpath('//li//span/text()')  # 獲取span標籤的內容
print(result)
from lxml import etree

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
 </div>
'''
html = etree.HTML(text)
nodeList = html.xpath('//a[contains(text(), "first")]/text()')
print(nodeList)

擴充套件:

#方法1:過濾標籤,返回全部文字 
>>> root.xpath('string()')
'child1 testchild2 test'

#方法2:以標籤為間隔,返回list
>>> root.xpath('//text()')
['child1 test', 'child2 test', '123']