本文将系统讲解如何绕过 Kununu 页面动态加载带来的采集限制,直接调用其后端 REST API 进行分页抓取,从而稳定获取全部员工评价数据,包括完整评论文本、评分、发布时间等关键信息,避免使用 Selenium 点击“查看更多”时出现的 JSON 数据不同步或滞后问题。

本文将系统讲解如何绕过 Kununu 页面动态加载带来的采集限制,直接调用其后端 REST API 进行分页抓取,从而稳定获取全部员工评价数据,包括完整评论文本、评分、发布时间等关键信息,避免使用 Selenium 点击“查看更多”时出现的 JSON 数据不同步或滞后问题。
Kununu 的评论页面(例如 https://www.kununu.com/de/adidas/kommentare)采用的是典型的客户端动态渲染配合服务端分页加载机制:初始 HTML 中嵌入的 #__NEXT_DATA__ JSON 通常只包含首屏内容,一般为前 10 条评论;后续评论则通过 Ajax 异步请求继续加载,而这些新增请求并不会自动回写或更新 #__NEXT_DATA__ 中的内容——这正是你会遇到“BeautifulSoup 已经能读取展开后的页面内容,但 JSON 数据始终只有 10 条”的核心原因。
相较于不够稳定、且容易被反爬策略打断的浏览器自动化点击方案(例如 show_more_reviews(driver, 2)),更推荐的做法其实是直接分析并调用 Kununu 内部评论 API 接口。这套接口设计相对规范,返回的 JSON 结构也比较清晰,同时无需登录,也没有复杂鉴权等额外限制。因此,对于需要批量抓取 Kununu 员工评价、追求稳定性和可维护性的任务来说,这种方式明显更高效、更适合长期使用。
✅ 正确做法:直连评论 API,分页抓取全量员工评价数据
首先,需要从页面中提取企业唯一标识 ID(也就是 URL 中的 company-id)。Kununu 会将这个 ID 隐藏在任意一条评论链接中(例如 ),可以借助 BeautifulSoup 快速完成解析:
from bs4 import BeautifulSoup
import requests
url = "https://www.kununu.com/de/adidas/kommentare"
soup = BeautifulSoup(requests.get(url).content, "html.parser")
# 提取 company ID(位于任意评论链接的第5段路径)
id_ = soup.select_one('a[href*="/review/"]')["href"].split("/")[5]接下来,就可以拼接 API 请求地址和对应参数。Kununu 评论接口通常采用以下固定格式:https://www.kununu.com/middlewares/profiles/{country}/{company-slug}/{company-id}/reviews
关键参数说明如下:
fetchFactorScores: 设置为"0"表示不抓取维度评分数据(如工作氛围、薪酬福利等子项评分),仅获取主评论内容;reviewType: 可设为"employees"(在职员工评价)或"former_employees"(前员工评价);page: 分页页码,从1开始,每页默认返回 10 条评论。
完整示例(抓取前 2 页,共 20 条 Kununu 评论):
api_url = "https://www.kununu.com/middlewares/profiles/de/adidas/{id_}/reviews"
params = {
"fetchFactorScores": "0",
"reviewType": "employees",
"urlParams": "",
"page": "1",
}
for page in range(1, 3):# 可扩展至所需页数,如 range(1, 6) 获取前50条
params["page"] = str(page)
response = requests.get(api_url.format(id_=id_), params=params)
response.raise_for_status()
data = response.json()
for review in data.get("reviews", []):
# 提取核心字段
rating = review.get("rating", 0)# 总体评分(1–5)
date = review.get("date", "")
author_role = review.get("role", "")
# 逐段提取评论正文(支持多段文本,如“优点”“缺点”“建议”)
texts = []
for text_obj in review.get("texts", []):
if text_obj.get("text"):
texts.append(text_obj["text"].strip())
print(f"[评分: {rating}] [{date}] {author_role}")
print("n".join(texts))
print("-" * 80)⚠️ 注意事项与优化建议
- 分页健壮性:API 返回结果中通常包含
data["pagination"]["totalPages"]字段,建议先请求第 1 页获取总页数,再循环抓取全部分页,避免手动硬编码页码; - 请求头与访问频率:建议添加
User-Agent请求头(例如'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'),并适当控制请求间隔(如time.sleep(1)),以降低被限流或触发风控的风险; - 异常处理:对
requests.get()建议使用try/except包裹,捕获网络请求异常、JSON 解析失败以及空响应等问题,提升程序稳定性; - 数据完整性:部分评论正文可能包含 HTML 标签(如
),建议通过html.unescape()或BeautifulSoup(text, "html.parser").get_text()做进一步清洗,以便后续存储和分析; - 替代方案:如果后续 API 路径发生变化,可以通过浏览器开发者工具(Network → XHR)实时抓取
reviews请求,快速定位最新的接口 endpoint 与请求参数。
通过这种方法,你可以彻底摆脱 Selenium 渲染延迟、按钮点击失败和 DOM 同步困难等问题,以更轻量、更高效、更易维护的方式稳定获取 Kununu 全量结构化员工评论与评分数据。
