从互联网上下载到网页,只是咱们迈向成功的第一步。拿到网页数据之后,咱们须要从中提取咱们想要的具体信息,html
好比标题、内容、时间、做者等。最多见的的提取方式有两种:XPath和正则表达式。node
先简单介绍一下XPATH和正则表达式。python
XPath即为 XML 路径语言(XML Path Language),它是一种用来肯定XML文档中某部分位置的语言。 XPath基于正则表达式
XML的树状结构,提供在数据结构树中找寻节点的能力(见维基百科 XPath)。数据结构
正则表达式(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正post
则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。优化
发表一下我的看法:网站
XPath主要是用来处理 XML 格式文档的,它是基于 XML 文档的层次结构来肯定一个到达指定节点的路径的,所以特别url
适合处理这种层级结构明显的数据(起初 XPath 的提出的初衷是将其做为一个通用的、介于 XPointer 与 XSLT 间的语法模型。spa
可是 XPath 很快的被开发者采用来看成小型查询语言)。
正则表达式能够处理任何格式的字符串文档,它是基于文本的特征来匹配、查找指定数据的。
在网上看到一个很形象的比喻:若是提取信息就像找一个建筑,那么正则表达式就是告诉你,这个建筑的左边是什么、右
边是什么、以及这个建筑自己有哪些特征,但这样的描述在全国范围内可能有不少地方的建筑都符合条件,找起来仍是不太方
便,除非你限制范围,好比指定北京海淀区等。而 XPath 就是告诉你这个建筑在中国-北京-海淀区-中关村-中关村大街-1
号,这样找起来就方便了不少,固然这不是说XPath就比正则表达式要好用,具体选择还得看应用场景,好比让你在一个广场
上等人,告诉对方你在哪里的时候,你总不会说我在广场上从东数第23块砖从北数第16块砖上站着吧,你极可能会说我在一个
雕像旁边或喷泉旁边等。下面主要说一下在爬虫中通常如何选择使用XPath和正则表达式。
在写爬虫的时候,通常会遇到 HTML、JSON、XML、纯文本等格式的文档,先来讲一下 HTML 和 XML,HTML 是 XML
的一个子集,所以它们的处理方式是同样的,首选使用 XPath 来获取信息,以博客园首页为例,若是咱们须要的数据是文章
的 url 列表,最好使用 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 ... '''
若是使用正则表达式的话,也能够达成一样的效果,不过,要转换一下思路。因为正则表达式是使用特征匹配的,所以
最基本的思路有两个,1、是基于url自己的特征;2、基于url所在位置的特征。(其实看到这里,你就应该对该使用正则表
达式仍是XPath有所体会了,使用XPath想都不用想,正则表达式还得想一下子)下面咱们来分析一下,第一个思路基本行不
通,因为网页上并非只有咱们想要的这些主要的文章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] # 输出 ''' 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和正则表达式均可以使用的状况下,选择的标准有这么几个(按优先级排序):
标准只是死的,它不会告诉你该如何去应用,其实通常状况下我是这么作的:
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