很多人一听"正则表达式"五个字,脑瓜子嗡嗡的。什么点号、反斜杠、中括号、小括号,感觉跟进了黑灯瞎火的仓库,伸手不见五指,不知道从哪摸起。
其实特简单。正则就是你在黑暗仓库里的"摸金手势"。 手势做对了,闭着眼睛也能把想要的东西掏出来。
一、场景:仓库停电了
你刚用 requests 要饭成功,老板(服务器)给你扔过来一张菜单 (HTML字符串)。但仓库停电了,你看不见,只能伸手摸。
菜单长这样:
html
<li class="food">蛋炒粉 - <span class="price">15元</span></li>
<li class="food">肉炒粉 - <span class="price">20元</span></li>
你的任务 :在黑暗里,把所有价格(数字+元)摸出来。
老板给你一副摸金手套 (re模块),手套上刻着各种手势。你做什么手势,它就摸什么东西。
二、基础手势:单字符匹配
.(点号):摸到啥算啥,除了墙角的换行符
python
import re
html = "蛋炒粉15元\n肉炒粉20元"
# 点号:任意摸一个字符
re.findall(r'.', html)
人话 :. 就像你伸手随便抓一把,抓到字母、数字、符号都算数。但注意------抓不到墙角的换行符(\n)。
坑 :在 Python 的 re 模块里,. 默认不匹配换行符 。如果你的货(HTML)是好几行,点号摸到\n就停了。
解决办法 :加上 re.DOTALL 标志,让点号变成"连墙角都能摸"。
python
re.findall(r'.+', html, re.DOTALL) # 现在能跨行摸了
\d:只摸数字(digit)
python
html = "蛋炒粉15元"
re.findall(r'\d', html) # ['1', '5']
re.findall(r'\d+', html) # ['15'] (+号表示"连续摸多个")
人话 :\d 就像你戴了数字感应手套,只摸 0-9,字母和汉字自动跳过。
\w:摸字母、数字、下划线(word)
python
html = "price_20"
re.findall(r'\w+', html) # ['price_20']
人话 :\w 摸的是"单词字符"------字母、数字、下划线。中文它也能摸(看编码),但通常用来摸英文变量名、账号名。
\s:摸空白(space)
python
html = "蛋炒粉 15元"
re.findall(r'\s', html) # [' ', ' '] (两个空格)
人话 :\s 摸的是空白 ------空格、制表符(\t)、换行符(\n)都算。
\n 和 \t:精确摸
| 手势 | 摸什么 |
|---|---|
\n |
只摸换行符(回车) |
\t |
只摸制表符(Tab键) |
什么时候用? 你要把一段文本按行拆开,或者把表格数据按列拆开的时候。
三、定位手势:从哪开始摸,到哪结束
仓库很大,你不能乱摸,得知道从哪下手,到哪停手。
^:从门口开始摸(字符串开头)
python
text = "15元一份"
re.findall(r'^15', text) # ['15'] (开头是15,摸到了)
re.findall(r'^20', text) # [] (开头不是20,没摸到)
人话 :^ 就像你站在仓库门口,只摸最开头的东西。开头不对,直接走人。
$:摸到墙根(字符串结尾)
python
text = "一份15元"
re.findall(r'15元$', text) # ['15元'] (结尾是15元,摸到了)
人话 :$ 就像你摸到仓库最里面的墙根,只检查最后是什么。
组合技 :^$ 一起用,可以判断"这行是不是空的"。
python
re.findall(r'^\s*$', text) # 只摸空白行(全是空格或啥都没有)
四、反义手势:大写就是"不"
手套翻个面,大写 就是反着摸。
| 小写(摸这个) | 大写(不摸这个) |
|---|---|
\d(数字) |
\D(非数字) |
\w(单词字符) |
\W(非单词字符) |
\s(空白) |
\S(非空白) |
python
html = "价格15元"
# \D 摸到"价格"和"元"(非数字)
re.findall(r'\D+', html) # ['价格', '元']
# \S 摸到所有非空白的东西
re.findall(r'\S+', html) # ['价格15元']
人话:大写就是**"除了这个都摸"**。你记不住的时候,想想"大写=大佬,大佬不挑食"。
五、组合技:量词(摸几个?)
基础手势只能摸一个字符,但仓库里的货长度不一,你得告诉手套:摸几个?
| 量词 | 人话 | 示例 |
|---|---|---|
* |
摸0次或多次(可有可无) | \d* → 数字可能有,可能没有 |
+ |
摸1次或多次(至少有一个) | \d+ → 至少一个数字(最常用) |
? |
摸0次或1次(最多一个) | https? → http 或 https |
{n} |
摸恰好n次 | \d{3} → 恰好3个数字 |
{n,m} |
摸n到m次 | \d{2,4} → 2到4个数字 |
实战:摸手机号(11位数字)
python
phone = "我的电话是13812345678,备用13987654321"
re.findall(r'1\d{10}', phone)
# 1开头,后面再摸10个数字,正好11位
# ['13812345678', '13987654321']
六、分组与选择:把货装篮子,或者二选一
|(竖线):或者
python
html = "老王炒粉店和老李烧烤摊"
re.findall(r'老王|老李', html) # ['老王', '老李']
人话 :| 就是**"或者"**。摸老王也行,摸老李也行。
()(括号):分组装篮子
python
html = '<span class="price">15元</span>'
# 把数字单独装篮子里
re.findall(r'<span class="price">(\d+)元</span>', html)
# ['15'] (只返回括号里的东西)
人话 :括号就像篮子。你摸了一整串,但只把篮子里的东西拿出来。
多个篮子:
python
html = "2026-04-29"
re.findall(r'(\d{4})-(\d{2})-(\d{2})', html)
# [('2026', '04', '29')] (年、月、日分别装三个篮子)
七、自定义范围:我只摸这几种
[...]:字符集(白名单)
python
html = "abc123ABC"
# 只摸小写字母
re.findall(r'[a-z]+', html) # ['abc']
# 只摸数字和大写字母
re.findall(r'[0-9A-Z]+', html) # ['123ABC']
人话 :中括号里写**"允许摸的名单"**。[a-z] 就是小写字母全家桶,[0-9] 就是数字全家桶。
[^...]:排除集(黑名单)
python
html = "abc123"
# 除了小写字母,其他都摸
re.findall(r'[^a-z]+', html) # ['123']
人话 :中括号里加个 ^,就是**"禁止摸的名单"**。[^a-z] 就是"小写字母不准摸,其他的随便"。
八、Python里的坑(再提醒一遍)
| 坑 | 说明 | 解决办法 |
|---|---|---|
. 不匹配换行符 |
多行HTML,点号摸到\n就断 |
加 re.DOTALL |
| 贪婪匹配 | .* 会尽可能多摸,连不该摸的都摸了 |
加 ? 变非贪婪:.*? |
| 反斜杠灾难 | \d 在字符串里要先被Python解析一次 |
字符串前加 r,写成 r'\d+' |
贪婪 vs 非贪婪:
python
html = "<span>15元</span><span>20元</span>"
# 贪婪(.*):尽可能多摸,把中间全吞了
re.findall(r'<span>(.*)</span>', html) # ['15元</span><span>20元']
# 非贪婪(.*?):摸到第一个就停
re.findall(r'<span>(.*?)</span>', html) # ['15元', '20元']
人话 :贪婪就是"看到货全搂怀里",非贪婪就是"够一个就收手"。爬HTML的时候,99%的情况要用非贪婪 .*?。
九、终极口诀(背下来)
点号摸万物,换行摸不到(要加DOTALL)。
\d是数字,\w是单词,\s是空白,大写全反义。
^在门口,$在墙根,定位不迷路。
*可有可无,+至少一个,?最多一个,{n,m}控制次数。
|是或者,()是篮子,中括号是白名单,[^]是黑名单。贪婪搂到底,非贪婪加问号,爬网页用
.*?。
写在最后
下次再有人问你"正则怎么写",你就说:
"进黑仓库,戴摸金手套,做手势,掏东西。"
他要是问你"什么手势",你就把这篇口诀甩他脸上。
散会。