引言
在电商运营、市场调研和竞品分析等场景中,获取商品的详细信息及用户评论是至关重要的环节。某东作为国内领先的电商平台,其商品数据蕴含着丰富的市场洞察和用户需求信息。无论是价格监控、竞品对比,还是选品决策,爬取某东商品的价格、评论和销量数据都是数据分析的基础工程。
然而,某东的反爬体系经过多年迭代,早已不是简单加个User-Agent就能绕过的级别。平台部署了多层反爬机制,包括IP封禁、请求头校验、访问频率限制、动态参数加密等。如果你用同一个IP地址在短时间内频繁访问,轻则弹出验证码,重则IP直接被封。
本文将从零开始,系统介绍如何爬取某东商品的价格、评论和销量数据,并以此为基础构建一套实用的竞品分析工具。
免责声明:本文仅限技术学习与交流,请勿将爬虫技术用于商业用途或对目标网站造成过大压力。请遵守目标网站的robots.txt协议及相关法律法规。

一、某东反爬体系全景解析
在动手写代码之前,必须先搞清楚某东的"防御工事"是如何构建的。
1.1 某东反爬的核心机制
某东的反爬机制是"多层级、动态化、行为导向"的。具体来说,主要包括以下几个层面:
IP频率限制:这是最基础也最有效的防线。某东会限制来自同一IP地址的频繁访问。实测表明,同一IP连续请求超过10次/分钟就会触发临时封禁。而且这个限制是动态的------夜间阈值虽然比白天宽松约30%,但在大促期间反而更严。常规的分布式爬虫策略在某东平台上效果有限。
请求头校验:某东会检查请求是否来自真实浏览器。缺少必要请求头或使用默认User-Agent的请求会被直接拦截。
动态参数加密 :某东的API请求中包含大量动态生成的加密参数(如eid、fp、_t等),这些参数与用户会话、时间戳和设备信息深度绑定。前端JavaScript会生成动态签名(如sign参数),同时通过Canvas、WebGL等技术采集浏览器指纹,确保请求的合法性和唯一性。
行为验证机制:某东会监测用户的鼠标轨迹、点击模式和页面停留时间。如果你的请求模式不符合正常用户的浏览习惯------比如没有搜索、浏览、停留页,直奔价格接口------机器学习模型一眼就能看穿。
数据渲染方式:商品详情页采用动态渲染技术,关键数据(如价格、库存)往往通过异步接口加载。传统的静态HTML解析方法无法直接获取这些数据。
1.2 为什么传统爬虫方案行不通?
新手通常会尝试直接用requests加个User-Agent去抓某东页面。但现实很骨感:某东PC端的商品详情页虽然核心数据在HTML里静态渲染,但价格等实时数据是通过异步接口加载的。更麻烦的是,部分敏感信息(如实时价格)需携带登录态Cookie、Referer等请求头。没有Cookie,返回的数据往往是空的。
某东平台接口校验严谨、数据权限划分明确,反爬管控力度较强。想要长期稳定获取数据,单纯靠"硬爬"是不现实的。
二、数据获取的两条路径
面对某东的反爬体系,目前主流的数据获取方案有两条路。
2.1 路径一:页面解析 + 接口抓包(适合个人学习)
核心思路 :通过分析某东商品页面的HTML结构和异步请求,用requests + BeautifulSoup提取数据。
某东PC端的商品详情页,核心数据全部在首屏HTML里静态渲染完成,根本不需要等JS执行。而评论区虽然有懒加载,但它的分页接口是标准RESTful风格,URL带page参数,响应是纯JSON。
优点:
-
无需申请任何API权限
-
实现简单,代码量小
-
适合个人学习和中小规模采集
缺点:
-
页面结构变化时需要更新解析逻辑
-
需要处理Cookie和请求头
-
容易被风控封禁
2.2 路径二:官方API(适合商用项目)
某东提供了两套完全独立的商品详情查询接口:
某东联盟接口 jd.union.open.goods.detail.query :适用于无某东店铺的供应链服务商、导购平台、比价工具等。优势是无需开通某东店铺,企业资质即可接入;原生附带佣金、优惠券、推广链接等分销数据。局限是不返回精准实时库存,平台设置调用频次QPS上限。
JOS商家开放平台接口 jingdong.item.get :适用于某东POP商家、自营供应商等。优势是权限层级更高,支持精确库存查询。局限是必须持有有效某东店铺。
接口返回标准结构化JSON数据,包含商品基础元数据(SKU、SPU编码、标题、品牌)、价格与实时营销数据(划线价、日常售价、实时到手价)、销售与口碑数据(总销量、月销量、好评数量、好评率)等。
选型建议:对于个人学习和中小规模采集,路径一(页面解析+接口抓包)是更务实的选择------无需申请权限,代码可控。本文后续将基于路径一展开。
三、爬虫实现:商品信息、价格、评论、销量全量采集
3.1 环境准备
pip install requests beautifulsoup4 pandas lxml fake_useragent
3.2 获取商品ID和Cookie
某东商品详情页的URL格式为:https://item.jd.com/{商品ID}.html。例如https://item.jd.com/100061438246.html,其中的100061438246就是商品ID。
Cookie获取(关键!) :某东会验证"是否登录",没有Cookie会返回空数据。获取方法:用Chrome浏览器打开某东商品页并登录;按F12打开开发者工具→点"Network"→刷新页面;随便点一个带item.jd.com的请求→在"Headers"里找"Cookie",复制整段内容。
3.3 爬取商品基本信息
商品标题、店铺名称等基本信息在商品详情页的HTML中,用requests + BeautifulSoup即可搞定。
import requests
from bs4 import BeautifulSoup
import json
import time
import random
from fake_useragent import UserAgent
class JDScraper:
def __init__(self, cookie: str, use_proxy: bool = False):
self.session = requests.Session()
self.ua = UserAgent()
self.cookie = cookie
self.use_proxy = use_proxy
# 站大爷隧道代理配置
if use_proxy:
self.proxies = {
"http": "http://隧道代理地址:端口",
"https": "https://隧道代理地址:端口"
}
else:
self.proxies = None
# 初始化时访问某东首页获取初始Cookie,模拟正常用户浏览行为
self.session.get('https://www.jd.com', headers=self._get_headers(), proxies=self.proxies)
def _get_headers(self):
return {
'User-Agent': self.ua.random,
'Cookie': self.cookie,
'Referer': 'https://www.jd.com/',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive'
}
def get_product_info(self, product_id: str) -> dict:
"""获取商品基本信息(标题、价格、店铺等)"""
url = f'https://item.jd.com/{product_id}.html'
try:
response = self.session.get(url, headers=self._get_headers(), proxies=self.proxies, timeout=10)
response.encoding = 'utf-8'
if response.status_code != 200:
print(f"请求失败,状态码: {response.status_code}")
return {}
soup = BeautifulSoup(response.text, 'lxml')
# 提取标题
title_elem = soup.find('div', class_='sku-name')
title = title_elem.text.strip() if title_elem else ""
# 提取店铺名称
shop_elem = soup.find('div', class_='shop-name')
shop_name = shop_elem.text.strip() if shop_elem else ""
# 提取商品参数
params = {}
param_items = soup.find_all('ul', class_='parameter2')
for item in param_items:
lis = item.find_all('li')
for li in lis:
text = li.text.strip()
if ':' in text:
key, value = text.split(':', 1)
params[key.strip()] = value.strip()
return {
'product_id': product_id,
'title': title,
'shop_name': shop_name,
'params': params,
'url': url
}
except Exception as e:
print(f"获取商品信息失败: {e}")
return {}
3.4 爬取商品价格
某东的价格是通过异步接口加载的,不能直接从HTML里解析。需要通过抓包找到价格接口。
通过分析某东商品页面的网络请求,可以发现价格接口的规律:
def get_product_price(self, product_id: str) -> dict:
"""获取商品实时价格(通过异步接口)"""
# 某东价格接口示例(实际地址需根据抓包确认)
price_url = f'https://p.3.cn/prices/mgets?skuIds=J_{product_id}'
try:
response = self.session.get(price_url, headers=self._get_headers(), proxies=self.proxies, timeout=10)
if response.status_code == 200:
data = response.json()
if data and len(data) > 0:
return {
'product_id': product_id,
'price': data[0].get('p', ''),
'mkt_price': data[0].get('m', ''), # 市场价
'raw': data[0]
}
except Exception as e:
print(f"获取价格失败: {e}")
return {}
注意:某东的价格接口地址和参数格式可能会变化,实际开发时需要先通过浏览器开发者工具确认当前的接口地址。
3.5 爬取商品评论
某东的评论数据通过API接口动态加载,且无需登录即可获取公开评论。评论接口的URL格式为:
https://club.jd.com/comment/productPageComments.action?productId={商品ID}&score=0&sortType=5&page={页码}&pageSize=10
参数说明:
-
score=0:全部评论(1-差评,2-中评,3-好评) -
sortType=5:按时间排序(6-按有用度) -
page:页码(从0开始) -
pageSize:每页数量def get_product_comments(self, product_id: str, max_pages: int = 10) -> list:
"""爬取商品评论(支持多页翻页)"""
all_comments = []for page in range(max_pages): url = f'https://club.jd.com/comment/productPageComments.action' params = { 'productId': product_id, 'score': 0, 'sortType': 5, 'page': page, 'pageSize': 10 } try: response = self.session.get(url, params=params, headers=self._get_headers(), proxies=self.proxies, timeout=10) if response.status_code == 200: data = response.json() comments = data.get('comments', []) if not comments: break for comment in comments: all_comments.append({ 'nickname': comment.get('nickname', ''), 'content': comment.get('content', ''), 'score': comment.get('score', 0), 'creation_time': comment.get('creationTime', ''), 'reference_name': comment.get('referenceName', ''), # 购买规格 'useful_count': comment.get('usefulCount', 0), # 有用数 'is_top': comment.get('isTop', False) }) print(f"第{page+1}页获取 {len(comments)} 条评论,累计 {len(all_comments)} 条") # 随机延迟,避免触发反爬 time.sleep(random.uniform(1, 3)) else: print(f"评论请求失败,状态码: {response.status_code}") break except Exception as e: print(f"获取评论失败: {e}") break return all_comments
3.6 爬取商品销量
某东的销量数据通常包含在商品详情页的HTML中,或者通过特定接口获取。部分实现方案通过解析页面中的"评价数"来间接估算销量。
def get_product_sales(self, product_id: str) -> dict:
"""获取商品销量和评价数据"""
url = f'https://item.jd.com/{product_id}.html'
try:
response = self.session.get(url, headers=self._get_headers(), proxies=self.proxies, timeout=10)
response.encoding = 'utf-8'
if response.status_code != 200:
return {}
soup = BeautifulSoup(response.text, 'lxml')
# 提取评价数
comment_elem = soup.find('span', class_='comment-count')
comment_count = comment_elem.text.strip() if comment_elem else "0"
# 提取好评率
rate_elem = soup.find('span', class_='good-rate')
good_rate = rate_elem.text.strip() if rate_elem else ""
return {
'product_id': product_id,
'comment_count': comment_count,
'good_rate': good_rate
}
except Exception as e:
print(f"获取销量数据失败: {e}")
return {}
3.7 完整的爬取流程
def scrape_jd_product(product_id: str, cookie: str, use_proxy: bool = True, max_comment_pages: int = 10):
"""完整爬取一个某东商品的所有数据"""
scraper = JDScraper(cookie, use_proxy=use_proxy)
print(f"开始爬取商品: {product_id}")
# 1. 获取基本信息
info = scraper.get_product_info(product_id)
print(f"标题: {info.get('title', '')}")
# 2. 获取价格
price = scraper.get_product_price(product_id)
print(f"价格: {price.get('price', '')}")
# 3. 获取销量/评价数据
sales = scraper.get_product_sales(product_id)
print(f"评价数: {sales.get('comment_count', '')}")
# 4. 获取评论
comments = scraper.get_product_comments(product_id, max_pages=max_comment_pages)
print(f"共获取 {len(comments)} 条评论")
return {
'info': info,
'price': price,
'sales': sales,
'comments': comments
}
# 使用示例
if __name__ == '__main__':
COOKIE = "你的Cookie" # 从浏览器复制
PRODUCT_ID = "100061438246" # 目标商品ID
result = scrape_jd_product(PRODUCT_ID, COOKIE, use_proxy=True, max_comment_pages=5)
# 保存数据
import pandas as pd
pd.DataFrame(result['comments']).to_csv('jd_comments.csv', index=False, encoding='utf-8-sig')
print("评论数据已保存到 jd_comments.csv")
四、构建竞品分析工具
4.1 竞品分析需要哪些数据?
一套完整的竞品分析系统需要采集以下维度的数据:
| 数据维度 | 具体字段 | 分析价值 |
|---|---|---|
| 商品基本信息 | 名称、店铺、规格参数 | 竞品识别和分类 |
| 价格数据 | 当前价格、促销价、价格历史 | 价格策略分析 |
| 销量数据 | 月销量、总销量、评价数 | 市场占有率分析 |
| 口碑数据 | 好评率、评分分布、评论内容 | 用户满意度分析 |
| 评论情感 | 正面/负面关键词、用户痛点 | 产品改进方向 |
4.2 批量爬取多个竞品
def batch_scrape_products(product_ids: list, cookie: str, use_proxy: bool = True):
"""批量爬取多个商品"""
results = {}
scraper = JDScraper(cookie, use_proxy=use_proxy)
for idx, pid in enumerate(product_ids):
print(f"\n[{idx+1}/{len(product_ids)}] 爬取商品: {pid}")
# 获取基本信息
info = scraper.get_product_info(pid)
price = scraper.get_product_price(pid)
sales = scraper.get_product_sales(pid)
results[pid] = {
'info': info,
'price': price,
'sales': sales
}
# 商品之间增加间隔
time.sleep(random.uniform(2, 5))
return results
4.3 竞品对比分析
import pandas as pd
import matplotlib.pyplot as plt
def analyze_competitors(results: dict):
"""竞品对比分析"""
data = []
for pid, item in results.items():
data.append({
'商品ID': pid,
'标题': item['info'].get('title', '')[:30],
'价格': item['price'].get('price', ''),
'评价数': item['sales'].get('comment_count', '0'),
'好评率': item['sales'].get('good_rate', '')
})
df = pd.DataFrame(data)
# 价格对比图
plt.figure(figsize=(10, 6))
df_sorted = df.sort_values('价格')
plt.bar(df_sorted['标题'], df_sorted['价格'])
plt.title('竞品价格对比')
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig('price_comparison.png')
return df
五、隧道代理:突破IP封锁的关键
5.1 为什么需要隧道代理?
即使你完美配置了User-Agent、Cookie和请求延迟,当需要批量爬取多个商品或持续监控价格变化时,IP封禁依然不可避免。价格监控场景天然具备高频、批量、规律性的特征,与普通用户行为差异非常明显。如果所有请求都来自一个固定IP,并且按时按点、分秒不差地去抓数据,平台一眼就能判定是机器操作。
某东的反爬机制同样严格:同一IP连续请求超过10次/分钟就会触发临时封禁。传统的解决方案是自行维护代理IP池,但这种方法存在两个核心痛点:
-
IP质量参差不齐:免费代理资源已被大量滥用,可用性低
-
手动维护繁琐:需要持续检测IP有效性,被封后手动更换
5.2 隧道代理的工作原理
隧道代理是传统代理的升级方案。你不需要手动维护IP池,只需配置好接入信息。每次发送请求时,隧道代理会自动把流量引导至不同的出口IP------相当于给爬虫配备了一条专属的"IP安全隧道"。
以站大爷隧道代理为例,实测数据非常亮眼:
| 指标 | 站大爷实测值 | 行业平均水平 |
|---|---|---|
| 24小时连接成功率 | 99.3% | 90%-95% |
| IP初始可用率 | 98.6% | 80%-90% |
| 强反爬采集成功率 | 98% | 约70% |
| 平均响应速度 | 88-189ms | 200-350ms |
| 故障自愈速度 | <30秒 | 3-5分钟 |
| 全国城市覆盖 | 300座+ | 200座以内 |
站大爷隧道代理的核心优势包括:
-
自动切换无感知:每次请求自动更换出口IP,无需手动干预
-
覆盖范围广泛:覆盖全国99%地域,支持300+城市IP精准定位
-
主备双隧道:持续更新IP池,稳定性有保障
-
灵活配置:支持精细化的IP轮换周期自定义
-
三种鉴权模式:白名单、用户名密码/白名单、用户名密码+白名单
5.3 在爬虫中集成站大爷隧道代理
在前文的爬虫代码中,我们已经预留了隧道代理的集成接口:
if use_proxy:
self.proxies = {
"http": "http://隧道代理地址:端口",
"https": "https://隧道代理地址:端口"
}
站大爷隧道代理的入口格式为 http://用户名:密码@tps.zdaye.com:8080。使用时只需将实际地址替换即可。
对于需要精细控制IP轮换的场景,站大爷隧道代理支持通过参数自定义IP更换频率:
# 每300秒更换一次IP
self.proxies = {
"http": "http://用户名:密码@tps.zdaye.com:8080?period=300",
"https": "http://用户名:密码@tps.zdaye.com:8080?period=300"
}
实际应用中,站大爷隧道代理可以将IP封禁率从80%以上降至5%以下,大幅提升采集的稳定性。
六、常见问题与避坑指南
6.1 请求返回空数据怎么办?
-
检查Cookie是否有效
-
确认是否携带了正确的Referer
-
某东会验证"是否登录",没有Cookie会返回空数据
6.2 IP被封怎么办?
-
使用站大爷隧道代理自动切换IP
-
增加请求间隔,避免固定频率
-
使用随机User-Agent轮换
6.3 价格接口失效怎么办?
-
某东的价格接口地址可能会变化
-
通过浏览器开发者工具重新抓包确认最新接口
-
考虑使用官方API替代
6.4 评论只能爬取部分页怎么办?
-
某东评论接口可能对总页数有限制
-
可以通过调整
pageSize参数尝试获取更多 -
结合时间范围分段采集
6.5 法律与合规提醒
-
爬取前查看某东的
robots.txt -
控制请求频率,避免对目标服务器造成过大压力
-
仅将数据用于学习和研究目的
七、总结
本文从某东的反爬机制入手,系统介绍了爬取商品价格、评论和销量数据、构建竞品分析工具的完整方案。核心要点可以概括为:
| 环节 | 关键技术 | 产出 |
|---|---|---|
| 数据获取 | 页面解析 + 接口抓包 | 商品信息、价格、评论、销量 |
| 反爬应对 | Cookie + User-Agent轮换 + 随机延迟 | 稳定的数据获取 |
| IP封禁 | 站大爷隧道代理 | 自动切换IP,稳定采集 |
| 竞品分析 | 批量采集 + 数据对比 | 价格对比、口碑分析 |
技术选型速览 :推荐"
requests+BeautifulSoup+ 接口抓包 + 站大爷隧道代理"的组合方案。某东PC端的商品核心数据在HTML中静态渲染,requests足以应对;价格和评论通过异步接口获取,抓包分析即可。配合隧道代理的自动IP切换,可以稳定支撑批量采集。
某东的商品数据是电商分析的"金矿"。通过合理的爬虫策略和数据分析方法,我们可以将这座金矿转化为可执行的洞察------了解竞品的价格策略、发现用户的真实需求、识别市场的机会窗口。
最后需要提醒的是:数据采集行为应当遵循平台规则,控制请求频率以避免对目标服务器造成过载。请遵守相关法律法规,仅将爬虫技术用于学习和研究目的。希望本文能帮助你在电商数据分析的道路上迈出坚实的一步。