摘要:在阅读这篇文章前最好把同系列文章实战正则表达式一验证手机号先仔细阅读一遍。但实际上这样一个表达式是无法从上面的中提取第一个元素的这里主要的问题是在默认情况下点号字符无法匹配换行符。但是很遗憾,正则表达式中没有排除型子表达式或者说排除型分组。
这篇文章通过提取html元素介绍了正则表达式中模式修饰符、贪婪匹配与非贪婪匹配、Unicode模式、环视等知识点。
在阅读这篇文章前最好把同系列文章php实战正则表达式(一):验证手机号先仔细阅读一遍。
有这样一个表格
用户名 | 职业 |
---|---|
Kobe Bryant | 篮球运动员 |
Jay Chou | 歌手、词曲创作人、制作人、演员、导演 |
Lionel Messi | 足球运动员 |
它的源码如下:
</>复制代码
用户名 职业 Kobe Bryant 篮球运动员 Jay Chou 歌手、词曲创作人、制作人、演员、导演 Lionel Messi 足球运动员
现在要提取第一个元素。最简单的正则表达式应该是这样:
s+.*
其中
s是php实战正则表达式(一):验证手机号介绍过的字符组简记法中的一个,代表回车符、空格、制表符等空白字符
量词+表示它所修饰的字符或字符组出现次数大于等于1
点号字符.在正则表达式中是一个特殊的元字符,它可以匹配“任意字符”
闭标签中的斜线/在php的正则表达式中是模式分隔符,所以需要转义来表示斜线字符。
但实际上这样一个表达式是无法从上面的中提取第一个元素的
这里主要的问题是在默认情况下点号字符.无法匹配换行符 。有两个方法可以解决这个问题:
使用模式修饰符s,正则表达式为/s+.*/s或(?s)s+.*。模式修饰符s的作用就是让点号字符.可以匹配换行符。
用[sS]或[wW]或[dD]代替点号字符.来匹配所有字符,正则表达式为s+[sS]*
关于模式修饰符(Pattern Modifiers),这里需要详细介绍一下(点击这里查看php支持的所有模式修饰符)。模式修饰符可以改变正则表达式的一些默认规则,常用的模式修饰符有i、s、U、u等,我们在后面会用到它们中的一些,这里不展开介绍每个模式修饰符的作用,后面用到了再具体介绍。这里主要对比一下/.../{modifier}与...(?{modifier})...两种表示方法的区别。
.*/s(?s).*模式修饰符 | /.../{modifier} | ...(?{modifier})... |
---|---|---|
示例 | / | |
名称(php手册) | 模式修饰符 | 模式内修饰符 |
名称(《正则指引》) | 预定义常量 | 模式修饰符 |
作用范围 | 整个正则表达式 | 不在分组(子表达式)中时,对它后面的全部正则表达式起作用;如果在分组(子表达式)中,则对它分组中的剩余部分起作用。在没有分组,且放在整个正则表达式最前面的时候相当于/.../{modifier} |
支持程度 | 支持所有模式修饰符 | 支持部分模式修饰符 |
其他编程语言 | 可能不支持 | 一般都支持 |
从上面的gif中可以看到提取的结果中有三个tr,而不是只有一个。这是因为正则表达式中量词默认是贪婪匹配,在这里,.*会匹配一切字符,直到最后没有字符再向前回溯,回溯到中的最后一个时与正则表达式中的相匹配,从而完成整个匹配过程,最后的结果也就是包含了三个。
可以使用模式修饰符U指定整个正则表达式为非贪婪模式,也可以使用非贪婪匹配量词指定某一个量词为非贪婪模式:
指定整个正则表达式为非贪婪模式:
/s+.*/Us
或(?Us)s+.*
非贪婪量词:
/s+.*?/s
完整的贪婪量词(匹配优先量词)与非贪婪量词(忽略优先量词)见下表:
贪婪量词 | 非贪婪量词 | 限定次数 |
---|---|---|
* | *? | 可能出现,可能不出现,出现次数没有上限 |
+ | +? | 至少出现1次,没有上限 |
? | ?? | 出现0次或1次 |
{m,n} | {m,n}? | 出现次数大于等于m,小于等于n |
{m,} | {m,}? | 至少出现m次,没有上限 |
{0,n} | {0,n}? | 出现0次-n次 |
假设我们想把表格中有关于运动员的记录都提取出来,我们可能会使用/.*运动员.*/s这样的正则表达式。
这个表达式在Unicode编码环境下可以匹配出结果,但是在GBK环境下就未必了。我们可以通过模式修饰符u来指定Unicode模式:
/.*运动员.*/us
在Unicode模式下,我们甚至可以使用码值来代替汉字:
/.*x{8fd0}x{52a8}x{5458}.*/us
php正则中使用x{hex}的形式来表示Unicode字符的码值,使用码值的好处是可以结合字符组来表示一段范围,如[x{4e00}-x{9fff}]表示匹配所有汉字字符。
上面的表达式可以匹配出结果,但是却不正确。我们可以看到,它匹配了整个字符串的第一个到最后一个。
直觉上,我们是想正则表达式先去匹配“运动员”,然后向左寻找最近的一个,向右寻找最近的一个。但事实上,正则表达式是从左往右匹配的,即从开始寻找,整个正则表达式的匹配情况见下表(空白字符没有显示出来)。
表达式 | 匹配值 | ||
---|---|---|---|
/ | |||
.* | 用户名 | 职业 | |
Kobe Bryant | 篮球 | ||
运动员 | 运动员 | ||
.* | |||
Jay Chou | 歌手、词曲创作人、制作人、演员、导演 | ||
Lionel Messi | 足球运动员 | ||
/us |
这里两个.*匹配到的字符都比预期要多。第二个.*匹配字符比预期多的原因是正则表达式默认是贪婪匹配模式,它会匹配剩余字符串中的每个字符,直到字符串的末尾,然后再向前回溯到最后一个,可以通过指定非贪婪匹配模式来解决这个问题。但是第一个.*匹配字符比预期多是正常现象,因为正则表达式是从左向右匹配的,表达式中的匹配字符串中第一个,后面的.*则匹配剩余的所有字符,直到字符串的末尾,然后再向前回溯到“运动员”。
我们先看看使用非贪婪匹配时的结果:
可以看到,第二个.*匹配的字符已经是我们想要的了。那么,对于第一个.*匹配字符比预期多这个问题怎么解决呢?
如果仅使用到目前为止我的文章中介绍的知识,也是有方法可以解决的。我们可以先从左到右匹配出所有的行(...),方法是使用php中的preg_match_all函数结合非贪婪匹配模式;然后再遍历每一行,过滤出其中包含“运动员”的行即可。
当然,我们也可以通过纯粹的正则表达式来解决这个问题。如果有一定正则表达式使用经验的朋友可能很容易联想到排除型字符组,我们介绍过字符组[...],它表示在同一位置可能出现的字符。而排除型字符组则表示在同一位置不能出现的字符,它的形式是[^...],通过紧跟在开方括号[后面的^来表示排除型字符组。例如,[^d]表示匹配的字符是除了数字以外的任意字符。
如果有排除型子表达式,类似于(^)*,我们只需要指定第一个.*把排除就行了。但是很遗憾,正则表达式中没有排除型子表达式或者说排除型分组。这种情况下,我们只能使用环视
/(.(?!))*运动员.*/Us
环视(look-around)不匹配任何字符,用来“停在原地,四处张望”。上面的表达式使用了否定顺序环视,它的形式是(?!...)。具体对于(.(?!))*来分析,每当.匹配了一个字符后,就向右看看,如果当前匹配字符的右边没有出现就匹配成功。
完整的环视有:
名字 | 记法 | 含义 |
---|---|---|
肯定顺序环视 | (?=...) | 向右看看,右边出现了环视中的内容才匹配 |
否定顺序环视 | (?!...) | 向右看看,右边不出现环视中的内容才匹配 |
肯定逆序环视 | (?<=...) | 向左看看,左边出现了环视中的内容才匹配 |
否定逆序环视 | (? | 向左看看,左边不出现环视中的内容才匹配 |
由于上面的正则表达式有一个分组(子表达式),所以匹配的结果除了下标0,还有下标1,这里下标1的结果其实没有什么用,我们可以用之前介绍过的非捕获分组:
/(?:.(?!))*运动员.*/Us
我们的真正目的是提取所有包含“运动员”的行,而上面只提取了第一个,所以需要将preg_match函数换成preg_match_all。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/21126.html
摘要:上一篇文章网络爬虫实战高级用法下一篇文章网络爬虫实战与正则表达式抓取猫眼电影排行本节我们看一下正则表达式的相关用法,正则表达式是处理字符串的强大的工具,它有自己特定的语法结构,有了它,实现字符串的检索替换匹配验证都不在话下。 上一篇文章:Python3网络爬虫实战---25、requests:高级用法下一篇文章:Python3网络爬虫实战---27、Requests与正则表达式抓取猫眼...
摘要:上一篇文章网络爬虫实战与正则表达式抓取猫眼电影排行下一篇文章网络爬虫实战解析库的使用上一节我们实现了一个最基本的爬虫,但提取页面信息时我们使用的是正则表达式,用过之后我们会发现构造一个正则表达式还是比较的繁琐的,而且万一有一点地 上一篇文章:Python3网络爬虫实战---27、Requests与正则表达式抓取猫眼电影排行下一篇文章:Python3网络爬虫实战---29、解析库的使用:...
摘要:所以我们如果想获取电影,只需要分开请求次,而次的参数设置为,,,,即可,这样我们获取不同的页面结果之后再用正则表达式提取出相关信息就可以得到的所有电影信息了。上一篇文章网络爬虫实战正则表达式下一篇文章网络爬虫实战解析库的使用 上一篇文章:Python3网络爬虫实战---26、正则表达式下一篇文章:Python3网络爬虫实战---28、解析库的使用:XPath 本节我们利用 Reque...
摘要:解析器在解析的时候实际上是依赖于解析器的,它除了支持标准库中的解析器,还支持一些第三方的解析器比如,下面我们对支持的解析器及它们的一些优缺点做一个简单的对比。 上一篇文章:Python3网络爬虫实战---28、解析库的使用:XPath下一篇文章:Python3网络爬虫实战---30、解析库的使用:PyQuery 前面我们介绍了正则表达式的相关用法,但是一旦正则写的有问题,可能得到的就...
小编写这篇文章的主要目的,是来给大家做出一个介绍,介绍关于python爬虫的一些技能技巧,包括怎么才能够爬取pixiv图片,作为一个小白来讲,还是需要一定的实战的,那么,具体的实战技能,下面就给大家详细的解答下。 自从接触python以后就想着爬pixiv,之前因为梯子有点问题就一直搁置,最近换了个梯子就迫不及待试了下。 爬虫无非request获取html页面然后用正则表达式或者beaut...
阅读 2121·2019-08-30 15:54
阅读 3753·2019-08-29 13:07
阅读 3278·2019-08-29 12:39
阅读 1952·2019-08-26 12:13
阅读 1699·2019-08-23 18:31
阅读 2311·2019-08-23 18:05
阅读 1989·2019-08-23 18:00
阅读 1197·2019-08-23 17:15