抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

正则表达式可以允许我们对于文本进行十分精细的处理,但首先我们需要明确其语法与使用方式。在日常使用时,可以结合https://regexr.com食用!网站提供了即时的正则表达式可视化,真是再好不过了!🎇🎇🎇

字符与语法

元字符

. ^ $ * + ? { } [ ] \ | ( )在进行正则表达式的构建中可以精细控制所需字符:

字符 指代
[ ] 指定一个字符类(希望匹配的字符的一个集合),这些字符可以单独地列出,也可以用字符范围来表示(用 ‘-‘ 分隔)
( ) 将范围内的字符整体选中
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示反义字符集合
$ 匹配输入字符串的结尾位置,如果设置Multiline属性,则 $ 也匹配 ‘\n’ 或 ‘\r’(段落结尾)
| 指明两项之间的一个选择
+ 匹配前面的子表达式1次或n次
* 匹配前面的子表达式0次或n次
? 匹配前面的子表达式1次或1次
. 匹配除换行符 \n 之外的任何单字符
\ 转义字符,下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符
{ } 限定符表达式的开始和结束,用于划分范围

在正则表达式中,通常使用 \d、\D、\s、\S、\w、\W、\b、\B 指代各类字符,属于特殊字符类如下表所示:
| 字符 | 指代 |
| —————- | —————- |
| \d | 匹配任何十进制数字,等价于字符类 [0-9] |
| \D | 匹配任何非数字字符,等价于字符类 0-9 |
| \s | 匹配任何空白字符,等价于字符类 [ \t\n\r\f\v] |
| \S | 匹配任何非空白字符,等价于字符类 [ ^\t\n\r\f\v] |
| \w | 匹配任何字母与数字字符,等价于字符类 [a-zA-Z0-9_] |
| \W | 匹配任何非字母与数字字符,等价于字符类 a-zA-Z0-9_ |
| \b | 匹配一个单词的开始或结束的位置,而不匹配任何实际的字符 |
| \B | 即匹配不在单词边界处的位置 |

对于 \b and \B (边界匹配元字符)的理解需要阐明的是:
\b (单词边界)匹配以下三种情况之一:

单词的开头:如果 \b 出现在一个字母或数字之前,或者在字符串的开头,它会匹配一个单词的开始位置。
单词的结尾:如果 \b 出现在一个字母或数字之后,或者在字符串的末尾,它会匹配一个单词的结束位置。
单词的内部:如果 \b 出现在两个连续的字母或数字之间,它不会匹配任何内容,因为没有单词边界。

例如:正则表达式 \bword\b 匹配整个单词 “word”,但不匹配 “words” 或 “sword”。
正则表达式 \b\d+\b 匹配一个完整的数字,例如 “123”,但不匹配 “abc123”。
正则表达式 \b[A-Z]+\b 匹配一个完整的大写字母单词,例如 “HELLO”,但不匹配 “hello”。

\B (非单词边界)匹配以下情况之一:

单词的内部:如果 \B 出现在两个连续的字母或数字之间,它会匹配这两个字符之间的位置,表示它们不是单词的边界。
非单词的开头或结尾:如果 \B 出现在一个字母或数字之前或之后,它会匹配这个位置,表示它不是单词的开头或结尾。
以下是一些元字符 \B 的实例:

例如:正则表达式 \Bword\B 匹配 “sword1” 中的 “word”,但不匹配 “password” 或 “words”。
正则表达式 \B\d+\B 匹配 “abc123def” 中的 “123”,但不匹配 “123” 或 “abc123”。
正则表达式 \B[A-Z]+\B 匹配 “HELLO WORLD” 中的 “ELL” 和 “ORL”,但不匹配 “HELLO” 或 “WORLD”。

修饰符

修饰符(也叫“标志位、flag”)是放在正则表达式之外的小开关,用来告诉引擎“该怎么匹配”。
re·模块中:

全称 简写 作用
re.IGNORECASE re.I 模糊大小写,A 也能匹配 a
re.MULTILINE re.M ^$ 匹配每一行的开头和结尾,而不是整段字符串的首尾(多行模式)
re.DOTALL re.S . 也能匹配换行符 \n(默认 . 遇到 \n 就停)
re.VERBOSE re.X 允许在正则里写注释空白,可读性大增
re.UNICODE re.U \w \b \s 等按 Unicode 规则(Python3 默认开启)
re.ASCII re.A re.U 相反,强制按 ASCII 解释 \w \b \s

可以同时开两个修饰(re.I | re.S)

位置匹配(zero-width assertions)

  1. 在‘多行模式(multline)’中,使用 ^ $分别匹配行首和行尾
  2. 使用 \b 匹配单词边界; \B匹配非单词边界
  3. 前瞻 & 后顾

行锚(Anchor)——只和“行首/行尾/单词边界”打交道
| 符号 | 含义 | 例子 |
|—-|—-|—-|
| ^ | 字符串开头(re.M 打开后表示每一行的开头) | ^Hello |
| $ | 字符串结尾(re.M 打开后表示每一行的结尾) | world$ |
| \b | 单词边界(\w 与 \W 之间的零宽位置) | \bcat\b |

前瞻后顾(Lookaround)
| 语法 | 含义 | 例子 |
|—-|—-|—-|
| (?=...) | 正向先行断言:右边必须出现 … | Windows(?=10|11) 只匹配后面是 10 或 11 的 “Windows” |
| (?!...) | 负向先行断言:右边不能出现 … | \d{3}(?!px) 匹配三位数字但后面不能跟 px |
| (?<=...) | 正向后发断言:左边必须出现 … | (?<=USD)\d+ 匹配 “USD123” 中的 123 |
| (?<!...) | 负向后发断言:左边不能出现 … | (?<![a-zA-Z])\d+ 匹配前面不是字母的数字 |

