一、什么是正则表达式
在实际开发过程中经常会有查找符合某些复杂规则的字符串的需要,比如:邮箱、图片地址、手机号码等。
二、python使用正则表达式---re模块
re模块提供了常见的正则匹配操作,如:匹配、搜索、替换等功能。
(一)、re里面的基本的方法
匹配操作 re.match() 从字符串的开始进行匹配,如果开始部分匹配成功就返回匹配对象,否则返回None。
import re
re1 = re.match("python","python_one") # 从头开始匹配,有则匹配,并返回对象,无则返回None.
print(re1)
print("-"*12+"match"+"-"*12)
re2 = re.match("python","fpython_one") # 从头开始匹配,有则匹配,并返回对象,无则返回None.
print(re2)
print("-"*12+"match"+"-"*12)
搜索操作 - re.search() re.search() 扫描字符串,如果找到匹配就返回匹配对象,否则返回None。从任意位置开始匹配。
# 这里出现了一个问题就是,re = re.match(),下一个re1 = re.match()就会报错,变量未被定义。
# 解决方案,取名不要和函数方法取一样的。
print("-"*12+"searchone"+"-"*12)
re_one = re.search("python","python_one") # 从任意位置匹配
print(re_one)
print("-"*12+"searchtwo"+"-"*12)
re_two = re.search("python","fpython_one")
print(re_two)
提取操作 re.findall() 查找所有匹配,返回所有匹配结果的列表。文本1(.*?)文本2 ,这是非贪婪模式。有匹配的数据,返回的是列表,无,空列表。
# 提取操作 - re.findall() re.findall() 查找所有匹配,返回所有匹配结果的列表
string = "abcdefghijklmnopqrstuvwxyz1234567890"
find01 = re.findall("a(.*?)0",string)
print(find01)
print("-"*12+"findall_2"+"-"*12)
string2 = "abcdefghijklmnopqrstuvwxyz1234567890"
find02 = re.findall("a",string2)
print(find02)
提取操作 re.finditer() 查找所有匹配,返回所有匹配结果的一个iterator(迭代器)。迭代器可以循环遍历得到。如下代码。
print("-"*12+"finditer"+"-"*12)
re_fi = re.finditer(pattern="python",string="pythonzzz")
print(re_fi)
for i in re_fi:
print(i)
print(i.group()) # 可以从迭代器遍历的对象里面找到匹配的内容。
替换操作 re.sub() 用于替换匹配的字符串。将匹配到的字符串替换为另一个字符串。
string_before = "python,zhang,hudh"
string_now = re.sub("python","zzz",string_before)
print(string_before) # 注意,这里不会改变原来的变量string_before,而是重新生成了一个对象储存
print(string_now)
# python,zhang,hudh
# zzz,zhang,hudh
print("-"*12+"re.sub"+"-"*12)
string_before = ["pythin","shfheu","python"] # 为啥列表不可以----因为争对的是原字符串
string_now = re.sub("python","zzz",'string_before')
print(string_before) # 注意,这里不会改变原来的变量string_before,而是重新生成了一个对象储存
print(string_now)
# ['pythin', 'shfheu', 'python']
# string_before
(二)、相关规则匹配单/多个字符---正则表达式介绍
多数字、字母和符号会简单地匹配自身,但规则有例外,有些字符是特殊的,并不匹配自身。因为这些字符,有表达式中,有特殊的含义。如果想匹配这些内容,只需要在符号前上1个\
就可以了,例如:匹配$
, 就可以写成\$。
下面是需要加\的特殊字符
# . ^ $ * + ? { } [ ] \ | ( )
规则,除了\t(1个制表)和\n(1个换行)匹配字符如下表格
代码 | 功能 |
---|---|
. | 匹配任意1个字符(除了\n) |
[ ] | 匹配1个[ ]中列举的字符,例如[0-9a-zA-Z],[Hh],字符有其中的,都能匹配 |
\d | 匹配1个数字,即0-9 |
\D | 匹配1个非数字,即不是数字 |
\s | 匹配1个空白,即 空格,tab键 |
\S | 匹配1个非空白 |
\w | 匹配非特殊字符,即a-z、A-Z、0-9、_、汉字 |
\W | 匹配特殊字符,即非字母、非数字、非汉字 |
* | 匹配0个或多个表达式 |
+ | 匹配1个或多个表达式 |
() | 匹配()内的表达式,也表示一个组 |
* | 匹配前一个字符出现0次或者无限次,即可有可无 |
+ | 匹配前一个字符出现1次或者无限次,即至少有1次 |
? | 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 |
{m} | 匹配前一个字符出现m次 |
{m,n} | 匹配前一个字符出现从m到n次 |
*
import re
ret = re.match("[A-Z][a-z]*","Q")
print(ret.group())
ret = re.match("[A-Z][a-z]*","ZvjA")
print(ret.group())
ret = re.match("[A-Z][a-z]*","Adjfff")
print(ret.group())
# Q 第一个字符必须是大写,第二字符是小写,且可有可无,但是后续有匹配的是小写的,因为*号前面就是小写的
# Zvj
# Adjfff
+
import re
ret = re.match("[A-Z][a-z]+","Qm")
print(ret.group()) # Qm
ret1 = re.match("[A-Z][a-z]+","Q") # 原因在于+,至少要匹配一次,第一个字符必须是大写,第二字符必须是小写(至少匹配一次)单个Q匹配不到
# 第一个字符必须是大写,第二字符必须是小写,因为*,0次或多次,单个Q,也可以匹配,,因为*号前面就是小写的,后续有匹配的是小写的,都匹配得到
print(ret1.group()) # Traceback (most recent call last):
# File "D:\python_env\Python_env2_spider\Lib\01_re正则表达式的学习\02_re_+.py", line 7, in <module>
# print(ret1.group())
# ^^^^^^^^^^
# AttributeError: 'NoneType' object has no attribute 'group'
?
import re
match_1 = re.match("zhangS?", "zhang")
# ?可匹配也可以不匹配S
print(match_1.group())
# zhang
{}
ret = re.match("[a-zA-Z0-9_]{5}","544432313")
print(ret.group())
ret = re.match("[a-zA-Z0-9_]{1,44}","15415751njjhbbjjnjhnjkjhb")
print(ret.group())
# 54443
# 15415751njjhbbjjnjhnjkjhb
(三)、贪婪模式和非贪婪模式
那么贪婪模式和非贪婪模式的区别就是:
- 贪婪模式:量词会尽可能多的匹配字符
- 非贪婪模式:量词会尽可能少的匹配字符
在正则表达式中,贪婪模式是默认的可以通过在量词后面加?(匹配0次或者1次)
将其转换为非贪婪模式,爬虫中通常使用非贪婪模式。
moder1 = re.findall("<p>.+?</p>",d)
print(moder1)
print(1)
moder2 = re.findall("<p>.*?</p>",d)
print(moder2) # ['<p>hdfufhfufdoff</p>', '<p>hyguff</p>']
moder3 = re.findall("<p>(.*?)</p>",d)
print(moder3) # ['hdfufhfufdoff', 'hyguff']
d = "<p>hdfufhfufdoff</p><p>hyguff</p>"
moder = re.match("<p>.+</p>",d)
print(moder.group())
moder1 = re.match("<p>.+?</p>",d)
print(moder1.group())
(四)、匹配开头和结尾,[^xxxx]取反
代码 | 功能 |
---|---|
^ | 匹配字符串开头 ^\d.+ 数字开头的都可以匹配 |
$ | 匹配字符串结尾 .+\d$ 数字结尾都可以匹配 |
[^xxx] | 取反,[^asd].+ 表示后续的字符只要不是asd都可以匹配 |
(五)、或者与分组
代码 | 功能 |
---|---|
| | 匹配左右任意一个表达式 "python|c++" |
(ab) | 将括号中字符作为一个分组 "(163|qq|126)\.com" \.是转义字符 |
\num | 引用分组num匹配到的字符串 |
(?P<name>) | 分组起别名 |
(?P=name) | 引用别名为name分组匹配到的字符串 |
|
import re
skill = ["python", "c", "c++", "java"]
# 遍历数据,变成字符对象,就可以用match了。
for i in skill:
match_obj = re.match("python|html", i) # | 匹配左右任意一个表达式
if match_obj:
print(f"{match_obj.group()}:yes")
else:
print(f"{i}:no")
# python:yes
# c:no
# c++:no
# java:no
()分组、\num ,\1 或者\\1 需要转义字符帮助,还有就是1代表的是第一个分组,输出第一个分组print(re_match.group(1))的内容。
import re
match_obj = re.match("<[a-zA-Z1-6]+>.*</[a-zA-Z1-6]+>", "<div>gtfdt</div>")
print(match_obj.group()) # <div>gtfdt</div>
match_obj = re.match("<([a-zA-Z1-6]+)>(.*)</\\1>", "<div>tfdt</div>")
print(match_obj.group()) # <div>tfdt</div>
print(match_obj.group(1)) # div
print(match_obj.group(2)) # tfdt
(?P<name>)
分组起别名
import re
match_obj = re.match("<[a-zA-Z1-6]+>.*</[a-zA-Z1-6]+>", "<div>gtfdt</div>")
print(match_obj.group()) # <div>gtfdt</div>
match_obj = re.match("<(?P<name1>[a-zA-Z1-6]+)>(.*)</(?P=name1)>", "<div>tfdt</div>")
print(match_obj.group()) # <div>tfdt</div>
print(match_obj.group(1)) # div
print(match_obj.group(2)) # tfdt
(六)、Python的re模块支持以下5种标志:
注意:re.match("pattern","string","flags" ) 这里的flags为python中的编译标注位,用于修改正则表达式的匹配方式。例如:re.match("pattern","string",re.S )
代码 | 功能 |
---|---|
re.I(IGNORECASE) | 忽略大小写,使匹配对大小写不敏感 |
re.M(MULTILINE) | 多行匹配,修改'^'和'$'的行为 |
re.S(DOTALL) | .可以匹配所有字符,包含换行符 |
re.U(UNICODE) | 使用Unicode匹配 |
re.X(VERBOSE) | 忽略空格和注释,使正则表达式更具有可读性 |