某东商品爬虫实战:爬取商品价格+评论+销量,做竞品分析工具

引言

在电商运营、市场调研和竞品分析等场景中,获取商品的详细信息及用户评论是至关重要的环节。某东作为国内领先的电商平台,其商品数据蕴含着丰富的市场洞察和用户需求信息。无论是价格监控、竞品对比,还是选品决策,爬取某东商品的价格、评论和销量数据都是数据分析的基础工程。

然而,某东的反爬体系经过多年迭代,早已不是简单加个User-Agent就能绕过的级别。平台部署了多层反爬机制,包括IP封禁、请求头校验、访问频率限制、动态参数加密等。如果你用同一个IP地址在短时间内频繁访问,轻则弹出验证码,重则IP直接被封。

本文将从零开始,系统介绍如何爬取某东商品的价格、评论和销量数据,并以此为基础构建一套实用的竞品分析工具。

免责声明:本文仅限技术学习与交流,请勿将爬虫技术用于商业用途或对目标网站造成过大压力。请遵守目标网站的robots.txt协议及相关法律法规。

一、某东反爬体系全景解析

在动手写代码之前,必须先搞清楚某东的"防御工事"是如何构建的。

1.1 某东反爬的核心机制

某东的反爬机制是"多层级、动态化、行为导向"的。具体来说,主要包括以下几个层面:

IP频率限制:这是最基础也最有效的防线。某东会限制来自同一IP地址的频繁访问。实测表明,同一IP连续请求超过10次/分钟就会触发临时封禁。而且这个限制是动态的------夜间阈值虽然比白天宽松约30%,但在大促期间反而更严。常规的分布式爬虫策略在某东平台上效果有限。

请求头校验:某东会检查请求是否来自真实浏览器。缺少必要请求头或使用默认User-Agent的请求会被直接拦截。

动态参数加密 :某东的API请求中包含大量动态生成的加密参数(如eidfp_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池,但这种方法存在两个核心痛点:

  1. IP质量参差不齐:免费代理资源已被大量滥用,可用性低

  2. 手动维护繁琐:需要持续检测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切换,可以稳定支撑批量采集。

某东的商品数据是电商分析的"金矿"。通过合理的爬虫策略和数据分析方法,我们可以将这座金矿转化为可执行的洞察------了解竞品的价格策略、发现用户的真实需求、识别市场的机会窗口。

最后需要提醒的是:数据采集行为应当遵循平台规则,控制请求频率以避免对目标服务器造成过载。请遵守相关法律法规,仅将爬虫技术用于学习和研究目的。希望本文能帮助你在电商数据分析的道路上迈出坚实的一步。

相关推荐
楷哥爱开发2 小时前
IPFoxy爬虫代理配置指南:从0搭建Crawl4AI网页爬虫教程
大数据·运维·人工智能·爬虫
名字还没想好☜7 小时前
Python asyncio.Queue 实战:用生产者-消费者给爬虫/任务流限速又解耦
开发语言·爬虫·python·并发·asyncio
2601_966949659 小时前
Python 量化数据质量校验:如何确保股票历史日线数据不存在缺失交易日?
开发语言·人工智能·爬虫·python·量化策略·量化·quantdash
benchmark_cc9 小时前
Claude Code + MCP + QuantDash:打造全自动量化研究流水线的终极指南
人工智能·后端·爬虫·算法·claude·mcp·quantdash
Buke..1 天前
【小程序逆向】某游快爆 AI 逆向 sign参数:AI 辅助分析与 MCP 工具链实战
前端·人工智能·爬虫·python·小程序·notepad++
码农飞哥1 天前
RAG 翻车实测 + LangGraph Agent 实时抓取修复
人工智能·爬虫·langchain·ai编程·亮数据
小白的成长路程1 天前
移动版藏内容,AI爬虫直接跳过
人工智能·爬虫
IT毕设实战小研1 天前
基于大数据的二手车数据分析与预测
java·大数据·后端·爬虫·python·算法·课程设计
深蓝电商API2 天前
AES 加密逆向思路
爬虫·aes 加密