做Python爬虫的小伙伴们,是不是经常被今日头条(Toutiao)的反爬机制劝退?尤其是想做舆情数据分析或热点事件复盘时,发现头条的评论数据藏在抽屉面板里,传统的 requests 根本拿不到,用 Selenium 又慢又容易被封。
今天给大家带来一篇DrissionPage 实战干货,教你如何通过监听接口的方式,精准抓取今日头条文章评论。不同于网上那些过时的教程,本文不涉及任何逆向JS,也不需要配置复杂的Cookie池,直接从浏览器底层拦截 article/v4/tab_comments/ 接口,一键导出包含评论内容、点赞数、发布地点、认证信息在内的13个核心字段。
无论你是想做用户画像分析、情感倾向判断,还是单纯想批量采集头条热评,这套代码都能帮你把“数据孤岛”打通。话不多说,直接上源码拆解!👇
一、导入与 CSV 初始化(地基)
import csvimport timefrom DrissionPage import ChromiumPage
•csv:负责写文件
•time:负责休眠,控制请求节奏
•ChromiumPage:DrissionPage 的浏览器对象,代替 Selenium
url = 'https://www.toutiao.com/article/7665544661083914771/'
目标文章 URL
头条的文章页是 SPA(单页应用),评论不在初始 HTML 里
f = open('头条评论.csv', mode='w', encoding='utf-8-sig', newline='')csv_writer = csv.DictWriter(f, fieldnames=[ '评论ID', '评论内容', '发布时间', '点赞数', '回复数', '转发数', '评分', '发布地点', '来源平台', '用户ID', '用户名', '是否认证', '是否PGC作者'])csv_writer.writeheader()
•utf-8-sig:Excel 打开不乱码
•newline='':Windows 下防止空行
•DictWriter:按字段名写,不怕列顺序乱
•writeheader():写表头,只执行一次
二、浏览器与接口监听(核心开关)
启动一个真实的 Chromium 浏览器
比 requests 更难被封,比 Selenium 更轻量
print('🟢 开始监听 article/v4/tab_comments/')dp.listen.start('article/v4/tab_comments/')
最关键的一步
监听头条评论接口
只要 URL 包含 article/v4/tab_comments/,全部拦截
不走 DOM,不走正则,直接拿 JSON
三、打开文章页 & 激活评论抽屉
try: drawer_btn = dp.wait.ele_displayed('.side-drawer-btn', timeout=10) print('✅ 找到「查看全部评论」按钮,正在点击...') drawer_btn.click() time.sleep(2)except Exception as e: print(f'⚠️ 未找到 side-drawer-btn,尝试继续:{e}')
•.side-drawer-btn:头条右侧“查看全部评论”按钮
•wait.ele_displayed:显式等待,比 sleep 稳
•点击后,评论面板从右侧滑出(抽屉式 UI)
•不点这个按钮,评论接口不一定触发
四、首次加载评论(人为制造请求)
•滚到底部,触发懒加载
load_more = dp.ele('.load-more-btn', timeout=5)load_more.click()time.sleep(2)
•.load-more-btn:评论区的“查看更多评论”按钮
•第一次点击,强制触发第一页评论接口
•否则 listen.wait() 可能一直空等
五、分页循环(主逻辑)
•页码计数器,纯粹用于日志
while True: print(f'📄 正在爬取第 {page} 页评论')
•无限循环,直到无数据或异常退出
resp = dp.listen.wait(timeout=10) if not resp: print('⚠️ 未监听到评论接口,结束') break
•listen.wait():阻塞等待接口返回
•timeout=10:10 秒没数据就认为结束了
•头条评论接口非常稳定,一般不会超时
json_data = resp.response.body
拿到接口返回的完整 JSON
此时数据已经在内存里,后面全是解析
六、JSON 路径解析(最容易出错的地方)
comments = ( json_data .get('data', {}) )
•头条的评论接口结构是:
{ "data": [ {"comment": {...}}, {"comment": {...}} ]}
直接 .get('data', {}),防止 KeyError
comments 是一个 list,每一项都是一个评论对象
if not comments: print('⚠️ 本页无评论数据,结束') break
七、单条评论字段提取(逐字段拆解)
for c in comments: comment = c.get('comment', {})
每条评论外层包了一层 comment
必须先取出 comment,再取具体字段
row = { '评论ID': comment.get('id'), '评论内容': comment.get('text'), '发布时间': comment.get('create_time'),
•id:评论唯一标识
•text:评论正文
•create_time:发布时间戳(毫秒)
'点赞数': comment.get('digg_count'), '回复数': comment.get('reply_count'), '转发数': comment.get('forward_count'),
•digg_count:头条内部叫法,就是点赞
•reply_count:楼中楼回复数
•forward_count:转发次数(头条特有)
'评分': comment.get('score'), '发布地点': comment.get('publish_loc_info'),
•score:部分评论有评分(如文章评价)
•publish_loc_info:发布地理位置(如“北京”)
'来源平台': comment.get('platform'), '用户ID': comment.get('user_id'), '用户名': comment.get('user_name'),
•platform:来源(如 iOS、Android、Web)
•user_id:用户唯一 ID
•user_name:昵称
'是否认证': comment.get('user_verified'), '是否PGC作者': comment.get('is_pgc_author'), }
•user_verified:是否认证用户(黄 V / 蓝 V)
•is_pgc_author:是否为 PGC(专业生产内容)作者
写入一行 CSV
循环结束后,文件里就有了一条完整评论
八、翻页逻辑(决定能不能爬完)
try: load_more = dp.ele('.load-more-btn', timeout=5) dp.scroll.to_bottom() load_more.click() page += 1 time.sleep(1.5)
再次寻找“查看更多评论”按钮
滚到底部,确保按钮可见
点击加载下一页
sleep(1.5):控制节奏,防止被限流
except Exception: print('✅ 已无更多评论,结束') break
九、收尾清理(好习惯)
print('🎉 评论爬取完成')f.close()dp.quit()
打印完成日志
关闭 CSV 文件(刷新缓冲区)
关闭浏览器,释放内存
十、这段代码最值钱的 5 个细节
监听接口,不碰 DOM
抽屉按钮必须点
首次加载必须人为触发
JSON 路径必须对准 comment
翻页靠按钮,不靠页码
十一、一句话总结
这不是在“爬网页”,而是在“接管头条的评论数据流”。