Python爬蟲(十)_XPath與lxml類庫

有同窗說,我正則用的很差,處理HTML文檔很累,有沒有其餘的方法?
有!那就是XPath,咱們能夠用先將HTML文檔轉換成XML文檔,而後用XPath查找HTML節點或元素。html

什麼是XML

    • XML指可擴展標記語言(Extensible Markup Language)
    • XML是一種標記語言,很相似HTML
    • XML的設計宗旨是傳輸數據,而非顯示數據。
    • XML的標籤須要咱們自行定義。
    • XML被設計爲具備自我描述性。
    • XML是W3C的推薦標準。

W3School官方文檔:http://www.w3school.com.cn/xml/index.aspnode

XML和HTML的區別

數據格式 描述 設計目標
XML Extensible Markup Language (可擴展標記語言) 被設計爲傳輸和存儲數據,其焦點是數據的內容。
HTML HyperText Markup Language(超文本標記語言) 顯示數據以及如何更好顯示數據。
HTML DOM Document Object Model for HTML (文檔對象模型) 經過 HTML DOM,能夠訪問全部的 HTML 元素,連同它們所包含的文本和屬性。能夠對其中的內容進行修改和刪除,同時也能夠建立新的元素。

XML文檔實例

<?xml version="1.0" encoding="utf-8"?>

<bookstore> 

  <book category="cooking"> 
    <title lang="en">Everyday Italian</title>  
    <author>Giada De Laurentiis</author>  
    <year>2005</year>  
    <price>30.00</price> 
  </book>  

  <book category="children"> 
    <title lang="en">Harry Potter</title>  
    <author>J K. Rowling</author>  
    <year>2005</year>  
    <price>29.99</price> 
  </book>  

  <book category="web"> 
    <title lang="en">XQuery Kick Start</title>  
    <author>James McGovern</author>  
    <author>Per Bothner</author>  
    <author>Kurt Cagle</author>  
    <author>James Linn</author>  
    <author>Vaidyanathan Nagarajan</author>  
    <year>2003</year>  
    <price>49.99</price> 
  </book> 

  <book category="web" cover="paperback"> 
    <title lang="en">Learning XML</title>  
    <author>Erik T. Ray</author>  
    <year>2003</year>  
    <price>39.95</price> 
  </book> 

</bookstore>

HTML DOM模型示例

HTML DOM定義了訪問和操做HTML文檔的標準方法,以樹結構方式表達了HTML文檔。python

XML的節點關係

1.父(Parent)
每一個元素以及屬性都有一個父。
下面是一個簡單的XML例子中,book元素時title、author、year以及priceweb

<?xml version="1.0" encoding="utf-8"?>

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>

 

2.子(Children)
元素節點可能有零個、一個或多個子。
在下面的例子中,title、author、year以及price元素都是book元素的子:dom

<?xml version="1.0" encoding="utf-8"?>

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>

3.同胞(Sibling)
擁有相同的父的節點
在下面的例子中,title、author、year以及price元素都是同胞:工具

<?xml version="1.0" encoding="utf-8"?>

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>

 

4.先輩(Ancestor)
某節點的父、父的父,等等。
在下面的例子中,title元素的先輩是book元素和bookstore元素:性能

<?xml version="1.0" encoding="utf-8"?>

<bookstore>

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>

</bookstore>

 

5.後代
某個節點的子,子的子,等等。
在下面的例子中,bookstore的後代是book、title、author、year以及price元素:學習

<?xml version="1.0" encoding="utf-8"?>

<bookstore>

<book>
  <title>Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
</book>

</bookstore>

 

什麼是XPath?

XPath(XML Path Language)是一門在XML文檔中查找信息的語言,可用來在XML中對元素和屬性進行遍歷。開發工具

W3School官方文檔:http://www.w3school.com.cn/xpath/index.asp測試

XPath 開發工具

  1. 開源的XPath表達式編輯工具:XML Quire(XML格式文件可用)
  2. Chrome插件Xpath Helper
  3. Firefox插件Xpath Checker

選取節點

XPath使用路徑表達式來選取XML文檔中的節點或者節點集。這些路徑表達式和咱們常規的電腦文件系統中看到的表達式很是類似。
下面列出了最經常使用的路徑表達式:

表達式 描述
nodename 選取此節點的全部子節點
/ 從根節點選取
// 從匹配選擇的當前節點選擇文檔中的節點,而不考慮它們的位置。
. 選取當前節點。
.. 選取當前節點的父節點
@ 選取屬性

在下面的表格中,咱們已列出了一些路徑表達式以及表達式的結果:

路徑表達式 描述
bookstore 選取bookstore元素的全部子節點。
/bookstore 選取根元素 bookstore。註釋:假如路徑起始於正斜槓( / ),則此路徑始終表明到某元素的絕對路徑!
bookstore/book 選取屬於bookstore的子元素的全部book元素
//book 選取全部book子元素,而無論它們在文檔中的位置
bookstore//book 選擇屬於bookstore元素的後代的全部bok元素,而無論它們位於bookstore之下的什麼位置
//@lang 選取名爲lang的全部屬性。

謂語(Predicates)

謂語用來查找某個特定的節點或者包含某個特定的值的節點,被嵌在方括號中。
在下面的表格中,咱們列出了帶有謂語的一些路徑表達式,以及表達式的結果:

路徑表達式 結果
/bookstore/book[1] 選取屬於bookstore子元素的第一個book元素。
/bookstore/book[last()] 選取數據bookstore子元素的最後一個book元素
/bookstore/book[last()-1] 選取屬於bookstore元素的倒數第二個book元素
/bookstore/book[position()<3] 選取最前面的兩個屬於bookstore元素的子元素book元素
//title[@lang] 選取全部擁有名爲lang的屬性的title元素
//title[@lang="eng"] 選取全部title元素,且這些元素擁有值爲eng的lang屬性
/bookstore/book[price>35.00] 選取全部bookstore元素的book元素,且其中的price元素的值必須大於35.00
/bookstore/book[price>35.00]/title 選取bookstore元素中的book元素的全部title元素,且其中的price元素的值必須大於35.00

選取未知節點

XPath通配符可用來選取未知的XML元素。

通配符 描述
* 匹配任何元素節點
@* 匹配任何屬性節點
node() 匹配任何類型的節點

在下面的表格中,咱們列出了一些路徑表達式,以及這些表達式的結果:

路徑表達式 結果
/bookstore/* 選取bookstore元素的全部子元素
//* 選取文檔中的全部元素
title[@*] 選取全部帶屬性的title元素

選取若干路徑

經過在路徑表達式中使用"|"運算符,您能夠選取若干個路勁。
實例
在下面的表格中,咱們列出了一些路徑表達式,以及這些表達式的結果:

路徑表達式 結果
'//book/title | //book/price' 選取book元素的全部title和price元素。
//title &#124 //price 選取文檔中的全部title和price元素
/bookstore/book/title | //price 選取屬於bookstore元素的book元素的title元素,以及文檔中的全部price元素

XPath的運算符

xpath運算符

以上就是XPath的語法內容,在運用到Python抓取時要先轉換爲xml.

lxml庫

lxml是一個HTML/XML的解析器,主要的功能是如何提取和解析HTML/XML數據。
lxml和正則同樣,也是用C實現,是一款高性能的Python HTML/XML解析器,咱們能夠利用以前學習的XPath語法,來快速的定位特定元素以及節點信息。
lxml python官方文檔:http://lxml.de/index.html
須要安裝C語言庫,可以使用pip安裝:pip install lxml(或經過wheel方式安裝)

初步使用

咱們利用它來解析HTML代碼,簡單實例:

#-*- coding:utf-8 -*-
#lxml_test.py

#使用lxml的etree庫
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> # 注意,此處缺乏一個 </li> 閉合標籤
     </ul>
 </div>
'''

#利用etree.HTML,將字符串解析爲HTML文檔
html = etree.HTML(text)

#按字符串序列化爲HTML文檔
result = etree.tostring(html)

print(result)

 

輸出結果:

<html><body>
<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></li>
</ul>
 </div>
</body></html>

lxml能夠自動修正html代碼,例子裏不只補全裏li標籤,還添加了body/html標籤

文件讀取:

除了直接讀取字符串,lxml還支持從文件裏讀取內容。咱們新建一個hello.html文檔:

<!--hello.html-->
<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"><span class="bold">third item</span></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></li>
    </ul>
</div>

再利用etree.parse()方法來讀取文件。

#lxml_parse.py
from lxml import etree

#讀取外部文件hello.html
html = etree.parse('./hello.html')
result = etree.tostring(html, pretty_print=True)

print(result)

輸出結果與以前相同:

<html><body>
<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></li>
</ul>
 </div>
</body></html>

 

XPath實例測試

1.獲取全部的<li>標籤

#xpath_li.py

from lxml import etree

html = etree.parse('hello.html')
print type(html) #顯示etree.parse()返回類型

result = html.xpath('//li')

print result  #打印<li>標籤的的元素集合
print len(result)
print type(result)
print type(result[0])

 

輸出結果:

<type 'lxml.etree._ElementTree'>
[<Element li at 0x1014e0e18>, <Element li at 0x1014e0ef0>, <Element li at 0x1014e0f38>, <Element li at 0x1014e0f80>, <Element li at 0x1014e0fc8>]
5
<type 'list'>
<type 'lxml.etree._Element'>

2.繼續獲取<li>標籤的全部class屬性

#xpath_li.py
from lxml import etree

html = etree.parse('htllo.html')
result = html.xpath('//li/@class')

print result

 

運行結果:

['item-0', 'item-1', 'item-inactive', 'item-1', 'item-0']

 

3.繼續獲取<li>標籤下hreflink1.html<a>標籤

#xpath_li.py
from lxml import etree

html = etree.parse('hello.html')
result = html.xpath('//li/a[@href="link1.html"]')

print result

 

運行結果:

[<Element a at 0x10ffaae18>]

 

4.獲取<li>標籤下的全部<span>標籤

#xpath_li.py

from lxml import etree

html = etree.parse('hello.html')

#result = html.xpath('//li/span')
#注意這麼寫是不對的

#由於/是用來獲取子元素的,而<span>不是<li>的子元素,因此,要用雙斜槓

result = html.xpath('//li//span')

print result

 

運行結果:

[<Element span at 0x10d698e18>]

 

5.獲取<li>標籤下的<a>標籤裏的全部class

from lxml import etree

html = etree.parse('hello.html')

result = html.xpath('//li/a//@class')

print result

 

運行結果

['blod']

 

6.獲取最後一個<li><a>的href

#xpath_li.py

from lxml import etree

html = etree.parse('hello.html')

result = html.xpath('//li[last()]/a/@href')
#謂語[last()]能夠找到最後一個元素

print result

 

運行結果

['link5.html']

 

7.獲取倒數第二個元素的內容

#xpath_li.py
from lxml import etree

html = etree.parse('hello.html')

result = html.xpath('//li[last()-1]/a')

#text方法能夠獲取元素內容
print(result[0].text)

 

運行結果

fourth item

 

8.獲取class值爲bold的標籤名

#xpath_li.py

from lxml import etree

html = etree.parse('hello.html')

result = html.xpath('//*[@class="bold"]')

#tag方法能夠獲取標籤名
print result[0].tag

 

運行結果

span
相關文章
相關標籤/搜索