从URL构造到字段提取的正则优化 —— 豆瓣影评的实践记录

69 阅读3分钟

爬虫代理

一、起点:一次“看似简单”的小任务

去年,我在做一个小实验:想抓取几部经典电影的豆瓣影评,看看不同年份观众的评价差异。刚开始觉得没什么难度,不就是循环翻页、解析HTML吗?

结果一上手,才发现里面暗藏玄机。

  • 链接结构各式各样\ 豆瓣影评的入口并不统一。既有批量翻页的地址:
https://movie.douban.com/subject/1292052/reviews?start=0

也有单独一篇影评的地址:

https://movie.douban.com/review/1234567/

表面相似,实则需要不同的解析方式。

  • 页面字段也不老实\ 评论的时间格式并不固定,有时是完整的日期加时间,有时就剩个年月日。作者昵称的定位也多变,有时在 <font style="color:rgb(0, 0, 0);"><a></font> 标签里,有时却嵌在别的节点中。最初写的字符串截取法,几乎每次都要改。

那几天,我的脚本几乎是“跑一次,改一次”。

二、摸索:问题到底卡在哪里

我后来重新梳理:真正困扰的核心是两个点。

  • URL 怎么分辨:翻页 URL 和单条 URL 的模式不一样,如果不做区分,逻辑根本跑不通。
  • 字段怎么抽取:评论时间和作者信息没有统一格式,写死的解析规则肯定不稳。

于是我开始往正则表达式的方向想:能不能写一套更“宽容”的模式,把这些变动都涵盖进去?

三、转机:用正则抽象出“通用规则”

经过几番尝试,慢慢找到了一些规律。

  • 翻页 URL:<font style="color:rgb(0, 0, 0);">reviews?start=(\d+)</font>
  • 单条影评 URL:<font style="color:rgb(0, 0, 0);">review/(\d+)/</font>
  • 评论时间:
(\d{4}[-/]\d{1,2}[-/]\d{1,2}(?:\s+\d{1,2}:\d{2}:\d{2})?)

既能匹配“2023-08-27”,也能识别“2023-08-27 12:30:00”。

  • 作者昵称:
<a href="/people/[^"]+/">([^<]+)</a>

一旦抽象成这些模式,后续就很顺畅了:URL 能自动识别类型,评论时间和作者也能统一提取。

四、代码实践:带代理的豆瓣采集

下面是简化版的示例代码,加入了爬虫代理配置,提高采集成功率:

import re
import requests

# 代理设置(亿牛云示例 www.16yun.cn)
proxy_host = "proxy.16yun.cn"
proxy_port = "31000"
proxy_user = "16YUN"
proxy_pass = "16IP"

proxies = {
    "http": f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}",
    "https": f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"
}

# 示例:豆瓣影评第一页
url = "https://movie.douban.com/subject/1292052/reviews?start=0"
resp = requests.get(url, proxies=proxies, timeout=10)
html = resp.text

# 解析翻页参数
page_pattern = re.compile(r"reviews\?start=(\d+)")
page = page_pattern.search(url)
if page:
    print("当前页码:", page.group(1))

# 提取影评 ID
review_pattern = re.compile(r"https://movie.douban.com/review/(\d+)/")
review_ids = review_pattern.findall(html)
print("影评ID示例:", review_ids[:5])

# 提取评论时间
time_pattern = re.compile(r"(\d{4}[-/]\d{1,2}[-/]\d{1,2}(?:\s+\d{1,2}:\d{2}:\d{2})?)")
times = time_pattern.findall(html)
print("评论时间:", times[:5])

# 提取作者昵称
author_pattern = re.compile(r'<a href="/people/[^"]+/">([^<]+)</a>')
authors = author_pattern.findall(html)
print("作者示例:", authors[:5])

五、回头看:最大的收获

这次经历让我有些感触:

  • 别依赖写死逻辑,哪怕今天能跑通,明天页面改动就全废。
  • 正则不是万能钥匙,但能兜底,尤其是在字段格式多变时。
  • 代理要跟上,特别是像豆瓣这种会有限流的网站。

过去我写爬虫时,总是先考虑“跑通”,很少想“如果格式变了怎么办”。这次之后,我更习惯去总结模式,再去写代码。

一句话总结:采集豆瓣影评的过程,其实是一堂“模式化思维”的课。链接和字段表面上杂乱无章,但只要把变化抽象成规则,就能让代码更稳、更耐用。