正则表达式

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑操作。

可以用 re 模块实现

1、匹配方法

1.1、match 方法

  • 尝试从字符串的起始位置匹配,若不是起始位置匹配成功的话,match()返回 None
  • 必须考虑开头的内容,匹配成功返回匹配结果,否则返回 None
  • 用于检测正则表达式与字符串是否匹配,第一个参数为正则,第二个为字符串,第三个为匹配模式
  • re.match(pattern,string,flags=0)
import re 
content = 'Hello 123 4567 World_This is a Regex Demo'
print(len(content))      # 字符串长度
result = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}.*Demo$',content)
print(result)
print(result.group())     # 返回匹配结果
print(result.span())     # 输出匹配结果的范围
41
<_sre.SRE_Match object; span=(0, 41), match='Hello 123 4567 World_This is a Regex Demo'>
Hello 123 4567 World_This is a Regex Demo
(0, 41)

1.2、search 方法

扫描整个字符串并返回第一个成功的匹配;为匹配方便,能用 search 就不用 match

match 方法

# 从中间匹配,返回 None
import re
content = 'Extra strings Hello 1234567 World_This is a Regex Demo'
result = re.match('He.*?(\d+).*?Demo',content)
print(result)
----
None

search 方法

import re
content = 'Extra strings Hello 1234567 World_This is a Regex Demo'
result = re.search('He.*?(\d+).*?Demo',content)
print(result)
print(result.group(1))
<_sre.SRE_Match object; span=(14, 54), match='Hello 1234567 World_This is a Regex Demo'>
1234567

实例

使用 search 方法,匹配 html 中第一个歌手名和歌曲名

import re
html = """<div id='songs-list'>
    <h2 class='title'>经典老歌</h2>
    <p class='introduction'>经典老歌列表</p>
    <ul id='list' class='list-group'>
        <li data-view='2'>一路上有你</li>
        <li data-view='7'>
            <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
        </li>
        <li data-view='4' class='active'>
            <a href="/3.mp3" singer="齐秦">往事随风</a>
        </li>
        <li data-view='6' class="tn"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
        <li data-view='5'><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
        <li data-view='5'>
            <a href="/6.mp3" singer="邓丽君"><i class='fa fa-user'></i>但愿人长久</a>
        </li>
    </ul>
</div>"""
result = re.search('<li.*?active.*?singer="(.*?)">(.*?)</a>',html,re.S)   # 匹配 class 为 active 的字段
print(result) 
print(result.group(1),result.group(2))       # re.rearch 只会返回符合匹配的第一个结果
<_sre.SRE_Match object; span=(139, 346), match='<li data-view=\'2\'>一路上有你</li>\n        <li data->
齐秦 往事随风

1.3、findall 方法

  • search 方法,只会返回第一个匹配结果
  • findall 方法,可以所有符合匹配的结果,返回类型为列表
# re.findall 方法可以将匹配结果以列表的形式返回
import re
html = """<div id='songs-list'>
    <h2 class='title'>经典老歌</h2>
    <p class='introduction'>经典老歌列表</p>
    <ul id='list' class='list-group'>
        <li data-view='2'>一路上有你</li>
        <li data-view='7'>
            <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
        </li>
        <li data-view='4' class='active'>
            <a href="/3.mp3" singer="齐秦">往事随风</a>
        </li>
        <li data-view='6' class="tn"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
        <li data-view='5'><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
        <li data-view='5'>
            <a href="/6.mp3" singer="邓丽君">但愿人长久</a>
        </li>
    </ul>
</div>"""
results = re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>',html,re.S)
print(results)
print(type(results))
for result in results:
    print(result)
[('/2.mp3', '任贤齐', '沧海一声笑'), ('/3.mp3', '齐秦', '往事随风'), ('/4.mp3', 'beyond', '光辉岁月'), ('/5.mp3', '陈慧琳', '记事本'), ('/6.mp3', '邓丽君', '但愿人长久')]
<class 'list'>
('/2.mp3', '任贤齐', '沧海一声笑')
('/3.mp3', '齐秦', '往事随风')
('/4.mp3', 'beyond', '光辉岁月')
('/5.mp3', '陈慧琳', '记事本')
('/6.mp3', '邓丽君', '但愿人长久')

例子

