北纬36度
python爬虫正则表达式用法

[toc]

常用匹配表参考

\w匹配字母、数字、下划线
\W匹配不是字母、数字、下划线的字符
\s匹配任意空白字符等价于[\t\n\t\f]
\S匹配任意非空字符
\d匹配任意数字等价[0-9]
\D匹配任意非数字的字符
\A匹配字符串开头
\Z匹配字符串结尾,如果存在换行,只会匹配换行前的结束字符串
\z匹配字符串结尾,如果存在换行,同时还会匹配换行符
\G匹配最后匹配完成的位置
\n匹配换行符
\t匹配制表符
^匹配一行字符串的开头
$匹配一行字符串的结尾
.匹配任意字符,除了换行符,当re.DOTAKK标记被指定,则可以匹配包含换行符的任意字符,这里表示匹配1个字符,如果想要匹配任意字符可以写成.*
[...]表示一组字符,单独列出,比如[amk]匹配a或m或k
[^...]不在[]的字符,比如[^abc]表示匹配a、b、c之外的字符
{n}精确匹配n个前面的表达式,比如\w{10}表示往后匹配10次只要符合\w规则即可
{n,m}匹配n到m次有前面正则表达式定义的片段,贪婪方式
a|b匹配a或b
{}匹配括号内的表达式
*匹配0或多个前面的子表达式或字符
+匹配1个或多个前面的子表达式或字符
匹配0个或1个前面正则表达式定义的片段

match方法

引入re
这个方法是从开头匹配

# 正则
import re

content = 'hello 123 456 world_this is a regex demo'
print(len(content))
result = re.match(r"^hello\s\d\d\d\s\d{3}\s\w{10}", content)
# 输出匹配的内容
print(result.group())
# 输出匹配的范围
print(result.span())

40
hello 123 456 world_this
(0, 24)

提取内容

首先将要匹配的内容提取的内容括起来
然后match(1)从1开始,1表示提取第一个括号

# 正则
import re

content = 'hello 123 456 world_this is a regex demo'
print(len(content))
result = re.match(r"^hello\s\d\d\d\s\d{3}\s\w{10}", content)
# 输出匹配的内容
print(result.group())
# 输出匹配的范围
print(result.span())
# 2.提取内容
result = re.match(r"^hello\s(\d\d\d)\s\d{3}\s\w{10}", content)
# 提取第一个123
print(result.group(1))
运行结果
123



通用匹配

# 正则
import re

content = 'hello 123 456 world_this is a regex demo'
print(len(content))
# 通用匹配
result = re.match(r'^hello.*demo$', content)
print(result.group())
# 这里对比只用.不用.*的区别,会发现只用.会报错,因为没匹配到任何东西,因为.的意义是匹配任意1个字符,只有和.*配合才代表匹配任意字符
result =re.match(r'^hello.demo$', content)
print(result.group())
运行结果
hello 123 456 world_this is a regex demo
Traceback (most recent call last):
  File "C:\Users\Quinn\PycharmProjects\pythonProject\正则表1.py", line 19, in <module>
    print(result.group())
AttributeError: 'NoneType' object has no attribute 'group'

贪婪模式

.*用法

所谓贪婪模式就是在此模式下使用比如.*尽量匹配多个字符,.*原意代表匹配任意字符+匹配0个或N个表达式(也就是匹配前面的这里的.表达式),这时候贪婪模式表示匹配最多的任意字符,而不是0个

# 贪婪模式示例
# 我们提取中间数字部分
result = re.match(r'^hello.*(\d+).*demo$', content)
print(result.span())
print(result.group(1))
输出
(0, 40)
6
# 原因:开启贪婪模式.*会尽量匹配多个字符,所以一直匹配到了数字7前面,而\d+则只能匹配了一个数字

非贪婪模式

.*?用法

我们愿意是提取数字部分,但是由于贪婪模式导致无法提取,可以使用.*?非贪婪模式

# 正则
import re

content = 'hello 123 456 world_this is a regex demo'
# 非贪婪模式示例
result = re.match(r'^hello.*?(\d+).*demo$', content)
print(result.span())
print(result.group(1))
输出
123


非贪婪模式就是尽可能匹配少的字符,.*匹配任意字符直到遇到\d+这时候在贪婪模式下会匹配,但是在非贪婪模式下就直接交给\d+去匹配了
尽量使用非贪婪模式而不是贪婪模式
下面情况非贪婪模式会导致无法匹配

