【爬虫系列】爬取糗事百科--正则表达式(超详细)

'# 【爬虫系列】爬取糗事百科--正则表达式(超详细)

一、背景与问题

在互联网数据采集领域,正则表达式(Regular Expression)始终是传统爬虫开发的基石。糗事百科作为国内知名的段子平台,其网页结构简洁但存在大量文本内容,为正则表达式提供了良好的应用场景。

通过正则表达式爬取糗事百科,我们需要解决以下核心问题:

  1. 解析动态生成的HTML内容
  2. 提取包含复杂结构的文本内容
  3. 处理分页请求和数据聚合
  4. 确保数据清洗的准确性

对于初学者而言,正则表达式是理解爬虫工作原理的绝佳切入点,但其在处理复杂网页结构时也存在明显局限性。本文将深入探讨正则表达式在爬虫中的应用场景、实现原理及实践技巧。

二、基本原理

正则表达式是一种强大的文本匹配工具,其核心原理基于状态机和字符集的组合。通过定义特定的模式,正则表达式可以高效地匹配、查找和替换文本内容。

在爬虫场景中,正则表达式的典型应用包括:

  1. 匹配HTML标签中的内容(如<div class="content">)
  2. 提取特定格式的文本(如时间戳[\d]{4}-[\d]{2}-[\d]{2})
  3. 处理复杂的文本结构(如多层嵌套的<p>标签)

1. 正则表达式匹配机制

正则表达式匹配分为普通匹配和捕获组两种模式:

  • 普通匹配:re.search() 直接返回匹配对象
  • 捕获组:re.findall() 返回所有匹配的子串

2. HTML解析的特殊性

HTML是不规范的标记语言,正则表达式处理HTML时需注意:

  • 混合标签结构(如<p><b>内容</b></p>)
  • 属性值的不确定性(如class="content")
  • 标签的嵌套关系

三、环境准备

import re
import requests
from urllib.parse import urljoin

确保安装requests库:

pip install requests

配置基础参数:

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}
base_url = 'https://www.qiushibaike.com/'

四、核心实现

1. 页面请求与响应处理

def fetch_page(url):
    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()
        return response.text
    except requests.RequestException as e:
        print(f"请求失败: {e}")
        return None

关键点:

  • 设置合理的超时时间(10秒)
  • 异常处理机制确保程序健壮性
  • 返回原始HTML文本用于正则匹配

2. 正则表达式提取糗事内容

def extract_qiushis(html):
    pattern = r'<div class="content">(.*?)</div>'
    matches = re.findall(pattern, html, re.DOTALL)
    return [re.sub(r'<.*?>', '', m).strip() for m in matches]

逐段解释:

  1. re.DOTALL 标志使.匹配换行符
  2. 捕获组()提取内容区域
  3. re.sub 去除HTML标签
  4. strip() 清除首尾空白

3. 分页处理与URL构造

def get_page_urls():
    urls = []
    for page in range(1, 6):  # 获取前5页数据
        url = urljoin(base_url, f'hot/page/{page}/')
        urls.append(url)
    return urls

注意事项:

  • 使用urljoin保证URL完整性
  • 避免请求过多导致被封IP
  • 设置合理的页面范围(建议1-10页)

五、完整案例

构建完整爬虫流程:

def main():
    all_qiushis = []
    
    for page_url in get_page_urls():
        html = fetch_page(page_url)
        if not html:
            continue
        content = extract_qiushis(html)
        all_qiushis.extend(content)
    
    # 保存结果
    with open('qiushis.txt', 'w', encoding='utf-8') as f:
        for q in all_qiushis:
            f.write(q + '\n')
    
    print(f"共提取{len(all_qiushis)}条糗事")

if __name__ == '__main__':
    main()

完整流程说明:

  1. 获取分页URL列表
  2. 逐页请求并提取内容
  3. 数据清洗和保存
  4. 基本异常处理机制

六、源码解析

1. 正则表达式优化技巧

# 更精确的正则表达式
pattern = r'<div class="content">([\s\S]*?)<div class="up"'

优化点:

  • 使用[\s\S]匹配所有字符(包含换行)
  • 添加up类作为结束标记
  • 避免过度贪婪匹配

2. HTML结构处理

