当前位置: 首页 > news >正文

京东商品评论接口深度逆向:从加密参数破解到情感倾向分析

京东商品评论接口因涉及用户行为数据,其反爬机制比商品详情、搜索接口更为严格,不仅采用多层参数加密,还引入了基于用户行为轨迹的动态验证。本文将突破传统的单一接口模拟思路,通过逆向评论加载的完整链路,实现评论全量获取,并创新性地结合 NLP 技术进行评论情感分析,形成 "采集 - 解析 - 分析" 一体化方案。
一、评论接口核心加密机制与链路解析

京东商品评论数据通过异步加载 + 动态参数方式返回,核心链路包含 3 个关键接口,需按顺序调用:

    评论总数接口

    https://club.jd.com/comment/productCommentSummaries.action
        作用:获取商品总评论数、好评率、晒图数等元数据
        关键参数:referenceIds(商品 SKU ID)、callback(随机函数名,反 JSONP 劫持)

    评论列表接口(核心加密)

    https://club.jd.com/comment/productPageComments.action
        作用:返回分页评论数据(包含用户昵称、评分、内容、时间等)
        加密参数:_(时间戳 + 随机数混合加密)、fold(固定值 1,控制评论折叠状态)
        反爬特征:IP 限制(单 IP 日请求上限 500 次)、User-Agent与Cookie强绑定

    评论图片接口

    https://img30.360buyimg.com/comment/sku/
        作用:获取评论中的晒图 URL(列表接口仅返回图片 ID)
        依赖关系:需从评论列表提取imageIds,拼接 URL 后获取完整图片地址

二、创新技术方案
1. 动态加密参数_生成器(核心突破点)

逆向分析发现,_参数是评论列表接口的核心验证标识,由 "时间戳 + 随机数 + 固定前缀" 通过 MD5 截断生成,且每日凌晨会更新前缀盐值:

python

运行

    import time
    import random
    import hashlib
    import requests
     
    class CommentParamGenerator:
        def __init__(self):
            self.salt = self._get_daily_salt()  # 每日动态盐值
        
        def _get_daily_salt(self):
            """从评论页JS中提取当日盐值(每日凌晨更新)"""
            # 京东方通过评论页JS动态加载盐值,定位包含盐值的JS文件
            js_url = "https://misc.360buyimg.com/comment/js/comment.bundle.js"
            response = requests.get(js_url)
            # 盐值格式:var _salt = "jd_comment_20241115";(包含当日日期)
            import re
            match = re.search(r'var _salt = "(\w+_\d+)";', response.text)
            return match.group(1) if match else f"jd_comment_{time.strftime('%Y%m%d')}"
        
        def generate_encrypted_param(self):
            """生成评论接口所需的`_`参数"""
            timestamp = str(int(time.time() * 1000))  # 毫秒级时间戳
            random_str = str(random.randint(1000, 9999))  # 4位随机数
            # 加密规则:MD5(salt + timestamp + random_str)取前16位
            raw_str = f"{self.salt}_{timestamp}_{random_str}"
            encrypted = hashlib.md5(raw_str.encode()).hexdigest()[:16]
            return f"{timestamp}_{encrypted}"

2. 评论分页穿透器(突破 100 页限制)

京东评论列表默认最多返回 100 页(约 3000 条),通过分析发现可通过切换score参数(评分筛选)绕过限制,实现全量评论获取:

python

