Rumah  >  Artikel  >  hujung hadapan web  >  用lxml解析HTML的方法

用lxml解析HTML的方法

高洛峰
高洛峰asal
2017-03-12 17:51:271968semak imbas

这篇文章介绍用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&#39;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>
&#39;&#39;&#39;
page = etree.HTML(html.lower().decode(&#39;utf-8&#39;))
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'}

如果要取得3499910bf9dac5ae3c52d5ede73834855db79b134e9f6b82c0b36e0489ee08ed之间的内容,

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标签:

    fad4044c8d323b31f737763fbb031a54World News only on this page94b3e26ee717c64999d7867364b1b4a3
    Ah, and here's some more text, by the way.
    e388a4556c0f65e1904146cc1a846bee... and this is a parsed fragment ...94b3e26ee717c64999d7867364b1b4a3

注意:XPATH返回的不一定就是唯一的节点,而是符合条件的所有节点。如上所示,只要是body里的p标签,不管是body的第一级节点,还是第二级,第三级节点,都会被取出来。

  如果想进一步缩小范围,直接定位到“fad4044c8d323b31f737763fbb031a54World News only on this page94b3e26ee717c64999d7867364b1b4a3”要怎么做呢?这就需要增加过滤条件。过滤的方法就是用“[”“]”把过滤条件加上。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()的意思则是取得节点包含的文本。比如: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']")


Atas ialah kandungan terperinci 用lxml解析HTML的方法. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn