XPath与正则表达式在文本数据提取时该如何选择?

  • A+
所属分类:编程茶楼

从互联网上下载到网页,只是我们迈向成功的第一步。拿到网页数据以后,我们需要从中提取我们想要的具体信息,

比如标题、内容、时间、作者等。最常见的的提取方式有两种:XPath和正则表达式。

  先简单介绍一下XPATH和正则表达式。

  XPath即为 XML 路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。 XPath基于

XML的树状结构,提供在数据结构树中找寻节点的能力(见维基百科 XPath)。

  正则表达式英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正

则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。

 

  发表一下个人见解:

  XPath主要是用来处理 XML 格式文档的,它是基于 XML 文档的层次结构来确定一个到达指定节点的路径的,因此特别

适合处理这种层级结构明显的数据(起初 XPath 的提出的初衷是将其作为一个通用的、介于 XPointer 与 XSLT 间的语法模型。

但是 XPath 很快的被开发者采用来当作小型查询语言)。

  正则表达式可以处理任何格式的字符串文档,它是基于文本的特征来匹配、查找指定数据的。

 

  在网上看到一个很形象的比喻:如果提取信息就像找一个建筑,那么正则表达式就是告诉你,这个建筑的左边是什么、右

边是什么、以及这个建筑本身有哪些特征,但这样的描述在全国范围内可能有很多地方的建筑都符合条件,找起来还是不太方

便,除非你限制范围,比如指定北京海淀区等。而 XPath 就是告诉你这个建筑在中国-北京-海淀区-中关村-中关村大街-1

号,这样找起来就方便了很多,当然这不是说XPath就比正则表达式要好用,具体选择还得看应用场景,比如让你在一个广场

上等人,告诉对方你在哪里的时候,你总不会说我在广场上从东数第23块砖从北数第16块砖上站着吧,你很可能会说我在一个

雕像旁边或喷泉旁边等。下面主要说一下在爬虫中一般如何选择使用XPath和正则表达式。

 

  在写爬虫的时候,一般会遇到 HTML、JSON、XML、纯文本等格式的文档,先来说一下 HTML 和 XML,HTML 是 XML

的一个子集,因此它们的处理方式是一样的,首选使用 XPath 来获取信息,以博客园首页为例,如果我们需要的数据是文章

的 url 列表,最好使用 XPath,见图

XPath与正则表达式在文本数据提取时该如何选择?

 

  文章 url 列表本身在视觉上是一个结构化特别明显的数据,而且通过分析 HTML DOM 树,我们发现需要采集的a标签的

层次规律特别整齐,很容易就能使用XPath语法来标示出a标签的路径,python代码示例

XPath与正则表达式在文本数据提取时该如何选择?

#coding:utf8 from lxml import etree import requests url = 'http://www.cnblogs.com/' response = requests.get(url) response.encoding = 'utf8' html = response.text root = etree.HTML(html) node_list = root.xpath("//div[@class='post_item_body']/h3/a") for node in node_list:     print node.attrib['href'] # 输出 ''' http://www.cnblogs.com/olivers/p/6073506.html http://www.cnblogs.com/-free/p/6073496.html ... '''

XPath与正则表达式在文本数据提取时该如何选择?

   如果使用正则表达式的话,也可以达成同样的效果,不过,要转换一下思路。由于正则表达式是使用特征匹配的,因此

最基本的思路有两个,一、是基于url本身的特征;二、基于url所在位置的特征。(其实看到这里,你就应该对该使用正则表

达式还是XPath有所体会了,使用XPath想都不用想,正则表达式还得想一会儿)下面我们来分析一下,第一个思路基本行不

通,由于网页上并不是只有我们想要的这些主要的文章url,还有一些推荐的文章url。见图

XPath与正则表达式在文本数据提取时该如何选择?

 

  而这些url本身是没有什么明显的特征区别的,区别只在于位置和人为赋予的这个位置的意义,因此基于url本身特征来使

用正则表达式是不可行的。第二个思路基于位置特征,见图

XPath与正则表达式在文本数据提取时该如何选择?

 

  通过对比推荐和非推荐的url的a标签,可以找出一个特征就是,非推荐的url的a标签的class属性都是 "titlelnk",因此我们

可以基于此构造正则表达式,python示例代码

XPath与正则表达式在文本数据提取时该如何选择?

#coding:utf8 import re import requests        url = 'http://www.cnblogs.com/' response = requests.get(url) response.encoding = 'utf8' html = response.text urls = re.findall('class=\"titlelnk\"\s*[^>]*?href=\"([^\"]+)', html) for url in urls:     print url           # 输出 '''  http://www.cnblogs.com/olivers/p/6073506.html http://www.cnblogs.com/-free/p/6073496.html ... '''

XPath与正则表达式在文本数据提取时该如何选择?

 

  当然使用正则表达式还有其他很多的形式,使用XPath也有很多其他的形式,但通过这个例子,我们可以体会到在采集文

章列表时,使用XPath是比较方便的。然后让我们改一下需求,现在我们需要采集文章的发布时间,见图

XPath与正则表达式在文本数据提取时该如何选择?

 

  这个很明显我们仅使用普通的XPath是无法取到时间的,会带上很多额外的字符,但太复杂的XPath写起来挺麻烦的,因

