news 2026/10/3 7:26:09

大众点评评论爬虫:Requests与BeautifulSoup移动端方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
大众点评评论爬虫:Requests与BeautifulSoup移动端方案

搞了几年爬虫,平时最常被问到的就是“怎么爬大众点评的评论”。这站点的反爬严格程度在业内是出了名的,但它的评论数据又特别有分析价值,无论是做餐饮选址调研、竞品监控,还是简单的数据练手,都绕不开这里。网上能找到的教程多半是残缺片段,要么缺请求头,要么解析逻辑早就失效。所以我把一套能直接跑通的基础抓取方案整理出来,代码拿过去改改选择器就能用,重点是把每一步为什么这么做讲清楚,免得你抄完代码遇到问题不知道怎么改。

这套方案的核心思路是:通过模拟移动端页面请求,绕过PC端复杂的验证逻辑,再用Python解析评论区数据,最后落成CSV文件。它不依赖Selenium这类重型自动化工具,requests加BeautifulSoup两个库就能完成,对新手非常友好,同时也给后续扩大抓取规模留了足够空间。无论你是刚学爬虫的学生,还是需要做数据采集的产品、运营同学,这套逻辑都能直接用。

1. 整体思路与方案选型:为什么用移动端而不是Web端

1.1 大众点评反爬机制的基本特征

大众点评的防护体系分好几个层次,最基础的是对请求头、Cookie的校验,进阶的有字体反爬、CSS映射、参数加密,以及频率维度的账号风控。直接拿PC端的网页地址去请求,通常连数据都拿不到就被拦了,服务器返回的要么是空页面,要么是一个加密参数相关的错误。即便你从浏览器复制了完整的Cookie,PC端的评论数据也是通过异步接口动态加载的,接口参数里混杂着签名逻辑,必须用专门的工具才能定位真实请求。

移动端相较之下温和很多,这里的“温和”不是指没有防护,而是请求链路更短、签名逻辑相对简单,数据直接以内嵌的JSON或HTML结构返回。大部分评论信息其实不需要逆向签名算法,只要请求头伪装得像一部正常的手机,就能稳定取到数据。这也是整套方案能“copy就run”的核心前提。

1.2 为什么选择requests而不是Selenium或Playwright

很多新手一上来就选Selenium,理由是“可视化,看到什么爬什么”。这个思路没有错,但用在点评这种重量级站点上非常吃亏。Selenium需要拉起完整浏览器,不仅内存开销大,而且启动特征明显,更容易触发反爬策略。再加上翻页、滚动、等待渲染这些动作都需要额外写逻辑,代码量并不比纯接口请求少,速度和稳定性却差很多。

用requests直接请求HTML页面,本质是模拟手机App或者移动端页面的网络行为,只要请求头构造到位,能在毫秒级拿到完整数据。解析侧用BeautifulSoup按class或者标签属性提取内容,逻辑直观且容错性高。这套方案唯一的短板是当页面结构变动时需要同步调整选择器,但相比逆向签名参数,维护成本低得多。

1.3 这套代码的完整工作流程

整个抓取过程可以拆成四个环节:构造请求、获取响应、解析提取、清洗落盘。构造请求这一步包括设置UA、Cookie、Referer等请求头;获取响应时判断状态码和返回内容类型,防止被反爬重定向到验证页面;解析环节用BeautifulSoup定位评论区容器,逐个提取用户名、评分、评论文本、发布时间;最后在内存中整理成结构化字典,写入CSV文件。

这样的分层设计不是炫技,而是为了便于单独调试。遇到“能请求但解析不到数据”的情况,你可以只替换解析模块;遇到“直接被重定向”的情况,则只需要检查请求头。把问题隔离出来,是实战中排查效率最高的做法。

2. 开发环境准备与前置操作

2.1 安装Python与依赖库

本机需要Python 3.8以上版本,推荐3.10或3.11,避免一些旧版本在字符串处理上的小问题。安装完Python之后,在终端执行下面两条命令,就能装齐所有依赖:

pip install requests beautifulsoup4 lxml

requests库负责发送HTTP请求,beautifulsoup4负责解析HTML,lxml是解析器,性能和容错比Python自带的html.parser更好。如果没有安装lxml,BeautifulSoup也可以运行,但遇到复杂嵌套结构时效率会明显下降。

Windows用户如果遇到pip指令找不到,多半是没有把Python的Scripts目录加入环境变量,重新运行安装包勾选“Add Python to PATH”可以彻底解决。macOS用户如果提示外部管理环境,建议用python3 -m pip install方式安装。

2.2 获取有效Cookie是前期最重要的步骤

大众点评的评论区需要登录后才能完整加载。实际操作中,你得先用手机号登录大众点评的网页版或者App,再在浏览器开发者工具中复制自己的Cookie。这个Cookie包含了你的登录凭证和用户身份信息,服务器靠它来判定你有没有权限读取评论数据。不带Cookie直接请求,十有八九会返回“请先登录”或者直接跳转到首页。

