在这里我们依然是想获取中间的数字,所以中间我们依然写的是(d+),数字两侧由于内容比较杂乱,所以两侧我们想省略来写,都写.,最后组成^He.(d+).*Demo$,看样子并没有什么问题,我们看下运行结果:
<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
7
奇怪的事情发生了,我们只得到了7这个数字,这是怎么回事?
这里就涉及一个贪婪匹配与非贪婪匹配的原因了,贪婪匹配下,.会匹配尽可能多的字符,我们的正则表达式中.后面是d+,也就是至少一个数字,并没有指定具体多少个数字,所以.*就尽可能匹配多的字符,所以它把123456也匹配了,给d+留下一个可满足条件的数字7,所以d+得到的内容就只有数字7了。
但这样很明显会给我们的匹配带来很大的不便,有时候匹配结果会莫名其妙少了一部分内容。其实这里我们只需要使用非贪婪匹配匹配就好了,非贪婪匹配的写法是.*?,多了一个?,那么它可以达到怎样的效果?我们再用一个实例感受一下:
import re content = 'Hello 1234567 World_This is a Regex Demo' reresult = re.match('^He.*?(\d+).*Demo$', content) print(result) print(result.group(1))
在这里我们只是将第一个.改成了.?,转变为非贪婪匹配匹配。结果如下:
<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
很好,这下我们就可以成功获取1234567了。原因可想而知,贪婪匹配是尽可能匹配多的字符,非贪婪匹配就是尽可能匹配少的字符,.?之后是d+用来匹配数字,当.?匹配到Hello后面的空白字符的时候,再往后的字符就是数字了,而d+恰好可以匹配,那么这里.?就不再进行匹配,交给d+去匹配后面的数字。所以这样,.?匹配了尽可能少的字符,d+的结果就是1234567了。
所以说,在做匹配的时候,字符串中间我们可以尽量使用非贪婪匹配来匹配,也就是用.?来代替.,以免出现匹配结果缺失的情况。
但这里注意,如果匹配的结果在字符串结尾,.*?就有可能匹配不到任何内容了,因为它会匹配尽可能少的字符,例如:
import re content = 'http://weibo.com/comment/kEraCN' reresult1 = re.match('http.*?comment/(.*?)', content) reresult2 = re.match('http.*?comment/(.*)', content) print('result1', result1.group(1)) print('result2', result2.group(1))
运行结果:
result1
result2 kEraCN
观察到.?没有匹配到任何结果,而.则尽量匹配多的内容,成功得到了匹配结果。
所以在这里好好体会一下贪婪匹配和非贪婪匹配的原理,对后面写正则表达式非常有帮助。
修饰符
正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。
我们用一个实例先来感受一下:
import re content = '''Hello 1234567 World_This is a Regex Demo ''' reresult = re.match('^He.*?(\d+).*?Demo$', content) print(result.group(1))
和上面的例子相仿,我们在字符串中加了个换行符,正则表达式也是一样的来匹配其中的数字,看一下运行结果:
AttributeError Traceback (most recent call last) <ipython-input-18-c7d232b39645> in <module>() 5 ''' 6 reresult = re.match('^He.*?(\d+).*?Demo$', content) ----> 7 print(result.group(1)) AttributeError: 'NoneType' object has no attribute 'group'
运行直接报错,也就是说正则表达式没有匹配到这个字符串,返回结果为None,而我们又调用了group()方法所以导致AttributeError。
那我们加了一个换行符为什么就匹配不到了呢?是因为.匹配的是除换行符之外的任意字符,当遇到换行符时,.*?就不能匹配了,所以导致匹配失败。
那么在这里我们只需要加一个修饰符re.S,即可修正这个错误。
reresult = re.match('^He.*?(\d+).*?Demo$', content, re.S)
在match()方法的第三个参数传入re.S,它的作用是使.匹配包括换行符在内的所有字符。
运行结果:
1234567
这个re.S在网页匹配中会经常用到,因为HTML节点经常会有换行,加上它我们就可以匹配节点与节点之间的换行了。
另外还有一些修饰符,在必要的情况下也可以使用:
修饰符描述
re.I使匹配对大小写不敏感
re.L做本地化识别(locale-aware)匹配
re.M多行匹配,影响 ^ 和 $
re.S使 . 匹配包括换行在内的所有字符
re.U根据Unicode字符集解析字符。这个标志影响 w, W, b, B.
re.X该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。
在网页匹配中较为常用的为re.S、re.I。
转义匹配
我们知道正则表达式定义了许多匹配模式,如.匹配除换行符以外的任意字符,但是如果目标字符串里面它就包含.我们改怎么匹配?
那么这里就需要用到转义匹配了,我们用一个实例来感受一下:
import re content = '(百度)' reresult = re.match('\(百度\)www\.baidu\.com', content) print(result)
当遇到用于正则匹配模式的特殊字符时,我们在前面加反斜线来转义一下就可以匹配了。例如.我们就可以用.来匹配,运行结果:
<_sre.SRE_Match object; span=(0, 17), match='(百度)'>
可以看到成功匹配到了原字符串。
以上是写正则表达式常用的几个知识点,熟练掌握上面的知识点对后面我们写正则表达式匹配非常有帮助。
search()
我们在前面提到过match()方法是从字符串的开头开始匹配,一旦开头不匹配,那么整个匹配就失败了。
我们看下面的例子:
import re content = 'Extra stings Hello 1234567 World_This is a Regex Demo Extra stings' reresult = re.match('Hello.*?(\d+).*?Demo', content) print(result)