我把 Python re 模块比喻成摸金手套

9 阅读6分钟

上回我给你讲了正则的"摸金手势"(\. \d + ? 那些),但那是招式本身。今天教你怎么戴手套、怎么出招、怎么收工——也就是 Python 里的 re 模块。

记住:正则语法是武功招式,re 模块是你出拳的手。


一、re 模块是啥?就是手套本体

上次我们说正则表达式是"摸金手势",那 re 模块就是那双能做出手势的手套

没有手套,你空比划手势,啥也摸不着。有了手套,你做什么手势,仓库里就掏出什么东西。

先戴手套:

import re

就这一行,手套戴上了。


二、re.match():站在门口检查

match() 的意思是:站在仓库门口,只看最开头那一点。

开头对上了,就返回结果;开头对不上,直接 None,门都不让你进。

import re

text = "15元一份蛋炒粉"

# 站在门口检查:是不是数字开头?
result = re.match(r'\d+', text)

if result:
    print(f"摸到了:{result.group()}")  # 15
    print(f"从哪开始:{result.start()}")  # 0
    print(f"到哪结束:{result.end()}")    # 2
else:
    print("开头不对,滚")

人话match() 就像保安查身份证,只看门口第一人。队伍中间有坏人他不管,但门口这人必须对。

:很多人用 match() 搜中间的字符,搜不到就骂娘。记住——match 只认开头!


三、re.search():进仓库找第一个

search() 的意思是:进仓库扫荡,找到第一个匹配的就算交差。

不管这货在开头、中间还是结尾,只要第一个对上了,立马收工。

text = "蛋炒粉15元,肉炒粉20元"

# 进仓库找:第一个价格在哪?
result = re.search(r'\d+元', text)

if result:
    print(f"摸到:{result.group()}")   # 15元
    print(f"位置:{result.span()}")    # (3, 6)

人话search() 就像你进仓库喊"第一个带价格的货在哪?",找到就停,后面的不管。

对比记住:

match 是保安只看门,search 是巡逻找第一个。


四、re.findall():扫荡仓库,全掏出来

findall() 是爬虫最常用的,意思是:把仓库翻个底朝天,所有匹配的货全装麻袋里,一次性给你。

text = "蛋炒粉15元,肉炒粉20元,牛排999元"

# 全掏出来!
prices = re.findall(r'\d+元', text)
print(prices)  # ['15元', '20元', '999元']

带分组(篮子)的 findall

text = '<span class="price">15元</span>'

# 只掏篮子里的数字
numbers = re.findall(r'<span class="price">(\d+)元</span>', text)
print(numbers)  # ['15']

人话findall 就像你叫了一群小弟进仓库,见到就抢,抢完堆成列表给你。简单粗暴,爬虫最爱。


五、re.finditer():一个个给你,省内存

findall() 有个毛病:货太多的时候,它全塞列表里,内存爆炸

finditer() 更聪明:一个个给你,你要一个我掏一个,不要的不占地方。

text = "蛋炒粉15元,肉炒粉20元,牛排999元"

# 迭代器模式,一个个摸
for match in re.finditer(r'\d+元', text):
    print(f"摸到:{match.group()},位置:{match.span()}")

什么时候用? 你要处理几万条数据的时候,用 finditerfindall 省内存。

人话findall 是"全倒地上你自己挑",finditer 是"你要一个我递一个"。


六、re.sub():调包,以假换真

sub() 的意思是:把仓库里某种货,全部换成另一种。

比如老板把"15元"的标签全撕了,换成"涨价后25元"。

text = "蛋炒粉15元,肉炒粉20元"

# 把所有数字+元,替换成【已售罄】
new_text = re.sub(r'\d+元', '【已售罄】', text)
print(new_text)  # 蛋炒粉【已售罄】,肉炒粉【已售罄】

高级玩法:用函数动态替换

def double_price(match):
    price = int(match.group().replace('元', ''))
    return f'{price * 2}元'

text = "蛋炒粉15元,肉炒粉20元"

# 价格全部翻倍
new_text = re.sub(r'\d+元', double_price, text)
print(new_text)  # 蛋炒粉30元,肉炒粉40元

