Python中利用xpath解析HTML的方法


Posted in Python onMay 14, 2018

在进行网页抓取的时候,分析定位html节点是获取抓取信息的关键,目前我用的是lxml模块(用来分析XML文档结构的,当然也能分析html结构), 利用其lxml.html的xpath对html进行分析,获取抓取信息。

首先,我们需要安装一个支持xpath的python库。目前在libxml2的网站上被推荐的python binding是lxml,也有beautifulsoup,不嫌麻烦的话还可以自己用正则表达式去构建,本文以lxml为例讲解。

假设有如下的HTML文档:

<html>
 <body>
  <form>
   <div id='leftmenu'>
    <h3>text</h3>
    <ul id='china'><!-- first location -->
     <li>...</li>
     <li>...</li>
       ......
    </ul>
    <ul id='england'><!-- second location-->
     <li>...</li>
     <li>...</li>
       ......
    </ul>
   </div>
  </form>
 </body>
</html>

直接使用lxml处理:

import codecs
 from lxml import etree
 f=codecs.open("ceshi.html","r","utf-8")
 content=f.read()
 f.close()
 tree=etree.HTML(content)

etree提供了HTML这个解析函数,现在我们可以直接对HTML使用xpath了,是不是有点小激动,现在就尝试下吧。

在使用xpath之前我们先来看看作为对照的jQuery和RE。

在jQuery里要处理这种东西就很简单,特别是假如那个ul节点有id的话(比如是<ul id='china'>):

$("#china").each(function(){...});

具体到此处是:

$("#leftmenu").children("h3:contains('text')").next("ul").each(function(){...});

找到id为leftmenu的节点,在其下找到一个内容包含为”text”的h3节点,再取其接下来的一个ul节点。

在python里要是用RE来处理就略麻烦一些:

block_pattern=re.compile(u"<h3>档案</h3>(.*?)<h3>", re.I | re.S)
m=block_pattern.findall(content)
item_pattern=re.compile(u"<li>(.*?)</li>", re.I | re.S)
items=item_pattern.findall(m[0])
for i in items:
  print i

那么用xpath要怎么做呢?其实跟jQuery是差不多的:

nodes=tree.xpath("/descendant::ul[@id='china']")

当然,现在没有id的话也就只能用类似于jQuery的方法了。完整的xpath应该是这样写的(注意,原文件中的TAG有大小写的情况,但是在XPATH里只能用小写):

nodes=tree.xpath(u"/html/body/form/div[@id='leftmenu']/h3[text()='text']/following-sibling::ul[1]")

更简单的方法就是像jQuery那样直接根据id定位:

nodes=tree.xpath(u"//div[@id='leftmenu']/h3[text()='text']/following-sibling::ul[1]")

这两种方法返回的结果中,nodes[0]就是那个“text”的h3节点后面紧跟的第一个ul节点,这样就可以列出后面所有的ul节点内容了。

如果ul节点下面还有其他的节点,我们要找到更深节点的内容,如下的循环就是把这些节点的文本内容列出:

nodes=nodes[0].xpath("li/a")
for n in nodes:
  print n.text

对比三种方法应该可以看出xpath和jQuery对于页面的解析都是基于XML的语义进行,而RE则纯粹是基于plain text。RE对付简单的页面是没有问题,如果页面结构复杂度较高的时候(比如一堆的DIV来回嵌套之类),设计一个恰当的RE pattern可能会远比写一个xpath要复杂。特别是目前主流的基于CSS的页面设计方式,其中大部分关键节点都会有id??对于使用jQuery的页面来说则更是如此,这时xpath相比RE就有了决定性的优势。

 附录:基本XPATH语法介绍,详细请参考XPath的官方文档

XPATH基本上是用一种类似目录树的方法来描述在XML文档中的路径。比如用“/”来作为上下层级间的分隔。第一个“/”表示文档的根节点(注意,不是指文档最外层的tag节点,而是指文档本身)。比如对于一个HTML文件来说,最外层的节点应该是”/html”。

同样的,“..”和“.”分别被用来表示父节点和本节点。

XPATH返回的不一定就是唯一的节点,而是符合条件的所有节点。比如在HTML文档里使用“/html/head/scrpt”就会把head里的所有script节点都取出来。

为了缩小定位范围,往往还需要增加过滤条件。过滤的方法就是用“[”“]”把过滤条件加上。比如在HTML文档里使用“/html/body/div[@id='main']”,即可取出body里id为main的div节点。