content2 = 'http://quinntian.com/category'
result = re.match(r'^http.*?com/(.*)', content2)
print(result.group(1))
result = re.match(r'^http.*?com/(.*?)', content2)
print(result.group(1))
运行结果
category

# 这里第二个并没有提取出来是因为没有匹配到任何内容

.+?用法

非贪婪模式2
<img src=``test.jpg` width=`60px` height=`80px`/>
src='.*?'则搜索出来的结果是:src=''
非贪婪模式3
.+? 表示至少匹配一个字符串
src='.+?'则搜索出来的结果是:src='’test.img'
content3 = "<img src=''test.jpg' width='60px' height='80px'/>"
result = re.match(".*(src='.+?')", content3)
print(result.group(1))
print(result.span())
运行
src=''test.jpg'
(0, 20)

修饰符

# 修饰符
content4 = """hello 123456 world this 
    is a regex demo"""
# 正常情况下上面字符串有换行符,因此无法匹配,因为.代表匹配任意除了换行符以外的字符
result = re.match(r'he.*?(\d+).*demo$', content4)
print(result.group(1))
# 传入第三个参数re.S
result = re.match(r'he.*?(\d+).*demo$', content4, re.S)
print(result.group(1))

常见修饰符
re.S使得.可以匹配换行符
re.I使匹配对大小写不敏感
re.M多行匹配,影响^和$
re.L做本地化识别
re.U根据Unicode字符集识别,会影响\w\W\b\B
re.X更灵活的格式

转义匹配

# 转义匹配
content5 = '(百度)www.baidu.com'
result = re.match(r'\(百度\)www\.baidu\.com', content5)
print(content5)
输出
(百度)www.baidu.com

当遇到特殊字符的时候可以使用\转义

search方法

  • 扫描整个字符串,并返回第一个匹配

# search方法
content6 = 'extra stings hello 123456 world this is regex demo'
result = re.match('hello.*?(d+).*?demo', content6)
print(result)
# 换用search
result = re.search('hello.*?(d+).*?demo', content6)
print(result)
输出结果
None
<re.Match object; span=(13, 50), match='hello 123456 world this is regex demo'>

case2

content7 = """<div id="songs-list">
    <h2>经典歌曲</h2>
    <p class="introduction">
    经典歌曲列表
    </p>
    <ul id="list" class="list-group">
        <li data-view="2">
            <a href="/2.mp3" singer="田馥甄">小幸运</a>
        </li>
        <li data-view="3">
            <a href="/3.mp3" singer="吴青峰">带我走</a>
        </li>
        <li data-view="4">
            <a href="/4.mp3" singer="苏打绿">小情歌</a>
        </li>
        <li data-view="5">
            <a href="/5.mp3" singer="飞儿乐团">月牙湾</a>
        </li>
    </ul>
</div>
"""
# 要开启换行
result = re.search('singer="(.*?)">(.*?)</a>', content7, re.S)
print(result)
运行结果
<re.Match object; span=(183, 203), match='singer="田馥甄">小幸运</a>'>

findall方法

返回所有匹配的结果,以列表方式存储

result = re.findall('singer="(.*?)">(.*?)</a>', content7, re.S)
print(result)
for r in result:
    print(r)
运行结果
[('田馥甄', '小幸运'), ('吴青峰', '带我走'), ('苏打绿', '小情歌'), ('飞儿乐团', '月牙湾')]
('田馥甄', '小幸运')
('吴青峰', '带我走')
('苏打绿', '小情歌')
('飞儿乐团', '月牙湾')

sub方法

替换或修改文本

# sub
content8 = 'kfhdkfhg43jkh43k434h2h46h45'
# content8 = re.search(r'\d', content8)
# print(content8)
content8 = re.sub(r'\d+', '', content8)
print(content8)
运行结果
kfhdkfhgjkhkhhh

compile方法

编译成字符表达式,没看出来什么用,还不如直接传写好的

# compile
c1 = '2021年3月7日20:18:23'
c2 = '2021年3月7日20:18:32'
c3 = '2021年3月7日20:18:39'
pattern = re.compile('\d{2}:\d{2}')
print(pattern)
r1 = re.sub(pattern, '', c1)
r2 = re.sub(pattern, '', c2)
r3 = re.sub(pattern, '', c3)
print(r1)
print(r2)
print(r3)
运行结果
2021年3月7日:23
2021年3月7日:32
2021年3月7日:39