爬虫菜鸟腾飞之路之Python正则表达式的基本组成和re模块方法

本文笔记大部分系网上听课所得,并对顺序做了调整。感觉这个课程挺好的,讲的很详细,课程地址:https://www.bilibili.com/video/BV1SJ411n7kk?p=1

另外,文中部分示例代码修改自:https://www.cnblogs.com/misswangxing/p/10736310.html

 

一、正则表示式的组成

在Python(读作拍萨恩或者拍森)中,正则表达式由原子,元字符和模式修正符(在其他语言中可能存在定界符)组成。

1.1 原子

原子是组成正则表达式的最小单位。一个正则表达式至少由一个原子组成。

  • 所有可见字符都是原子,例如:a,b,c,d,A,B,C,D,你,我,他,+,-,*,/
  • 所有不可见字符都是元组,例如:\t \n \r ...
  • 正则专用转义字符:

为在正则的元字符和原子当中,使用了部分的符号作为语法,为了匹配这些符号的单纯的字符格式在正则表达式中,添加\即可去掉其特殊意义,获取字符,例如,匹配“.”正则表达式要写成“\.”,匹配“*”则写成“\*”

  • 表1.1 正则专用转义字符
    \d表示0~9之间任意【一个】字符
    \D表示除了0~9之外的任意【一个】字符     ->[^0123456789]自定义排除列表(见表1.2)
    \s表示任意【一个】空白字符(不可见字符)->[\n\r\t\v\f]自定义原子列表格式(见表1.2)
    \S表示任意【一个】非空白字符 ->[^\n\r\t\v\f]自定义排除列表
    \w表示0~9a~zA~Z和_中的任意【一个】字符  ->[a-zA-Z0-9_]自定义原子列表格式
    \W表示除了0~9a~zA~Z和_意外的任意【一个】字符
    .表示除了\n的任意【一个】字符(使用模式修正符re.S可以令其也匹配\n)
    \b表示能够当做英文字符分割的符号,(除了字母和数字都是词边界)
    \B表示不能够当做英文字符分割的符号(字母和数字)
    
    patter = r'\byou'#表示you前面的符号可以当做英文字符分割符
    url = 'if you lose yourself'
    res = re.search(pattern,url)#参见表2.1
    print(res)#输入:you     表示you前面的空格可以作为英文字符分隔符
    url = 'if you lose mmmyourself'
    res = re.search(pattern,url)
    print(res)#输入为:None   表示you前面的m不可以作为英文字符分隔符

     

 1.2 元字符(原子修饰符)

表1.2
[]自定义原子列表,表示自定义原子列表中的任意一个字符,如果多个字符在ASCII码中是连续的,则可以进行缩写,例如:[0123456789]=[0-9]
[^]自定义排除列表,表示原子列表之外的任意一个字符,如果多个字符在ASCII码中是连续的,则可以进行缩写,例如:[^0-9]表示0-9以外的字符。
表1.3 与数量有关的元字符
+表示1~多个修饰的原子   例如:“a+”匹配1个以上的a
?表示0~1个修饰的原子
*表示0~多个修饰的原子
{m}表示m个修饰的原子
{m,n}表示[m,n]个修饰的原子
{m,}表示m~多个修饰的原子
{,n}表示[0,n]个修饰的原子

 注:表中[m,n]为数学上的闭区间表示,即包括m和n

表1.4
\A表示限定内容必须在整个字符串的开头部位
\Z表示限定内容必须在整个字符串的结束部位
^表示限定内容必须在整个字符串的开头部位,支持多行匹配模式(见注)
$表示限定内容必须在整个字符串的结束部位,支持多行匹配模式
|表示选择关系,其左右的内容二选一,例如:ab|cd表示ab或者cd
()
  • 改变正则表达式中的优先级关系,例如 a(b|c)d表示abd或者acd
  •  将多个原子视为一个,方便使用元字符,【可以叫做组或者模式单元(组)】,例如:(go)+ogle可以匹配gogogoogle
  •  将匹配的内容作为模式单元进行存储(见表1.6第二项)

         注:匹配模式(见表1.5),如果字符串中包含\n字符,则可以使用多行匹配模式,例如,aaa\nbbb视做一行。对^$有效对\A与\Z无效。