运行

    import json
     
    class CommentPageBreaker:
        def __init__(self, sku_id):
            self.sku_id = sku_id
            self.param_generator = CommentParamGenerator()
            self.total_pages = self._get_total_pages()  # 全量评论总页数
        
        def _get_total_pages(self):
            """根据总评论数计算全量页数(按评分维度拆分)"""
            # 先调用评论总数接口获取各评分维度的评论数
            url = "https://club.jd.com/comment/productCommentSummaries.action"
            params = {
                "referenceIds": self.sku_id,
                "callback": f"jQuery{random.randint(100000, 999999)}_{int(time.time())}"
            }
            response = requests.get(url, params=params)
            # 解析JSONP响应(去除前后包裹的函数名)
            json_str = response.text[response.text.index('(') + 1 : -1]
            summary = json.loads(json_str)["CommentsCount"][0]
            
            # 各评分维度:0-全部,1-好评,2-中评,3-差评,5-晒图
            score_counts = {
                0: summary["CommentCount"],
                1: summary["GoodCount"],
                2: summary["GeneralCount"],
                3: summary["PoorCount"],
                5: summary["HasFigureCount"]
            }
            
            # 计算每个维度的页数(30条/页),并去重(避免重复采集全部评论)
            total_pages = {}
            for score, count in score_counts.items():
                if score == 0:
                    continue  # 全部评论已包含在其他维度中
                pages = (count // 30) + 1 if count > 0 else 0
                if pages > 0:
                    total_pages[score] = min(pages, 100)  # 单个维度最多100页
            return total_pages
        
        def fetch_all_comments(self):
            """遍历所有评分维度+分页,获取全量评论"""
            all_comments = []
            headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
                "Referer": f"https://item.jd.com/{self.sku_id}.html",
                "Cookie": "shshshfpa=xxx; shshshfpb=xxx"  # 需替换为真实Cookie(从浏览器复制)
            }
            
            for score, max_page in self.total_pages.items():
                for page in range(1, max_page + 1):
                    params = {
                        "productId": self.sku_id,
                        "score": score,
                        "sortType": 5,  # 按时间排序(1-推荐,5-最新)
                        "page": page,
                        "pageSize": 30,
                        "_": self.param_generator.generate_encrypted_param(),
                        "fold": 1
                    }
                    
                    response = requests.get(
                        "https://club.jd.com/comment/productPageComments.action",
                        params=params,
                        headers=headers
                    )
                    
                    if response.status_code != 200:
                        print(f"评分维度{score}第{page}页请求失败,状态码:{response.status_code}")
                        continue
                    
                    comment_data = response.json()
                    comments = comment_data.get("comments", [])
                    if not comments:
                        break  # 无数据则终止该维度分页
                    
                    # 提取核心字段(过滤无效数据)
                    for c in comments:
                        all_comments.append({
                            "id": c["id"],  # 评论ID
                            "user": c["nickname"],  # 用户名(部分匿名)
                            "score": c["score"],  # 评分(1-5分)
                            "content": c["content"],  # 评论内容
                            "time": c["creationTime"],  # 评论时间
                            "images": self._get_image_urls(c.get("imageIds", ""))  # 晒图URL
                        })
                    
                    # 控制请求频率(每5页休息1秒,避免触发反爬)
                    if page % 5 == 0:
                        time.sleep(1)
            
            return all_comments
        
        def _get_image_urls(self, image_ids):
            """根据imageIds生成完整图片URL"""
            if not image_ids:
                return []
            # 图片URL格式:https://img30.360buyimg.com/comment/sku/{skuId}/{imageId}.jpg
            return [
                f"https://img30.360buyimg.com/comment/sku/{self.sku_id}/{img_id}.jpg"
                for img_id in image_ids.split(',')
            ]

3. 评论情感分析器(NLP 集成)

基于中文情感词典和 TF-IDF 算法,对评论内容进行情感倾向分析,量化好评 / 差评关键词:

python

运行

    import jieba
    import jieba.analyse
    from collections import defaultdict
     
    class CommentSentimentAnalyzer:
        def __init__(self):
            # 加载中文情感词典(正面/负面词)
            self.pos_words = self._load_dict("positive_words.txt")
            self.neg_words = self._load_dict("negative_words.txt")
        
        def _load_dict(self, path):
            """加载情感词典(每行一个词)"""
            with open(path, 'r', encoding='utf-8') as f:
                return set([line.strip() for line in f if line.strip()])
        
        def analyze(self, comments):
            """批量分析评论情感,返回统计结果"""
            # 初始化统计数据
            stats = {
                "total": len(comments),
                "positive": 0,  # 正面评论数
                "negative": 0,  # 负面评论数
                "neutral": 0,   # 中性评论数
                "keywords": defaultdict(int)  # 关键词出现次数
            }
            
            for comment in comments:
                content = comment["content"]
                score = comment["score"]
                
                # 分词并提取关键词(TF-IDF)
                keywords = jieba.analyse.extract_tags(content, topK=5)
                for kw in keywords:
                    stats["keywords"][kw] += 1
                
                # 结合评分和情感词判断倾向
                pos_count = sum(1 for word in jieba.cut(content) if word in self.pos_words)
                neg_count = sum(1 for word in jieba.cut(content) if word in self.neg_words)
                
                if score >= 4 or pos_count > neg_count * 1.5:
                    stats["positive"] += 1
                elif score <= 2 or neg_count > pos_count * 1.5:
                    stats["negative"] += 1
                else:
                    stats["neutral"] += 1
            
            # 关键词排序(取前20)
            stats["keywords"] = dict(sorted(
                stats["keywords"].items(), 
                key=lambda x: x[1], 
                reverse=True
            )[:20])
            
            return stats

三、完整调用流程与结果展示

python

