正则表达式可以允许我们对于文本进行十分精细的处理,但首先我们需要明确其语法与使用方式。在日常使用时,可以结合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)
- 在‘多行模式(multline)’中,使用 ^ $分别匹配行首和行尾
- 使用 \b 匹配单词边界; \B匹配非单词边界
- 前瞻 & 后顾
行锚(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
2re.search(r'(\d{4})-(\d{2})-(\d{2})', '2025-08-25').groups()
# ('2025', '08', '25')对应组 1,2,3 - 命名捕获组
(?P<name>...)使用名字命名更清晰。1
2m = re.search(r'(?P<year>\d{4})-(?P<month>\d{2})', '2025-08')
m.group('year') # '2025' - 非捕获组
(?:...)只分组不编号,不占用内存,用于性能或避免干扰编号。
引用 则是把标记过的内容拿来复用 :
反向引用(同一条正则里复用)可以使用编号形式 (\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 | import re |
替换
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
54import 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)