打开目标店铺的评论区页面后,按F12进入开发者工具,在Network面板刷新页面,找到名为review或者包含shop的请求,在Headers区域找到Cookie:开头的值,整段复制下来备用。这里要注意,Cookie是有时效性的,通常几小时到几天不等,如果代码运行中突然失效,需要回到浏览器重新复制。

提示:Cookie属于个人敏感信息,只应在自己的本地脚本中使用,不要分享给他人,更不要提交到公开代码仓库。

2.3 从哪个入口获取原始HTML

店铺评论区的数据地址可以通过浏览器直接访问,移动端的地址格式大概是https://m.dianping.com/shop/商家ID/review_all这类结构。商家ID就是店铺详情页URL里数字部分。如果直接访问PC端地址,大概率触发验证,所以这里统一用无线端的页面地址作为抓取源。

打开移动端页面后,右键查看网页源代码,搜一下“comment”或者“review”,如果能找到评论文本,说明这是一个服务端渲染页面,非常适合用当前方案抓取。如果你看到的是一堆空标签和JS变量,那说明数据是异步加载的,这时就要检查Cookie是不是过期了,或者地址是否被重定向到了登录页。

3. 核心代码逐段拆解

3.1 构造请求头:让服务器以为你是手机

请求头里面最重要的三个字段是User-Agent、Referer和Cookie。User-Agent决定了服务器认为你是什么设备,这里模拟一部常规的Android手机;Referer告诉服务器你从哪个页面跳转过来,能够增加请求的合理性;Cookie承担登录态认证。除此之外还可以加上Accept-Language来声明语言偏好,加上Accept来控制返回内容的类型。

下面这一段是请求头的标准写法:

