python

京东商品评论爬虫(Selenium模拟浏览器+多线程)

2026-07-06 #python#爬虫

前言

京东作为国内最大的电商平台之一,其商品评论数据对于竞品分析、用户舆情监控、商品推荐算法训练等场景都具有极高的价值。然而,京东的评论数据并不像12306或B站那样可以通过简单的API直接获取——评论数据是通过动态加载的,需要模拟浏览器行为才能获取。

本文将基于真实项目代码,详细介绍如何使用 DrissionPage(一个比Selenium更强大的浏览器自动化库)模拟浏览器访问京东商品页,监听评论接口并抓取数据,同时结合 多线程 技术大幅提升爬取效率。

技术背景

本项目涉及以下核心技术和库:

  • DrissionPage:基于Selenium改造的浏览器自动化库,语法更简洁,支持监听网络请求
  • ThreadPoolExecutor:Python标准库中的线程池,实现多线程并发爬取
  • json:解析评论接口返回的JSON数据
  • CSS选择器:定位页面元素(评论按钮、下一页按钮等)

为什么用DrissionPage而不是Selenium?
DrissionPage对Chromium的封装更加人性化,特别是 listen 功能可以非常方便地监听指定URL的请求和响应,而不需要额外配置 webdriverwaitexecute_script,非常适合爬虫场景。

实现思路

京东评论爬虫的整体架构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
┌─────────────────────────────────────────────┐
│ Step 1: 启动浏览器,访问商品页 │
│ DrissionPage.ChromiumPage() │
└──────────────────┬──────────────────────────┘


┌─────────────────────────────────────────────┐
│ Step 2: 开始监听评论接口 │
│ dp.listen.start('pc_club_productPage...') │
└──────────────────┬──────────────────────────┘


┌─────────────────────────────────────────────┐
│ Step 3: 点击"评论"标签,触发数据加载 │
│ dp.ele('css:#detail > ...').click() │
└──────────────────┬──────────────────────────┘


┌─────────────────────────────────────────────┐
│ Step 4: 等待并获取评论接口响应 │
│ resp = dp.listen.wait() │
│ json_data = resp.response.body │
└──────────────────┬──────────────────────────┘


┌─────────────────────────────────────────────┐
│ Step 5: 解析评论JSON,提取字段 │
│ data['comments'] -> 每条评论的字典 │
└──────────────────┬──────────────────────────┘


┌─────────────────────────────────────────────┐
│ Step 6: 点击"下一页",继续爬取 │
│ (单线程版)或多线程并发爬取(优化版) │
└──────────────────┬──────────────────────────┘


┌─────────────────────────────────────────────┐
│ Step 7: 保存所有评论为JSON文件 │
│ json.dump(all_comments, f, ...) │
└─────────────────────────────────────────────┘

核心代码解析

1. 配置浏览器(首次运行需要)

京东评论爬虫使用了Chromium内核的浏览器,需要先配置Chrome路径:

1
2
3
4
5
# 配置浏览器.py
from DrissionPage import ChromiumOptions

path = r'C:\Program Files\Google\Chrome\Application\chrome.exe'
ChromiumOptions().set_browser_path(path).save()

运行一次后即可,后续不需要再配置。

2. 单线程版:基础爬取逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import json
from DrissionPage import ChromiumPage
import time

# 实例化浏览器对象
dp = ChromiumPage()

# 设置访问地址(京东商品页URL)
Url = "https://item.jd.com/100076766489.html"

# 监听数据包 —— 这是核心步骤!
# 指定监听的URL模式,DrissionPage会自动捕获匹配的请求和响应
dp.listen.start('https://api.m.jd.com/?appid=item-v3&functionId=pc_club_productPageComments')

# 访问商品页面
dp.get(Url)

# 点击"评论"标签,触发评论数据加载
# 使用CSS选择器定位评论按钮
dp.ele('css:#detail > div.tab-main.large > ul > li:nth-child(5)').click()

# 初始化评论列表
all_comments = []

# 最大页数限制
max_pages = 20
current_page = 0