# 处理带转义字符的文本
pattern = r'<div class="content">(.*?)</div>'
html = "<div class=\"content\">&lt;测试&gt;</div>"
print(re.search(pattern, html).group(1))  # 输出: &lt;测试&gt;

注意事项:

  • 转义字符需通过html.unescape()处理
  • 确保正则表达式不包含特殊字符

3. 性能优化方案

# 使用生成器提高内存效率
def extract_qiushis(html):
    pattern = r'<div class="content">(.*?)</div>'
    for match in re.finditer(pattern, html, re.DOTALL):
        yield re.sub(r'<.*?>', '', match.group(1)).strip()

优化策略:

  • 使用finditer逐个处理匹配项
  • 避免一次性处理大量数据
  • 使用生成器避免内存溢出

七、进阶使用

1. 处理复杂结构

# 匹配带属性的标签
pattern = r'<p class="content">(.*?)</p>'

2. 提取评论信息

# 匹配评论内容和点赞数
pattern = r'<div class="content">(.*?)</div><div class="stats">.*?点赞 (\d+)'

3. 处理特殊字符

# 清洗特殊字符
def clean_text(text):
    return re.sub(r'[^\u4e00-\u9fa5\s]', '', text)

八、性能与工程实践

1. 性能优化策略

优化策略说明
限制并发数使用concurrent.futures控制并发
增加缓存使用requests_cache缓存响应
优化正则使用re.compile预编译正则表达式

2. 异常处理规范

try:
    response = requests.get(url, headers=headers, timeout=10)
    response.raise_for_status()
except requests.Timeout:
    print("请求超时")
except requests.TooManyRedirects:
    print("重定向过多")
except requests.HTTPError as e:
    print(f"HTTP错误: {e}")

3. 安全注意事项

  1. 遵守robots.txt规则
  2. 设置合理的请求间隔(建议1秒)
  3. 使用代理IP池避免封禁
  4. 添加用户代理随机化

九、常见问题与踩坑

1. 常见错误示例

# 错误示例:未使用re.DOTALL导致匹配失败
pattern = r'<div class="content">(.*?)</div>'
html = "<div class="content">测试内容</div>\n<div class="content">第二条</div>"
print(re.findall(pattern, html))  # 输出: ['测试内容', '第二条']

2. 常见问题分析

问题原因解决方案
匹配失败未使用re.DOTALL添加re.DOTALL标志
内容不完整嵌套标签干扰使用更精确的结束标记
超时服务器响应慢增加超时时间或使用代理

3. 正则表达式陷阱

  • 贪婪匹配:.*? vs .*
  • 字符转义:[<] vs \<
  • 多线程安全:re.compile的线程安全问题

十、最佳实践

1. 正则表达式使用规范

  1. 避免过度使用:复杂结构建议使用BeautifulSoup
  2. 使用预编译:re.compile(pattern)提高性能
  3. 模块化正则:每个功能模块对应独立正则
  4. 日志记录:记录匹配结果和错误信息

2. 数据清洗规范

def clean_qiushi(text):
    text = re.sub(r'<.*?>', '', text)  # 去除标签
    text = re.sub(r'[\n\r\t]+', ' ', text)  # 替换空白符
    text = re.sub(r'[\u200b\u200e\u200f]', '', text)  # 去除隐藏字符
    return text.strip()

3. 爬虫策略建议

  • 频率控制:每2秒发送一次请求
  • 请求头模拟:设置合理的User-Agent
  • 异常重试:添加重试机制
  • 数据校验:验证提取结果的完整性

十一、总结

正则表达式作为爬虫开发的基础工具,其在糗事百科数据采集中的应用展示了其强大之处。通过深入理解正则表达式的匹配原理、优化技巧和使用规范,我们可以高效地完成文本提取任务。

然而,正则表达式并非万能工具。在处理复杂网页结构时,建议结合BeautifulSoup、XPath等更专业的解析工具。同时,需注意遵守网站规则,避免触发反爬机制。

在实际开发中,建议:

  1. 简单结构使用正则表达式
  2. 复杂结构使用解析库
  3. 动态内容使用Selenium或Playwright
  4. API接口优先调用官方接口

通过合理选择工具和技术,我们可以在保证效率和稳定性的前提下,完成高质量的数据采集工作。

none
最后修改于:2026年09月22日 07:11

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日