破解SHEIN反爬升级:实战列表、详情、评论页全链路数据采集

张开发
2026/4/17 10:39:47 15 分钟阅读

分享文章

破解SHEIN反爬升级:实战列表、详情、评论页全链路数据采集
1. SHEIN反爬机制深度解析最近在做一个电商数据分析项目时发现SHEIN的反爬系统又升级了。作为一个经常和数据打交道的开发者我不得不承认SHEIN的技术团队确实很拼几乎每个月都会更新他们的防护策略。这次新增的anti-in、smdeviceid、armortoken和x-gw-auth等参数校验让很多传统的爬虫方法直接失效。这些参数中最关键的是smdeviceid它实际上是一个设备指纹标识。SHEIN的后台会通过这个参数来判断请求是否来自真实用户。根据我的测试如果这个参数缺失或者不正确服务器会直接返回403错误。anti-in参数则是一个动态生成的校验值它的有效期很短通常只有几分钟。armortoken和x-gw-auth这两个参数看起来像是用来保护API网关的。它们的具体生成算法比较复杂但经过反复测试发现这两个参数和用户会话、设备信息都有关系。有意思的是不同地区的站点对这些参数的要求还不完全一样比如美国站对x-gw-auth的校验就比泰国站更严格。2. 列表页数据采集实战2.1 请求参数构造技巧列表页是数据采集的起点但也是最容易触发反爬的地方。经过多次尝试我发现SHEIN的列表页API对参数的要求非常严格。下面这个代码片段展示了我最终采用的参数构造方法def treat_page(self, region, link, page): base_url fhttps://{region.lower()}.shein.com/api/productList/info/get? if pdsearch in link: routeid_pattern re.compile(pdsearch/(.*?)/) else: routeid_pattern re.compile(-(\d).html) routeid re.search(routeid_pattern, link).group(1) if region in [BR]: _lange pt-br elif region in [PH, SG, US, MY]: _lange en elif region in [TH]: _lange th else: raise param { _ver: 1.1.8, _lang: _lange, type: selection if RecommendSelection in link else search if search in link else entity, routeId: routeid, page: page, reqSheinClub: true, isPaid: 0, source: sort, sourceStatus: 1, sort: 7, requestType: firstLoad if page 1 else pageChange }这里有几个关键点需要注意_ver参数必须和当前网站版本一致这个可以通过查看网页源代码获取_lang参数要根据不同地区设置对应的语言代码requestType在第一次请求和翻页时的值是不一样的2.2 请求头处理与设备指纹生成构造好参数只是第一步更关键的是如何生成正确的请求头。SHEIN现在会对请求头中的多个字段进行校验device gen_device(region, referer, path, random_user_agent, site_typeweb) headers { accept: application/json, text/plain, */*, accept-encoding: gzip, deflate, br, accept-language: zh-CN,zh;q0.9, referer: referer, anti-in: device.get(msg, {}).get(headers, {}).get(anti-in), smdeviceid: device.get(msg, {}).get(headers, {}).get(smdeviceid), armortoken: device.get(msg, {}).get(headers, {}).get(armortoken), x-gw-auth: device.get(msg, {}).get(headers, {}).get(x-gw-auth), user-agent: random_user_agent, x-requested-with: XMLHttpRequest, uber-trace-id: ffae1d5fbd3ecdba:ffae1d5fbd3ecdba:0:0, x-csrf-token: CmT59WLJ-n7-tWFF--_58eJMNbbrPx6qlMFk }这里最关键的gen_device函数是用来生成设备指纹的。经过逆向分析发现SHEIN的设备指纹生成算法会考虑以下因素浏览器指纹包括Canvas、WebGL等网络环境特征设备硬件信息时区设置在实际操作中我发现使用真实设备的指纹信息成功率最高。如果完全模拟生成很容易被识别出来。3. 详情页数据采集方案3.1 商品详情API分析详情页的数据采集比列表页更复杂因为SHEIN在这里加入了更多的防护措施。通过抓包分析我发现详情页的API endpoint是https://m.shein.com/{region}/api/productInfo/productDetail/get这个接口需要传递多个必要参数params { currency: currency_map[region], # 根据地区变化的货币代码 fromSpa: 1, goods_id: str(goods_id), # 商品ID imgRatio: 3-4, mallCode: 1, showFeedbackRec: 1, template: 0, version: 1.0.4, withI18n: 0, _ver: 1.1.8, _lang: _lange # 语言代码 }特别要注意的是currency参数它必须和商品所在地区匹配。比如美国站就必须用USD泰国站用THB。如果这个参数设置错误虽然有时能返回数据但价格信息会是错的。3.2 反反爬策略实践在详情页采集过程中我遇到了几个常见问题及解决方案403禁止访问这通常是因为设备指纹不完整或者过期。我的解决办法是每5次请求就重新生成一次设备指纹。goods not exist错误有时候明明商品存在却返回这个错误。经过测试发现这是因为goods_id参数格式不正确必须确保它是字符串类型而不是数字。请求频率限制SHEIN对高频请求非常敏感。我采用的方法是设置随机延迟同时在请求失败时自动退避。下面是改进后的请求代码x 0 while x 15: random_user_agent ua.random device gen_device(region, referer, path, random_user_agent) headers { referer: referer, anti-in: device.get(msg, {}).get(headers, {}).get(anti-in), smdeviceid: device.get(msg, {}).get(headers, {}).get(smdeviceid), armortoken: device.get(msg, {}).get(headers, {}).get(armortoken), x-gw-auth: device.get(msg, {}).get(headers, {}).get(x-gw-auth), user-agent: random_user_agent, x-requested-with: XMLHttpRequest, } try: response requests.get(url, headersheaders, cookiesdevice.get(msg, {}).get(cookies, {}), paramsparams, timeout20, verifyFalse) if response.status_code 403: raise Exception(403 Forbidden) break except Exception as e: x 1 time.sleep(3 * x) # 指数退避 continue4. 评论页数据采集技巧4.1 评论API调用方法评论数据是分析商品表现的重要指标但SHEIN的评论API防护也很严格。评论API的endpoint是https://m.shein.com/{region}/bff-api/product/get_goods_review_detail?调用这个API需要传递以下关键参数para { _ver: 1.1.8, _lang: _lange, comment_rank: , goods_id: , goods_spu: product_relation_id, # 商品关联ID is_picture: , local_site_abt_flag: , local_site_query_flag: , size: , sku: sku_id, # SKU ID sort: time_desc, # 按时间降序 store_code: store_code, # 店铺代码 tag_id: , tag_rule_id: typeB, store_comment_flag: 1, isLowestPriceProductOfBuyBox: 0, mainProductSameGroupId: , page: page, # 页码 cat_id: cat_id, # 类目ID }这里最容易被忽视的是tag_rule_id参数它必须设置为typeB才能获取到完整的评论数据。另外store_code和cat_id这两个参数可以从详情页的返回数据中获取。4.2 评论数据解析与存储获取到评论数据后还需要进行有效的解析和存储。评论API返回的数据结构比较复杂主要信息都在modules字段中。下面是一个典型的数据处理流程def process_comments(self, res): 处理评论数据 if not res or res[msg] ! ok: return None comment_info res[info][comment_info] processed_comments [] for comment in comment_info: comment_data { user_id: comment.get(user_id, ), user_name: comment.get(user_name, ), comment_rank: int(comment.get(comment_rank, 0)), comment_content: comment.get(comment_content, ), add_time: self.timestamp_to_date(int(comment.get(add_time, 0))), likes: int(comment.get(likes, 0)), images: [img[origin_img] for img in comment.get(img_list, [])], sku_info: comment.get(sku_info, ) } processed_comments.append(comment_data) return { total: res[info][total], average_rating: float(res[info][average_rating]), comments: processed_comments }在处理评论数据时有几个细节需要注意add_time是Unix时间戳需要转换为可读格式comment_rank是星级评分范围是1-5img_list中包含用户上传的图片需要单独提取5. 全链路采集优化建议经过多次实战我总结出几个提高采集成功率的经验设备指纹轮换不要一直使用同一个设备指纹建议每50次请求就更换一次。可以使用真实设备的指纹库来轮换。请求参数动态化像_ver这样的参数应该定期更新最好能自动从网页中抓取最新值。智能限速策略单纯的固定延迟效果不好我采用的是基于响应时间的动态限速算法def dynamic_sleep(last_response_time): 动态睡眠时间计算 base_sleep 1.5 # 基础睡眠时间 variance random.uniform(0.5, 1.5) # 随机波动 adjust_factor last_response_time / 0.5 # 根据上次响应时间调整 return base_sleep * variance * adjust_factor错误自动恢复当遇到403错误时应该自动更换IP和设备指纹而不是简单重试。数据验证机制采集到的数据应该进行完整性检查比如检查关键字段是否存在数值是否在合理范围内等。在实际项目中我还建立了一个健康度评分系统用来监控采集质量。主要包括以下几个指标请求成功率数据完整度反爬触发频率数据更新及时性通过这些指标的实时监控可以及时发现并解决采集过程中的问题。

更多文章