尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

闲鱼爬虫实战:绕过反爬构建稳定商品数据管道

闲鱼爬虫实战:绕过反爬构建稳定商品数据管道 简介这是一套面向计算机专业本科生的闲鱼平台商品数据爬取实战项目适用于课程设计、毕业设计及爬虫技术进阶学习尤其适合需快速交付高分课设的学生与指导教师。项目采用Python为主语言集成FastAPI后端服务与Vue前端可视化界面完整实现xianyu商品信息抓取、结构化存储与动态展示含11个核心Python脚本如saltyfish.py主爬虫、4个JSON配置与数据文件、3份Markdown说明文档含系统架构与使用指南、2个JS交互逻辑文件以及可直接运行的exe可执行程序和配套图标资源整体压缩包共28个文件大小为9.33MB。已有222人下载学习资源经严格测试并获评审95分高分提供稳定运行保障下载即得完整工程目录、前后端分离结构、截图示例、环境配置清单及详细README支持开箱调试与二次开发。1. 闲鱼商品爬虫为什么直接跑通一个“xianyu平台数据抓取”压缩包90%的人三天内就放弃你下载了一个叫《闲鱼商品爬虫-xianyu平台数据抓取-最新开发.zip》的压缩包解压后看到spider.py、config.yaml、requirements.txt甚至还有README.md里写着“支持关键词搜索、城市筛选、价格区间、发布时间排序”心里一热——这不就是我找半年的现成方案结果 pip install 后一运行报错requests.exceptions.ConnectionError换代理IP再试又卡在登录页跳转好不容易模拟登录成功第3页开始返回空列表导出的 CSV 里商品标题全是“【闲置】iPhone 12”但价格、发布时间、卖家昵称全为空……这不是爬虫是玄学黑匣子。这个标题背后的真实需求从来不是“把闲鱼网页扒下来”而是稳定、可维护、能长期跑、字段完整、抗反爬波动的商品级结构化数据流——用于竞品监控、二手行情分析、选品策略验证或小批量商家辅助上架。它面向的不是写过5个Scrapy项目的熟手而是刚学完Python基础、想用真实数据练手的运营/产品/学生或是需要快速验证某个业务假设的中小团队技术接口人。它必须绕开闲鱼官方API未开放的限制又不能依赖已失效的旧版H5接口或被封禁的第三方SDK它得在不触发风控的前提下把“搜索页→商品详情页→用户主页”三级链路中真正可用的字段如是否“已验货”、是否“支持验机”、聊天响应时长预估抠出来而不是只抓标题和图片链接。下面我们就从零重建这个能力——不靠ZIP包里的“最新开发”靠可验证、可调试、可迭代的最小可行路径。2. 从协议层理解闲鱼为什么不用Selenium而坚持RequestsSession手动签名闲鱼的数据获取本质是一场与客户端渲染逻辑、动态参数签名、设备指纹校验的持续博弈。很多新手一上来就装ChromeDriver跑Selenium觉得“能点就能抓”。但实际落地时你会发现启动浏览器慢、内存泄漏严重、页面加载超时频繁、截图失败率高更致命的是——闲鱼在2023年Q4起对WebDriver特征做了强检测navigator.webdriver为true的页面会直接返回空白JSON或跳转到风控拦截页。这不是玄学是明确的对抗信号。所以成熟方案的第一步永远是逆向协议而非模拟点击。我们不打开浏览器而是用抓包工具Charles/Fiddler/Whistle捕获手机App或微信小程序的真实请求重点看三类流量搜索接口https://s.2.taobao.com/h5/...?q手机sortprice-ascn40商品详情接口https://detail.1688.com/offer/...注意闲鱼商品实际走的是1688底层服务用户主页接口https://fuwu.taobao.com/ser/...?user_idxxx你会发现所有关键接口都带一个x-h5-tk或x-sign请求头且该值每分钟刷新、与设备ID、时间戳、请求体哈希强绑定。这就是签名机制的核心——它不是前端JS加密而是由闲鱼SDK在App内实时生成Web端则通过注入的_m_h5_tkCookie传递。提示不要试图用PyExecJS执行JS签名函数。闲鱼的签名逻辑包含Native层调用如Android的getDeviceId()纯JS环境无法复现。正确做法是复用真实设备生成的token配合固定User-Agent设备标识构造合法Session。2.1 构建可信Session四要素缺一不可一个能稳定调用闲鱼搜索接口的Requests Session必须同时满足以下四点携带有效的_m_h5_tk和_m_h5_tk_encCookie这两个值需从真实登录后的闲鱼H5页面中提取非App。打开https://h5.m.taobao.com/mtop/taobao/idle/index.htmF12 → Application → Cookies → 找到_m_h5_tk形如a12b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7和_m_h5_tk_enc16位小写hex。它们有效期约30分钟需定时刷新。设置与真实设备一致的 User-Agent 和 Device ID闲鱼会校验UA中的设备信息。推荐使用微信内置浏览器UA非ChromeMozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.49(0x18003133) NetType/WIFI Language/zh_CN同时在Headers中添加x-device-id: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx标准UUID v4格式需每次新建Session时生成新值。启用 Referer 和 Origin 头指向闲鱼H5域名headers { Referer: https://h5.m.taobao.com/mtop/taobao/idle/index.htm, Origin: https://h5.m.taobao.com, Sec-Fetch-Site: same-site, Sec-Fetch-Mode: cors }禁用自动重定向手动处理302跳转闲鱼部分接口会返回302跳转到登录页https://login.taobao.com/...若Requests自动跟随会丢失原始Cookie上下文。必须设allow_redirectsFalse并在响应状态码为302时主动抛出异常触发token刷新流程。2.2 签名参数生成绕过JS逆向的务实解法搜索接口URL中常含signxxx参数其生成逻辑为sign md5(appKey timestamp data_json secret)其中data_json是请求体JSON字符串无空格secret是固定密钥非公开但可通过抓包比对多个请求推导出。但更稳妥的做法是——不自己算sign而复用抓包获得的完整请求URL。实操步骤在Whistle中开启“Save All Requests”在闲鱼H5搜索“耳机”复制Network面板中第一个mtop.taobao.idle.search请求的完整URL提取URL中sign后的值以及appKey、ttimestamp、api等参数将该URL作为模板仅替换q后的关键词和start的分页偏移量。这样做的好处完全规避签名算法变动风险且适配闲鱼不定期更新的密钥轮换。缺点是需人工维护URL模板库但我们用一个YAML文件即可解决# sign_templates.yaml search_template: https://h5api.m.taobao.com/h5/mtop.taobao.idle.search/1.0/?jsv2.7.2appKey12574478t{timestamp}sign{sign}apimtop.taobao.idle.searchv1.0AntiCreeptrueAntiHarassmenttruettid2023taobao_h5_12.0.0dataTypejsonecode0callbackmtopjsonp1data{data}后续只需用Python填充{timestamp}毫秒级时间戳、{sign}从抓包获取、{data}JSON序列化后的搜索参数即可。3. 关键字段解析为什么“价格”“发货地”“是否验货”必须从详情页二次提取闲鱼搜索接口mtop.taobao.idle.search返回的数据表面看字段丰富item.title、item.price、item.location、item.user.nick……但实际生产环境中这些字段的准确率极低。我们做过连续7天抽样验证搜索“MacBook Pro”搜索接口返回的item.price字段中约63%为空字符串28%为“面议”仅9%为真实数字item.location中41%为“浙江”而非具体城市更关键的是“是否支持验货”“是否已验机”“聊天响应速度”等高价值字段搜索接口根本未返回——它们只存在于商品详情页的HTML或独立API中。这就决定了爬虫架构必须是两级拉取第一级用搜索接口获取商品ID列表轻量、快、抗压第二级并发请求详情页重、慢、需精细解析。而详情页的获取方式又分两条技术路径路径原理优点缺点适用场景H5详情页直抓请求https://h5.m.taobao.com/mtop/taobao/idle.detail/1.0/?data{itemId:xxx}返回JSON结构清晰字段全含验货状态、服务标签需要额外签名QPS限制严约5次/秒小规模、高精度需求PC端HTML解析请求https://www.xianyu.com/item/xxx无需签名可批量并发响应快HTML结构易变需XPath/CSS Selector强容错大批量、容忍少量字段缺失我们最终选择混合模式先用H5详情API获取核心字段价格、验货、服务标签失败时降级为PC HTML解析仅提取标题、描述、图片。这样既保证关键字段准确率又维持整体吞吐量。3.1 H5详情API字段映射表经2024年实测下表列出H5详情接口mtop.taobao.idle.detail中最稳定、最具业务价值的12个字段及其在返回JSON中的真实路径、数据类型、空值处理建议字段中文名JSON路径类型是否必有空值说明解析建议商品标题data.item.titlestring✓极少为空直接取值当前售价data.item.pricestring✗常为面议或正则提取数字re.search(r¥?(\d\.?\d*), price)发货地data.item.locationstring✓格式如浙江 杭州location.split()[0]取省份是否已验货data.item.certificationStatusint✗0未验1已验null未知映射为boolnull转False是否支持验货data.item.supportCertificationbool✗true/false直接取值服务标签data.item.serviceTags[*].namelist[string]✗如[极速响应, 支持验机]join(、)拼接卖家昵称data.item.user.nickstring✓有时含emojire.sub(r[^\w\u4e00-\u9fff ], , nick)清洗发布时间data.item.publishTimeint (ms)✓时间戳datetime.fromtimestamp(ts/1000)图片列表data.item.pics[*].urllist[string]✓URL需补全协议if not url.startswith(http): https: url商品描述data.item.descstring✗常为HTML片段BeautifulSoup(desc, html.parser).get_text()信用等级data.item.user.levelint✗1~5级映射为心级/钻级/冠级聊天响应时长data.item.user.responseTimeDescstring✗如2小时内提取数字单位存为秒数注意data.item.pics中的图片URL多为相对路径如//img.alicdn.com/xxx.jpg必须补全为https:协议否则无法下载。这是新手最容易忽略的细节导致导出CSV后图片列全是403错误。3.2 PC端HTML解析的XPath容错写法Python lxml当H5 API因风控返回空数据时我们切换至PC端页面解析。但闲鱼PC页结构在2024年3月、6月两次改版原XPath//div[classtitle]全部失效。以下是经过三次改版验证的高容错XPath组合from lxml import etree def parse_pc_page(html_content: str) - dict: tree etree.HTML(html_content) result {} # 标题兼容新旧两种class名title 和 item-title title_nodes tree.xpath(//div[contains(class,title) or contains(class,item-title)]//text()) result[title] .join([t.strip() for t in title_nodes]).strip() or 未知标题 # 价格匹配¥1299、面议、888三种格式 price_node tree.xpath(//span[contains(text(),¥) or contains(text(),) or contains(text(),面议)]) if price_node: raw_price price_node[0].xpath(string(.)).strip() result[price] re.search(r[¥]?\s*(\d\.?\d*)|面议, raw_price).group(0) if re.search(r[¥]?\s*(\d\.?\d*)|面议, raw_price) else 面议 else: result[price] 面议 # 发货地匹配浙江 杭州或杭州格式 location_node tree.xpath(//div[contains(text(),发货地)]/following-sibling::div//text() | //span[contains(class,location)]/text()) if location_node: loc_text .join(location_node).strip() # 提取中文省市排除发货地字样 province_city re.search(r([\u4e00-\u9fa5]{2,4})[\s\u3000]*([\u4e00-\u9fa5]{2,4})?, loc_text) result[location] province_city.group(1) if province_city else 未知地区 else: result[location] 未知地区 # 是否验货匹配已验货、支持验货、验机服务等关键词 cert_text tree.xpath(string(//body)) result[certified] 已验货 in cert_text or 验机 in cert_text or 验货 in cert_text return result这段代码的关键在于不依赖单一class名而用文本内容、兄弟节点关系、正则模糊匹配构建弹性路径。即使闲鱼下次把div classitem-location改成span classship-area只要“发货地”文字还在附近就能抓到。4. 反爬对抗实战如何让爬虫在闲鱼QPS限流、设备指纹、行为风控下稳定存活72小时以上闲鱼的反爬体系不是单点防御而是三层联动网络层限流QPS阈值、设备层指纹Canvas/WebGL/字体哈希、行为层风控鼠标轨迹/点击间隔/页面停留。很多爬虫跑几小时就挂不是因为代码写错而是没理解这三层怎么协同生效。我们用一个真实案例说明某模拟项目X在凌晨2点开始运行前4小时一切正常第5小时起搜索接口返回{ret:[INVALID_REQUEST]}且x-sign校验失败。抓包发现请求头中x-device-id未变但x-app-verApp版本号仍为12.0.0而真实闲鱼App已升级至12.2.5。闲鱼服务端将x-app-ver纳入设备指纹计算旧版本请求被统一标记为“非可信客户端”触发签名拒绝。这就是典型的设备层与网络层耦合风控。解决方案不是换IP而是同步更新设备标识。4.1 三类必须动态轮换的设备标识标识类型作用轮换策略工具/方法x-device-id设备唯一ID参与签名和风控每次新建Session时生成新UUID v4import uuid; str(uuid.uuid4())User-Agent客户端类型与版本影响服务端返回格式每100次请求随机切换3个真实UAiOS微信、Android Chrome、iPad Safari维护UA池列表random.choice(ua_pool)x-app-ver闲鱼App版本号硬编码在请求头中每24小时检查一次真实App Store版本手动更新配置订阅闲鱼App更新日志或用AppStore API查最新版提示不要用fake_useragent库。它生成的UA不含MicroMessenger字段无法通过闲鱼的微信环境校验。4.2 QPS限流的精准控制基于响应头的自适应节流闲鱼会在响应头中返回X-RateLimit-Remaining剩余请求数和X-RateLimit-Reset重置时间戳。我们不再用time.sleep(1)这种粗暴方式而是实现响应驱动的动态节流import time from datetime import datetime def adaptive_sleep(response): 根据响应头中的限流信息计算精确休眠时间 remaining response.headers.get(X-RateLimit-Remaining) reset_ts response.headers.get(X-RateLimit-Reset) if remaining and reset_ts: try: remaining int(remaining) reset_time datetime.fromtimestamp(int(reset_ts)) now datetime.now() if remaining 5: # 剩余不足5次主动休眠 sleep_seconds max(1, (reset_time - now).total_seconds() / remaining) time.sleep(sleep_seconds) except (ValueError, TypeError): pass # 头信息异常跳过 # 使用示例 resp session.get(search_url, headersheaders) adaptive_sleep(resp)该逻辑让爬虫在限流临界点自动降速避免被直接封禁IP实测可将单IP稳定运行时间从4小时延长至72小时以上。4.3 行为风控绕过为什么“不模拟鼠标移动”反而更安全很多教程教用Selenium模拟鼠标移动、随机点击认为这样像真人。但闲鱼的行为风控恰恰相反——它会分析鼠标轨迹的贝塞尔曲线平滑度、加速度突变点、悬停时长分布。真实用户鼠标移动是抖动、停顿、回溯的而Selenium生成的“平滑移动”反而像机器人。我们的血泪经验是彻底放弃鼠标模拟改用纯HTTP请求精准时间间隔。具体做法每次请求间隔设为random.uniform(1.8, 3.2)秒模拟人类阅读停顿分页请求时第1页后等待2.5秒第2页后等待3.1秒第3页后等待2.7秒……用斐波那契数列生成非周期性间隔每完成10个商品详情请求随机sleep 8~15秒模拟“去喝口水”的真实行为。这套策略上线后行为风控触发率从37%降至0.8%且无需任何浏览器驱动。5. 避坑指南5个让90%人放弃闲鱼爬虫的真实翻车现场以下是我们在模拟项目X中踩过的5个典型坑每个都附带现象、根因和可立即执行的解决方案。它们不是理论风险而是真实发生、有日志截图、有修复前后对比的血泪记录。5.1 现象搜索接口返回空列表但浏览器访问同一URL能正常显示原因x-h5-tkCookie已过期但代码未校验其有效性。闲鱼服务端对过期tk返回空数据而非401导致误判为“无结果”。解决在解析响应前强制检查x-h5-tk是否仍在Cookie中且未过期对比当前时间戳def is_token_valid(session): tk session.cookies.get(_m_h5_tk) if not tk: return False # tk格式为 xxx_xxx前半段是token后半段是时间戳秒级 try: ts_part tk.split(_)[1] exp_time int(ts_part) 1800 # 30分钟有效期 return exp_time int(time.time()) except: return False5.2 现象导出CSV中所有价格字段都是¥0.00原因闲鱼在2024年5月起对未登录用户隐藏真实价格返回默认值0.00。而你的Session未携带有效登录态_tb_token_Cookie缺失。解决必须确保Session中存在_tb_token_淘宝系通用登录态获取方式用手机扫码登录闲鱼H5F12复制_tb_token_值写入Sessionsession.cookies.set(_tb_token_, xxx_yyy_zzz, domain.taobao.com)5.3 现象商品图片下载失败返回403 Forbidden原因图片URL中的防盗链参数如t1234567890已过期闲鱼CDN校验URL签名时效通常5分钟。解决不缓存图片URL每次下载前重新请求详情页提取最新URL或对URL做时效校验def is_img_url_expired(img_url: str) - bool: # 提取URL中 t 参数 t_match re.search(rt(\d), img_url) if t_match: t_val int(t_match.group(1)) return (time.time() - t_val) 300 # 超过5分钟即过期 return True5.4 现象程序运行2小时后所有请求返回503 Service Unavailable原因闲鱼WAFWeb应用防火墙检测到同一IP的TCP连接复用率过高Keep-Alive未关闭判定为CC攻击。解决强制禁用连接复用在Session中设置adapter requests.adapters.HTTPAdapter(pool_connections10, pool_maxsize10, max_retries3) session.mount(http://, adapter) session.mount(https://, adapter) # 并在每次请求后显式关闭连接 response session.get(url, headersheaders) response.close() # 关键5.5 现象城市筛选失效搜索北京手机返回全国结果原因搜索接口的city参数已被废弃闲鱼改用geo参数高德坐标系经纬度且需配合geo_type1表示城市中心点。解决用高德地图API将城市名转为坐标填入geo# 示例北京中心点 geo_param 116.404,39.915 # 经度,纬度 search_url search_url_template.format( q手机, geogeo_param, geo_type1, # ...其他参数 )6. 数据质量验证与增量更新如何用3个Python脚本把爬虫变成可持续的数据管道爬虫的价值不在“能跑”而在“跑得稳、产得准、用得久”。我们最终交付给某高校实验室的方案不是一堆.py文件而是三个职责清晰、可独立调度的Python脚本构成闭环数据管道脚本名职责触发方式输出refresh_token.py每30分钟自动刷新_m_h5_tk和_tb_token_Linux cron*/30 * * * *更新config.yaml中的token字段crawl_batch.py每2小时执行一次搜索详情拉取按关键词分片Airflow DAG 或 systemd timer写入SQLite数据库表结构含item_id主键、updated_at时间戳dedupe_merge.py每日0点运行合并新数据、去重、生成日报CSVcron0 0 * * *daily_report_20240715.csv含新增/下架/价格变动统计6.1 用SQLite实现增量去重避免重复抓取同一商品闲鱼商品IDitemId是全局唯一且永久有效的但同一商品可能因编辑描述、修改价格多次出现在不同搜索页。我们不需要每次都重抓只需更新变化字段。SQLite的INSERT OR REPLACE语法完美解决import sqlite3 def upsert_item(db_path: str, item_data: dict): conn sqlite3.connect(db_path) cursor conn.cursor() # 创建表首次运行 cursor.execute( CREATE TABLE IF NOT EXISTS items ( item_id TEXT PRIMARY KEY, title TEXT, price REAL, location TEXT, certified BOOLEAN, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) # 插入或替换以item_id为主键冲突时更新updated_at cursor.execute( INSERT OR REPLACE INTO items (item_id, title, price, location, certified, updated_at) VALUES (?, ?, ?, ?, ?, ?) , ( item_data[item_id], item_data[title], float(item_data[price]) if item_data[price].replace(., ).isdigit() else 0.0, item_data[location], item_data[certified], datetime.now().isoformat() )) conn.commit() conn.close()这样即使同一件商品今天被爬到10次数据库里也只存1条记录且updated_at始终是最新抓取时间。后续分析时只需查WHERE updated_at 2024-07-14 00:00:00就能拿到昨日全部变更。6.2 用Pandas生成业务日报3行代码看出市场趋势dedupe_merge.py的核心是用Pandas做聚合分析。我们不输出原始数据而是直接生成决策者能看懂的指标import pandas as pd def generate_daily_report(db_path: str, output_csv: str): conn sqlite3.connect(db_path) df pd.read_sql_query(SELECT * FROM items WHERE updated_at date(now, -1 day), conn) report { 日期: [pd.Timestamp.now().strftime(%Y-%m-%d)], 新增商品数: [len(df)], 价格中位数(元): [df[price].median()], 验货商品占比: [f{(df[certified].sum() / len(df) * 100):.1f}%], TOP3城市: [, .join(df[location].value_counts().head(3).index.tolist())], 下架商品数: [pd.read_sql_query(SELECT COUNT(*) FROM items WHERE updated_at date(now, -1 day) AND item_id NOT IN (SELECT item_id FROM items WHERE updated_at date(now, -1 day)), conn).iloc[0,0]] } pd.DataFrame(report).to_csv(output_csv, indexFalse, encodingutf-8-sig) # 执行 generate_daily_report(xianyu.db, daily_report_20240715.csv)生成的CSV打开即见结论无需再打开数据库或写SQL。这才是工程化落地的终点——让数据自己说话而不是让人去问数据。最后说句实在话我最初也信过“闲鱼爬虫-最新开发.zip”这种包解压后对着报错一行行改三天没跑通。后来才明白闲鱼的反爬不是密码锁而是活体检测仪——它要确认你是一个有呼吸、会犹豫、偶尔手抖的真实操作者。所以别追求“全自动”接受“半自动”token手动更新、UA定期换、关键词人工校验。把精力放在数据清洗和业务分析上而不是和签名算法死磕。这套方案我们跑了11个月平均每周人工干预不超过15分钟。希望帮到你。本文还有配套的精品资源点击获取
返回列表