while current_page < max_pages:
# 等待数据包加载 —— 阻塞直到监听到匹配的请求
resp = dp.listen.wait()

# 获取响应数据(自动解析为dict或str)
json_data = resp.response.body

# 处理响应数据的类型(可能是str也可能是dict)
if isinstance(json_data, str):
data = json.loads(json_data)
elif isinstance(json_data, dict):
data = json_data
else:
raise TypeError("json_data 必须是 str 或 dict 类型")

# 提取每条评论,添加到列表
for comment in data['comments']:
all_comments.append(comment)

# 查找"下一页"按钮
next_button = dp.ele('css:#comment-0 > div.com-table-footer > div > div > a.ui-pager-next')

# 判断"下一页"按钮是否存在且可点击(没有disabled类)
if next_button and 'disabled' not in next_button.attr('class'):
next_button.click() # 点击下一页
time.sleep(2) # 等待页面加载
current_page += 1
else:
break # 没有下一页了,退出循环

# 保存所有评论到JSON文件
with open('京东评论区.json', 'w', encoding='utf-8') as f:
json.dump(all_comments, f, ensure_ascii=False, indent=4)

print(f"✅ 所有评论已保存到 京东评论区.json,共 {len(all_comments)} 条")

代码关键点:

  1. dp.listen.start(pattern):开始监听匹配 pattern 的网络请求。这是DrissionPage最强大的功能之一,不需要手动抓包分析,直接监听即可。
  2. dp.listen.wait():阻塞等待,直到监听到一个匹配的请求,返回 Response 对象。
  3. resp.response.body:直接获取响应体,DrissionPage会自动判断是JSON还是文本。
  4. CSS选择器定位#detail > div.tab-main.large > ul > li:nth-child(5) 定位到第5个tab(即”评论”标签)。

3. 评论数据字段详解

京东评论接口返回的每条评论包含非常丰富的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
comments_info = {
"id": "评论的唯一标识符",
"guid": "用户的唯一标识符",
"content": "评论的具体内容",
"creationTime": "评论的创建时间",
"isDelete": "评论是否被删除",
"isTop": "评论是否置顶",
"userImageUrl": "用户头像的URL",
"topped": "是否置顶的标志",
"replyCount": "评论的回复数量",
"score": "评论的评分(1-5星)",
"imageStatus": "图片状态",
"usefulVoteCount": "有用投票的数量",
"userClient": "用户使用的客户端类型",
"discussionId": "评论的讨论ID",
"imageCount": "评论中包含的图片数量",
"anonymousFlag": "是否匿名",
"plusAvailable": "用户的PLUS会员状态",
"mobileVersion": "用户使用的手机版本",
"images": "评论中包含的图片列表",
"videos": "评论中包含的视频列表",
"productColor": "商品颜色(用户购买时的选项)",
"productSize": "商品尺寸(用户购买时的选项)",
"location": "用户位置",
"nickname": "用户昵称",
"userLevelName": "用户等级名称",
"userClientShow": "用户客户端显示",
}

4. 多线程版:大幅提升爬取效率

单线程版每爬一页需要等待页面加载,效率较低。多线程版通过 ThreadPoolExecutor 实现并发爬取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import json
from DrissionPage import ChromiumPage
import time
from concurrent.futures import ThreadPoolExecutor, as_completed

def fetch_comments(page_number):
"""
爬取指定页的评论(每个线程独立启动一个浏览器实例)
page_number: 要爬取的页码
"""
# 每个线程创建独立的浏览器实例(线程安全)
dp = ChromiumPage()

Url = "https://item.jd.com/100076766489.html"

# 监听评论接口
dp.listen.start('https://api.m.jd.com/?appid=item-v3&functionId=pc_club_productPageComments')
dp.get(Url)
dp.ele('css:#detail > div.tab-main.large > ul > li:nth-child(5)').click()

# 如果是第N页(N>1),需要先翻页到目标页
if page_number > 1:
for _ in range(page_number - 1):
next_button = dp.ele('css:#comment-0 > div.com-table-footer > div > div > a.ui-pager-next')
if next_button and 'disabled' not in next_button.attr('class'):
next_button.click()
time.sleep(2)
else:
dp.close()
return [] # 页码不存在,返回空列表

