过去在网页解析中,一直使用的都是Xpath,CSS,或者是BS4解析,很少会用到正则,毕竟一个大型网站的前端代码基本上每过一段时间就会更新一次。所以,用正则表达式的话,很容易过段时间就不能正常解析出来想要的结果。相比较来说,在重写的过程中,上面三个所要花费的时间开销比正则要小的多。不过在每次和别人交流的时候,基本上大神都推荐用正则,这个时候难免知其然而不知其所以然。可能是自己的正则用的还不到家,正则在文本匹配中的妙用还是了解的太少。
一篇写的非常不错的博客:打开链接
上面提到的博客中的内容不再坠余,这里重点说下正则表达式的扩展表示法。
1.1 (?iLmsux)(必须放在匹配字符串的开头)
用户可以直接在正则表达式里面指定一个或者多个标记,而不是通过compile()或者其他re模块函数。
括号里面的iLmsux表示如下(u并不常用,没有列出):
re.I,re.IGNORECASE 不区分大小写的匹配
re.L,re.LOCALE 根据所使用的本地语言环境通过\w,\W,\b,\B,\s,\S实现匹配
re.M,re.MULTILINE ^和$分别匹配目标字符串中行的起始和结尾,而不是严格匹配整个字符串本身的起始和结尾。
re.S,re.DOTALL "."(点号)通常匹配除了\n(换行符)之外的所有单个字符;该模式表示"."(点号)能够匹配全部字符。
re.X,re.VERBOSE 通过反斜线转义,否则所有空格加上#(以及在该行中所有后续文字)都被忽略,除非在一个字符串中或者允许注释并且提高可读性。
re.findall(r"(?i)yes", "yes? Yes. YES!") # 忽略大小写的匹配
# ['yes', 'Yes', 'YES']
text = """
The first line
the second line
the third line
"""
re.findall(r"th.+", text) # 不使用多行匹配
# ['the second line', 'the third line']
re.findall(r"(?s)th.+", text)# 进行多行匹配
# ['the second line\nthe third line\n']
re.findall(r"(?is)th.+", text)# 忽略大小写的多行匹配
# ['The first line\nthe second line\nthe third line\n']
# 使用拆分的模式串进行匹配电话号,这样更加便于理解,这也可以说是re.X的用处所在
re.search(r"""(?x)
\((\d{3})\) # 匹配区号
[ ] # 匹配空白符
(\d{3}) # 前缀
- # 横线
(\d{4}) # 终点数字
""", '(010) 555-1212').groups()
# ('010', '555', '1212')
1.2 (?:...)
通过使用该符号,可以对部分正则表达式进行分组,但是并不会保存该分组用于后续的检索或者应用。
text = """
http://google.com
http://www.google.com
http://code.google.com
"""
re.findall(r"http://(?:\w+\.)*(\w+\.com)", text)
# ['google.com', 'google.com', 'google.com'] 观察输出结果可以发现(?:...)中的内容并没有保留
1.3 (?P
re.search(r"\((?P\d{3})\) (?P\d{3})-(?:\d{4})", '(010) 555-1212').groupdict()
# {'name1': '010', 'name2': '555'}
# 使用\g(name)标识符来检索保存的匹配
re.sub(r"\((?P\d{3})\) (?P\d{3})-(?:\d{4})", "(\g) \g-xxxx", "(010) 555-1212")
# '(010) 555-xxxx'
1.4 (?P=name)
可以在一个相同的正则表达式中重用模式,而不必稍后再次在(相同)正则表达式中指定相同的模式。
不会保留该分组用于以后的检索或者应用。
text = "(010) 555-1212 010-555-1212 10105551212"
bool(re.match(r"""(?x)
# 匹配 (010) 555-1212
\((?P\d{3})\)[ ](?P\d{3})-(?P\d{4})
[ ]
# 开始重用模式
# 匹配 010-555-1212
(?P=name1)-(?P=name2)-(?P=name3)
[ ]
# 匹配 10105551212
1(?P=name1)(?P=name2)(?P=name3)
""", text))
# True
text = """
Guido van Rossum
Tim Peters
Alex Martelli
Just van Rossum
Raymond Hettinger
"""
# 只有当一个字符串后面跟着 van Rossum的时候才做匹配操作
re.findall(r"\w+(?= van Rossum)", text)
# ['Guido', 'Just']
1.5 (?!...)
# 忽略以 noreply 和 postmaster 开头的e-mail地址
text = """
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
"""
# (?m)不严格匹配整个字符串的起始和结尾
# 之后的字符串需要不匹配(?!...)表达式才能够匹配成功
re.findall(r'(?m)^\s+(?!noreply|postmaster)(\w+)', text)
# ['sales', 'eng', 'admin']
# 使用finditer迭代器
["%[email protected]" % e.group(1) for e in re.finditer(r'(?m)^\s+(?!noreply|postmaster)(\w+)', text)]
# ['[email protected]', '[email protected]', '[email protected]']
1.6 (?=...)
(?=.com) 当一个字符串后面跟着.com才做匹配操作。
text = "[email protected]"
re.findall(r"\w+(?=.com)", text)
# ['gmail']
1.7 (?<=...)
text = "(010) 555-1212"
re.findall("(?<=\(\d{3}\)) (\d{3})-(\d{4})", text)
# [('555', '1212')]
1.8 (?
(?
pat=re.compile(r'(?
1.9 (?(id|name)Y|N)
(?(1)Y|N) 如果匹配组1(\1)存在,就与Y匹配,否则,就与X匹配。|N为可选项
re.findall(r"(\d)?abc(?(1)\d|abc)", "1abc2")
# ['1']
#"(?(id/name) yes |no)": 组是否匹配,匹配返回
pat=re.compile(r'a(\d)?bc(?(1)\d)') #no省略了,完整的是a\dbc\d ==> a2bc3,总共5位,第2位是可有可无的数字,第5为是数字
print pat.findall('abc9') #返回组1,但第2位(组1)没有,即返回了''
print pat.findall('a8bc9') #完整的模式,返回组1
print pat.match('a8bc9').group()
print pat.match('a8bc9').group(1)
print pat.findall('a8bc') #第5位不存在,则没有匹配到
# ['']
# ['8']
# a8bc9
# 8
# []
Python正则表达式中常用的扩展表示法基本上就是这些,还有一些目前并没有用到过,等用到的时候再进行总结!!!