人话sub 就是调包计。你告诉我什么 pattern,我见到就换,连换几次都能计数。


七、re.split():按标记切蛋糕

split() 的意思是:按某种标记,把一长串东西切成几段。

text = "蛋炒粉15元|肉炒粉20元|牛排999元"

# 按 | 切开
items = re.split(r'\|', text)
print(items)  # ['蛋炒粉15元', '肉炒粉20元', '牛排999元']

用正则切更猛:

text = "蛋炒粉15元,肉炒粉20元;牛排999元、饮料5元"

# 按各种分隔符切(逗号、分号、顿号)
items = re.split(r'[,;、]', text)
print(items)

人话split 就是切蛋糕。你告诉我从哪下刀,我切成几块给你。


八、re.compile():定制手套,预制招式

如果你要反复用同一个手势去摸不同的仓库,每次都重写一遍 pattern 很蠢。

compile() 就是把摸金手套按你的手势定制好,以后直接戴上去摸,不用每次都重新比划。

# 定制一副"掏价格"专用手套
price_pattern = re.compile(r'<span class="price">(\d+)元</span>')

# 现在去不同的仓库,直接戴这副手套
html1 = '<span class="price">15元</span>'
html2 = '<span class="price">20元</span>'
html3 = '<span class="price">999元</span>'

print(price_pattern.findall(html1))  # ['15']
print(price_pattern.findall(html2))  # ['20']
print(price_pattern.findall(html3))  # ['999']

好处:

  • 速度快:编译一次,多次使用,省时间
  • 可复用:像函数一样,到处调用
  • 可配置:编译时带上标志位(见下文)

人话compile 就是预制菜。炒一次料,以后每次直接下锅。


九、标志位:手套的特殊模式

re 模块有几个开关,就像手套上的夜视仪、防滑垫、静音模式

标志位缩写人话什么时候开
re.IGNORECASEre.I不区分大小写摸"Price"还是"price"都行
re.DOTALLre.S点号能摸换行符HTML跨行,必须开
re.MULTILINEre.M^$能匹配每行开头结尾处理多行文本
re.VERBOSEre.X允许正则里写注释和换行写复杂正则时,给自己留备注

实战组合:

html = """<span class="price">
15元
</span>"""

# 点号跨行摸 + 不区分大小写
pattern = re.compile(r'<span.*?>(.*?)</span>', re.S | re.I)

result = pattern.search(html)
print(result.group(1).strip())  # 15元

注意:多个标志位用 | 组合,就像同时开夜视+静音。


十、实战:一套完整的爬虫抓取流程

把今天学的串起来,写一个掏价格+菜名的完整脚本:

import re

# 老板扔过来的菜单(HTML)
html = """
<div class="menu">
    <div class="item">
        <h3>蛋炒粉</h3>
        <span class="price">15元</span>
    </div>
    <div class="item">
        <h3>肉炒粉</h3>
        <span class="price">20元</span>
    </div>
    <div class="item">
        <h3>惠灵顿牛排</h3>
        <span class="price">999元</span>
    </div>
</div>
"""

# Step 1:定制手套(编译正则,开跨行模式)
pattern = re.compile(
    r'<h3>(.*?)</h3>.*?<span class="price">(\d+)元</span>',
    re.S
)

# Step 2:进仓库扫荡(finditer省内存)
for match in pattern.finditer(html):
    name = match.group(1).strip()
    price = match.group(2)
    print(f"菜名:{name},价格:{price}元")

# 输出:
# 菜名:蛋炒粉,价格:15元
# 菜名:肉炒粉,价格:20元
# 菜名:惠灵顿牛排,价格:999元

十一、终极口诀(背下来)

match看门口,search找第一个,findall全掏空,finditer省内存。

sub搞调包,split切蛋糕,compile预制手套,标志位开外挂。

re.S跨行摸,re.I不区分大小写,复杂正则先编译,爬虫效率翻几倍。


写在最后

下次再有人问你"Python正则怎么用",你就说:

"先import re戴手套,然后compile预制招式,最后findallfinditer往死里掏。"

他要是问你"matchsearch啥区别",你就说:

"match是保安只看门,search是巡逻找第一个,爬虫一般用findall全抢光。"

散会。