先演示一段獲取頁面鏈接代碼示例:
#coding=utf-8
from lxml import etree
html = '''
<html>
<head>
<meta name="content-type" content="text/html; charset=utf-8" />
<title>友情鏈接查詢 - 站長工具</title>
<!-- uRj0Ak8VLEPhjWhg3m9z4EjXJwc -->
<meta name="Keywords" content="友情鏈接查詢" />
<meta name="Description" content="友情鏈接查詢" />
</head>
<body>
<h1 class="heading">Top News</h1>
<p style="font-size: 200%">World News only on this page</p>
Ah, and here's some more text, by the way.
<p>... and this is a parsed fragment ...</p>
<a href="http://www.cydf.org.cn/" rel="nofollow" target="_blank">青少年發展基金會</a>
<a href="http://www.4399.com/flash/32979.htm" target="_blank">洛克王國</a>
<a href="http://www.4399.com/flash/35538.htm" target="_blank">奧拉星</a>
<a href="http://game.3533.com/game/" target="_blank">手機游戲</a>
<a href="http://game.3533.com/tupian/" target="_blank">手機壁紙</a>
<a href="http://www.4399.com/" target="_blank">4399小游戲</a>
<a href="http://www.91wan.com/" target="_blank">91wan游戲</a>
</body>
</html>
'''
page = etree.HTML(html.lower().decode('utf-8'))
hrefs = page.xpath(u"//a")
for href in hrefs:
print href.attrib
打印出的結果為:
{'href': 'http://www.cydf.org.cn/', 'target': '_blank', 'rel': 'nofollow'}
{'href': 'http://www.4399.com/flash/32979.htm', 'target': '_blank'}
{'href': 'http://www.4399.com/flash/35538.htm', 'target': '_blank'}
{'href': 'http://game.3533.com/game/', 'target': '_blank'}
{'href': 'http://game.3533.com/tupian/', 'target': '_blank'}
{'href': 'http://www.4399.com/', 'target': '_blank'}
{'href': 'http://www.91wan.com/', 'target': '_blank'}
如果要取得<a></a>之間的內容,
for href in hrefs:
print href.text
結果為:
青少年發展基金會
洛克王國
奧拉星
手機游戲
手機壁紙
4399小游戲
91wan游戲
使用lxml前注意事項:先確保html經過了utf-8解碼,即code = html.decode('utf-8', 'ignore'),否則會出現解析出錯情況。因為中文被編碼成utf-8之后變成 '/u2541' 之類的形式,lxml一遇到 “/”就會認為其標簽結束。
XPATH基本上是用一種類似目錄樹的方法來描述在XML文檔中的路徑。比如用“/”來作為上下層級間的分隔。第一個“/”表示文檔的根節點(注意,不是指文檔最外層的tag節點,而是指文檔本身)。比如對於一個HTML文件來說,最外層的節點應該是"/html"。
定位某一個HTML標簽,可以使用類似文件路徑里的絕對路徑,如page.xpath(u"/html/body/p"),它會找到body這個節點下所有的p標簽;也可以使用類似文件路徑里的相對路徑,可以這樣使用:page.xpath(u"//p"),它會找到整個html代碼里的所有p標簽:
<p style="font-size: 200%">World News only on this page</p>
Ah, and here's some more text, by the way.
<p>... and this is a parsed fragment ...</p>
注意:XPATH返回的不一定就是唯一的節點,而是符合條件的所有節點。如上所示,只要是body里的p標簽,不管是body的第一級節點,還是第二級,第三級節點,都會被取出來。
如果想進一步縮小范圍,直接定位到“<p style="font-size: 200%">World News only on this page</p>”要怎么做呢?這就需要增加過濾條件。過濾的方法就是用“[”“]”把過濾條件加上。lxml里有個過濾語法:
p = page.xpath(u"/html/body/p[@style='font-size: 200%']")
或者:p = page.xpath(u"//p[@style='font-size:200%']")
這樣就取出了body里style為font-size:200%的p節點,注意:這個p變量是一個lxml.etree._Element對象列表,p[0].text結果為World News only on this page,即標簽之間的值;p[0].values()結果為font-size: 200%,即所有屬性值。其中 @style表示屬性style,類似地還可以使用如@name, @id, @value, @href, @src, @class....
如果標簽里面沒有屬性怎么辦?那就可以用text(),position()等函數來過濾,函數text()的意思則是取得節點包含的文本。比如:<div>hello<p>world</p>< /div>中,用"div[text()='hello']"即可取得這個div,而world則是p的text()。函數position()的意思是取得節點的位置。比如“li[position()=2]”表示取得第二個li節點,它也可以被省略為“li[2]”。
不過要注意的是數字定位和過濾 條件的順序。比如“ul/li[5][@name='hello']”表示取ul下第五項li,並且其name必須是hello,否則返回空。而如果用 “ul/li[@name='hello'][5]”的意思就不同,它表示尋找ul下第五個name為"hello“的li節點。
此外,“*”可以代替所有的節點名,比如用"/html/body/*/span"可以取出body下第二級的所有span,而不管它上一級是div還是p或是其它什么東東。
而 “descendant::”前綴可以指代任意多層的中間節點,它也可以被省略成一個“/”。比如在整個HTML文檔中查找id為“leftmenu”的 div,可以用“/descendant::div[@id='leftmenu']”,也可以簡單地使用“ //div[@id='leftmenu']”。
text = page.xpath(u"/descendant::*[text()]")表示任意多層的中間節點下任意標簽之間的內容,也即實現蜘蛛抓取頁面內容功能。以下內容使用text屬性是取不到的:
<div class="news"> 1. <b>無流量站點清理公告</b> 2013-02-22<br /> 取不到的內容 </div> <div class="news"> 2. <strong>無流量站點清理公告</strong> 2013-02-22<br />
取不到的內容
</div> <div class="news"> 3. <span>無流量站點清理公告</span> 2013-02-22<br />
取不到的內容
</div> <div class="news"> 4. <u>無流量站點清理公告</u> 2013-02-22<br />
取不到的內容
</div>
這些“取不到的內容”使用這個是取不到的。怎么辦呢?別擔心,lxml還有一個屬性叫做“tail”,它的意思是結束節點前面的內容,也就是說在“<br />”與“</div>”之間的內容。它的源碼里面的意思是“text after end tag”
至於“following-sibling::”前綴就如其名所說,表示同一層的下一個節點。"following-sibling::*"就是任意下一個節點,而“following-sibling::ul”就是下一個ul節點。
如果script與style標簽之間的內容影響解析頁面,或者頁面很不規則,可以使用lxml.html.clean模塊。模塊 lxml.html.clean 提供 一個Cleaner 類來清理 HTML 頁。它支持刪除嵌入或腳本內容、 特殊標記、 CSS 樣式注釋或者更多。
cleaner = Cleaner(style=True, scripts=True,page_structure=False, safe_attrs_only=False)
print cleaner.clean_html(html)
注意,page_structure,safe_attrs_only為False時保證頁面的完整性,否則,這個Cleaner會把你的html結構與標簽里的屬性都給清理了。使用Cleaner類要十分小心,小心擦槍走火。
忽略大小寫可以:
page = etree.HTML(html)
keyword_tag = page.xpath("//meta[translate(@name,'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz')='keywords']")
這里有詳細的Cleaner類初始化參數說明:http://lxml.de/api/lxml.html.clean.Cleaner-class.html