headers = { "User-Agent": "Mozilla/5.0 (Linux; Android 13; Xiaomi 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36", "Referer": "https://m.dianping.com/shop/xxx/review_all", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9", "Cookie": "你复制的完整Cookie内容" }

这里建议把Cookie单独定义成一个变量,方便失效时快速替换。User-Agent的选择也有技巧,不要用太新或太老的版本,选市面上主流机型常见的UA,数据特征更自然。

3.2 发送请求与状态码判断

请求发出之后,第一步不是急着解析数据,而是判断服务器到底给了我们什么。常规状态码200表示正常返回,但在这类站点里,200也不一定代表拿到了真正的数据,有可能返回的是一个带验证逻辑的空白壳页面。因此还需要检查返回内容长度和关键词。

import requests session = requests.Session() session.headers.update(headers) url = "https://m.dianping.com/shop/商家ID/review_all" resp = session.get(url, timeout=10) if resp.status_code == 200 and "comment" in resp.text: print("页面获取成功") elif resp.status_code == 200 and len(resp.text) < 500: print("疑似被拦截,返回页面过短") else: print("状态码异常:", resp.status_code)

使用requests.Session而不是直接requests.get,是为了复用底层的TCP连接,连续请求时效率更高。同时Session能够自动维持某些Cookie状态,避免因为连接断开导致部分请求失败。

3.3 用BeautifulSoup解析评论数据

拿到HTML之后,接下来的工作就是精准定位。打开移动端评论区页面,用开发者工具的Elements面板查看评论区域的class名称,找到包含用户名、评分、时间、评论内容这些字段的父容器。这个class名不是固定的,不同商家的页面可能不同,所以解析前要先手动确认一次。

from bs4 import BeautifulSoup soup = BeautifulSoup(resp.text, "lxml") review_list = soup.select("div.review-list > div.review-item") for item in review_list: nickname = item.select_one("a.name") rating = item.select_one("span.star-score") comment_time = item.select_one("span.time") content = item.select_one("p.review-content") data_item = { "用户名": nickname.get_text(strip=True) if nickname else "", "评分": rating.get("class") if rating else "", "时间": comment_time.get_text(strip=True) if comment_time else "", "评论内容": content.get_text(strip=True) if content else "", }

这里有两个细节值得注意。第一,select_one返回的是Tag对象或None,直接调用get_text会报错,所以每次都要先判断是否存在,这是新手最容易踩的坑。第二,评分在移动端常以class属性的形式存在,例如star-score-50表示五星,所以用get("class")取的是整个class列表,你可以从中提取数值再换算成对应的分数值。

3.4 翻页逻辑与请求频率控制

评论区通常不止一页,移动端翻页常见的方式是URL中的页码参数。循环抓取每页时要做一个最基本的自我保护:每次请求之间随机停顿。这个停顿不是简单的sleep(1),而是用一个随机范围,避免请求间隔过于规律。

import random import time all_reviews = [] for page in range(1, 5): page_url = f"https://m.dianping.com/shop/商家ID/review_all?pageno={page}" resp = session.get(page_url, timeout=10) if resp.status_code == 200: soup = BeautifulSoup(resp.text, "lxml") items = soup.select("div.review-list > div.review-item") if not items: print(f"第{page}页没有数据,停止翻页") break for item in items: all_reviews.append(extract_review(item)) time.sleep(random.uniform(2, 4))

每次翻页循环新增了一个判断:如果当前页没有解析到任何评论,立即退出循环。这个机制很实用,因为很多商家评论总数只有一两页,如果傻乎乎继续翻,只会请求到空页面。最后的随机延时控制在2到4秒,既不会太慢影响效率,也不会因为请求太密被限制。

3.5 数据清洗与CSV导出

原始HTML里提取出来的文本经常夹杂着空格、换行和\u3000这样的全角空格,直接写入CSV会让数据很难看,所以要做一次统一的清洗。写CSV时还有一个容易被忽略的编码问题:Windows下用Excel直接打开CSV,必须用utf-8-sig编码保存,否则中文会变成乱码。

import csv def clean_text(text): return text.replace("\u3000", " ").replace("\n", " ").strip() with open("dianping_comments.csv", "w", newline="", encoding="utf-8-sig") as f: fieldnames = ["用户名", "评分", "时间", "评论内容"] writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(all_reviews)

utf-8-sig和utf-8的区别在于文件头是否写入BOM标记,有BOM的CSV文件才能被Excel正确识别中文编码。对于后续要写代码做数据分析的情况,读取时同样要对应指定encoding="utf-8-sig"。

4. 常见问题与排查技巧

4.1 请求失败或被重定向到登录页

这个问题的触发频率最高,原因有两个。一是Cookie失效,检测方法很简单:在浏览器里打开一次移动端页面,看看评论是否正常显示。如果页面显示需要登录,说明Cookie已经失效,重新复制即可。二是请求头不完整,特别容易漏掉Referer字段。大众点评会校验请求的来源页面,如果你直接以空白Referer请求数据,很容易被归类为异常流量。

解决方法是给headers补全Referer和Accept字段,并确保Cookie是最新的。同时建议给session.get加上allow_redirects=False参数去检查响应状态,如果拿到302就很明确是被重定向了,不需要再继续解析。

4.2 解析不到数据,明明页面能打开

出现这个情况,最大的嫌疑是页面结构变了。大众点评的前端代码会不定时调整class名,同一套选择器跑半个月就可能失效。排查思路很直接:把拿到的HTML保存到本地文件,用浏览器打开,手工搜索评论关键词,确认当前页面使用的class名是什么,再相应更新select方法里的选择器。

为防止同类问题反复出现,解析阶段尽量使用稳定性更高的属性作为锚点。比如评论内容有时在p标签中,有时在div标签中,但外层容器的>

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 7:26:01

Agent 技术摘要 05 —— BP、SaaS、B2B、C2B、B2C

本系列为笔者在实习过程中的所见所闻记录&#xff0c;内容为技术摘要&#xff0c;旨在提供关于Agent领域相关技术名词的通俗和简要介绍&#xff0c;详细内容暂不展开&#xff0c;供同在该领域进修的童鞋们参考。 01 BP Business Plan&#xff08;商业计划书&#xff09;&#x…

作者头像 李华
网站建设 2026/10/3 7:24:33

Datawhale学习笔记:上下文工程与Agent记忆、RAG的联系与优化

上下文工程 vs Agent 记忆 vs RAG&#xff1a;联系与优化实战 主题&#xff1a;Context Engineering&#xff08;上下文工程&#xff09;、Agent Memory&#xff08;智能体记忆&#xff09;、RAG&#xff08;检索增强生成&#xff09;三者的概念辨析、关系梳理与工程优化方案&a…

作者头像 李华
网站建设 2026/10/3 7:24:32

从“报菜名”到“画地图”:云智变AI文献综述写作功能科普——云智变AI官网www.yunzhibian.cn,微信公众号搜一搜 云智变ai学术

导师翻到文献综述那一章&#xff0c;皱了皱眉&#xff1a;“你这不是综述&#xff0c;是报菜名。” 你心里委屈&#xff1a;我明明读了二十篇文献&#xff0c;按时间顺序一篇一篇总结&#xff0c;怎么就成报菜名了&#xff1f; 问题在于&#xff0c;文献综述不是“我读过什么…

作者头像 李华
网站建设 2026/10/3 7:24:17

【贪心算法】LC 763.划分字母区间

文章目录前言一、题目1、原题链接2、题目描述二、个人思路整理1、思路分析2、解题代码三、知识风暴前言 本专栏文章为《LeetCode 热题 100》的刷题题解&#xff0c;相关内容如有侵权&#xff0c;立即删除。 一、题目 1、原题链接 763.划分字母区间 2、题目描述 二、个人思路整…

作者头像 李华
网站建设 2026/10/3 7:23:17

ThreadLocal 残留数据引发的随机业务错乱

开发中为了传递上下文、存储登录用户信息、临时缓存参数&#xff0c;很多项目都会用到 ThreadLocal。使用起来很方便&#xff0c;不用反复传参&#xff0c;整个线程链路随时可以获取上下文数据。 但线上很多偶发的诡异问题&#xff0c;都是它悄悄造成的。最头疼的是这类问题没有…

作者头像 李华