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

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

作者: 小马哥是没有感情的 | 来源:发表于2019-10-25 20:02 被阅读0次

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

    比如标题、内容、时间、作者等。最常见的提取方式有两种: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代码示例

    #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
    '''
    

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

    最基本的思路有两个,一、是基于url本身的特征二、基于url所在位置的特征。(其实看到这里,你就应该对该使用正则表达式还是XPath有所体会了,使用XPath想都不用想,正则表达式还得想一会儿)
    下面我们来分析一下,第一个思路基本行不通,由于网页上并不是只有我们想要的这些主要的文章url,还有一些推荐的文章url。

    见图

    url分析

    而这些url本身是没有什么明显的特征区别的,区别只在于位置和人为赋予的这个位置的意义,因此基于url本身特征来使用正则表达式是不可行的。第二个思路基于位置特征。

    见图:

    url分析

    通过对比推荐和非推荐的url的a标签,可以找出一个特征就是,非推荐的url的a标签的class属性都是 "titlelnk",因此我们可以基于此构造正则表达式。

    python示例代码

    #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写起来挺麻烦的,因此我们可以通过先取到带额外字符的时间,再对数据做清洗来得到时间。然后再看一下使用正则表达式,由于时间的格式很固定,并且网页中不存在会干扰到我们的时间,因此正则写起来就比较简单了,而且还不用过滤。

    python示例代码:

    #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这种基于页面层次结构的语法可以说完全没用,但使用正则的话却可以轻易达到目的。

    python示例代码:

    #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]
    
    # 输出
    '''
    [u'DDD', u'Connect', u'Connect', u'Mac', u'Visual', u'Studio', u'MSSQL', u'Server', u'on', u'Linux']
    '''
    

    在这三个例子中,第一个和第二个都会涉及到XPath和正则表达式的选择问题,第三个也会涉及到选择,但几乎立马就放弃了XPath,下面我们来总结一下 XPath 和正则表达式到底该如何选用。

    也就是说,首先要分清目标数据是层次结构明显还是特征明显,这个分清楚了,该选什么也就基本确定了,后续要思考的其实算是优化的步骤。
      简单来说,在XPath和正则表达式都可以使用的情况下,选择的标准有这么几个(按优先级排序):

    1. 匹配准确度
    2. 语法复杂度(思考花费时间长短)
    3. 后续处理复杂度
    4. 可维护性
    5. 可读性

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

    1. 首先明白目的是什么(拿到准确的目标数据),然后想一下使用XPath的话,加上后续处理需要写几行代码?大概估计一下,再估计一下使用正则需要写几行代码,这时候你差不多就有个判断了

    2. 然后再想一下万一目标网站改了一下格式,我需要维护的话,怎么改起来方便?(这个推荐使用XPath,可读性和可维护行都是比较好的,正则表达式的可读性并不怎么好)

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

    匹配具体字段而言的。

    如果想让代码达到最优,需要考虑的东西还有很多,比如XPath和正则表达式的执行效率,一般情况下,正则表达式的效率是比较高的,这个前提是不太复杂的正则表达式,有些特别复杂的正则表达式可能严重减慢执行效率。

    转载自:Mr:D


    参考文章:
    《用 python 写的爬虫,有哪些提高的技能?》 https://www.zhihu.com/question/36832667
    《【Python爬虫】入门知识》 http://www.jianshu.com/p/74b94eadae15
    《xpath与正则表达式抽取网页信息的速度比较》http://pcliuyang.blog.51cto.com/8343567/1341117

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/ohxqvctx.html