分组与引用

分组 就是给一段表达式打括号做标记 :

  1. 普通捕获组 () ()的内容即为捕获的对象,其编号从 1 开始,按左括号顺序递增。
    1
    2
    re.search(r'(\d{4})-(\d{2})-(\d{2})',  '2025-08-25').groups()
    # ('2025', '08', '25')对应组 1,2,3
  2. 命名捕获组 (?P<name>...) 使用名字命名更清晰。
    1
    2
    m = re.search(r'(?P<year>\d{4})-(?P<month>\d{2})', '2025-08')
    m.group('year') # '2025'
  3. 非捕获组 (?:...) 只分组不编号,不占用内存,用于性能或避免干扰编号。

引用 则是把标记过的内容拿来复用 :

反向引用(同一条正则里复用)可以使用编号形式 (\1 \2 …) 或者命名形式如 (?P=name)

*使用 group() 可以调取分组内容

使用方式

python使用函数一览

在python的re模块中常用函数有re.match()re.search()re.fullmatch()re.sub()re.split()re.split()re.complie()re.escape()re.subn(),可以将函数分为 “查找、替换、分割、编译”

查找

re.match()re.search()re.findall()re.finditer()函数中,其中pattern代表匹配模式、string代表需查找的字符串、flags代表限定字符
|函数名|使用方法|
|—|——|
|re.match(pattern,string,flags)|从字符串开头开始匹配|
|re.search(pattern,string,flags)|扫描整个字符串,找到第一处即停|
|re.findall(pattern,string,flags)|以列表(list)的形式返回目标字符串中所有匹配对象|
|re.finditer(pattern,string,flags)|搜索所有匹配对象,返回迭代类型|

举例:编写程序实现下述功能,提示用户输入用户名,要求用户名以字母开头,长度不少于3位,只能包含字母、数字、下划线,如果用户输入符合要求,则提示注册成功,否则提示用户名不符合要求,请重新输入,一直循环直到用户名符合要求为止。

1
2
3
4
5
6
7
import re
admin = input("请输入用户名(用户名以字母开头,长度不少于3位,只能包含字母、数字、下划线):")
match = re.search(r"[a-zA-Z][\w_]{2,}",admin,re.M)
while match is None:
print("用户名不符合要求!")
admin = input("请再次输入用户名!:")
print(f"用户创建成功!请核对账号名称{admin}")

替换

re.sub()re.subn()中,repl代表替换匹配字符串的字符串、count代表替换的最大次数(默认为全部替换)
| 函数名 | 使用方法 |
| ————————————————————————— | ———————— |
| re.sub(pattern, repl, string, count=0, flags=0) | 替换所有匹配的对象 |
| re.subn(pattern, repl, string, count=0, flags=0) | 同上,但额外返回替换次数 |

分割

re.split()中。maxsplit代表最大切分次数
| 函数名 | 使用方法 |
| ———————————————————————— | —————— |
| re.split(pattern, string, maxsplit=0, flags=0) | 按正则切分字符串 |

编译 & 转义

不常用的几类函数;
| 函数原型 | 使用方法 |
| ——————————————— | ——————————————- |
| re.compile(pattern, flags=0) | 把正则预编译成 Pattern 对象,后面可反复用 |
| re.escape(string) | 把字符串里所有正则元字符转义,安全拼正则 |
| re.purge() | 清空正则缓存(几乎用不到) |

实战举例

网页数据后处理,将豆瓣的评分数据处理成 CSV 文件。
网页爬虫:豆瓣电影Top250爬虫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import requests
from bs4 import BeautifulSoup
import time
import random

def scrape_douban_top250(output_file):
session = requests.Session()
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
}

base_url = "https://movie.douban.com/top250"
all_content = []

for start in range(0, 250, 25):
url = f"{base_url}?start={start}"
print(f"正在抓取: {url}")
time.sleep(random.uniform(1, 3)) # 避免请求过快被封

response = session.get(url, headers=headers)
if response.status_code == 200:
soup = BeautifulSoup(response.text, 'html.parser')
movies = soup.find_all('div', class_='item')
if not movies:
print("⚠️ 没有找到电影数据,请检查网页结构是否变化!")
break

for movie in movies:
title = movie.find('span', class_='title')
if title: # 确保找到标题
title = title.text.strip()
else:
title = "未知电影"

rating = movie.find('span', class_='rating_num')
if rating: # 确保找到评分
rating = rating.text.strip()
else:
rating = "暂无评分"

quote_elem = movie.find('span', class_='inq')
quote = quote_elem.text.strip() if quote_elem else "无简介"

all_content.append(f"电影名称: {title}\n评分: {rating}\n简介: {quote}\n")

if all_content:
with open(output_file, 'w', encoding='utf-8') as f:
f.write("\n".join(all_content))
print(f"✅ 数据已保存到 {output_file}")
else:
print("❌ 未能获取数据,请检查网络或网页结构!")

if __name__ == "__main__":
scrape_douban_top250("douban_top250.txt")

正则提取部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

import csv
import re
# 实战梳理豆瓣网页数据
with open ('douban_top250.txt','r',encoding='utf-8') as file:
content = file.read()
# print(content)

# 将爬取的数据保存到csv中
name = re.findall(r'(?<=\u7535\u5f71\u540d\u79f0:\s).{1,}$',content,re.M)

rank = re.findall(r'\d.\d$',content,re.M)

rows = list(zip(name, rank))

# 将name rank放在csv中
# print(name,rank)

with open('cleaned_movies.csv', 'w', newline='', encoding='utf-8-sig') as fil:
writer = csv.writer(fil)
writer.writerow(['电影名', '评分']) # 写表头
writer.writerows(rows)

评论