# 匹配所有歌曲,有些有 a 标签 有些没有
# 例子 1
import re
html = """<div id='songs-list'>
    <h2 class='title'>经典老歌</h2>
    <p class='introduction'>经典老歌列表</p>
    <ul id='list' class='list-group'>
        <li data-view='2'>一路上有你</li>
        <li data-view='7'>
            <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
        </li>
        <li data-view='4' class='active'>
            <a href="/3.mp3" singer="齐秦">往事随风</a>
        </li>
        <li data-view='6' class="tn"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
        <li data-view='5'><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
        <li data-view='5'>
            <a href="/6.mp3" singer="邓丽君">但愿人长久</a>
        </li>
    </ul>
</div>"""
# \s(匹配任意空白字符)、* 匹配 0 或多个表达式,因为第一首歌没有 a 标签因此需要匹配
# <a.*?>)?  表示可以匹配 a 标签也可以匹配没有 a 标签,?表示是否
# (\w+)匹配多个字符(歌名)
# (</a>)? 可以匹配 </a> ,也可以匹配没有  </a>
results = re.findall('<li.*?>\s*?(<a.*?>)?(\w+)(</a>)?\s*?</li>',html,re.S)
print(results)
for result in results:
    print(result)
[('', '一路上有你', ''), ('<a href="/2.mp3" singer="任贤齐">', '沧海一声笑', '</a>'), ('<a href="/3.mp3" singer="齐秦">', '往事随风', '</a>'), ('<a href="/4.mp3" singer="beyond">', '光辉岁月', '</a>'), ('<a href="/5.mp3" singer="陈慧琳">', '记事本', '</a>'), ('<a href="/6.mp3" singer="邓丽君">', '但愿人长久', '</a>')]
('', '一路上有你', '')
('<a href="/2.mp3" singer="任贤齐">', '沧海一声笑', '</a>')
('<a href="/3.mp3" singer="齐秦">', '往事随风', '</a>')
('<a href="/4.mp3" singer="beyond">', '光辉岁月', '</a>')
('<a href="/5.mp3" singer="陈慧琳">', '记事本', '</a>')
('<a href="/6.mp3" singer="邓丽君">', '但愿人长久', '</a>')

1.4、sub 方法

  • sub 方法可以用来修改文本
  • 第一个参数为正则,第二个为要替换成的字符串,第三个为原字符串
  • 替换字符串中每一个匹配的子串后返回替换后的字符串,更方便提取信息
# 将 1234567 替换成空
import re
content = 'Extra strings Hello 1234567 World_This is a Regex Demo Extra strings'
content = re.sub('\d+','',content)
print(content)
Extra strings Hello  World_This is a Regex Demo Extra strings

例子 1

# 替换原字符串可以使用 \1
import re
# \1 可以将 (\d+)即匹配结果拿出来
content = 'Extra strings Hello 1234567 World_This is a Regex Demo Extra strings'
content = re.sub('(\d+)',r'\1 8910',content)
print(content)
----

Extra strings Hello 1234567 8910 World_This is a Regex Demo Extra strings

例子 2

# 使用 re.sub 提取所有歌名实现与上面例子1结果一致
import re
html = """<div id='songs-list'>
    <h2 class='title'>经典老歌</h2>
    <p class='introduction'>经典老歌列表</p>
    <ul id='list' class='list-group'>
        <li data-view='2'>一路上有你</li>
        <li data-view='7'>
            <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
        </li>
        <li data-view='4' class='active'>
            <a href="/3.mp3" singer="齐秦">往事随风</a>
        </li>
        <li data-view='6' class="tn"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
        <li data-view='5'><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
        <li data-view='5'>
            <a href="/6.mp3" singer="邓丽君">但愿人长久</a>
        </li>
    </ul>
</div>"""
html = re.sub('<a.*?>|</a>','',html)     # 将 a 标签用 空白替换
print(html)
results = re.findall('<li.*?>(.*?)</li>',html,re.S)
print(results)
for result in results:
    print(result.strip())   # strip 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列
<div id='songs-list'>
    <h2 class='title'>经典老歌</h2>
    <p class='introduction'>经典老歌列表</p>
    <ul id='list' class='list-group'>
        <li data-view='2'>一路上有你</li>
        <li data-view='7'>
            沧海一声笑
        </li>
        <li data-view='4' class='active'>
            往事随风
        </li>
        <li data-view='6' class="tn">光辉岁月</li>
        <li data-view='5'>记事本</li>
        <li data-view='5'>
            但愿人长久
        </li>
    </ul>
</div>
['一路上有你', '\n        \t沧海一声笑\n        ', '\n        \t往事随风\n        ', '光辉岁月', '记事本', '\n        \t但愿人长久\n        ']
一路上有你
沧海一声笑
往事随风
光辉岁月
记事本
但愿人长久

例子 3

# 爬取豆瓣读书
import requests
import re