1.3 模式修正符

表1.5 模式修正符
修正符或者写成对应值注释
re.Are.ASCII256在ASCII模式下进行匹配(只能匹配ASCII码表中的字符)
re.Ure.UNICODE32在unicode模式下进行匹配操作(默认模式)
re.Sre.DOTALL16使得"."可以匹配任意字符(包括\n)
re.Mre.MULTILINE8表示使用多行匹配模式,和^$相关
re.Xre.VERBOSE64表示匹配的时候忽略正则表达式中的空格或者注释
re.Ire.IGNORECASE2表示匹配过程忽略大小写

注1:还有一个re.L表示字符集本地化,对应整型值4,但是对于中文环境基本上没有什么作用,不能匹配中文字符,所有这个修正符并不常用。

注2:在使用中可以添加多个模式,使用‘|’隔开。例如:re.compile('', re.I|re.M|re.S),具体可使用模式修正符的方法,参见第二节。

表1.6 扩展的正则语法
(?Limsux)模式修正符(见表1.5)的应用(可同时使用多个),例如(?imx)xxxx表示匹配过程中忽略大小写、使用多行模式并且忽略正则表达式中的空格或者注释
(?:)分组的不捕获模式,计算索引时会跳过这个分组,即取消单元存储功能,节省资源  (参见1.4 .2)
(?P<name>)自定义模式单元的名称,表示字典模式单元匹配(见表后示例,亦有索引模式单元)(参见1.4 .1)
(?P=name)获取自定义单元的内容,表示字典模式单元匹配,使用时,正则表达式中要有(?P<name>),并且这一部分不作为一组(参见1.4 .1分组用法示例代码)
(?#)正则注释内容,解析的时候不会做任何处理
(?=)零宽断言,正向先行断言(见注1)(本表后示例)
(?!)零宽断言,负向先行断言
(?<=)零宽断言,正向后行断言(注2)
(?<!)零宽断言,负向后行断言
?(?(id/name)Y|N)类似于变成语言中的三目运算符:a>b?fun1():fun2()。根据模式单元是否存在决定使用Y的的规则(模式单元存在)或者使用N的规则(模式单元不存在),与(?P<name>)结合使用(本表后的示例)
  • 注1 正向->有            先行->向前或者说向右进行查找            
           负向->没有        后行->向后或者说向左进行查找
  • 注2:对于零宽断言,可以两边同时使用,例如:'(?<!www\.)lmonkey\d(?!\.com)'#意思是匹配其前没有www.的并且其后没有.com的lmonkey\d
  • 注3:后行括号中需要匹配的字符串长度必须是固定长度,例如(?<=a{3})(?<=aaa)(?<!a\w\d)都可以,诸如(?<=a+)的都是非法的
'''
零宽断言示例
'''
pattern1 = r'lmonkey\d(?=\.\w?)'#意思就是匹配其后有.加若干字母的lmonkey
pattern2 = r'lmonkey\d(?!\.\wom)'#意思就是匹配其后没有.\wom的lmonkey
pattern3 = r'(?<=www\.)lmonkey\d'#意思就是匹配其前面有.的lmonkey
pattern4 = r'(?<!www\.)lmonkey\d'#意思就是匹配其前面没有.com的lmonkey
pattern5 = r'(?<!www\.)lmonkey\d(?!\.com+)'
st = 'www.lmonkey1.com-www.lmonkey2.cn-aaa.lmonkey3.net-bbb.lmonkey4.dommm-bbb.lmonkey5.edu'
res1 = re.findall(pattern1, st)#方法参见表2.1
res2 = re.findall(pattern2, st)
res3 = re.findall(pattern3, st)
res4 = re.findall(pattern4, st)
res5 = re.findall(pattern5, st)
print('res1>>',res1) 
print('res2>>',res2)
print('res3>>',res3)
print('res4>>',res4)
print('res5>>',res5)

'''
输出结果:
res1>> ['lmonkey1', 'lmonkey2', 'lmonkey3', 'lmonkey4', 'lmonkey5']
res2>> ['lmonkey2', 'lmonkey3', 'lmonkey5']
res3>> ['lmonkey1', 'lmonkey2']
res4>> ['lmonkey3', 'lmonkey4', 'lmonkey5']
res5>> ['lmonkey3', 'lmonkey4', 'lmonkey5']
'''


'''
(?(id/name)Y|N)示例
'''
pattern1 = r'(?P<name>www)?(?(name).*|\w+\.)'
st1 = 'www.baidu.com'
st2 = 'aaa.baidu.com'
reg = re.compile(pattern1)
res1 = reg.search(st1)
res2 = reg.search(st2)
print(res1)
print(res2)
'''
输出结果:
<re.Match object; span=(0, 13), match='www.baidu.com'>
<re.Match object; span=(0, 4), match='aaa.'>
'''

pattern2 = r'(www)?(?(1).*|\w+\.)'
st1 = 'www.baidu.com'
st2 = 'aaa.baidu.com'
reg = re.compile(pattern2)
res1 = reg.search(st1)
res2 = reg.search(st2)
print(res1)
print(res2)
'''
输出结果:
<re.Match object; span=(0, 13), match='www.baidu.com'>
<re.Match object; span=(0, 4), match='aaa.'>
'''

1.4 分组(模式单元)用法

本节部分参考了《python 正则表达式详解》中的五 分组用法

 Python中的正则表达式使用“(”表示分组,按照每个分组中前半部分出现的顺序 "(" 判定分组的索引,索引从 1 开始,

1.4.1  每个分组在访问的时候可以使用索引,也可以使用别名。

'''
如果你在写正则的时候需要在正则里面重复书写某个表达式,那么你可以使用正则的引用分组功能,需要注意的是引用的【不是】前面分组的【正则表达式】 而是捕获到的【内容】,并且引用的分组不算在分组总数中.
'''

st = '<title>百度</title><body>www.baidu.com</body>'

'''
重复引用
#索引模式单元的应用 \1代表了([a-z]+)匹配到的内容,并不算一组
'''
pattern = r'<([a-z]+)>.*</\1>'
reg = re.compile(pattern)
res = reg.search(st)#方法参见表2.2
print(reg.groups)#参见表2.3
print(res.groups())#参见表2.4
print(res.group(1))
'''
输出结果:
1
('title',)
title
'''

'''
重复引用
字典模式单元的应用,(?P=biaoti)代表了(?P<biaoti>[a-z]+)所匹配的内容,并不算一组
'''
pattern = r'<(?P<biaoti>[a-z]+)>.*</(?P=biaoti)>'
reg = re.compile(pattern)
res = reg.search(st)
print(reg.groups)
print(res.groupdict())#参见表2.4
print(res.group('biaoti'))
print(res.group(1))
'''
输出结果:
1
{'biaoti': 'title'}
title
title
'''

1.4.2  有时候可能只是为了把正则表达式分组,而不需要捕获其中的内容,这时候可以使用非捕获分组

r'''
(?:)取消单元存储功能
'''
pattern = r'(?:bai)(du)'
reg = re.compile(pattern)#参见表2.2
st = 'baidudududu'
res = reg.search(st)
print(reg.groups)#参见表2.3
print(res.groups())
'''
输出结果:
1
('du',)

也就是说,对于(?:bai)只做匹配,不当做一组来记录,也就对应了res.groups()返回的元组中只有'du'
'''

s = 'Hello, Mr.Gumby : 2016/10/26'
p = re.compile(r'(?:(?P<name>\w+\.\w+)|(\d+/)+)')
m = p.search(s)
print(p.groups)
print(m.groups())
'''
输出结果:
2
('Mr.Gumby', None)
'''


 

二  re模块中正则表达式方法

2.1 常用的re.xxx

表2.1 常用的re.xxx
方法名功能格式形参返回值或注释
compile()

1.编译正则表达式,获取正则表达式对象。

2.可以循环利用,提高程序效率

re.compile(正则字符串,模式修正符)正则表达式对象
purge()清除正则表达式缓存re.purge()当你在程序中使用 re 模块,无论是先使用 compile 还是直接使用比如 findall 来使用正则表达式操作文本,re 模块都会将正则表达式先编译一下, 并且会将编译过后的正则表达式放到缓存中,这样下次使用同样的正则表达式的时候就不需要再次编译, 因为编译其实是很费时的,这样可以提升效率,而默认缓存的正则表达式的个数是 100, 当你需要频繁使用少量正则表达式的时候,缓存可以提升效率,而使用的正则表达式过多时,缓存带来的优势就不明显了
escape()对字符串中的非数字和非字母部分进行转义re.escape(字符串)字符串
findall()对字符串进行正则匹配,获取所有匹配的内容re.findall(正则表达式,匹配字符串,模式修正符)匹配到的字符串元组
finditer()对字符串进行正则匹配,获取所有匹配到的内容的迭代器对象re.finditer(正则表达式,匹配字符串,模式修正符)包含所有匹配结果对象的迭代器
match()在开头位置对字符串进行正则匹配,相当于这个方法往正则表达式上加了" ^ "re.match(正则表达式,匹配字符串,模式修正符)匹配结果对象
search()在任意位置对字符串进行正则匹配re.search(正则表达式,匹配字符串,模式修正符)匹配结果对象
split()使用正则表达式切割字符串re.split(正则表达式,要切割的字符串[,切割次数])例如:切割3次生成4个字符串切割后字符串的列表
sub()正则替换re.sub(正则表达式,被替换内容的字符串,替换的字符串[,最大替换次数])替换后的字符串
subn()正则替换re.subn(正则表达式,被替换内容的字符串,替换的字符串[,最大替换次数])元组:(替换后的字符串,替换进行的次数)

 

2.2 常用的正则表达式对象方法和属性(借助compile()方法获取的对象)

注:regex = re.compile(...)

表2.2 正则表达式对象的常用方法
方法名功能格式形参返回值或注释
findall()使用正则表达式对象对制定的字符串进行匹配,获取所有匹配内容regex.findall(匹配字符串)匹配到的字符串元组
finditer()对字符串进行正则匹配,获取所有匹配到的内容的生成器对象regex.finditer(匹配字符串)包含所有匹配结果对象的迭代器
match()在开头位置对字符串进行正则匹配regex.match(要匹配的字符串)第一次匹配到的结果对象
search()在任意位置对字符串进行正则匹配regex.search(要匹配的字符串)第一次匹配到的结果对象
sub()正则替换regex.sub(被替换内容的字符串,替换的字符串[,最大替换次数])替换后的字符串
subn()正则替换regex.subn(被替换内容的字符串,替换的字符串[,最大替换次数])替换后的字符串
split()正则表达式分割regex.split(要切割的字符串)切割后字符串的列表
fullmatch()如果整个字符串匹配此正则表达式,则返回相应的匹配对象。   regex.fullmatch(string[,pos[,endpos]])匹配结果对象

注:sub和subn的repl参数

s = "the sum of 7 and 9 is [7+9]."

# 基本用法 将目标替换为固定字符串
print(re.sub('\[7\+9\]', '16', s))
# output> the sum of 7 and 9 is 16.

# 高级用法 1 使用前面匹配的到的内容 \1 代表 pattern 中捕获到的第一个分组的内容
print(re.sub('\[(7)\+(9)\]', r'\2\1', s))
# output> the sum of 7 and 9 is 97.


# 高级用法 2 使用函数型 repl 参数, 处理匹配到的 SRE_Match 对象
def replacement(m):
    p_str = m.group()
    if p_str == '7':
        return '77'
    if p_str == '9':
        return '99'
    return ''
print(re.sub('\d', replacement, s))
# output> the sum of 77 and 99 is [77+99].


# 高级用法 3 使用函数型 repl 参数, 处理匹配到的 SRE_Match 对象 增加作用域 自动计算
scope = {}
example_string_1 = "the sum of 7 and 9 is [7+9]."
example_string_2 = "[name = 'Mr.Gumby']Hello,[name]"

def replacement(m):
    code = m.group(1)
    st = ''
    try:
        st = str(eval(code, scope))
    except SyntaxError:
        exec code in scope
    return st

# 解析: code='7+9'
#       str(eval(code, scope))='16'
print(re.sub('\[(.+?)\]', replacement, example_string_1))
# output> the sum of 7 and 9 is 16.

# 两次替换
# 解析1: code="name = 'Mr.Gumby'"
#       eval(code)
#       raise SyntaxError
#       exec code in scope
#       在命名空间 scope 中将 "Mr.Gumby" 赋给了变量 name

# 解析2: code="name"
#       eval(name) 返回变量 name 的值 Mr.Gumby
print(re.sub('\[(.+?)\]', replacement, example_string_2))
# output> Hello,Mr.Gumby

 

表2.3 正则表达式对象常用属性
属性名作用
flags获取当前正则对象的模式修正符
pattern获取正则表达式的字符串格式
groups模式单元的个数   
geoupindex一个字典,用于映射由 (?P<id>) 组号定义的任何符号组名。如果模式中没有使用符号组,则字典为空。

 

p = re.compile('''(?:(?P<name>\w+\.\w+)|(?P<no>\s+\.\w+)).*? (\d+)''', re.X)
print(p.flags)
print(p.groupindex)
print(p.pattern)
print(p.groups)

'''
输出结果:
96        #这里的96是32+64,即re.U+re.X,re.U是默认模式,这个机制跟Linux系统中的文件权限很像
{'name': 1, 'no': 2}
(?:(?P<name>\w+\.\w+)|(?P<no>\s+\.\w+)).*? (\d+)
3
'''

注:正则表达式添加pos参数的用法。

选取要匹配的字符串中的(pos,endpos]位置的字符来进行匹配,不写endpos参数,则默认到最后

  •         findall(string[,pos[,endpos]])
  •         finditer(string[,pos[,endpos]])
  •         match(string[,pos[,endpos]])
  •         search(string[,pos[,endpos]])

2.3 匹配结果对象常用的方法和属性

表2.4 匹配结果对象常用的方法
start([group])返回指定分组的开始位置,默认返回正则表达式所匹配到的第一个字符的索引
end([group=0])返回指定分组的结束位置,默认返回正则表达式所匹配到的最后一个字符的索引
expand(template)根据模版返回相应的字符串,类似与 sub 函数里面的 repl, 可使用 \1 或者 \g<name> 来选择分组
group([group1, ...])根据提供的索引或名字返回响应分组的内容,默认返回 start() 到 end() 之间的字符串, 提供多个参数将返回一个元组
groupdict([default=None])返回一个包含所有匹配到的命名分组的字典,没有命名的分组不包含在内,key 为组名, value 为匹配到的内容,参数 default 为没有参与本次匹配的命名分组提供默认值
groups([default=None]) 以元组形式返回每一个分组匹配到的字符串,包括没有参与匹配的分组,其值为 default
span([group]) 返回指定分组的起止位置组成的元组,默认返回由 start() 和 end() 组成的元组
__getitem__(g)这与之相同 m.group(g),这允许从match中更容易地访问个人组

 

s = 'Hello, Mr.Shiar : 2016/10/26'
reg = re.compile('''(?:(?P<name>\w+\.\w+)|(?P<no>\s+\.\w+)).*? (\d+)''', re.X)
res = reg.search(s)

# 返回指定分组的结束位置,默认返回正则表达式所匹配到的最后一个字符的索引
print(res.end())
#输出结果:22

# 根据模版返回相应的字符串,类似与 sub 函数里面的 repl, 可使用 \1 或者 \g<name> 来选择分组
print(res.expand("my name is \\1"))
#输出结果:my name is Mr.Shiar

# 根据提供的索引或名字返回响应分组的内容,默认返回 start() 到 end() 之间的字符串, 提供多个参数将返回一个元组
print(res.group())
#输出结果:Mr.Shiar : 2016
print(res.group(1,2))
#输出结果:('Mr.Shiar', None)

# 返回 返回一个包含所有匹配到的命名分组的字典,没有命名的分组不包含在内,key 为组名, value 为匹配到的内容,参数 default 为没有参与本次匹配的命名分组提供默认值
print(res.groupdict('default_string'))
#输出结果:{'name': 'Mr.Shiar', 'no': 'default_string'}

# 以元组形式返回每一个分组匹配到的字符串,包括没有参与匹配的分组,其值为 default
print(res.groups('default_string'))
#输出结果:('Mr.Shiar', 'default_string', '2016')

# 返回指定分组的起止未知组成的元组,默认返回由 start() 和 end() 组成的元组
print(res.span(3))
#输出结果:(18, 22)

# 返回指定分组的开始位置,默认返回正则表达式所匹配到的第一个字符的索引
print(res.start(3))
#输出结果:18
表2.5 匹配结果对象常用的属性
endpos本次搜索结束位置索引
lastgroup本次搜索匹配到的最后一个分组的别名
lastindex本次搜索匹配到的最后一个分组的索引
pos本次搜索开始位置索引
re本次搜索使用的 SRE_Pattern 对象
regs列表,元素为元组,包含本次搜索匹配到的所有分组的起止位置
strin本次搜索操作的字符串
s = 'Hello, Mr.Shiar : 2016/10/26'
m = re.search(', (?P<name>\w+\.\w+).*?(\d+)', s)

# 本次搜索的结束位置索引
print(m.endpos)
# output> 28

# 本次搜索匹配到的最后一个分组的别名
# 本次匹配最后一个分组没有别名
print(m.lastgroup)
# output> None

# 本次搜索匹配到的最后一个分组的索引
print (m.lastindex)
# output> 2

# 本次搜索开始位置索引
print(m.pos)
# output> 0

# 本次搜索使用的 SRE_Pattern 对象
print(m.re)
# output> re.compile(', (?P<name>\\w+\\.\\w+).*?(\\d+)')

# 列表,元素为元组,包含本次搜索匹配到的所有分组的起止位置 第一个元组为正则表达式匹配范围
print(m.regs)
# output> ((7, 22), (7, 15), (18, 22))

# 本次搜索操作的字符串
print(m.string)
# output> Hello, Mr.Shiar : 2016/10/26

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python 爬虫中,正则表达式是非常常用的技术,可以帮助我们从网页中提取出需要的信息。下面是一些常用正则表达式符号和方法: 1. ".":匹配任意单个字符,除了换行符。 2. "^":匹配字符串的开始位置。 3. "$":匹配字符串的结束位置。 4. "*":匹配前一个字符出现 0 次或多次。 5. "+":匹配前一个字符出现 1 次或多次。 6. "?":匹配前一个字符出现 0 次或 1 次。 7. "{m}":匹配前一个字符出现 m 次。 8. "{m,n}":匹配前一个字符出现 m 到 n 次。 9. "(...)":分组匹配,匹配括号内的表达式。 10. "[...]":匹配括号内的任意一个字符。 11. "[^...]":匹配不在括号内的任意一个字符。 12. "\d":匹配数字,等同于 [0-9]。 13. "\D":匹配非数字,等同于 [^0-9]。 14. "\s":匹配空白字符,包括空格、制表符、换行符等。 15. "\S":匹配非空白字符。 16. "\w":匹配单词字符,包括字母、数字、下划线。 17. "\W":匹配非单词字符。 在 Python 中,使用 re 模块进行正则表达式的匹配。常用方法包括: 1. re.compile(pattern):将正则表达式编译成一个对象,提高匹配效率。 2. re.search(pattern, string):在字符串中搜索匹配正则表达式的第一个位置。 3. re.findall(pattern, string):在字符串中搜索匹配正则表达式的所有位置,并返回一个列表。 4. re.sub(pattern, repl, string):将字符串中所有匹配正则表达式的字符替换为指定的字符串。 以上是一些常用正则表达式符号和方法,希望能对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值