[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-post-cn-tiktok-shop-shangpin-shuju-caiji":3},{"success":4,"payload":5,"related":84,"errors":160},true,{"id":6,"slug":7,"title":8,"excerpt":9,"body_html":10,"meta_title":11,"meta_description":12,"canonical_url":13,"canonical_override":14,"og_title":15,"og_description":16,"og_image_url":17,"faq_json":18,"key_takeaways":34,"allow_index":4,"noindex_google_only":40,"allow_follow":4,"published_at":41,"updated_at":42,"content_updated_at":43,"featured_image_url":44,"featured_image_alt":45,"card_image_url":46,"reading_time_minutes":47,"author":48,"categories":52,"tags":59,"alternates":76,"locale_paths":82,"views":83},215,"tiktok-shop-shangpin-shuju-caiji","采集 TikTok Shop 商品数据用于市场调研","官方渠道优先：TikTok Shop Partner API 和 Research API 实际开放了哪些数据，什么时候授权数据更划算，以及如何在正确的市场里慢速读取公开商品页。","\u003Cp>如果你需要 TikTok Shop 商品数据来做定价或选品调研，应当先走官方渠道。TikTok Shop Partner API 提供完整的商品目录和订单数据，但仅限于授权了你的应用的店铺。TikTok Research API 返回欧盟店铺的商品、价格和销量字段，但只对获批的非商业研究者开放。大多数研究竞品的商业团队，最终要么从分析厂商那里购买授权数据，要么搭一个慢速、克制的采集器，读取公开商品页本来就发布的结构化数据，并通过目标市场所在国家的 IP 访问。\u003C\u002Fp>\n\n\u003Cp>本文面向市场调研而写，与 TikTok 没有关联，也未获其背书，不涉及 TikTok 私有的、带签名的内部 API。\u003C\u002Fp>\n\n\u003Ch2>团队真正需要哪些 TikTok Shop 商品数据\u003C\u002Fh2>\n\n\u003Cp>大多数调研问题归结起来是八个左右的字段：商品标题、当前价格、划线价（由此得出折扣）、销量、店铺、评分和评价数、类目，以及哪些达人在推广这件商品。\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>价格调研。当前价格、划线价、币种和进行中的促销，在同款或同类商品的不同卖家之间比较。这需要一致的时间戳和固定的市场。英国店面的价格和德国店面的价格是两条不同的观测，而不是一条带噪声的观测。\u003C\u002Fli>\n\u003Cli>选品分析。某家店铺覆盖哪些类目，上架了多少 SKU 和变体，价格在类目中处于什么位置。通常每周一次快照就够了。\u003C\u002Fli>\n\u003Cli>趋势发现。按类目统计销量和评价数随时间的变化。绝对数值不如斜率重要，这一点倒是幸运，因为显示的销量本身就不精确（下文详述）。\u003C\u002Fli>\n\u003Cli>达人与联盟关系梳理。哪些达人链接到了某件商品。这是法律分量最重的字段，因为它涉及的是人，而不是商品。\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>如果你为其他电商平台搭建过价格追踪器，整体结构会很熟悉。我们关于\u003Ca href=\"\u002Fcn\u002Fblog\u002Fjingzhengduishou-jiage-jiankong\">竞品价格自动监控\u003C\u002Fa>和\u003Ca href=\"\u002Fcn\u002Fblog\u002Fdili-dingxiang-jiage-jiankong\">跨市场价格追踪\u003C\u002Fa>的指南介绍了数据管道部分。TikTok Shop 的不同之处在于，商品目录有很大一部分受市场和登录状态限制。\u003C\u002Fp>\n\n\u003Ch2>获取 TikTok Shop 数据的官方渠道\u003C\u002Fh2>\n\n\u003Cp>结构化的 TikTok Shop 数据有三个正当来源，但没有一个会直接把竞争对手的商品目录交给商业团队。即便如此也要先看看：前两个在符合条件时免费，第三个往往比自建更便宜。\u003C\u002Fp>\n\n\u003Ch3>TikTok Shop Partner Center 与 TTS API\u003C\u002Fh3>\n\n\u003Cp>\u003Ca href=\"https:\u002F\u002Fpartner.tiktokshop.com\u002Fdocv2\u002Fpage\u002Ftts-api-concepts-overview\" rel=\"noopener noreferrer\">TikTok Shop Partner Center\u003C\u002Fa> 是开发者为 TikTok Shop 开放平台注册应用的地方。TikTok 官方概述称，TTS API 提供对“经授权的 TikTok Shop 数据，包括商品目录、订单、发货、支付等”的访问。其接口类别包括 Product、Order、Fulfillment、Return &amp; Refund、Logistics、Promotion、Finance、Seller、Authorization、Events、Data Reconciliation 和 Supply Chain API。\u003C\u002Fp>\n\n\u003Cp>关键词是\u003Cem>经授权\u003C\u002Fem>。文档列出了三种授权类型：卖家（店铺授予你的应用访问权限）、达人和合作伙伴。每一种都取决于数据所有者主动同意。如果你本身是卖家，或者是客户为卖家的工具厂商，这就是你能拿到的最好数据：这些店铺的精确价格、真实库存和真实订单数。但它不能用来查看没有接入你应用的店铺。文档还\u003Ca href=\"https:\u002F\u002Fpartner.tiktokshop.com\u002Fdocv2\u002Fpage\u002Fregions-and-languages\" rel=\"noopener noreferrer\">列出了支持的地区和语言\u003C\u002Fa>，并注明可用范围会随着新市场开放而变化。\u003C\u002Fp>\n\n\u003Ch3>TikTok Research API：确实包含欧盟店铺数据\u003C\u002Fh3>\n\n\u003Cp>\u003Ca href=\"https:\u002F\u002Fdevelopers.tiktok.com\u002Fproducts\u002Fresearch-api\" rel=\"noopener noreferrer\">TikTok Research API\u003C\u002Fa> 主要以公开视频、评论和账号数据为人所知，但它还有三个常被忽略的 TikTok Shop 接口。\u003Ca href=\"https:\u002F\u002Fdevelopers.tiktok.com\u002Fdocs\u002Fen\u002Fresearch-api-specs-query-tiktok-shop-info\" rel=\"noopener noreferrer\">店铺信息\u003C\u002Fa>接口返回店铺评分、评价数、已售件数和一个表现值。\u003Ca href=\"https:\u002F\u002Fdevelopers.tiktok.com\u002Fdocs\u002Fen\u002Fresearch-api-specs-query-tiktok-shop-products\" rel=\"noopener noreferrer\">店铺商品\u003C\u002Fa>接口返回商品名称、价格、描述、销量、评价数以及 1–5 星分布。\u003Ca href=\"https:\u002F\u002Fdevelopers.tiktok.com\u002Fdocs\u002Fen\u002Fresearch-api-specs-query-tiktok-shop-reviews\" rel=\"noopener noreferrer\">商品评价\u003C\u002Fa>接口返回评价文本、评分和时间戳。三个接口的范围都限于在欧盟可售的店铺和商品，每次调用最多返回 10 条结果。\u003C\u002Fp>\n\n\u003Cp>门槛在于资格。访问权限面向美国、EEA、英国、加拿大和瑞士的学术机构，欧盟的非营利研究组织，以及部分研究青少年安全的巴西机构。申请者必须“独立于商业利益”。\u003Ca href=\"https:\u002F\u002Fdevelopers.tiktok.com\u002Fdocs\u002Fen\u002Fresearch-api-faq\" rel=\"noopener noreferrer\">Research API FAQ\u003C\u002Fa> 给出的标准配额是每天 1,000 次请求。研究欧盟社交电商的大学团队应当申请；品牌的定价团队不符合资格，也不应借用研究合作方的访问权限。\u003C\u002Fp>\n\n\u003Ch3>来自分析厂商的授权数据\u003C\u002Fh3>\n\n\u003Cp>市面上有一整类 TikTok Shop 分析产品，通过看板或 API 出售商品排行、估算销量、达人与商品的关联以及类目趋势。购买之前先问三件事：覆盖哪些市场？销量数字是观测所得还是模型估算？能否在内部转发这些数据？说不清数字来源的厂商给你的就是估算值，你也应当这样对待。\u003C\u002Fp>\n\n\u003Ctable>\n\u003Cthead>\u003Ctr>\u003Cth>渠道\u003C\u002Fth>\u003Cth>谁能获取\u003C\u002Fth>\u003Cth>覆盖内容\u003C\u002Fth>\u003Cth>能拿到竞品数据吗？\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\u003Ctd>TTS API（Partner Center）\u003C\u002Ftd>\u003Ctd>卖家，以及获得卖家、达人或合作伙伴授权的应用开发者\u003C\u002Ftd>\u003Ctd>授权店铺的完整目录、价格、库存、订单、促销、财务\u003C\u002Ftd>\u003Ctd>不能，只限接入了你应用的店铺\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Research API 的 TikTok Shop 接口\u003C\u002Ftd>\u003Ctd>获批的非商业研究者\u003C\u002Ftd>\u003Ctd>欧盟店铺：店铺评分和销量、商品价格和销量、评价\u003C\u002Ftd>\u003Ctd>能，但仅限非商业研究\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>分析厂商\u003C\u002Ftd>\u003Ctd>任何付费者\u003C\u002Ftd>\u003Ctd>排行、估算销量、达人关联、历史数据\u003C\u002Ftd>\u003Ctd>能，在授权范围内；准确度参差不齐\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>公开商品页\u003C\u002Ftd>\u003Ctd>处于已开通市场的任何人\u003C\u002Ftd>\u003Ctd>页面对未登录访客展示的内容\u003C\u002Ftd>\u003Ctd>能，但受服务条款和 robots.txt 约束\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Ch2>公开的 TikTok Shop 页面：开放网络上有什么\u003C\u002Fh2>\n\n\u003Cp>TikTok Shop 主要是应用内体验，但在 \u003Ccode>shop.tiktok.com\u003C\u002Fcode> 也有按市场组织的浏览器店面，商品详情页位于 \u003Ccode>\u002Fpdp\u002F\u003C\u002Fcode> 路径下。你看到哪个市场取决于你从哪里连接。我们从德国的服务器请求一个 \u003Ccode>www.tiktok.com\u002Fshop\u002Fpdp\u002F…\u003C\u002Fcode> URL 时，它返回 301 跳转到 \u003Ccode>shop.tiktok.com\u002Fde\u002Fpdp\u002F…\u003C\u002Fcode>。而从同一台机器请求不带路径的 \u003Ccode>www.tiktok.com\u002Fshop\u003C\u002Fcode>，则跳转到了 \u003Ccode>shop.tiktok.com\u002Fus\u003C\u002Fcode>。\u003C\u002Fp>\n\n\u003Cp>关于市场覆盖：TikTok 新闻中心宣布\u003Ca href=\"https:\u002F\u002Fnewsroom.tiktok.com\u002Ftiktok-shop-expands-across-europe?lang=en-150\" rel=\"noopener noreferrer\">奥地利、比利时、荷兰和波兰将于 2026 年 6 月 15 日上线\u003C\u002Fa>，加入法国、德国、爱尔兰、意大利、西班牙和英国的行列。Partner Center 的地区列表（最后更新于 2026 年 7 月 6 日）还包括美国、墨西哥、巴西、日本、印度尼西亚、马来西亚、菲律宾、新加坡、泰国和越南。这样有官方来源的市场共有 20 个。第三方列表列出的更多，但我们无法用 TikTok 自己的页面核实，所以这里不收录。\u003C\u002Fp>\n\n\u003Cp>这对采集意味着三点：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>地理位置决定你看到什么。同一件商品在每个市场的价格、币种、可售情况或卖家都可能不同，甚至根本不存在。要采集英国店面，你的流量就必须来自英国。这正是这里需要支持国家定向的住宅 IP 的主要原因，例如使用 \u003Ccode>USERNAME-country-GB\u003C\u002Fcode> 这样的\u003Ca href=\"\u002Fcn\u002Fproxy\u002Fresidential\">住宅代理\u003C\u002Fa>用户名。这与伪装无关。\u003C\u002Fli>\n\u003Cli>页面高度依赖 JavaScript。店面是客户端渲染的应用。\u003Ccode>shop.tiktok.com\u003C\u002Fcode> 的响应头中包含 \u003Ccode>x-processed-by: Modern.js\u003C\u002Fcode>，这是字节跳动开源的 Web 框架。预计需要真实浏览器。\u003C\u002Fli>\n\u003Cli>存在拦截页。从我们的数据中心测试机访问时，\u003Ccode>shop.tiktok.com\u002Fde\u003C\u002Fcode> 和 \u003Ccode>shop.tiktok.com\u002Frobots.txt\u003C\u002Fcode> 都返回了一个标题为 “Security Check”、会加载 CAPTCHA 脚本的 HTML 页面。我们就此止步。破解或绕过这个挑战不在本文范围内；如果你的采集器反复碰到它，正确的做法是放慢或停止。\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Ch2>商品页上的数据在哪里\u003C\u002Fh2>\n\n\u003Cp>在现代店面上，你想要的字段很少整齐地放在 HTML 里。它们通常位于以下三处之一，按稳定性从高到低排列：遵循 \u003Ca href=\"https:\u002F\u002Fschema.org\u002FProduct\" rel=\"noopener noreferrer\">schema.org Product\u002FOffer\u003C\u002Fa> 的 JSON-LD 块（为搜索引擎发布，参见 \u003Ca href=\"https:\u002F\u002Fdevelopers.google.com\u002Fsearch\u002Fdocs\u002Fappearance\u002Fstructured-data\u002Fproduct\" rel=\"noopener noreferrer\">Google 的商品标记文档\u003C\u002Fa>）、Open Graph 和商品 meta 标签，以及框架内嵌的 hydration 状态，也就是 \u003Ccode>&lt;script&gt;\u003C\u002Fcode> 标签里的一大段 JSON，应用用它来渲染页面而无需再发一次请求。\u003C\u002Fp>\n\n\u003Cp>方法就是读取页面已经发给你的内容。把 JSON-LD 和 meta 标签当作有文档的格式来解析。对于 hydration 状态，先清点这些 JSON 块（ID 和大小），自己在浏览器里看一个，然后只映射你需要的字段。有两件事明确不在范围内：调用应用在后台使用的私有 XHR 接口，以及复现它们的签名或 token 参数。伪造签名是规避，不是采集。我们从测试机上无法加载商品页（见上文的拦截页），所以不会声称 TikTok Shop 页面带有 JSON-LD。下面的代码无论有没有都同样适用。\u003C\u002Fp>\n\n\u003Ctable>\n\u003Cthead>\u003Ctr>\u003Cth>数据项\u003C\u002Fth>\u003Cth>通常所在位置\u003C\u002Fth>\u003Cth>可靠性\u003C\u002Fth>\u003Cth>备注\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\u003Ctd>商品标题\u003C\u002Ftd>\u003Ctd>JSON-LD \u003Ccode>name\u003C\u002Fcode>、\u003Ccode>og:title\u003C\u002Fcode>、hydration 状态\u003C\u002Ftd>\u003Ctd>高\u003C\u002Ftd>\u003Ctd>可能按市场本地化；以商品 ID 而不是标题作为键\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>当前价格\u003C\u002Ftd>\u003Ctd>JSON-LD \u003Ccode>offers.price\u003C\u002Fcode>、hydration 状态\u003C\u002Ftd>\u003Ctd>中\u003C\u002Ftd>\u003Ctd>因市场、变体和促销而异；始终保存币种\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>划线价 \u002F 折扣\u003C\u002Ftd>\u003Ctd>hydration 状态；JSON-LD 中很少有\u003C\u002Ftd>\u003Ctd>中低\u003C\u002Ftd>\u003Ctd>划线价由卖家设定；折扣自己计算\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>销量\u003C\u002Ftd>\u003Ctd>hydration 状态、可见标签；Research API（欧盟）\u003C\u002Ftd>\u003Ctd>低到中\u003C\u002Ftd>\u003Ctd>标签常为缩写；当作趋势使用，而不是经审计的销售额\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>评分、评价数\u003C\u002Ftd>\u003Ctd>JSON-LD \u003Ccode>aggregateRating\u003C\u002Fcode>、hydration 状态\u003C\u002Ftd>\u003Ctd>中\u003C\u002Ftd>\u003Ctd>不同市场的评价计数方式可能不同；Research API 提供欧盟的星级分布\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>店铺名称 \u002F ID\u003C\u002Ftd>\u003Ctd>hydration 状态、可见的卖家区块\u003C\u002Ftd>\u003Ctd>高\u003C\u002Ftd>\u003Ctd>保存 ID；店铺名称会变\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>类目\u003C\u002Ftd>\u003Ctd>面包屑 JSON-LD、hydration 状态\u003C\u002Ftd>\u003Ctd>中\u003C\u002Ftd>\u003Ctd>各市场类目树不同；映射到你自己的分类体系\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>变体 \u002F SKU\u003C\u002Ftd>\u003Ctd>hydration 状态\u003C\u002Ftd>\u003Ctd>中\u003C\u002Ftd>\u003Ctd>价格区间会掩盖买家实际所选变体的价格\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>达人 \u002F 联盟链接\u003C\u002Ftd>\u003Ctd>主要在应用内；部分在 hydration 状态中\u003C\u002Ftd>\u003Ctd>网页端低\u003C\u002Ftd>\u003Ctd>属于个人数据；这里厂商或合作方数据通常更好\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\n\u003Ch2>克制的采集设计\u003C\u002Fh2>\n\n\u003Cp>研究用采集器应当表现得像一个慢速、诚实的访客：一次一个页面、固定的 URL 集合、积极缓存、遇到挑战就停。TikTok 的条款和 robots 规则把界限划得比大多数网站都清楚，所以先从这里看起。\u003C\u002Fp>\n\n\u003Ch3>robots.txt 和条款实际怎么说\u003C\u002Fh3>\n\n\u003Cp>\u003Ccode>www.tiktok.com\u002Frobots.txt\u003C\u002Fcode>（获取于 2026 年 10 月 3 日）对所有 user agent 禁止 \u003Ccode>\u002Fshop\u002Fview\u002Fproduct\u002F\u003C\u002Fcode>，并对一份点名列表中的爬虫封禁整个站点，其中包括 GPTBot、ClaudeBot、CCBot、PerplexityBot 和 Bytespider。它没有提到 \u003Ccode>\u002Fshop\u002Fpdp\u002F\u003C\u002Fcode>，也没有 Sitemap 行。我们从测试机读取不到 \u003Ccode>shop.tiktok.com\u002Frobots.txt\u003C\u002Fcode>，因为它返回的是 HTML 挑战页。这是个陷阱：Python 的 \u003Ccode>urllib.robotparser\u003C\u002Fcode> 会把 HTML 页面解析为“没有规则”，也就是“全部允许”。下面的代码只信任纯文本的 200 响应。\u003C\u002Fp>\n\n\u003Cp>条款比 robots.txt 更严格。\u003Ca href=\"https:\u002F\u002Fwww.tiktok.com\u002Flegal\u002Fpage\u002Fus\u002Fterms-of-service\u002Fen\" rel=\"noopener noreferrer\">美国服务条款\u003C\u002Fa>（与 TikTok USDS Joint Venture LLC 签订，最后更新于 2026 年 7 月 15 日）禁止“出于任何目的，使用任何自动化系统或软件从平台”抓取或提取数据，“经书面批准的除外”。\u003Ca href=\"https:\u002F\u002Fwww.tiktok.com\u002Flegal\u002Fpage\u002Feea\u002Fterms-of-service\u002Fen\" rel=\"noopener noreferrer\">EEA 条款\u003C\u002Fa>的表述大致相同。这些条款能否约束未登录的访客取决于司法管辖区，但它们是优先选择官方和授权渠道的有力理由。\u003C\u002Fp>\n\n\u003Ch3>设计规则及其理由\u003C\u002Fh3>\n\n\u003Col>\n\u003Cli>\u003Cstrong>从已知 URL 列表开始，而不是爬取。\u003C\u002Fstrong>调研问题针对的是具体商品或抽样集合。爬取搜索页和类目页正是负载和服务条款风险累积的地方，而且 TikTok 的 robots.txt 本来就禁止 \u003Ccode>\u002Fsearch?\u003C\u002Fcode> 路径。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>每次运行只用一个市场，并固定下来。\u003C\u002Fstrong>对出口 IP 做国家定向，并把浏览器的 locale 和时区设成一致，让一批数据中的每条观测都来自同一个市场。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>保持低速率。\u003C\u002Fstrong>每个市场每 20–40 秒一个页面，用来对几百件商品做每日价格快照已经绰绰有余。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>按（商品 ID、市场、日期）缓存。\u003C\u002Fstrong>如果今天的英国观测已经有了，就不要再抓。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>把挑战当作停止信号。\u003C\u002Fstrong>记录下来，等待，并降低速率。不要通过轮换 IP 来绕过同一个封锁。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>不要登录。\u003C\u002Fstrong>只采集未登录访客能看到的内容。登录状态下采集会把自动化与账号及其条款绑定，还会触及个人数据。\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Ch2>一个谨慎的 Playwright Python 示例\u003C\u002Fh2>\n\n\u003Cp>下面的脚本在 Chromium 中通过国家定向代理逐个打开商品 URL。它先检查真正的 robots.txt，遇到非 200 响应或 “Security Check” 标题就停止，并提取 JSON-LD 商品数据、几个 meta 标签，以及一份内嵌 JSON 块清单供你查看。它没有使用任何 TikTok 专用的选择器，因为我们无法验证它们；任何缺失的字段都返回 \u003Ccode>None\u003C\u002Fcode>。代理设置遵循 \u003Ca href=\"https:\u002F\u002Fplaywright.dev\u002Fpython\u002Fdocs\u002Fnetwork#http-proxy\" rel=\"noopener noreferrer\">Playwright 文档中的 proxy 选项\u003C\u002Fa>。我们用 Playwright 1.55 在本地测试页面上验证了提取和 robots 逻辑，但没有在真实的 TikTok Shop 页面上运行过。\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-python\">import json\nimport random\nimport time\nimport urllib.robotparser\nfrom urllib.parse import urlsplit\n\nfrom playwright.sync_api import sync_playwright\n\n# 国家在代理用户名中指定；locale\u002F时区要与之匹配。\nPROXY = {\n    \"server\": \"http:\u002F\u002Fgate.proxyhat.com:8080\",\n    \"username\": \"USERNAME-country-GB\",\n    \"password\": \"PASSWORD\",\n}\nUA_NAME = \"acme-price-research\"  # 在 robots.txt 分组中用来匹配的标识\n\n# 你已有的商品 URL（来自你自己的目录匹配、合作方\n# 导出或人工调研），而不是通过爬取搜索页发现的 URL。\nURLS = [\n    \"https:\u002F\u002Fshop.tiktok.com\u002F...\",  # 粘贴浏览器中显示的真实商品 URL\n]\n\n\ndef robots_verdict(context, url):\n    \"\"\"Return True\u002FFalse from a real robots.txt, or None if we couldn't get one.\"\"\"\n    parts = urlsplit(url)\n    resp = context.request.get(f\"{parts.scheme}:\u002F\u002F{parts.netloc}\u002Frobots.txt\")\n    ctype = resp.headers.get(\"content-type\", \"\")\n    # 在 urllib.robotparser 中，HTML 挑战页会被解析为“没有规则”（= 全部允许），\n    # 所以只信任纯文本的 200 响应。\n    if resp.status != 200 or \"text\u002Fplain\" not in ctype:\n        return None\n    rp = urllib.robotparser.RobotFileParser()\n    rp.parse(resp.text().splitlines())\n    return rp.can_fetch(UA_NAME, url)\n\n\ndef _types(node):\n    t = node.get(\"@type\", [])\n    return t if isinstance(t, list) else [t]\n\n\ndef extract(page):\n    \"\"\"Read what the page itself publishes: JSON-LD, Open Graph\u002Fproduct meta,\n    and an inventory of embedded JSON blobs for a human to inspect.\"\"\"\n    jsonld = []\n    for raw in page.locator('script[type=\"application\u002Fld+json\"]').all_text_contents():\n        try:\n            data = json.loads(raw)\n        except json.JSONDecodeError:\n            continue\n        nodes = data if isinstance(data, list) else data.get(\"@graph\", [data])\n        jsonld.extend(n for n in nodes if isinstance(n, dict))\n\n    meta = {}\n    for prop in (\"og:title\", \"og:url\", \"product:price:amount\", \"product:price:currency\"):\n        loc = page.locator(f'meta[property=\"{prop}\"]')\n        if loc.count():\n            meta[prop] = loc.first.get_attribute(\"content\")\n\n    blobs = page.evaluate(\n        \"\"\"() =&gt; [...document.querySelectorAll('script[type=\"application\u002Fjson\"]')]\n                 .map(s =&gt; ({id: s.id || null, chars: s.textContent.length}))\"\"\"\n    )\n\n    product = next((n for n in jsonld if \"Product\" in _types(n)), None)\n    record = {\"name\": None, \"price\": None, \"currency\": None,\n              \"availability\": None, \"rating\": None, \"review_count\": None}\n    if product:\n        record[\"name\"] = product.get(\"name\")\n        offers = product.get(\"offers\") or {}\n        if isinstance(offers, list):\n            offers = offers[0] if offers else {}\n        record[\"price\"] = offers.get(\"price\") or offers.get(\"lowPrice\")\n        record[\"currency\"] = offers.get(\"priceCurrency\")\n        record[\"availability\"] = offers.get(\"availability\")\n        agg = product.get(\"aggregateRating\") or {}\n        record[\"rating\"] = agg.get(\"ratingValue\")\n        record[\"review_count\"] = agg.get(\"reviewCount\") or agg.get(\"ratingCount\")\n    return {\"record\": record, \"meta\": meta, \"json_blobs\": blobs,\n            \"has_jsonld_product\": product is not None}\n\n\ndef main():\n    results = []\n    with sync_playwright() as p:\n        browser = p.chromium.launch(proxy=PROXY)\n        context = browser.new_context(locale=\"en-GB\", timezone_id=\"Europe\u002FLondon\")\n        page = context.new_page()\n        for url in URLS:\n            verdict = robots_verdict(context, url)\n            if verdict is not True:\n                results.append({\"url\": url, \"status\": \"skipped\", \"robots\": verdict})\n                continue\n            resp = page.goto(url, wait_until=\"domcontentloaded\", timeout=45_000)\n            status = resp.status if resp else None\n            if status != 200 or page.title().strip().lower() == \"security check\":\n                # 不要尝试破解。记录下来，放慢速度，继续下一个。\n                results.append({\"url\": url, \"status\": \"challenge_or_error\", \"http\": status})\n                time.sleep(120)\n                continue\n            data = extract(page)\n            data.update({\"url\": url, \"final_url\": page.url, \"status\": \"ok\",\n                         \"fetched_at\": time.strftime(\"%Y-%m-%dT%H:%M:%SZ\", time.gmtime()),\n                         \"proxy_country\": \"GB\"})\n            results.append(data)\n            time.sleep(random.uniform(20, 40))  # 一次一个页面，慢慢来\n        browser.close()\n    print(json.dumps(results, indent=2, ensure_ascii=False))\n\n\nif __name__ == \"__main__\":\n    main()\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>当 \u003Ccode>has_jsonld_product\u003C\u002Fcode> 返回 false 时，查看 \u003Ccode>json_blobs\u003C\u002Fcode>。在普通浏览器中打开一件商品，找到包含价格的那个 JSON 块，为你需要的两三个键写一个简短、明确的映射，再加一个测试，在这些键位置变化时明确报错。关于 JavaScript 方面的更全面介绍，参见\u003Ca href=\"\u002Fcn\u002Fblog\u002Fcaiji-javascript-zhongdu-wangzhan\">采集重度依赖 JavaScript 的网站\u003C\u002Fa>。\u003C\u002Fp>\n\n\u003Ch2>数据质量陷阱\u003C\u002Fh2>\n\n\u003Cp>大多数 TikTok Shop 数据集出问题是在分析阶段，而不是采集阶段：跨市场比较价格却没有归一化，把促销价当成划线价，把四舍五入的销量当成销售额。\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>地区与币种。每一行都保存市场、币种和出口国家。按观测当天的汇率换算币种，永远不要跨市场求平均价格。\u003C\u002Fli>\n\u003Cli>限时抢购与促销。短期促销可能让显示价格变动几个小时。每天在同一时间做快照，同时记录显示价格和划线价，以免把一次促销误判为永久降价。\u003C\u002Fli>\n\u003Cli>销量取整。显示的数字常为缩写，即便页面数据中是精确整数，那也是平台给出的数字，而不是经审计的数据。使用随时间的差值，并预期它呈阶梯状而不是平滑曲线。\u003C\u002Fli>\n\u003Cli>按市场划分的商品目录。某件商品在一个市场缺失并不代表缺货，可能只是那里不卖。把“该市场不可售”与“缺货”分开记录。\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>同样的问题在我们介绍过的其他电商平台上也会出现。\u003Ca href=\"\u002Fcn\u002Fblog\u002Fguimo-zhuaqu-shein-2026\">Shein 价格情报指南\u003C\u002Fa>讨论了区域店面，\u003Ca href=\"\u002Fcn\u002Fblog\u002Ftemu-shangpin-jiage-zhuqu-zhinan-2026\">Temu 商品数据指南\u003C\u002Fa>讨论了内嵌 JSON，\u003Ca href=\"\u002Fcn\u002Fblog\u002Fcaiji-shopify-dianpu-zhinan\">Shopify 指南\u003C\u002Fa>则讨论了有文档的公开商品接口，与没有此类接口的平台形成了有益的对照。\u003C\u002Fp>\n\n\u003Ch2>法律与伦理边界\u003C\u002Fh2>\n\n\u003Cp>公开的商品列表是这个数据集中风险最低的部分，达人则是风险最高的部分。商品价格是商业信息，但当达人的账号名、主页和联盟活动关联到可识别的个人时，它们就属于 \u003Ca href=\"https:\u002F\u002Feur-lex.europa.eu\u002Feli\u002Freg\u002F2016\u002F679\u002Foj\" rel=\"noopener noreferrer\">GDPR\u003C\u002Fa> 下的个人数据。为欧盟或英国的项目采集这些数据，需要合法依据、数据最小化和保存期限，即便主页是公开的也一样。能聚合就聚合，例如“被超过 50 位达人推广的商品”，而不是为个人建立档案。\u003C\u002Fp>\n\n\u003Cp>在此之上还有前面引用的 TikTok 条款，以及商标问题：如实描述 TikTok Shop，不要把你的数据集或工具包装成与其有关联。如果调研是商业性且持续进行的，官方或授权渠道几乎总是更持久的选择。公开页面采集器最适合官方渠道覆盖不到的、范围窄、量小的核查，比如确认英国买家今天实际看到的是什么。\u003C\u002Fp>\n\n\u003Ch2>这种方法解决不了什么\u003C\u002Fh2>\n\n\u003Cp>一个克制的公开页面采集器给不了你完整的类目覆盖、达人归因、直播销量或仅限应用内的内容。它过不了拦截页，也不应该去尝试。几个月下来，它还会随着前端改版而失效。如果你需要上述任何一项，缺口在于数据授权，而不是工程。至于剩下部分的网络层面，我们的\u003Ca href=\"\u002Fcn\u002Fuse-cases\u002Fweb-scraping\">网页采集应用场景页面\u003C\u002Fa>和\u003Ca href=\"\u002Fcn\u002Flocations\">地区列表\u003C\u002Fa>列出了你可以路由经过的国家。\u003C\u002Fp>","TikTok Shop 商品数据采集与市场调研 | ProxyHat","如何获取 TikTok Shop 商品数据做价格调研：Partner API、Research API 的欧盟店铺接口、授权数据厂商，以及克制的公开页面采集。","\u002Fcn\u002Fblog\u002Ftiktok-shop-shangpin-shuju-caiji",null,"TikTok Shop 商品数据与市场调研","获取 TikTok Shop 商品数据做价格调研：Partner API、Research API 欧盟店铺接口、授权数据厂商与克制的页面采集。","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fzh\u002Ftiktok-shop-shangpin-shuju-caiji-og.webp",[19,22,25,28,31],{"question":20,"answer":21},"TikTok Shop 商品数据有官方 API 吗？","有。通过 Partner Center 提供的 TikTok Shop TTS API 开放了商品目录、价格、库存和订单数据，但仅限于授权了你的应用的店铺、达人或合作伙伴。它不能用来查看竞争对手的店铺。",{"question":23,"answer":24},"TikTok Research API 包含 TikTok Shop 数据吗？","欧盟部分包含。Research API 提供店铺信息、店铺商品（含价格和销量）以及商品评价接口，范围限于在欧盟可售的店铺和商品。只有获批的非商业研究者才能使用，例如学术机构和非营利研究组织。",{"question":26,"answer":27},"为什么 TikTok Shop 的价格在不同国家看起来不一样？","TikTok Shop 在每个市场运营独立的店面，请求会按所在位置被路由到对应市场。价格、币种、卖家和可售情况都可能不同，所以每个市场都要用该国的 IP 采集，并在每条观测记录中保存市场信息。",{"question":29,"answer":30},"TikTok 的条款允许自动采集 TikTok Shop 页面吗？","TikTok 的美国和 EEA 条款禁止未经书面批准的自动化数据提取，www.tiktok.com 的 robots.txt 也对所有爬虫禁止了 \u002Fshop\u002Fview\u002Fproduct\u002F。商业用途请优先使用官方或授权数据，任何公开页面采集都应尽量少量、慢速，并且不登录。",{"question":32,"answer":33},"TikTok Shop 的销量数字准确吗？","最好只当作方向性参考。显示的数字经常是缩写，即便是精确数字，也是平台自报而非经过审计。用它随时间的变化来发现趋势，而不要当作营收数据。",[35,36,37,38,39],"TTS API 只返回授权了你的应用的店铺、达人或合作伙伴的数据，拿不到竞品数据。","Research API 开放欧盟店铺、商品、价格和销量数据，但仅限非商业研究者。","TikTok Shop 按市场分设店面，所以每个国家都要用该国的 IP 采集。","读取 JSON-LD 和页面内嵌数据；不要碰私有签名接口和挑战页面。","TikTok 条款禁止未经批准的自动化提取，因此应优先使用官方或授权数据。",false,"2026-10-29T09:00:00+00:00","2026-10-29T09:00:19+00:00","2026-10-03T09:41:40+00:00","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fzh\u002Ftiktok-shop-shangpin-shuju-caiji-master.webp","Collecting TikTok Shop Product Data for Market Research","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog-covers\u002Fzh\u002Ftiktok-shop-shangpin-shuju-caiji-card.webp",16,{"name":49,"slug":50,"bio":51,"avatar_url":14,"website_url":14,"social_links":14},"ProxyHat Team","proxyhat-team","The ProxyHat editorial team covers proxies, web scraping, and data extraction.",[53],{"id":54,"slug":55,"name":56,"description":14,"meta_title":57,"meta_description":58,"parent_id":14},5,"dianshang","电商与竞争情报","电商数据抓取指南 | ProxyHat 博客","电商数据抓取、价格监控和竞争情报指南。",[60,64,68,72],{"id":61,"slug":62,"name":63},1,"web-scraping","网页抓取",{"id":65,"slug":66,"name":67},14,"ecommerce","电商",{"id":69,"slug":70,"name":71},15,"price-monitoring","价格监控",{"id":73,"slug":74,"name":75},18,"geo-targeting","地理定位",{"ko":77,"zh":13,"tr":78,"ru":79,"en":80,"de":81,"x-default":80},"\u002Fko\u002Fblog\u002Ftiktok-shop-sangpum-data-sujip","\u002Ftr\u002Fblog\u002Ftiktok-shop-urun-verisi-pazar-arastirmasi","\u002Fru\u002Fblog\u002Fdannye-tovarov-tiktok-shop-issledovanie","\u002Fblog\u002Ftiktok-shop-product-data-research","\u002Fde\u002Fblog\u002Ftiktok-shop-produktdaten-marktforschung",{"ko":77,"zh":13,"tr":78,"ru":79,"en":80,"de":81},2,[85,105,126,143],{"id":86,"slug":87,"title":88,"excerpt":89,"featured_image_url":90,"featured_image_alt":14,"card_image_url":91,"og_image_url":92,"published_at":93,"updated_at":94,"content_updated_at":95,"reading_time_minutes":96,"is_featured":40,"indexable":40,"views":97,"author":98,"categories":99,"tags":101},52,"dili-dingxiang-jiage-jiankong","地理定向价格监控：跨市场价格追踪实战指南","了解如何使用 ProxyHat 进行地理定向价格监控，绕过地理限制，精准追踪不同市场的商品价格。包含代理类型对比、Python 实现示例及防封禁最佳实践。","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog\u002Fdili-dingxiang-jiage-jiankong-master.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog\u002Fdili-dingxiang-jiage-jiankong-card.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog\u002Fdili-dingxiang-jiage-jiankong-og.webp","2026-02-26T03:50:37+00:00","2026-07-19T03:13:07+00:00","2026-10-03T08:29:56+00:00",9,34,{"name":49,"slug":50,"bio":51,"avatar_url":14,"website_url":14,"social_links":14},[100],{"id":54,"slug":55,"name":56,"description":14,"meta_title":57,"meta_description":58,"parent_id":14},[102,103,104],{"id":65,"slug":66,"name":67},{"id":69,"slug":70,"name":71},{"id":73,"slug":74,"name":75},{"id":96,"slug":106,"title":107,"excerpt":108,"featured_image_url":109,"featured_image_alt":14,"card_image_url":110,"og_image_url":111,"published_at":112,"updated_at":94,"content_updated_at":113,"reading_time_minutes":114,"is_featured":4,"indexable":40,"views":115,"author":116,"categories":117,"tags":119},"dianshang-shuju-caiji-daili","电商数据爬取代理：完整指南","学习如何使用住宅代用物进行电子商务数据刮刮. 包括亚马逊、Shopify和沃尔玛战略、Python和Node.js代码实例、地理目标价格监测以及基础设施规模化.","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog\u002Fdianshang-shuju-caiji-daili-master.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog\u002Fdianshang-shuju-caiji-daili-card.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog\u002Fdianshang-shuju-caiji-daili-og.webp","2026-01-12T01:55:44+00:00","2026-02-26T00:32:26+00:00",17,197,{"name":49,"slug":50,"bio":51,"avatar_url":14,"website_url":14,"social_links":14},[118],{"id":54,"slug":55,"name":56,"description":14,"meta_title":57,"meta_description":58,"parent_id":14},[120,121,122,123],{"id":65,"slug":66,"name":67},{"id":69,"slug":70,"name":71},{"id":61,"slug":62,"name":63},{"id":83,"slug":124,"name":125},"residential-proxies","住宅代理",{"id":127,"slug":128,"title":129,"excerpt":130,"featured_image_url":131,"featured_image_alt":14,"card_image_url":132,"og_image_url":133,"published_at":134,"updated_at":94,"content_updated_at":135,"reading_time_minutes":96,"is_featured":40,"indexable":40,"views":136,"author":137,"categories":138,"tags":140},40,"shishi-jiage-jiankong-jiagou","构建实时价格监控基础设施","设计和建设具有重点排队,工人池,变化检测,住宅代理轮换等功能的实时价格监测系统. 完成Python和Node.js执行指南.","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog\u002Fshishi-jiage-jiankong-jiagou-master.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog\u002Fshishi-jiage-jiankong-jiagou-card.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog\u002Fshishi-jiage-jiankong-jiagou-og.webp","2026-02-13T13:33:22+00:00","2026-02-26T00:33:08+00:00",63,{"name":49,"slug":50,"bio":51,"avatar_url":14,"website_url":14,"social_links":14},[139],{"id":54,"slug":55,"name":56,"description":14,"meta_title":57,"meta_description":58,"parent_id":14},[141,142],{"id":65,"slug":66,"name":67},{"id":69,"slug":70,"name":71},{"id":144,"slug":145,"title":146,"excerpt":147,"featured_image_url":148,"featured_image_alt":14,"card_image_url":149,"og_image_url":150,"published_at":151,"updated_at":94,"content_updated_at":152,"reading_time_minutes":96,"is_featured":40,"indexable":40,"views":153,"author":154,"categories":155,"tags":157},26,"caiji-shopify-dianpu-zhinan","如何使用代理爬取Shopify店铺：完整指南","学会如何用 JSON API 端点和住宅代理来刮刮 Shopify 存储数据. 完整的Python和Node.js代码用于提取产品,价格,和库存数据.","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog\u002Fcaiji-shopify-dianpu-zhinan-master.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog\u002Fcaiji-shopify-dianpu-zhinan-card.webp","https:\u002F\u002Fstatic.proxyhat.com\u002Fstorage\u002Fblog\u002Fcaiji-shopify-dianpu-zhinan-og.webp","2026-01-30T00:06:37+00:00","2026-10-03T08:21:34+00:00",762,{"name":49,"slug":50,"bio":51,"avatar_url":14,"website_url":14,"social_links":14},[156],{"id":54,"slug":55,"name":56,"description":14,"meta_title":57,"meta_description":58,"parent_id":14},[158,159],{"id":65,"slug":66,"name":67},{"id":61,"slug":62,"name":63},[]]