第三章. 数据解析之正则
Regular Expression,译作正则表达式或正规表示法,表示有规则的表达式,意思是说,描述一段文本排列规则的表达式。
正则表达式并不是Python的一部分。而是一套独立于编程语言,用于处理复杂文本信息的强大的高级文本操作工具。正则表达式拥有自己独特的规则语法以及一个独立的正则处理引擎,我们根据正则语法编写好规则(模式)以后,引擎不仅能够根据规则进行模糊文本查找,还可以进行模糊分割,替换等复杂的文本操作,能让开发者随心所欲地处理文本信息。正则引擎一般由编程语言提供操作,像python就提供了re模块或regex模块来调用正则处理引擎。
正则表达式在处理文本的效率上不如系统自带的字符串操作,但功能却比系统自带的要强大许多。
最早的正则表达式来源于Perl语言,后面其他的编程语言在提供正则表达式操作时基本沿用了Perl语言的正则语法,所以我们学习python的正则以后,也可以在java,php,go,javascript,sql等编程语言中使用。
正则对字符串或文本的操作,无非是分割、匹配、查找和替换。
在线测试工具 http://tool.chinaz.com/regex/
【1】元字符(Metacharacters)
元字符是具有特殊含义的字符。
元字符 | 描述 |
---|---|
. | 叫通配符、万能通配符或通配元字符,匹配1个除了换行符\n以外任何原子 |
[] | 匹配一个中括号中出现的任意原子 |
[^原子] | 匹配一个没有在中括号出现的任意原子 |
+ | 叫加号贪婪符,指定左边原子出现1次或多次 |
***** | 叫星号贪婪符,指定左边原子出现0次或多次 |
? | 叫非贪婪符,指定左边原子出现0次或1次 |
{n,m} | 叫数量范围贪婪符,指定左边原子的数量范围,有{n},{n, }, {,m}, {n,m}四种写法,其中n与m必须是非负整数。 |
^ | 叫开始边界符或开始锚点符,匹配一行的开头位置 |
$ | 叫结束边界符或结束锚点符,匹配一行的结束位置 |
| | 指定原子或正则模式进行二选一或多选一 |
() | 对原子或正则模式进行捕获提取和分组划分整体操作, |
\ | 转义字符,可以把原子转换特殊元字符,也可以把特殊元字符转成原子。 |
import re
"""re.findall(正则模式, 文本) 基于正则模式查找所有匹配的文本内容"""
# part1: 通配符->. 字符集-> []
ret1 = re.findall("a", "a,b,c,d,e")
ret1 = re.findall(".", "a,b,c,d,e")
ret1 = re.findall("[ace]", "a,b,c,d,e")
ret1 = re.findall("[a-z]", "a,b,c,d,e")
ret1 = re.findall("[0-9]", "1,2,3,4,5")
ret1 = re.findall("\d", "1,2,3,4,5")
ret1 = re.findall("[0-9a-z]", "1,a,2,b,3")
ret1 = re.findall("[^a-z]", "1,a,2,b,3")
ret1 = re.findall("[^0-9,]", "1,a,2,b,3")
print(ret1)
# part2:重复元字符-> + * {} ?
ret2 = re.findall("[0-9a-zA-Z]", "apple,banana,orange,melon")
ret2 = re.findall("\w", "apple,banana,orange,melon")
ret2 = re.findall("\w+", "apple,banana,orange,melon")
ret2 = re.findall("\w+?", "apple,banana,orange,melon") # 取消贪婪匹配
ret2 = re.findall("\w*", "apple,banana,orange,melon")
ret2 = re.findall("\w{6}", "apple,banana,orange,melon")
# part3: 位置元字符-> ^ $
ret3 = re.findall("^\w{5}", "apple,banana,peach,orange,melon")
ret3 = re.findall("\w{5}$", "apple,banana,peach,orange,melon")
ret3 = re.findall("^\w{5}$", "apple,banana,peach,orange,melon")
print(ret3)
# part4:
# | 指定原子或正则模式进行二选一或多选一
# () 具备模式捕获的能力,也就是优先提取数据的能力,通过(?:) 可以取消模式捕获
ret4 = re.findall(",\w{5},", ",apple,banana,peach,orange,melon,") # 筛选出5个字符的单词
ret4 = re.findall(",(\w{5}),", ",apple,banana,peach,orange,melon,") # 筛选出5个字符的单词
ret4 = re.findall("\w+@\w+\.com", "[email protected],[email protected],....") # 筛选出5个字符的单词
ret4 = re.findall("(\w+)@qq\.com", "[email protected],[email protected],....") # 筛选出5个字符的单词
ret4 = re.findall("(?:\w+)@(?:qq|163)\.com", "[email protected],[email protected],....") # 筛选出5个字符的单词
print(ret4)
# part5: 转义符-> \d \D \w \W \n \s \S \b \B
""" \b 1个单词边界原子 """
txt = "my name is nana. nihao,nana"
ret = re.findall(r"na", txt)
ret = re.findall(r"\bna", txt)
ret = re.findall(r"\bna\w{2}", txt)
print(ret) # ['na', 'na', 'na']
转义元字符是\
开头的元字符,由于某些正则模式会在开发中反复被用到,所以正则语法预定义了一些特殊正则模式以方便我们简写。
元字符 | 描述 | 示例 |
---|---|---|
\d | 匹配一个数字原子,等价于[0-9] 。 |
\d |
\D | 匹配一个非数字原子。等价于[^0-9] 或[^\d] 。 |
"\D" |
\b | 匹配一个单词边界原子,也就是指单词和空格间的位置。 | er\b |
\B | 匹配一个非单词边界原子,等价于 [^\b] |
r"\Bain"r"ain\B" |
\n | 匹配一个换行符 | |
\t | 匹配一个制表符,tab键 | |
\s | 匹配一个任何空白字符原子,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v] 。 |
"\s" |
\S | 匹配一个任何非空白字符原子。等价于[^ \f\n\r\t\v] 或 [^\s] 。 |
"\S" |
\w | 匹配一个包括下划线的单词原子。等价于[A-Za-z0-9_] 。 |
"\w" |
\W | 匹配任何非单词字符。等价于[^A-Za-z0-9_] 或 [^\w] 。 |
"\W" |
注意:python本身没有内置正则处理的,python中的正则就是一段字符串,我们需要使用python模块中提供的函数把字符串发送给正则引擎,正则引擎会把字符串转换成真正的正则表达式来处理文本内容。
【2】re模块中的常用函数
re
模块提供了一组正则处理函数,使我们可以在字符串中搜索匹配项:
函数 | 描述 |
---|---|
findall | 按指定的正则模式查找文本中所有符合正则模式的匹配项,以列表格式返回结果。 |
search | 在字符串中任何位置查找首个符合正则模式的匹配项,存在则返回re.Match对象,不存在返回None |
match | 判定字符串开始位置是否匹配正则模式的规则,匹配则返回re.Match对象,不匹配返回None |
split | 按指定的正则模式来分割字符串,返回一个分割后的列表 |
sub | 把字符串按指定的正则模式来查找符合正则模式的匹配项,并可以替换一个或多个匹配项成其他内容。 |
【2.1】findall
def findall(pattern, string, flags=0)
findall()
函数返回包含所有匹配项的列表,如果找不到匹配项,则返回一个空列表。
【2.2】search
def search(pattern, string, flags=0)
search()
函数搜索匹配的字符串,如果匹配上则返回匹配对象re.Match。如果有多个匹配项,则仅返回匹配项的第一个匹配项,如果找不到匹配项,则返回值为None
import re
ret = re.search("1[3-9]\d{9}", "我的手机号码是13928835900,我女朋友的手机号是15100363326")
print(ret)
print(ret.start(), ret.end(), ret.span())
print(ret.group())
ret = re.search("(?P<tel>1[3-9]\d{9}).*?(?P<email>\[email protected])", "我的手机号码是13928835900,我的邮箱是[email protected]")
print(ret)
print(ret.group("tel"))
print(ret.group("email"))
【2.3】match
def match(pattern, string, flags=0)
match()
函数搜索匹配的字符串开始位置,如果匹配上则返回匹配对象,如果找不到匹配项,则返回值为None
【2.4】split
def split(patter, string, maxsplit=0, flags=0)
split()
函数返回一个列表,对字符串进行正则分割。
import re
txt = "my name is moluo"
ret = re.split("\s", txt)
print(ret) # ['my', 'name', 'is', 'moluo']
可以通过指定maxsplit
参数来控制分割的次数,例如,仅在第1次出现时才拆分字符串:
import re
txt = "my name is yuan"
ret = re.split("\s+", txt)
print(ret)
【2.5】sub和subn
def sub(pattern, repl, string, count=0, flags=0) 返回匹配后的结果
def subn(pattern, repl, string, count=0, flags=0) 返回匹配后的结果和次数
sub()
函数用选择的文本替换匹配:
import re
txt = "my name is yuan"
# ret = re.sub("\s+"," " ,txt)
ret = re.sub("\s+", " ", txt, 2)
print(ret)
【2.6】compile()
def compile(pattern, flags=0)
import re
re_email = re.compile(r"(?:\+86)?1[3-9]\d{9}")
ret = re_email.findall("15100649928,[email protected],13653287791,[email protected]")
print(ret)
如果一个正则表达式要使用几千遍,每一次都会编译,出于效率的考虑进行正则表达式的编译,就不需要每次都编译了,节省了编译的时间,从而提升效率
【3】正则进阶
【3.1】.*?
import re
text = '<12> <xyz> <!@#$%> <1a!#e2> <>'
ret = re.findall("<\d+>", text)
ret = re.findall("<\w+>", text)
ret = re.findall("<.+>", text)
ret = re.findall("<.+?>", text)
ret = re.findall("<.*?>", text)
print(ret)
【3.2】模式修正符
模式修正符,也叫正则修饰符,模式修正符就是给正则模式增强或增加功能的。
通用flags(修正符)
值 | 说明 |
---|---|
re.I | 是匹配对大小写不敏感 |
re.L | 做本地化识别匹配 |
re.M | 多行匹配,影响到^和$ |
re.S | 使.匹配包括换行符在内的所有字符 |
re.U | 根据Unicode字符集解析字符,影响\w、\W、\b、\B |
re.X | 通过给予我们功能灵活的格式以便更好的理解正则表达式 |
import re
text = """
<12
>
<x
yz>
<!@#$%>
<1a!#
e2>
<>
"""
ret = re.findall("<.*?>", text)
ret = re.findall("<.*?>", text, re.S)
print(ret)
练习:豆瓣Top250页面解析
【4】练习
工作中,正则一般用于验证数据、校验用户输入的信息、爬虫、运维日志分析等。其中如果是验证用户输入的数据:
场景 | 正则表达式 |
---|---|
用户名 | ^[a-z0-9_-]{3,16}$ |
密码 | ^[a-z0-9_-]{6,18}$ |
手机号码 | ^(?:\+86)?1[3-9]\d{9}$ |
颜色的十六进制值 | `^#?([a-f0-9] |
电子邮箱 | ^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+\.[a-z]+$ |
URL | `^(?:https:// |
IP 地址 | `((2[0-4]\d |
HTML 标签 | ^<([a-z]+)([^<]+)*(?:>(.*)<\/\1> |
utf-8编码下的汉字范围 | ^[\u2E80-\u9FFF]+$ |
1、编写正则,匹配整数或者小数(包括正数和负数)
2、编写正则,匹配年月日日期 格式2018-12-31
3、编写正则,匹配qq号 5-12
4、编写正则,11位的电话号码
5、编写正则,长度为8-10位的用户密码 : 包含数字字母下划线
6、编写正则,从18位省份证中提取用户生日日期
7、编写正则,从文本"a@com [email protected] [email protected] [email protected] [email protected]"中匹配qq邮箱地址
8、从以下多行文本中提取href=""中的双引号的值,并提取标签内容 <a>内容<a>
"""
<a href="http://www.badu.com/s?wd=hahaha">hahaha</a>
<a href="http://www.tmall.com/">tmall</a>
<a href="http://www.tmall.com/">tmall</a>
"""
课堂代码
import re
"""
1、编写正则,匹配文本中的整数或者小数(包括正数和负数)
"""
# txt = "10.3 10 20 -20 +20 --20 ++20 -30.5444"
# # ret = re.findall(r"-?\+?\d+", txt)
# ret = re.findall(r"[\+\-]?(?:(?:\d+\.\d+)|(?:\d+))", txt)
# print(ret)
# # ['10', '3', '10', '20', '-20', '+20', '-20', '+20']
"""
2、编写正则,匹配年月日日期 格式: 2018-12-31
"""
# txt = "2018-12-31 2018-12-01 2018-12 2018-31 0000-12-31 2018-1-31 2018-01-31 20-01-31 20-01-1 2020-1-1 2020-01-01"
# ret = re.findall(r"[12]\d{3}-\d+-\d+", txt)
# print(ret) # ['2018-12-31', '2018-12-01', '2018-1-31', '2018-01-31', '2020-1-1', '2020-01-01']
#
"""
3、编写正则,匹配qq号 5-12数字
"""
# txt = "20181231 40001 2202020133 13311233220222 20202012024222 222050sss2222 33020202222 2001 202011 2020.0101222"
# ret = re.findall(r"[1-9]\d{4,11}", txt)
# print(ret) # ['20181231', '40001', '2202020133', '133112332202', '202020120242', '222050', '33020202222', '202011', '101222']
"""
4、编写正则,11位的手机号码
"""
# txt = "1331234546 1501233453 15812345678 158-1234-5678 158 1234 5678 20022221111 10012345678 19012345678"
# ret = re.findall(r"1[3-9]\d{9}", txt)
# print(ret) #
#
# # 如果 158-1234-5678 和 158 1234 5678也算呢?
# txt = "1331234546 1501233453 15812345678 158-1234-5678 158 1234 5678 20022221111 10012345678 19012345678"
# ret = re.findall(r"1[3-9]\d[\- ]?\d{4}[\- ]?\d{4}", txt)
# print(ret) #
"""
5、编写正则,长度为8-10位的用户密码 : 包含数字字母下划线
"""
# password = input("请输入长度为8-10位的用户密码(包含数字字母下划线):")
# ret = re.match(r"^\w{8,10}$", password)
# print(ret)
"""
6、编写正则,从18位省份证中提取用户生日日期
"""
# idCard = "51142119991021155x"
# ret = re.findall(r"^[1-6]\d{5}(\d{8})\d{3}[\dxX]$", idCard)
# # ret = re.findall(r"^(?:1[1-5]|2[1-3]|3[1-7]|4[1-6]|5[0-4]|6[1-5])\d{4}(\d{8})\d{3}[\dxX]$", idCard)
# print(ret)
"""
7、编写正则,从文本"a@com [email protected] [email protected] [email protected] [email protected]"中匹配qq邮箱地址
"""
# txt = "a@com [email protected] [email protected] [email protected] [email protected]"
# ret = re.findall(r"\w+@\w+\.\w+(?:.cn)?", txt)
# print(ret) # ['[email protected]', '[email protected]', '[email protected]', '[email protected]']
"""
8、从以下多行文本中提取href=""中的双引号的值,并提取标签内容 <a>内容<a>
"""
# txt = """
# <a href="http://www.badu.com/s?wd=hahaha">hahaha</a>
# <a href="http://www.tmall.com/">tmall</a>
# <a href="http://www.tmall.com/">tmall</a>
# """
# ret = re.findall(r'<a href="(?P<href>.*?)">(?P<content>.*?)</a>', txt, re.M+re.S)
# print(ret)
标签:匹配,09,ret,9.1,re,正则,com,findall,day
From: https://www.cnblogs.com/dream-ze/p/17337546.html