避坑指南:用Python爬京东评论时,你可能会遇到的5个问题及解决方案(附完整代码)
Python爬取京东评论的5个实战避坑指南
1. 请求头设置的艺术与科学
当你第一次尝试用Python爬取京东评论时,最可能遇到的第一个障碍就是请求被直接拒绝。这不是因为你的代码有问题,而是因为京东的反爬机制已经识别出这是一个自动化请求。
现代网站的反爬系统越来越智能,它们会检查请求头中的各种细节。User-Agent只是最基本的检查项,还包括Accept-Language、Referer甚至Connection等字段。一个真实的浏览器请求会包含完整的头部信息,而简单的requests.get()如果不做处理,头部信息会非常简陋。
这是我常用的完整请求头设置:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Referer': 'https://item.jd.com/',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'same-origin',
'Sec-Fetch-User': '?1',
'Cache-Control': 'max-age=0'
}
提示:定期更新你的User-Agent字符串,使用较新的浏览器版本号,这能让你的请求看起来更像真实用户。
2. 解密京东评论接口的关键参数
京东的评论接口有几个关键参数如果理解错误,会导致获取的数据不完整或完全错误。其中最容易出问题的两个参数是fold和isShadowSku。
fold=1:表示获取折叠的评论(通常是默认值)isShadowSku=0:表示获取主商品的评论,而不是变体商品的评论
但实际应用中,这些参数的含义可能会随着京东的接口更新而变化。我建议的做法是先通过浏览器开发者工具观察正常请求时的参数,然后对照着设置。
这是我常用的参数组合:
params = {
'productId': '100142621600', # 商品ID
'score': 3, # 评分类型(1-5分)
'sortType': 5, # 排序类型(5表示推荐排序)
'page': 0, # 页码(从0开始)
'pageSize': 10, # 每页评论数量
'isShadowSku': 0, # 是否影子商品
'fold': 1 # 是否折叠
}
注意:京东的page参数是从0开始计数的,这与很多其他网站从1开始不同,这个小细节可能导致你获取的数据总是差一页。
3. 处理JSON数据结构的健壮性技巧
京东评论接口返回的JSON数据结构可能会发生变化,或者某些字段在某些情况下不存在。直接通过键名访问如data['comments']可能会导致KeyError异常。
我强烈建议使用.get()方法并提供默认值,这样可以确保程序不会因为缺少某个字段而崩溃。下面是一个健壮的数据提取示例:
comment = {
'nickname': item.get('nickname', '匿名用户'),
'score': item.get('score', 5),
'content': item.get('content', ''),
'productColor': item.get('productColor', '默认颜色'),
'creationTime': item.get('creationTime', '未知时间'),
'imageCount': len(item.get('images', [])),
'usefulVoteCount': item.get('usefulVoteCount', 0),
'replyCount': item.get('replyCount', 0)
}
此外,京东的评论接口有时会返回嵌套较深的数据结构。例如,用户等级信息可能位于userClient']下的userLevel']中。处理这种情况时,可以采用链式.get():
user_level = item.get('userClient', {}).get('userLevel', 0)
4. 避免IP被封的智能请求策略
频繁请求京东接口是最容易被封IP的行为。单纯的time.sleep()虽然有效,但不够智能。我推荐以下几种策略组合使用:
-
随机延时:在固定延时基础上增加随机性
import random time.sleep(1 + random.random() * 2) # 1-3秒随机延时 -
请求速率限制:使用令牌桶算法控制请求速率
from ratelimit import limits, sleep_and_retry @sleep_and_retry @limits(calls=30, period=60) # 每分钟最多30次请求 def call_api(): return requests.get(url, headers=headers, params=params) -
代理IP池:使用多个代理IP轮换
proxies = { 'http': 'http://user:pass@proxy_ip:port', 'https': 'http://user:pass@proxy_ip:port' } response = requests.get(url, headers=headers, params=params, proxies=proxies) -
自动重试机制:对失败的请求自动重试
from tenacity import retry, stop_after_attempt, wait_exponential @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10)) def get_comments(): response = requests.get(url, headers=headers, params=params) if response.status_code != 200: raise Exception(f"请求失败,状态码:{response.status_code}") return response.json()
5. 解决CSV文件写入的编码与格式问题
将爬取的数据保存到CSV文件时,最常见的两个问题是中文乱码和格式不规范。以下是经过实战验证的解决方案:
完整文件写入示例:
import csv
from datetime import datetime
filename = f'jd_comments_{datetime.now().strftime("%Y%m%d_%H%M%S")}.csv'
with open(filename, 'w', encoding='utf-8-sig', newline='') as f:
fieldnames = ['nickname', 'score', 'content', 'productColor',
'creationTime', 'imageCount', 'usefulVoteCount']
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for comment in comments:
# 处理内容中的换行符和逗号
comment['content'] = comment['content'].replace('\n', ' ').replace(',', ',')
writer.writerow(comment)
关键点说明:
encoding='utf-8-sig':使用带BOM的UTF-8编码,确保Excel能正确识别中文newline='':防止在Windows系统下出现空行- 内容清洗:替换换行符和英文逗号,避免破坏CSV格式
- 动态文件名:包含时间戳,避免重复覆盖
对于大规模数据,建议分批写入并定期保存:
BATCH_SIZE = 100
with open(filename, 'w', encoding='utf-8-sig', newline='') as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
batch = []
for i, comment in enumerate(comments, 1):
batch.append(process_comment(comment))
if i % BATCH_SIZE == 0:
writer.writerows(batch)
batch = []
print(f'已保存{i}条评论')
if batch: # 写入剩余数据
writer.writerows(batch)
6. 实战中的进阶技巧与经验分享
在实际爬取京东评论的过程中,我还总结出一些非常有用的进阶技巧:
动态商品ID获取:有时你需要从商品详情页URL中提取商品ID。京东的URL格式有多种变体,需要正则表达式灵活处理:
import re
def extract_product_id(url):
patterns = [
r'https://item.jd.com/(\d+).html',
r'jd.com/\d+[?&]sku=(\d+)',
r'wareId=(\d+)'
]
for pattern in patterns:
match = re.search(pattern, url)
if match:
return match.group(1)
return None
评论图片下载:京东评论中的图片URL需要特殊处理才能获取高清版本:
def get_large_image(url):
if 's128x96_jfs' in url:
return url.replace('s128x96_jfs', 'jfs')
return url
分页爬取优化:京东的评论接口有时会返回不准确的总页数信息。更可靠的做法是持续爬取直到返回空数据:
page = 0
while True:
params['page'] = page
response = requests.get(url, headers=headers, params=params)
data = response.json()
if not data.get('comments'):
break
# 处理评论数据
process_comments(data['comments'])
page += 1
time.sleep(1 + random.random())
数据去重:在长时间运行爬虫时,可能会遇到重复数据。可以使用评论ID作为唯一标识进行去重:
seen_ids = set()
for comment in comments:
comment_id = comment.get('id')
if comment_id in seen_ids:
continue
seen_ids.add(comment_id)
# 处理新评论
异常处理增强:网络请求可能因各种原因失败,完善的异常处理能让爬虫更健壮:
try:
response = requests.get(url, headers=headers, params=params, timeout=10)
response.raise_for_status()
data = response.json()
if data.get('errorCode'):
raise Exception(f"接口返回错误:{data.get('errorCode')}")
except requests.exceptions.RequestException as e:
print(f"请求失败:{str(e)}")
# 可以在这里添加重试逻辑或代理切换
except json.JSONDecodeError:
print("响应不是有效的JSON格式")
except KeyError as e:
print(f"缺少必要字段:{str(e)}")
更多推荐



所有评论(0)