其中@id表示属性id,类似的还可以使用如@name, @value, @href, @src, @class….

而 函数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']”。

至于“following-sibling::”前缀就如其名所说,表示同一层的下一个节点。”following-sibling::*”就是任意下一个节点,而“following-sibling::ul”就是下一个ul节点。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python使用fileinput模块实现逐行读取文件的方法
Apr 29 Python
在Python中处理列表之reverse()方法的使用教程
May 21 Python
TensorFlow模型保存/载入的两种方法
Mar 08 Python
使用python编写udp协议的ping程序方法
Apr 22 Python
使用50行Python代码从零开始实现一个AI平衡小游戏
Nov 21 Python
Python imread、newaxis用法详解
Nov 04 Python
python+Django+pycharm+mysql 搭建首个web项目详解
Nov 29 Python
pytorch 实现在一个优化器中设置多个网络参数的例子
Feb 20 Python
Python参数传递实现过程及原理详解
May 14 Python
Django中和时区相关的安全问题详解
Oct 12 Python
python的scipy.stats模块中正态分布常用函数总结
Feb 19 Python
python自动化测试之Selenium详解
Mar 13 Python
利用pyinstaller将py文件打包为exe的方法
May 14 #Python
django的登录注册系统的示例代码
May 14 #Python
django允许外部访问的实例讲解
May 14 #Python
运行django项目指定IP和端口的方法
May 14 #Python
python使用Flask操作mysql实现登录功能
May 14 #Python
查看Django和flask版本的方法
May 14 #Python
Python处理中文标点符号大集合
May 14 #Python
You might like
我的php学习笔记(毕业设计)
2012/02/21 PHP
php数组保存文本与文本反编成数组实例
2014/11/13 PHP
thinkPHP中验证码的简单使用方法
2015/12/26 PHP
thinkphp3.x中session方法的用法分析
2016/05/20 PHP
php结合ajax实现手机发红包的案例
2016/10/13 PHP
Jquery replace 字符替换实现代码
2010/12/02 Javascript
深入探密Javascript数组方法
2015/01/08 Javascript
表单验证插件Validation应用的实例讲解
2015/10/10 Javascript
Javascript获取随机数的实现方法
2016/06/22 Javascript
详解Vue2+Echarts实现多种图表数据可视化Dashboard(附源码)
2017/03/21 Javascript
jQuery插件FusionCharts绘制2D柱状图和折线图的组合图效果示例【附demo源码】
2017/04/10 jQuery
Angular4项目中添加i18n国际化插件ngx-translate的步骤详解
2017/07/02 Javascript
基于LayUI实现前端分页功能的方法
2017/07/22 Javascript
利用Ionic2 + angular4实现一个地区选择组件
2017/07/27 Javascript
vue实现长图垂直居上 vue实现短图垂直居中
2017/10/18 Javascript
老生常谈JS中的继承及实现代码
2018/07/06 Javascript
微信小程序实现折叠展开效果
2018/07/19 Javascript
解决VUE项目使用Element-ui 下拉组件的验证失效问题
2020/11/07 Javascript
Python中使用PyQt把网页转换成PDF操作代码实例
2015/04/23 Python
使用pdb模块调试Python程序实例
2015/06/02 Python
Python中格式化format()方法详解
2017/04/01 Python
5个很好的Python面试题问题答案及分析
2018/01/19 Python
django中ORM模型常用的字段的使用方法
2019/03/05 Python
python中如何使用分步式进程计算详解
2019/03/22 Python
使用Python和Scribus创建一个RGB立方体的方法
2019/07/17 Python
python输入中文的实例方法
2020/09/14 Python
linux centos 7.x 安装 python3.x 替换 python2.x的过程解析
2020/12/14 Python
python3列表删除大量重复元素remove()方法的问题详解
2021/01/04 Python
电子商务专业个人的自我评价
2013/12/19 职场文书
党员反对四风思想汇报范文
2014/10/25 职场文书
自主招生推荐信格式模板
2015/03/24 职场文书
超市督导岗位职责
2015/04/10 职场文书
幼儿园音乐教学反思
2016/02/18 职场文书
小学思品教学反思
2016/02/20 职场文书
Spring boot应用启动后首次访问很慢的解决方案
2021/06/23 Java/Android
抖音动画片,皮皮虾,《治愈系》动画在用这首REMIX作为背景音乐,Anak ,The last world with you完整版
2022/03/16 杂记