有同學說,我正則用的不好茉稠,處理HTML文檔很累描馅,有沒有其他的方法?
有而线!那就是XPath,我們可以用先將HTML文檔轉(zhuǎn)換成XML文檔铭污,然后用XPath查找HTML節(jié)點或元素。
什么是XML
- XML指可擴展標記語言(Extensible Markup Language)
- XML是一種標記語言膀篮,很類似HTML
- XML的設(shè)計宗旨是傳輸數(shù)據(jù)嘹狞,而非顯示數(shù)據(jù)。
- XML的標簽需要我們自行定義誓竿。
- XML被設(shè)計為具有自我描述性磅网。
- XML是W3C的推薦標準。
W3School官方文檔:http://www.w3school.com.cn/xml/index.asp
XML和HTML的區(qū)別
數(shù)據(jù)格式 | 描述 | 設(shè)計目標 |
---|---|---|
XML | Extensible Markup Language (可擴展標記語言) | 被設(shè)計為傳輸和存儲數(shù)據(jù)烤黍,其焦點是數(shù)據(jù)的內(nèi)容知市。 |
HTML | HyperText Markup Language(超文本標記語言) | 顯示數(shù)據(jù)以及如何更好顯示數(shù)據(jù)傻盟。 |
HTML DOM | Document Object Model for HTML (文檔對象模型) | 通過 HTML DOM,可以訪問所有的 HTML 元素嫂丙,連同它們所包含的文本和屬性娘赴。可以對其中的內(nèi)容進行修改和刪除跟啤,同時也可以創(chuàng)建新的元素诽表。 |
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文檔的標準方法,以樹結(jié)構(gòu)方式表達了HTML文檔隅肥。
XML的節(jié)點關(guān)系
1.父(Parent)
每個元素以及屬性都有一個父竿奏。
下面是一個簡單的XML例子中,book元素時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>
2.子(Children)
元素節(jié)點可能有零個、一個或多個子秃症。
在下面的例子中候址,title、author种柑、year以及price元素都是book元素的子:
<?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)
擁有相同的父的節(jié)點
在下面的例子中岗仑,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)
某節(jié)點的父荠雕、父的父,等等驶赏。
在下面的例子中炸卑,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.后代
某個節(jié)點的子,子的子母市,等等矾兜。
在下面的例子中,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 開發(fā)工具
- 開源的XPath表達式編輯工具:XML Quire(XML格式文件可用)
- Chrome插件Xpath Helper
- Firefox插件Xpath Checker
選取節(jié)點
XPath使用路徑表達式來選取XML文檔中的節(jié)點或者節(jié)點集。這些路徑表達式和我們常規(guī)的電腦文件系統(tǒng)中看到的表達式非常相似篙挽。
下面列出了最常用的路徑表達式:
表達式 | 描述 |
---|---|
nodename | 選取此節(jié)點的所有子節(jié)點 |
/ | 從根節(jié)點選取 |
// | 從匹配選擇的當前節(jié)點選擇文檔中的節(jié)點荆萤,而不考慮它們的位置。 |
. | 選取當前節(jié)點。 |
.. | 選取當前節(jié)點的父節(jié)點 |
@ | 選取屬性 |
在下面的表格中链韭,我們已列出了一些路徑表達式以及表達式的結(jié)果:
路徑表達式 | 描述 |
---|---|
bookstore | 選取bookstore元素的所有子節(jié)點偏竟。 |
/bookstore | 選取根元素 bookstore。注釋:假如路徑起始于正斜杠( / )敞峭,則此路徑始終代表到某元素的絕對路徑踊谋! |
bookstore/book | 選取屬于bookstore的子元素的所有book元素 |
//book | 選取所有book子元素,而不管它們在文檔中的位置 |
bookstore//book | 選擇屬于bookstore元素的后代的所有bok元素旋讹,而不管它們位于bookstore之下的什么位置 |
//@lang | 選取名為lang的所有屬性殖蚕。 |
謂語(Predicates)
謂語用來查找某個特定的節(jié)點或者包含某個特定的值的節(jié)點,被嵌在方括號中沉迹。
在下面的表格中睦疫,我們列出了帶有謂語的一些路徑表達式,以及表達式的結(jié)果:
路徑表達式 | 結(jié)果 |
---|---|
/bookstore/book[1] | 選取屬于bookstore子元素的第一個book元素鞭呕。 |
/bookstore/book[last()] | 選取數(shù)據(jù)bookstore子元素的最后一個book元素 |
/bookstore/book[last()-1] | 選取屬于bookstore元素的倒數(shù)第二個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 |
選取未知節(jié)點
XPath通配符可用來選取未知的XML元素缨伊。
通配符 | 描述 |
---|---|
* | 匹配任何元素節(jié)點 |
@* | 匹配任何屬性節(jié)點 |
node() | 匹配任何類型的節(jié)點 |
在下面的表格中,我們列出了一些路徑表達式进宝,以及這些表達式的結(jié)果:
路徑表達式 | 結(jié)果 |
---|---|
/bookstore/* | 選取bookstore元素的所有子元素 |
//* | 選取文檔中的所有元素 |
title[@*] | 選取所有帶屬性的title元素 |
選取若干路徑
通過在路徑表達式中使用"|"運算符,您可以選取若干個路勁枷恕。
實例
在下面的表格中党晋,我們列出了一些路徑表達式,以及這些表達式的結(jié)果:
路徑表達式 | 結(jié)果 |
---|---|
'//book/title | //book/price' | 選取book元素的所有title和price元素徐块。 |
//title | //price | 選取文檔中的所有title和price元素 |
/bookstore/book/title | //price | 選取屬于bookstore元素的book元素的title元素未玻,以及文檔中的所有price元素 |
XPath的運算符
以上就是XPath的語法內(nèi)容,在運用到Python抓取時要先轉(zhuǎn)換為xml.
lxml庫
lxml是一個HTML/XML的解析器胡控,主要的功能是如何提取和解析HTML/XML數(shù)據(jù)扳剿。
lxml和正則一樣,也是用C實現(xiàn)昼激,是一款高性能的Python HTML/XML解析器庇绽,我們可以利用之前學習的XPath語法,來快速的定位特定元素以及節(jié)點信息橙困。
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)
輸出結(jié)果:
<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還支持從文件里讀取內(nèi)容明未。我們新建一個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)
輸出結(jié)果與之前相同:
<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])
輸出結(jié)果:
<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.繼續(xù)獲取<li>
標簽的所有class
屬性
#xpath_li.py
from lxml import etree
html = etree.parse('htllo.html')
result = html.xpath('//li/@class')
print result
運行結(jié)果:
['item-0', 'item-1', 'item-inactive', 'item-1', 'item-0']
3.繼續(xù)獲取<li>
標簽下href
為link1.html
的<a>
標簽
#xpath_li.py
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li/a[@href="link1.html"]')
print result
運行結(jié)果:
[<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
運行結(jié)果:
[<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
運行結(jié)果
['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
運行結(jié)果
['link5.html']
7.獲取倒數(shù)第二個元素的內(nèi)容
#xpath_li.py
from lxml import etree
html = etree.parse('hello.html')
result = html.xpath('//li[last()-1]/a')
#text方法可以獲取元素內(nèi)容
print(result[0].text)
運行結(jié)果
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
運行結(jié)果
span