# 等待当前页的评论数据加载
resp = dp.listen.wait()
json_data = resp.response.body

if isinstance(json_data, str):
data = json.loads(json_data)
else:
data = json_data

# 提取评论
page_comments = []
for comment in data['comments']:
page_comments.append(comment)

dp.close() # 关闭浏览器,释放资源
return page_comments

# 使用线程池并发爬取
all_comments = []
max_pages = 20

with ThreadPoolExecutor(max_workers=5) as executor:
# 提交所有任务(爬取第1~20页)
future_to_page = {executor.submit(fetch_comments, page): page
for page in range(1, max_pages + 1)}

# 处理完成的任务
for future in as_completed(future_to_page):
page = future_to_page[future]
try:
comments = future.result()
all_comments.extend(comments)
print(f"✅ 完成第 {page} 页的评论抓取,获得 {len(comments)} 条")
except Exception as e:
print(f"❌ 第 {page} 页的评论抓取失败: {e}")

# 保存结果
with open('京东评论区.json', 'w', encoding='utf-8') as f:
json.dump(all_comments, f, ensure_ascii=False, indent=4)

print(f"✅ 所有评论已保存,共 {len(all_comments)} 条")

多线程版的关键设计:

  1. 每个线程独立浏览器实例fetch_comments 函数内部创建自己的 ChromiumPage() 对象,避免多线程共享浏览器状态导致的竞态条件
  2. max_workers=5:最多同时运行5个浏览器实例,可以根据机器性能调整
  3. as_completed:按任务完成顺序处理结果,而不是按提交顺序
  4. 资源释放:每次任务完成后调用 dp.close() 关闭浏览器

注意:多线程版会同时启动多个浏览器窗口,确保电脑内存足够(建议16GB以上)。如果资源有限,可以改用 max_workers=2 或减少同时爬取的页数。

运行效果

1
2
3
4
5
6
7
8
9
正在启动浏览器...
✅ 完成第 1 页的评论抓取,获得 10 条
✅ 完成第 2 页的评论抓取,获得 10 条
✅ 完成第 3 页的评论抓取,获得 10 条
...
✅ 完成第 10 页的评论抓取,获得 10 条
✅ 所有评论已保存,共 100 条

生成的文件:京东评论区.json

JSON文件内容预览:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[
{
"id": 1234567890,
"guid": "abc123def456",
"content": "质量很好,物流也快,好评!",
"creationTime": "2024-12-15 14:23:05",
"score": 5,
"replyCount": 2,
"usefulVoteCount": 15,
"nickname": "j***d",
"location": "北京",
"productColor": "黑色",
"productSize": "64GB",
"images": ["https://img.jd.com/..."],
"anonymousFlag": false,
"plusAvailable": true
}
]

总结与优化方向

通过本文的京东评论爬虫项目,我们学习了:

  1. DrissionPage浏览器自动化:比Selenium更简洁的API,特别是 listen 监听功能
  2. 动态页面爬取:模拟点击、等待数据加载、监听接口响应
  3. 多线程并发:使用 ThreadPoolExecutor 大幅提升爬取效率
  4. 数据持久化:将结构化评论数据保存为JSON文件

可优化方向:

  • 使用Chrome Headless模式:不弹出浏览器窗口,在后台运行,节省资源
  • 增加Cookie池:准备多个京东账号的Cookie,轮流使用,降低单个账号被限流的风险
  • 失败重试机制:某一页爬取失败时自动重试3次
  • 增量爬取:记录已爬取的最大评论ID,只爬取新增评论
  • 分布式爬取:结合Redis任务队列,多机协同爬取大规模商品
  • 数据清洗与分析:对评论内容做情感分析、关键词提取等后续处理
  • 异步优化:使用 asyncio + DrissionPage 的异步API(如果支持),进一步提升并发能力

注意:使用浏览器自动化爬取数据时,请合理控制频率,避免对京东服务器造成过大压力。本代码仅供学习交流使用。

评论
分享