此我们可以通过先取到带额外字符的时间,再对数据做清洗来得到时间。然后再看一下使用正则表达式,由于时间的格式很固

定,并且网页中不存在会干扰到我们的时间,因此正则写起来就比较简单了,而且还不用过滤。python示例代码

XPath与正则表达式在文本数据提取时该如何选择?

#coding:utf8 import re from lxml import etree  import requests url = 'http://www.cnblogs.com/' response = requests.get(url) response.encoding = 'utf8' html = response.text # XPath 用法 root = etree.HTML(html) time_node_list = root.xpath("//div[@class='post_item_foot']/text()") # 这个XPath匹配到的数据也是不对的,存在很多空白节点 # 我们来处理一下 # 删除空白节点 并去除空格 time_node_list = [node.strip() for node in time_node_list if node.strip()] # 提取时间字符串 time_list = [' '.join(node.split()[1:]) for node in time_node_list] for time_str in time_list:     print time_str                                 # 输出 ''' 2016-11-17 15:23 2016-11-17 15:10 2016-11-17 14:34 2016-11-17 11:58 ... ''' # 正则用法 time_list = re.findall('(\d+-\d+-\d+\s*\d+:\d+)', html) # 这样写是不对的,因为在源码中还有很多时间格式存在于标签中,因此对正则优化如下 time_list = re.findall(u'发布于\s*(\d+-\d+-\d+\s*\d+:\d+)', html) for time_str in time_list:     print time_str # 输出 ''' 2016-11-17 15:23 2016-11-17 15:10 2016-11-17 14:34 2016-11-17 11:58 ... '''

XPath与正则表达式在文本数据提取时该如何选择?

 

  在这个例子中我们其实就可以体会到XPath的一些局限性了,下面我们再来看一个XPath基本做不到的例子。

  需求改为获取页面中所有的英文单词。首先分析一下,这个需求可以说和页面的结构完全没关系,XPath这种基于页面层

次结构的语法可以说完全没用,但使用正则的话却可以轻易达到目的。python示例代码

XPath与正则表达式在文本数据提取时该如何选择?

#coding:utf8 import re from lxml import etree  import requests url = 'http://www.cnblogs.com/' response = requests.get(url) response.encoding = 'utf8' html = response.text # 首先替换掉源码中的各种标签 html = re.sub('<[^>]+/?>', '', html) # 匹配英文单词 words = re.findall("[a-z]+", html, re.I) print len(words) print words[:10] # 输出 ''' 303 [u'DDD', u'Connect', u'Connect', u'Mac', u'Visual', u'Studio', u'MSSQL', u'Server', u'on', u'Linux'] '''

XPath与正则表达式在文本数据提取时该如何选择?

 

  在这三个例子中,第一个和第二个都会涉及到XPath和正则表达式的选择问题,第三个也会涉及到选择,但几乎立马就放

弃了XPath,下面我们来总结一下 XPath 和正则表达式到底该如何选用。

  第一个例子中,目标数据所在的位置可以很方便的用 XPath 语法标示,并且不会产生误匹配。而使用正则表达式的时候

却可能会发生误匹配,需要收集更多的特征来保证匹配的准确性,这无疑就增加了复杂度,因此推荐使用XPath。第二个例子

中,目标数据所在位置使用XPath来不能完全匹配,存在多余数据,需要进一步的处理。但使用正则表达式的时候,却可以一

步到位,节省了工作量,因此推荐使用正则表达式。

  也就是说,首先要分清目标数据是层次结构明显还是特征明显,这个分清楚了,该选什么也就基本确定了,后续要思考的

其实算是优化的步骤。

  简单来说,在XPath和正则表达式都可以使用的情况下,选择的标准有这么几个(按优先级排序):

    1. 匹配准确度

    2. 语法复杂度(思考花费时间长短)

    3. 后续处理复杂度

    4. 可维护性

    5. 可读性

  标准只是死的,它不会告诉你该如何去应用,其实一般情况下我是这么做的:

      1. 首先明白目的是什么(拿到准确的目标数据),然后想一下使用XPath的话,加上后续处理需要写几行代码?大概

    估计一下,再估计一下使用正则需要写几行代码,这时候你差不多就有个判断了

      2.然后再想一下万一目标网站改了一下格式,我需要维护的话,怎么改起来方便?(这个推荐使用XPath,可读性和

    可维护行都是比较好的,正则表达式的可读性并不怎么好)

  其实在真正的工作中,一个网页一般需要提取很多个字段,因此XPath和正则表达式混用是很经常的,这个选择只是针对

匹配具体字段而言的。

  如果想让代码达到最优,需要考虑的东西还有很多,比如XPath和正则表达式的执行效率,一般情况下,正则表达式的效

率是比较高的,这个前提是不太复杂的正则表达式,有些特别复杂的正则表达式可能严重减慢执行效率。

 

 

 

参考文章:

《用 python 写的爬虫,有哪些提高的技能?》 https://www.zhihu.com/question/36832667

【Python爬虫】入门知识 http://www.jianshu.com/p/74b94eadae15

《xpath与正则表达式抽取网页信息的速度比较》http://pcliuyang.blog.51cto.com/8343567/1341117

from:

https://www.cnblogs.com/dyfblog/p/6073069.html

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: