這篇文章介紹用lxml解析HTML的方法
先示範一段取得頁面連結程式碼範例:
#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'}
如果要取得3499910bf9dac5ae3c52d5ede73834854a11e5928b3688cf38c9163b657eb5ac之間的內容,
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標籤:
90ce244b5a5321cadd932bf501f122e4World News only on this page94b3e26ee717c64999d7867364b1b4a3
Ah, and here's some more text, 1. . and this is a parsed fragment ...94b3e26ee717c64999d7867364b1b4a3
變數是一個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()的意思是取得節點所包含的文字。例如:e388a4556c0f65e1904146cc1a846beehelloe388a4556c0f65e1904146cc1a846beeworld94b3e26ee717c64999d7867364b1b4a36fb279ad3fd4344cbdd93aac6ad173ac中,用"p[text()='hello']"即可取得這個p,而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,而不管它上一级是p还是p或是其它什么东东。
而 “descendant::”前缀可以指代任意多层的中间节点,它也可以被省略成一个“/”。比如在整个HTML文档中查找id为“leftmenu”的 p,可以用“/descendant::p[@id='leftmenu']”,也可以简单地使用“ //p[@id='leftmenu']”。
text = page.xpath(u"/descendant::*[text()]")表示任意多层的中间节点下任意标签之间的内容,也即实现蜘蛛抓取页面内容功能。以下内容使用text属性是取不到的:
<p class="news"> 1. <b>无流量站点清理公告</b> 2013-02-22<br /> 取不到的内容 </p> <p class="news"> 2. <strong>无流量站点清理公告</strong> 2013-02-22<br /> 取不到的内容 </p> <p class="news"> 3. <span>无流量站点清理公告</span> 2013-02-22<br /> 取不到的内容 </p> <p class="news"> 4. <u>无流量站点清理公告</u> 2013-02-22<br /> 取不到的内容 </p>
这些“取不到的内容”使用这个是取不到的。怎么办呢?别担心,lxml还有一个属性叫做“tail”,它的意思是结束节点前面的内容,也就是说在“df250b2156c434f3390392d09b1c9563”与“94b3e26ee717c64999d7867364b1b4a3”之间的内容。它的源码里面的意思是“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']")
以上是用lxml解析HTML的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!