← 返回网络爬虫目录
← 返回学习笔记首页
正则表达式在爬虫中的应用
网络爬虫专题 · 掌握正则表达式提取数据
专题: Python网络爬虫系统学习
关键词: Python, 网络爬虫, 正则表达式, re模块, findall, 匹配模式, 数据提取, 文本清洗
一、正则表达式概述
1.1 什么是正则表达式
正则表达式(Regular Expression,简称 regex 或 RE)是一种用于描述字符串匹配模式的表达式。它使用单个字符串来描述和匹配一系列符合某个句法规则的字符串。正则表达式最初由数学家在 20 世纪 50 年代提出,随后被引入到 Unix 工具(如 grep、sed、awk)中,如今几乎所有主流编程语言都内置了正则表达式的支持。
正则表达式的核心价值在于:它提供了一种高度简洁和强大的方式来处理文本。对于爬虫开发人员来说,正则表达式是必不可少的工具——无论使用 Requests 直接获取 HTML 内容后手动解析,还是使用 BeautifulSoup 等高级解析库,正则都在数据清洗和精确提取环节发挥着关键作用。
1.2 爬虫中正则的三大用途
第一,提取特定格式数据。 网页中往往包含大量结构化数据:URL 链接、邮箱地址、电话号码、日期时间、价格信息等。这些数据遵循固定的格式模式,非常适合用正则表达式直接抓取。
第二,文本清洗。 从网页抓取的原始内容通常包含大量噪声:HTML 标签、JavaScript 代码、CSS 样式、多余的空白字符等。正则表达式可以高效地移除这些干扰项,保留有价值的文本内容。
第三,数据验证。 在爬虫采集到数据后,往往需要验证数据的格式是否符合预期。例如验证邮箱格式是否正确、手机号是否合法、URL 是否完整等。正则表达式提供了标准的验证手段,避免无效数据进入后续处理流程。
1.3 Python re 模块简介
Python 的 re 模块是标准库中用于正则表达式操作的核心模块,无需额外安装即可使用。re 模块提供了丰富的函数接口,支持从简单的字符串匹配到复杂的模式搜索和替换。它使用 NFA(非确定型有限自动机)算法实现,功能强大且性能优秀。使用时只需在代码开头执行 import re 即可引入该模块。
import re # 导入正则模块,Python 内置,无需 pip 安装
re 模块的设计遵循 "先编译、后使用" 和 "直接使用" 两种模式。在需要多次使用同一正则表达式的场景下,建议先编译以提高性能,后面会详细讨论。
二、re 模块核心函数
2.1 re.match() — 从字符串开头匹配
re.match() 尝试从字符串的起始位置匹配一个模式。如果起始位置匹配成功,则返回一个 Match 对象;如果起始位置不匹配,则返回 None。注意:match 只检查字符串的开头,即使模式在其他位置也能匹配,只要开头不匹配就返回空。
import re
result = re.match(r'\d+', '2026年是蛇年')
if result:
print(result.group()) # 输出:2026
# match 从头匹配,以下匹配失败
result2 = re.match(r'蛇年', '2026年是蛇年') # 返回 None
2.2 re.search() — 搜索第一个匹配
re.search() 扫描整个字符串并返回第一个成功的匹配。与 match 不同,search 不限制从开头匹配,它会在字符串中任意位置查找第一个符合模式的子串。找到后立即返回 Match 对象,不再继续向后搜索。
import re
text = '联系方式:13800138000,备用:13912345678'
phone = re.search(r'1[3-9]\d{9}', text)
if phone:
print(phone.group()) # 输出:13800138000(只返回第一个匹配)
# 要获取所有匹配,需使用 findall 或 finditer
2.3 re.findall() — 查找所有匹配(返回列表)
re.findall() 是爬虫开发中使用频率最高的函数。它会搜索整个字符串,以列表形式返回所有不重叠的匹配结果。如果模式中包含分组,则返回元组组成的列表;如果包含多个分组,每个匹配返回一个元组。
import re
html = '价格:¥29.9,原价:¥99.0,折扣价:¥19.9'
prices = re.findall(r'¥(\d+\.?\d*)', html)
print(prices) # 输出:['29.9', '99.0', '19.9']
# 多分组情况:提取标签名和属性
tags = re.findall(r'<(\w+)([^>]*)>', '
')
print(tags) # 输出:[('div', ' class="main"'), ('a', ' href="/"')]
2.4 re.finditer() — 查找所有匹配(返回迭代器)
re.finditer() 与 findall 功能类似,但它返回一个迭代器,生成 Match 对象而非字符串。当匹配结果非常多时,迭代器模式可以节省内存,因为不需要一次性将所有结果加载到内存中。同时,通过 Match 对象可以获取分组详情、匹配位置等信息。
import re
text = '苹果 12元,香蕉 5元,橘子 8元'
for match in re.finditer(r'(\w+)\s+(\d+)元', text):
print(f'商品:{match.group(1)},价格:{match.group(2)}元')
# 输出:
# 商品:苹果,价格:12元
# 商品:香蕉,价格:5元
# 商品:橘子,价格:8元
2.5 re.sub() — 替换匹配的文本
re.sub() 用于替换字符串中所有匹配正则表达式的子串。它接收三个主要参数:模式、替换字符串、原始字符串。替换字符串中可以使用 \1、\2 等反向引用,也可使用命名分组 \g<name>。该函数在爬虫数据清洗阶段非常实用。
import re
# 去除 HTML 标签
html = '<p>这是一段<strong>重要</strong>文本</p>'
clean_text = re.sub(r'<[^>]+>', '', html)
print(clean_text) # 输出:这是一段重要文本
# 格式化手机号:138 0013 8000
phone = '13800138000'
formatted = re.sub(r'(\d{3})(\d{4})(\d{4})', r'\1 \2 \3', phone)
print(formatted) # 输出:138 0013 8000
2.6 re.split() — 按模式分割字符串
re.split() 根据正则表达式的匹配结果来分割字符串,返回分割后的列表。相比字符串自带的 split() 方法,re.split() 可以按更复杂的模式进行分割,例如按多个标点符号或按空白字符分割。
import re
# 按标点符号和空白分割
text = 'Python,Java;JavaScript C++ Rust'
langs = re.split(r'[,;.\s]+', text)
print(langs) # 输出:['Python', 'Java', 'JavaScript', 'C++', 'Rust']
# 使用分组保留分隔符
text2 = '第1章 基础 第2章 进阶 第3章 高级'
parts = re.split(r'(第\d章)', text2)
print(parts) # 输出:['', '第1章', ' 基础 ', '第2章', ' 进阶 ', '第3章', ' 高级']
2.7 re.compile() — 编译正则,提高性能
re.compile() 将正则表达式的字符串形式编译成一个 Pattern 对象。编译后的 Pattern 对象拥有 match()、search()、findall() 等相同方法。当同一个正则表达式需要被多次使用时,预编译可以显著提升性能,因为正则的编译过程只需执行一次。此外,编译时还可以指定 flags 参数(如 re.IGNORECASE 忽略大小写)。
import re
# 编译正则表达式(推荐在循环或多次使用时采用)
pattern = re.compile(r'https?://[^\s\'"<>]+')
urls = pattern.findall(long_html_text)
# 编译时指定 flag:忽略大小写
pattern_ci = re.compile(r'python', re.IGNORECASE)
print(pattern_ci.findall('Python python PYTHON'))
# 输出:['Python', 'python', 'PYTHON']
三、正则语法基础
3.1 元字符速查表
元字符是正则表达式中具有特殊含义的字符。掌握它们是使用正则表达式的第一步。以下是 Python re 模块中常用的元字符:
元字符 含义 示例
. 匹配除换行符以外的任意单个字符 a.b 匹配 "acb"、"a b" 但不匹配 "ab"
^ 匹配字符串的开头 ^Python 匹配以 Python 开头的字符串
$ 匹配字符串的结尾 com$ 匹配以 com 结尾的字符串
* 匹配前一个字符 0 次或多次 ab*c 匹配 "ac"、"abc"、"abbc" 等
+ 匹配前一个字符 1 次或多次 ab+c 匹配 "abc"、"abbc" 但不匹配 "ac"
? 匹配前一个字符 0 次或 1 次 ab?c 匹配 "ac"、"abc" 但不匹配 "abbc"
{n} 匹配前一个字符恰好 n 次 \d{4} 匹配 4 位数字,如 "2026"
{n,} 匹配前一个字符至少 n 次 \d{3,} 匹配至少 3 位数字
{n,m} 匹配前一个字符 n 到 m 次 \d{2,4} 匹配 2 到 4 位数字
\ 转义字符 \. 匹配点号本身,\\匹配反斜杠
| 或操作 cat|dog 匹配 "cat" 或 "dog"
(...) 分组捕获 (\d{3})-(\d{4}) 捕获两组数字
[...] 字符集合 [aeiou] 匹配任意一个元音字母
[^...] 否定字符集合 [^0-9] 匹配任意非数字字符
3.2 字符类详解
字符类用于表示一类字符的集合,是正则中非常实用的特性。Python re 模块提供了多种快捷字符类:
\d :匹配任意数字,等价于 [0-9]
\D :匹配任意非数字字符,等价于 [^0-9]
\w :匹配单词字符(字母、数字、下划线),等价于 [a-zA-Z0-9_]
\W :匹配非单词字符,等价于 [^a-zA-Z0-9_]
\s :匹配空白字符(空格、制表符、换行符等),等价于 [ \t\n\r\f\v]
\S :匹配非空白字符,等价于 [^ \t\n\r\f\v]
[abc] :匹配 a、b、c 中的任意一个
[a-z] :匹配任意小写英文字母
[^abc] :匹配除 a、b、c 之外的任意字符
在爬虫开发中,最常用的字符类是 \d(提取数字)、\s(处理空白)以及自定义字符类如 [一-鿿](提取中文字符)。
3.3 量词与贪婪匹配
量词用于指定前一个字符或分组出现的次数。Python re 模块默认使用贪婪匹配模式,即量词会尽可能多地匹配字符。例如,模式 <.+> 在字符串 "<div>内容</div>" 中会匹配整个 "<div>内容</div>",而不是只匹配 "<div>"。
在量词后面加上问号即可切换为懒惰匹配模式(非贪婪模式),使量词尽可能少地匹配字符:
import re
text = '<div>内容1</div><div>内容2</div>'
# 贪婪模式:匹配尽可能多的字符
greedy = re.findall(r'<div>(.*)</div>', text)
print(greedy) # 输出:['内容1</div><div>内容2']
# 懒惰模式:匹配尽可能少的字符
lazy = re.findall(r'<div>(.*?)</div>', text)
print(lazy) # 输出:['内容1', '内容2'](这才是我们想要的)
常见的懒惰量词包括 *?(0 次或多次,尽可能少)、+?(1 次或多次,尽可能少)、??(0 次或 1 次,尽可能少)、{n,m}?(n 到 m 次,尽可能少)。在爬虫中提取 HTML 内容时,几乎总是使用懒惰匹配来避免过度匹配。
3.4 分组与捕获
分组使用圆括号 () 将正则表达式的一部分括起来,实现多个维度的功能:
捕获分组 (pattern) :将匹配的内容捕获到内存中,可以通过 group() 方法按序号访问
命名分组 (?P<name>pattern) :为分组指定名称,通过 group('name') 访问,增强可读性
非捕获分组 (?:pattern) :仅用于分组而不捕获内容,不占用分组编号
import re
url = 'https://example.com/articles/12345?page=2'
# 命名分组示例
pattern = re.compile(r'https?://(?P<domain>[^/]+)/(?P<path>\S+)')
match = pattern.search(url)
if match:
print(match.group('domain')) # 输出:example.com
print(match.group('path')) # 输出:articles/12345?page=2
四、爬虫常用正则模式
以下是网络爬虫开发中使用频率最高的正则表达式模式集合,熟练掌握这些模式可以大幅提高数据提取效率。
4.1 提取 URL 链接
网页中 URL 的格式较为固定,通常以 http:// 或 https:// 开头,不包含空白字符和引号。以下模式可以匹配绝大多数 URL:
import re
html = '<a href="https://example.com/page?q=1">链接1</a>'
url_pattern = re.compile(r'https?://[^\s\'"<>]+')
urls = url_pattern.findall(html)
print(urls) # 输出:['https://example.com/page?q=1']
4.2 提取邮箱地址
邮箱地址的格式为 用户名@域名.后缀。用户名部分允许字母、数字、点号、下划线、百分号、加号和减号:
import re
text = '请联系 support@example.com 或 admin@test.org.cn'
email_pattern = re.compile(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}')
emails = email_pattern.findall(text)
print(emails) # 输出:['support@example.com', 'admin@test.org.cn']
4.3 提取手机号码
中国大陆手机号以 1 开头,第二位为 3-9 中的任意数字,后面紧跟 9 位数字:
import re
text = '联系电话:13812345678,备用:15987654321'
phone_pattern = re.compile(r'1[3-9]\d{9}')
phones = phone_pattern.findall(text)
print(phones) # 输出:['13812345678', '15987654321']
4.4 提取数字(整数和小数)
在爬取商品信息、价格数据等场景中,提取数字是最常见的需求之一:
import re
text = '价格¥29.90,销量 1586 件,评分 4.8 分'
num_pattern = re.compile(r'\d+\.?\d*')
numbers = num_pattern.findall(text)
print(numbers) # 输出:['29.90', '1586', '4.8']
4.5 提取中文字符
在处理中文网页时,常常需要单独提取汉字部分以过滤掉英文、数字和标点:
import re
text = 'Python爬虫2026教程——从入门到精通'
chinese = re.findall(r'[一-鿿]+', text)
print(chinese) # 输出:['爬虫', '教程', '从入门到精通']
4.6 提取 HTML 标签内容
在没有使用 BeautifulSoup 的情况下,可以直接用正则从 HTML 中提取特定标签内的文本。注意使用懒惰匹配以防止跨标签捕获:
import re
html = '<h1>标题内容</h1><p>段落文本</p><p>另一段</p>'
content_pattern = re.compile(r'<h1>(.*?)</h1>')
headers = content_pattern.findall(html)
print(headers) # 输出:['标题内容']
# 提取所有 <p> 标签内容
p_pattern = re.compile(r'<p>(.*?)</p>')
paragraphs = p_pattern.findall(html)
print(paragraphs) # 输出:['段落文本', '另一段']
4.7 提取 JSON 中的指定字段值
当爬虫获取到 JSON 格式的响应数据时(许多网站 API 返回 JSON),可以使用正则快速提取特定字段的值,避免完整解析 JSON 的开销:
import re
json_data = '{"name": "张三", "email": "zhangsan@example.com", "age": 28}'
# 提取 name 字段的值
name = re.search(r'"name"\s*:\s*"([^"]+)"', json_data)
if name:
print(name.group(1)) # 输出:张三
五、正则与 BeautifulSoup 结合
5.1 在 find_all() 中使用正则
BeautifulSoup 的 find_all() 方法允许传入正则表达式作为参数。当需要查找所有符合特定模式 class 属性或 id 属性的标签时,这种方式非常高效:
import re
from bs4 import BeautifulSoup
html = '''
<div class="product-price">¥29.99</div>
<div class="product-name">商品名称</div>
<div class="product-price sale">¥19.99</div>
'''
soup = BeautifulSoup(html, 'html.parser')
# 查找所有 class 中包含 "price" 的标签
price_tags = soup.find_all(class_=re.compile(r'price'))
for tag in price_tags:
print(tag.text)
# 输出:
# ¥29.99
# ¥19.99
# 查找所有 id 以 "product" 开头的标签
product_tags = soup.find_all(id=re.compile(r'^product'))
5.2 在 text 参数中使用正则
BeautifulSoup 的 text 参数(在最新版本中更名为 string)同样支持正则表达式,用于按文本内容匹配标签:
import re
from bs4 import BeautifulSoup
html = '''
<ul>
<li>苹果 10元</li>
<li>香蕉 5元</li>
<li>葡萄 15元</li>
<li>测试</li>
</ul>
'''
soup = BeautifulSoup(html, 'html.parser')
# 查找所有文本中包含数字和"元"的 li 标签
items = soup.find_all('li', text=re.compile(r'\d+元'))
for item in items:
print(item.text)
# 输出:
# 苹果 10元
# 香蕉 5元
# 葡萄 15元
5.3 在 CSS 选择器中使用正则
BeautifulSoup 的 select() 方法虽然不支持直接在 CSS 选择器中使用正则,但可以结合 has-class 检测或其他属性匹配技巧来变通实现。此外,通过 select() 获取标签集合后,再配合正则进行二次筛选是一种常见的组合用法:
import re
from bs4 import BeautifulSoup
html = '''
<div class="article-2026">2026年文章</div>
<div class="article-2025">2025年文章</div>
<div class="note">笔记</div>
'''
soup = BeautifulSoup(html, 'html.parser')
# 用 select 获取所有 div,再用正则筛选
all_divs = soup.select('div')
year_divs = [div for div in all_divs
if re.search(r'article-\d{4}', div.get('class', ''))]
for div in year_divs:
print(div.text)
# 输出:
# 2026年文章
# 2025年文章
六、实战示例
6.1 提取页面所有图片 URL
爬取网页中的图片 URL 是最常见的任务之一。图片链接通常出现在 <img> 标签的 src 属性中,也可能在 data-src 或 data-original 等延迟加载属性中:
import re
import requests
# 获取页面内容(示例用静态字符串替代)
html = '''
<img src="https://example.com/images/photo1.jpg" alt="照片1">
<img data-src="https://example.com/images/photo2.jpg" class="lazy">
<img src="https://example.com/icons/icon.png">
'''
# 提取所有 src 或 data-src 属性中的图片 URL
img_pattern = re.compile(r'(?:src|data-src)=["\']([^"\']+\.(?:jpg|png|gif|webp))["\']', re.IGNORECASE)
img_urls = img_pattern.findall(html)
for url in img_urls:
print(url)
# 输出:
# https://example.com/images/photo1.jpg
# https://example.com/images/photo2.jpg
# https://example.com/icons/icon.png
6.2 提取文章发布时间
新闻网站和博客通常会以不同格式在页面中标注发布时间。以下模式可以匹配常见的日期时间格式:
import re
html = '''
<span class="date">2026-05-01</span>
<time datetime="2026-05-01T10:30:00+08:00">2026年5月1日</time>
<div class="publish">2026/05/01 10:30</div>
'''
# 匹配多种日期格式
date_pattern = re.compile(
r'\d{4}[-/年]\d{1,2}[-/月]\d{1,2}'
)
dates = date_pattern.findall(html)
print(dates) # 输出:['2026-05', '2026-05', '2026/05/01']
# 更精确的模式:匹配完整日期
full_date_pattern = re.compile(
r'\d{4}[-/年]\d{1,2}[-/月]\d{1,2}[日]?\s*\d{1,2}:\d{2}'
)
6.3 提取价格和数字
电商爬虫中提取价格信息是核心需求。价格常常带有货币符号,可能包含千分位分隔符:
import re
html = '''
<span class="price">¥1,299.00</span>
<span class="old-price">¥1,599.00</span>
<span class="discount">-18%</span>
'''
# 提取所有带 ¥ 符号的价格
price_pattern = re.compile(r'¥([\d,]+\.\d{2})')
prices = price_pattern.findall(html)
print(prices) # 输出:['1,299.00', '1,599.00']
# 去除千分位逗号并转为浮点数
clean_prices = [float(p.replace(',', '')) for p in prices]
print(clean_prices) # 输出:[1299.0, 1599.0]
6.4 清洗 HTML 文本
从网页抓取的文本经常混杂着 HTML 标签、多余的空白字符、JavaScript 代码等。使用正则表达式可以高效地完成一系列清洗操作:
import re
def clean_html_text(html):
"""清洗 HTML 文本,返回纯文本"""
# 1. 移除 JavaScript 代码块
text = re.sub(r'<script[^>]*>.*?</script>', '', html, flags=re.DOTALL | re.IGNORECASE)
# 2. 移除 CSS 样式块
text = re.sub(r'<style[^>]*>.*?</style>', '', text, flags=re.DOTALL | re.IGNORECASE)
# 3. 移除 HTML 标签
text = re.sub(r'<[^>]+>', '', text)
# 4. 将 HTML 实体转换为对应字符
text = re.sub(r'&', '&', text)
text = re.sub(r'<', '<', text)
text = re.sub(r'>', '>', text)
text = re.sub(r' ', ' ', text)
text = re.sub(r'"', '"', text)
# 5. 将连续空白(包括换行)压缩为单个空格
text = re.sub(r'\s+', ' ', text)
# 6. 去除首尾空白
return text.strip()
# 测试
dirty_html = '''
<div>
<h1>标题</h1>
<p>这是一段<strong>文本</strong>。</p>
<script>alert('hello')</script>
</div>
'''
clean = clean_html_text(dirty_html)
print(clean) # 输出:标题 这是一段文本。
七、正则性能优化
7.1 预编译正则表达式
在爬虫中,同一个正则表达式可能需要对成千上万个页面重复使用。此时预编译(re.compile)可以带来显著的性能提升。编译后的 Pattern 对象内部已经将正则字符串解析为有限状态机,后续每次调用都省去了编译步骤:
import re
# 不推荐:每次循环都重新编译
def extract_urls_slow(html_list):
url_list = []
for html in html_list:
urls = re.findall(r'https?://[^\s\'"<>]+', html)
url_list.extend(urls)
return url_list
# 推荐:预编译一次,多次使用
URL_PATTERN = re.compile(r'https?://[^\s\'"<>]+')
def extract_urls_fast(html_list):
url_list = []
for html in html_list:
urls = URL_PATTERN.findall(html)
url_list.extend(urls)
return url_list
# 在大型爬虫项目中,预编译可节省 30%-50% 的正则执行时间
7.2 避免回溯灾难
正则引擎在匹配失败时会进行回溯——尝试所有可能的路径。某些模式会导致指数级的回溯次数,严重时甚至导致程序挂起(称为"灾难性回溯"或 Catastrophic Backtracking)。
常见的危险模式:嵌套量词如 (a+)+b、使用贪婪量词匹配可选内容、过多的分支结构。以下是一些优化建议:
使用原子组(atomic group) :在某些正则引擎中,原子组 (?>...) 可以防止回溯。虽然 Python re 模块不直接支持原子组,但可以使用 possessive quantifier 语法或重新设计模式来避免回溯。
具体化字符类 :使用 [a-z] 而不是 .* 来限定匹配范围,减少引擎需要尝试的可能性。
避免嵌套量词 :如 (.*)* 或 (.+)+ 这种模式在高并发爬虫中极其危险,务必避免。
使用懒惰量词 :在合适场景下使用 .*? 替代 .*,可以有效减少回溯次数。
设置超时 :对于不可控的输入,可以考虑使用信号量或第三方库来限制正则匹配的执行时间。
# 危险:灾难性回溯模式
# 如果输入较长且不匹配,以下模式可能导致 CPU 100%
dangerous = re.compile(r'(\d+)+!')
# 安全:明确指定匹配范围
safe = re.compile(r'\d+!')
# 另一个危险模式:嵌套贪婪
dangerous2 = re.compile(r'<div>(.*)*</div>')
# 安全方案
safe2 = re.compile(r'<div>(.*?)</div>')
7.3 使用原始字符串
Python 中定义正则表达式时务必使用原始字符串(在字符串前加 r 前缀),如 r'\d+'。原始字符串会取消反斜杠的转义处理,避免正则表达式中的转义字符被 Python 字符串解析器先行处理:
# 不推荐:使用普通字符串
pattern1 = re.compile('\\d+\\.\\d+') # 需要双重转义,容易出错
# 推荐:使用原始字符串(r 前缀)
pattern2 = re.compile(r'\d+\.\d+') # 清晰直观
# 文件名示例
# 不写 r 前缀时,\s 会被 Python 解释为转义序列
# 写了 r 前缀后,\s 原样传递给 re 模块
# 匹配反斜杠本身
# 普通字符串需要 4 个反斜杠
bad = re.compile('\\\\\\\\')
# 原始字符串只需 2 个
good = re.compile(r'\\\\')
7.4 合理选择正则 vs 其他方法
虽然正则表达式功能强大,但并非所有场景都适合使用。在实际的爬虫项目中,应根据具体情况选择最合适的工具:
处理 HTML/XML :优先使用 BeautifulSoup、lxml 等专用解析器。正则表达式难以处理嵌套标签和复杂的 HTML 结构。
处理 JSON :使用 json 模块而非正则解析 JSON 数据。
简单字符串操作 :字符串自带的 find()、split()、replace() 方法通常比正则更快。
模式匹配和数据提取 :当需要提取 URL、邮箱、电话等格式固定的数据时,正则表达式是不可替代的最佳选择。
数据清洗 :正则表达式在文本清洗中表现出色,尤其是去除 HTML 标签、压缩空白、替换特定模式等场景。
总结来说,正则表达式的定位是"精确的瑞士军刀"——处理精确格式的数据时无出其右者,但不适合作为解析复杂文档结构的重型工具。在爬虫项目中,推荐采用"解析库 + 正则"的组合策略:使用 BeautifulSoup 或 lxml 解析文档结构,使用正则提取结构内部的格式化数据。
核心要点总结:
1. 正则表达式是爬虫工程师必须掌握的核心技能,在数据提取、清洗、验证三个环节中都有应用。
2. re 模块的 findall 是爬虫中最常用的函数,search 用于首次匹配,sub 用于替换清洗。
3. 懒惰匹配 .*? 在爬虫场景中远优于贪婪匹配 .*,能有效避免跨标签匹配。
4. 爬虫中最常用的模式:URL 提取、邮箱/手机号提取、数字与价格提取、中文字符提取。
5. 正则与 BeautifulSoup 结合使用可以发挥各自优势——Soup 负责结构解析,正则负责精确提取。
6. 性能优化三原则:预编译避免重复编译、使用原始字符串避免转义混淆、避免嵌套量词防止回溯灾难。
7. 不滥用正则——处理 HTML 结构应优先使用专用解析器,正则只适合提取特定格式的文本数据。