常用模块之正则模块
"""
正则表达式与 re 模块的关系
1. 正则表达式是一门独立的技术, 任何语言均可使用
2.python 中要想使用正则表达式需要通过 re 模块
"""
初识正则表达式
网站手机号校验功能示例: https://reg.jd.com/reg/person?ReturnUrl=https://www.jd.com/
有无正则校验的区别
- # 纯 python 代码校验
- while True:
- phone_number = input('please input your phone number :')
- if len(phone_number) == 11 and phone_number.isdigit() and (phone_number.startswith('13') or phone_number.startswith('14') or phone_number.startswith('15') or phone_number.startswith('18')):
- print('是合法的手机号码')
- else:
- print('不是合法的手机号码')
- # 正则表达式校验
- import re
- phone_number = input('please input your phone number :')
- if re.match('^(13|14|15|18)[0-9]{9}$',phone_number):
- print('是合法的手机号码')
- else:
- print('不是合法的手机号码')
- # 正则在所有语言中都可以使用 不是 python 独有的
- # 匹配大段文本中特定的字符
正则表达式在线测试
与 re 模块没有任何关系, 仅仅是测试正则表达式: http://tool.chinaz.com/regex/
测试匹配手机号
正则表达式应用场景
爬虫
数据分析
推荐书籍: 正则指引
字符组概念
在同一个位置可能出现的各种字符组成了一个字符组, 在正则表达式中用 [] 表示(一个字符组每次只能匹配一个字符)
小例子
匹配 0-9 数字 ([0123456789] 也支持简写[0-9])(如果想匹配横杠, 转义即可)
匹配 A-Z 字母(依据上面的简写规则[A-Z])
匹配 a-z 字母(依据上面的简写规则[a-z])
ps: 这种上面到上面的范围必须是从小到大 [a-Z] 不行,[A-z]可以(但是内部有几个特殊符号), 因为内部对应的 asicc 码 A 在所有字母里面最小, z 在所有字母里面最大
正则表达式符号介绍
按照博客中的表格罗列的去记即可
了解
\w,\s,\d 与 \ W,\S,\D 相反的匹配关系(对应的两者结合就是匹配全局)
\t 匹配制表符
\b 匹配结尾的指定单词
优先掌握
^: 以什么什么开头
^[a-z]
^[0-9]
$: 以什么什么结尾
ps:^ 与 $ 连用能够精准匹配固定长度的目标字符,^ 只能出现在开头(),$ 只能出现结尾
|: 或
ab|abc 优先匹配前面的正则表达式 ab, 匹配上就不再用后面的 abc, 可以通过调换顺序修改优先级
^: 出现在 [] 中的 ^ 表示除了 ^ 后面的其他都匹配
[^a]除了 a 其他都匹配
[^a-z]除了小写字母 a-z 其他都匹配
上面的是匹配单个字符的符号, 接下来需要学习匹配个数的限制符号
量词
+: 我想要匹配 13838384388,\d 一次只能匹配到一个数字, 要想一次讲号码全部匹配上需要用到 \ d+,+ 表示重复一次或者多次, 并且正则中默认都是贪婪匹配, 越多越好
*: 匹配 0 次或多次
?: 匹配 0 次或一次
ps:
1. 对于这个 0 次也能匹配出来的结果, 暂时不用考虑
2.*,+,? 的工作区间可以用坐标轴的形式表示出来
{n}: 明确指定个数
注意: 量词需要写在匹配符号的后面, 并且只约束紧挨着它的那个正则表达式
小例子
逐个匹配出后面的道: 轨道 通道 地道 魔道 人道
匹配人名: 海燕海娇海东
字符集
分组
一次性匹配 a1b2c3
用 [a-z][0-9] 会匹配到三个结果
想法: 重复写三次, 但是太麻烦, 加量词的话只能限制离得最近的正则表达式
这个时候就可以用分组([a-z][0-9])+
匹配身份证号
依据博客一次分析(下面的正则之所以加 ^ 和 $ 是因为我们仅仅只想匹配身份证号, 教学演示方便, 不加照样也能匹配上, 只不过前后可以出现很多其他字符)
1.^[1-9]\d{13,16}[0-9x]$: 不完善的地方在于默认是 x 的情况只在 18 位才可能出现, 但是这个表达式没有做这一层的限制
- ^[1-9]\d{
- 14
- }(\d{
- 2
- }[0-9x])?$
- ^([1-9]\d{
- 16
- }[0-9x]|[1-9]\d{
- 14
- })$
转义符
在正则表达式中, 有很多有特殊意义的是元字符, 比如 \ n 和 \ s 等, 如果要在正则中匹配正常的 "\n" 而不是 "换行符" 就需要对 ""进行转义, 变成'\'.
在 python 中, 无论是正则表达式, 还是待匹配的内容, 都是以字符串的形式出现的, 在字符串中 \ 也有特殊的含义, 本身还需要转义.
所以如果匹配一次 "\n", 字符串中要写成'\\n'
所以如果匹配一次 "\\n", 字符串中要写成'\\\\n'
简便操作, 利用 r 可以让整个字符串都不再转义(了解: r 其实就是 real 的意思, 真实不转义)
贪婪匹配与非贪婪匹配
- <.*>: 先拿着里面的.* 去匹配所有的内容, 然后再根据>往回退着找, 遇到即停止
- <.*?>: 先拿着? 后面的>去匹配符合条件的最少的内容, 然后把匹配的结果返回
ps: 根据匹配的内部原理可以很好的理解
.*?x
就是取前面任意长度的字符, 直到一个 x 出现
至此整个后期项目里面能用到的正则表达式就已经给你讲完了, 把这些记住足够你在后面的项目和爬虫中用了
讲了这么久, 一点 python 的事儿都还没扯, 现在就要来学在 python 里面如何使用
re 模块使用
三个必须掌握的方法
- findall
- search
- match
- import re
- ?
- # 第一个参数是正则表达式, 第二个参数是待匹配的文本内容
- ?
- ret = re.findall('a', 'eva egon yuan') # 返回所有满足匹配条件的结果, 放在列表里
- print(ret)
- ?
- ret = re.search('a', 'eva egon yuan')
- print(ret.group()) # 结果:'a'
- # 函数会在字符串内查找模式匹配, 直到找到第一个匹配然后返回一个包含匹配信息的对象, 该对象可以通过调用 group()方法得到匹配的字符串, 如果字符串没有匹配, 则返回 None,
并且需要注意的是如果 ret 是 None, 再调用. group()会直接报错. 这一易错点可以通过 if 判断来进行筛选
- if ret:
- print(ret.group())
- ?
- ret = re.match('a', 'abc').group() # 同 search, 不过仅在字符串开始处进行匹配
- print(ret) # 'a'
- # match 是从头开始匹配, 如果正则规则从头开始可以匹配上, 就返回一个对象, 需要用 group 才能显示, 如果没匹配上就返回 None, 调用 group()就会报错
其他方法
- ret = re.split('[ab]', 'abcd') # 先按'a'分割得到''和'bcd', 在对''和'bcd'分别按'b'分割
- print(ret) # ['','', 'cd']
- ?
- ret = re.sub('\d', 'H', 'eva3egon4yuan4', 1) # 将数字替换成'H', 参数 1 表示只替换 1 个
- print(ret) # evaHegon4yuan4
- ?
- ret = re.subn('\d', 'H', 'eva3egon4yuan4') # 将数字替换成'H', 返回元组(替换的结果, 替换了多少次)
- print(ret)
- ?
- obj = re.compile('\d{
- 3
- }') #将正则表达式编译成为一个 正则表达式对象, 规则要匹配的是 3 个数字
- ret = obj.search('abc123eeee') #正则表达式对象调用 search, 参数为待匹配的字符串
- print(ret.group()) #结果 : 123
- ?
- import re
- ret = re.finditer('\d', 'ds3sy4784a') #finditer 返回一个存放匹配结果的迭代器
- print(ret) # <callable_iterator object at 0x10195f940>
- print(next(ret).group()) #查看第一个结果
- print(next(ret).group()) #查看第二个结果
- print([i.group() for i in ret]) #查看剩余的左右结果
扩展
分组优先机制
- import re
- res = re.search('^[1-9]\d{14}(\d{2}[0-9x])?$',110105199812067023)
- print(res.group())
- print(res.group(1)) # 获取正则表达式括号阔起来分组的内容
- print(res.group(2)) # search 与 match 均支持获取分组内容的操作 跟正则无关是 python 机制
- ?
- # 而针对 findall 它没有 group 取值的方法, 所以它默认就是分组优先获取的结果
- ret = re.findall('www.(baidu|oldboy).com', 'www.oldboy.com')
- print(ret) # ['oldboy'] 这是因为 findall 会优先把匹配结果组里内容返回, 如果想要匹配结果, 取消权限即可
- ?
- ret = re.findall('www.(?:baidu|oldboy).com', 'www.oldboy.com') # ?: 取消分组优先
- print(ret) # ['www.oldboy.com']
补充
- import re
- ?
- ?
- ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")
- # 还可以在分组中利用?<name > 的形式给分组起名字
- # 获取的匹配结果可以直接用 group('名字')拿到对应的值
- print(ret.group('tag_name')) #结果 :h1
- print(ret.group()) #结果 :<h1>hello</h1>
- """
- 注意? P=tag_name 相当于引用之前正则表达式, 并且匹配到的值必须和前面的正则表达式一模一样
- """
- ?
- # 匹配整数
- ret=re.findall(r"\d+","1-2*(60+(-40.35/5)-(-4*3))")
- print(ret) #['1', '2', '60', '40', '35', '5', '4', '3']
- ?
- ret=re.findall(r"\d+\.\d*|(\d+)","1-2*(60+(-40.35/5)-(-4*3))")
- print(ret) #['1', '2', '60', '','5','4','3']
- ret.remove("")
- print(ret) #['1', '2', '60', '5', '4', '3']
来源: http://www.bubuko.com/infodetail-3365844.html