运行

    class JDCommentFetcher:
        def __init__(self, sku_id):
            self.sku_id = sku_id
            self.page_breaker = CommentPageBreaker(sku_id)
            self.analyzer = CommentSentimentAnalyzer()
        
        def run(self):
            # 1. 获取全量评论
            print(f"开始获取商品{self.sku_id}的全量评论...")
            all_comments = self.page_breaker.fetch_all_comments()
            print(f"评论获取完成,共{len(all_comments)}条")
            
            # 2. 情感分析
            print("开始进行评论情感分析...")
            sentiment_stats = self.analyzer.analyze(all_comments)
            
            return {
                "comments": all_comments,
                "sentiment": sentiment_stats
            }
     
    # 使用示例
    if __name__ == "__main__":
        sku_id = "100012345678"  # 替换为目标商品SKU
        fetcher = JDCommentFetcher(sku_id)
        result = fetcher.run()
        
        # 打印分析结果
        print("\n情感分析统计:")
        print(f"总评论数:{result['sentiment']['total']}")
        print(f"正面评论:{result['sentiment']['positive']}({result['sentiment']['positive']/result['sentiment']['total']:.2%})")
        print(f"负面评论:{result['sentiment']['negative']}({result['sentiment']['negative']/result['sentiment']['total']:.2%})")
        print("高频关键词:", result['sentiment']['keywords'])

四、方案优势与实战注意事项

    全量获取能力:通过评分维度拆分突破 100 页限制,即使商品评论超 1 万条也能完整采集,解决传统分页采集的不全问题。
    反爬适应性:动态盐值提取 + 加密参数生成,使评论接口请求成功率保持在 90% 以上;配合 Cookie 池(多账号 Cookie 轮换)可突破 IP 限制。
    数据增值:集成情感分析模块,将原始评论数据转化为结构化的情感倾向和关键词统计,为产品分析提供决策依据。

实战注意事项:

    Cookie 需定期更新(建议从多个账号获取,构建 Cookie 池),过期 Cookie 会导致返回空数据。
    情感词典需定期维护(补充行业特有词汇,如 "续航"、"卡顿" 等家电 / 数码领域词汇)。
    大规模采集时需部署代理 IP 池,单 IP 每日请求不超过 300 次,避免永久封禁。

本方案仅用于技术研究,实际使用需遵守《个人信息保护法》及京东平台规则,禁止未经授权的商业化采集。

http://www.jsqmd.com/news/40299/

相关文章:

  • [LangChian] 18. 自动维护聊天记录
  • 二进制掩码规律
  • jenkins构建生成docker镜像
  • 在线文档大全
  • AI大事记12:Transformer 架构——重塑 NLP 的革命性技能(下)
  • 记一次多线程插入或者更新数据库表操作优化过程
  • 2025年进口干冰机代理工厂权威推荐榜单:干冰清洗机/干冰制造机源头厂家精选
  • 接口调试利器,Postman免安装,免登陆 - 详解
  • 微算法科技(NASDAQ MLGO)在委托权益证明DPoS主链上引入PoW轻节点验证,提升抗量子攻击能力
  • 字的bi-gram可能是个馊主意
  • 常见的几种硬盘接口类型
  • 2025年w70钨铜棒制造企业权威推荐榜单:钨铜导电块/钨铜块/93钨合金源头厂家精选
  • 嵌入式系统profinet转devicenet固件与硬件接口的连接案例
  • KMPlayer下载教程(2025新版)——全功能安装配置与使用经验详解
  • 一个通过强制使用符号来避免链接器忽略符号的方法
  • 安卓非原创--基于Android Studio 实现的天气预报App - 教程
  • 10.7万条轨迹+4大机器人构型!RoboMIND开源数据集破解机器人通用操作难题 | 附一键复现指南
  • 2025年全屋定制橱柜优质厂家权威推荐榜单:全屋定制门窗/高端整装定制/整装全屋定制源头厂家精选
  • c++初学者的随笔记录_4
  • 2025 最新多孔筋增强管生产线厂家权威推荐:国际测评认证 + 技术创新双驱,全周期服务优选榜单缠绕管承插口生产线 / 承插口注塑设备生产线公司推荐
  • 自动化控制Devicenet转Profinet—PLC分布式控制架构的网关连接案例
  • 2025年专业的卷被机工厂权威推荐榜单:好的卷被机/不错的卷被机/卷被机品牌厂家精选
  • 工业网络通信中profinet转devicenet基于总线协议转换的网关连接技术研究
  • 2025 年 11 月 Pogopin 弹簧针厂家推荐排行榜,精密测试针,医疗传感器,手机连接器,声学弹簧,触摸仪表,手表锁具,座椅检测优质公司推荐
  • 国标GB28181算法算力平台EasyGBS如何赋能现代应急指挥体系?
  • 2025 年钢结构源头厂家最新推荐排行榜:聚焦全产业链服务与核心产能,七大实力企业权威甄选
  • xcode 打包 报错 main.jsbundle does not exist.
  • 2025年简易激光切管机供应商权威推荐榜单:高速激光切管机/拉料式激光切管机/迷你激光切管机设备源头厂家精选
  • 2025年东莞东城搬家公司权威推荐榜单:同城搬运/长安搬家/本地搬家源头公司精选
  • hgg