content = requests.get('https://book.douban.com/').text
pattern = re.compile('<li.*?cover.*?href="(.*?)".*?title="(.*?)".*?more-meta.*?author">(.*?)</span>.*?year=">(.*?)</span>.*?</li>',re.S)
results = re.findall(pattern,content)
print(results)
for result in results:
    url,name,author,date = result
    author = re.sub('\s','',author)
    date = re.sub('\s','',date)
    print(url,name,author,data)

1.5、compile 方法

将一个正则表达式串编译成正则对象,以便于复用该匹配模式

import re

content = """Hello 1234567 World_This
is a Regex Demo
"""
pattern = re.compile('Hello.*Demo',re.S)
result = re.match(pattern,content)
# result = re.match('Hello.*Demo',content,re.S)  与这个匹配结果一样
print(result)
----

<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This\nis a Regex Demo'>

2、匹配模式

2.1、常规匹配

常规匹配就是尽可能地使用匹配规则匹配每一个字符,比较繁琐

2.2、泛匹配

相较于常规匹配,泛匹配可以省略很多没必要的东西,尽量使用 .去匹配;其中 .(点)可以匹配任意字符(除换行符), (星)可以匹配前面的字符无限次

import re
content = 'Hello 123 4567 World_This is a Regex Demo'
result = re.match('^Hello.*Demo$',content)
print(result)
print(result.group())
print(result.span())
<_sre.SRE_Match object; span=(0, 41), match='Hello 123 4567 World_This is a Regex Demo'>
Hello 123 4567 World_This is a Regex Demo
(0, 41)

3、匹配目标

  • 从一串字符串中匹配其中的数据,将要匹配的字符串用小括号括起来,指定左右端点
  • 实际上标记了一个子表达式的开始和结束位置,被标记的子表达式会以此对应每个分组,调用 group 方法可获取匹配结果
# 匹配 123,左右端点空白\s
import re
content = 'Hello 123 4567 World_This is a Regex Demo'
result = re.match('^Hello\s(\d+)\s.*Demo$',content)
print(result)
print(result.group(1))   # group(1) 表示第一个小括号中的匹配结果
print(result.span())
<_sre.SRE_Match object; span=(0, 41), match='Hello 123 4567 World_This is a Regex Demo'>
123
(0, 41)

4、贪婪匹配和非贪婪匹配

贪婪匹配

贪婪匹配使用 .* 匹配任意字符和表达式,尽可能多的匹配字符和表达式

import re
content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^He.*(\d+).*Demo$',content)    # 由于要尽可能多的匹配,但又至少要有一个数字,因此匹配结果为7
print(result)
print(result.group(1))
print(result.span())
<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
7
(0, 40)

非贪婪匹配

  • 与贪婪匹配不一样的是尽可能少的匹配,只需在.* 后面添加一个 ?(将贪婪转换为非贪婪)即可
  • 尽可能多的使用非贪婪匹配
  • 若匹配结果在结尾处,使用非贪婪匹配有可能为,可以使用贪婪匹配
import re
content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^He.*?(\d+).*Demo$',content)    # 由于要尽可能多的匹配,但又至少要有一个数字,因此匹配结果为7
print(result)
print(result.group(1))
print(result.span())
<_sre.SRE_Match object; span=(0, 40), match='Hello 1234567 World_This is a Regex Demo'>
1234567
(0, 40)

5、修饰符

  • 泛匹配(.*)不能匹配换行符,当要匹配 html 内容时,就会显得很麻烦
  • 使用修饰符(re.S)匹配换行符
修饰符描述
re.I使匹配对大小不敏感
re.L做本地化识别(local-aware)匹配
re.M多行匹配,影响 ^ 和 $
re.S使 . (点)匹配包括换行在内的所有字符
re.U根据 Unicode 字符集解析字符,这个标志影响 \w、\W、\b 和 \B
re.X该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解
import re 
content = '''Hello 1234567 World_This 
is a Regex Demo'''
result = re.match('^He.*?(\d+).*Demo$',content,re.S)
print(result)
print(result.group(1))
<_sre.SRE_Match object; span=(0, 41), match='Hello 1234567 World_This \nis a Regex Demo'>
1234567

6、转义匹配

在遇到特殊字符时,如 $ . 等,需要将其先转义才能正常匹配

import re
content = 'price is $5.00'    # 有特殊字符 $ . 需要先转义
result = re.match('^.*\s\$5\.00$',content)
print(result)
print(result.group())
<_sre.SRE_Match object; span=(0, 14), match='price is $5.00'>
price is $5.00

7、总结

尽量使用泛匹配,使用括号得到匹配目标、尽量使用非贪婪模式,有换行符就用 re.S

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风老魔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值