如何使用 Playwright 构建 Shopify 爬虫
Lead Scraping Automation Engineer
摘要:
- Shopify 爬虫应当先发现商品 URL 再抽取字段。 同源的商品链接比主题的样式类更稳定。
- 公开的结构化商品数据可以在不点击每个色块的情况下暴露变体。 在依赖之前先在目标店铺上验证响应。
- Playwright 可以在读取店面数据时保持浏览器上下文。 将商品和货币请求都放在该上下文内。
- 本地示例返回变体标识符和原始价格数值。 货币换算和跨店铺匹配仍是单独的任务。
- 可免费开始。 新 Scrapeless 账号包含免费的 Scraping Browser 运行环境 —— 访问 app.scrapeless.com 注册。
引言:商品卡片不会描述每一个变体
店面上的商品卡片通常汇总了多个可购买变体。读取卡片上展示的价格有助于发现商品,但无法确定每个尺码或颜色的价格与可用性。
本 Shopify 商品数据抽取教程使用 Playwright 打开一个公开店面、发现商品链接,并在页面的浏览器上下文中读取结构化变体数据。示例刻意只针对一个商品。你可以在构建更大的目录任务前先检查返回字段。
Playwright 控制浏览器;你的抽取契约则决定什么算是一条有效记录。降价提醒工作流 解释了记录下来的观测如何成为下游事件。
在写选择器之前先决定抽取来源
最有用的来源,是包含所需字段、同时又最稳定的公开界面。
| 表面 | 适用场景 | 需要检查的限制 |
|---|---|---|
| 公共商品响应 | 变体 ID、选项数组、价格字段 | 访问权限和完整性在不同店面之间会变化 |
| 内嵌结构化数据 | 商品身份和一个已描述的报价 | 可能只汇总当前选中的报价 |
| 渲染出的商品控件 | 在尺码、市场或购买选项被选中之后的状态 | 需要针对具体店铺进行检查 |
| 商品链接 URL 模式 | 发现与去重 | 无头店面可能会使用不同的路由结构 |
示例会使用观测到的 /products/<handle> 链接,然后读取该商品的公开 .js 响应。Shopify 的 Ajax 商品接口 记录了该路由以及与本地化相关的请求方式。其变体数组有文档规定的上限;仅因为请求成功并不能声称已覆盖大型变体目录中的全部变体。
当一个店面需要交互才能暴露某个报价时,检查其控件,并在交互之后等待一个商品特定的状态标记。通用的价格选择器可能会误选推荐卡片、分期金额,或者前一个变体的价格。
前置条件与安装
本地示例需要 Node.js、pnpm 和 Playwright 的 Chromium 运行时。
请使用已安装 Playwright 包所支持的 Node.js 版本。经验证的环境使用的是 Node.js 24。云端扩展另外需要一个有效的 Scrapeless 账号以及写在 SCRAPELESS_API_KEY 中的服务 API 密钥;该凭证是前置条件,但本地采集器不需要它。
在新建的工作目录中运行以下命令:
bash
pnpm add playwright @scrapeless-ai/sdk
pnpm exec playwright install chromium
Playwright 的 浏览器库安装 将包与浏览器下载分离。将 JavaScript 示例保存为 .mjs 文件,以便 Node.js 将其视作 ES 模块。云端扩展使用 Scrapeless Node.js SDK。
构建本地“发现并抽取”采集器
该采集器会找到一个已检查的商品锚点,并从结构化响应中返回每个变体。
将以下内容保存为 shopify.mjs,并运行 node shopify.mjs。该特定商品是一个公开示例,所以在复用之前请重新核对 URL 和店铺政策。脚本会在发现锚点缺失时报告,而不是悄悄切换到其他商品。
javascript
import { chromium } from 'playwright';
const browser = await chromium.launch({ headless: true });
try {
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://www.taylorstitch.com/', {
waitUntil: 'domcontentloaded', timeout: 45000
});
const result = await page.evaluate(async () => {
const link = Array.from(document.querySelectorAll('a[href]'))
.map(a => new URL(a.href))
.find(u => u.origin === location.origin &&
/^\/products\/jack-in-valley-plaid-waffle-2610\/?$/.test(u.pathname));
if (!link) throw new Error('Expected product anchor is absent');
const root = window.Shopify.routes.root;
const productResponse = await fetch(link.pathname.replace(/\/$/, '') + '.js');
const cartResponse = await fetch(root + 'cart.js');
if (!productResponse.ok || !cartResponse.ok) {
throw new Error('Product or currency response unavailable');
}
const product = await productResponse.json();
const cart = await cartResponse.json();
if (!product.id || !Array.isArray(product.variants) || !cart.currency) {
throw new Error('Incomplete public product response');
}
return {
product_id: String(product.id), title: product.title,
source_url: link.href, currency: cart.currency,
observed_at: new Date().toISOString(),
variants: product.variants.map(v => ({
variant_id: String(v.id), options: v.options,
price_raw: v.price, compare_at_price_raw: v.compare_at_price ?? null,
available: v.available, sku: v.sku ?? null
}))
};
});
console.log(JSON.stringify(result, null, 2));
} finally {
await browser.close();
}
domcontentloaded 是导航边界,而不是保证每个小部件都已完成加载。经检查的主页会在该边界暴露商品锚点。对于其他主题,请在抽取前等待你真正需要的链接或商品控件。
只读的购物车请求会识别该浏览器上下文的呈现货币。它不会添加商品或开始结账。商品与货币读取仍发生在不同的时间点;不要将它们描述为一个原子级的库存快照。
保留返回的变体契约
结果是一个包含变体数组的产品封装,而不是一个现成的竞争对手价格历史记录。
| 字段 | 含义 | 校验规则 |
|---|---|---|
product_id |
商店的产品标识符,序列化为文本 | 必须存在 |
source_url |
发现的公共产品 URL | 保持店面主机名不变 |
currency |
当前浏览器上下文中报告的货币 | 必须在比较前已知 |
observed_at |
采集器的捕获时间 | 与变化字段一同保留 |
variant_id |
商店的变体标识符 | 必须保持与其父项的关联 |
options |
源选项值 | 保留顺序和值 |
price_raw |
响应中使用的原生单位的货币值 | 在展示前确认单位语义 |
compare_at_price_raw |
可为空的比较金额 | Null 并不意味着零 |
available |
公开的可购买信号 | 不是库存数量 |
sku |
来源的可选商家 SKU | 不是通用产品标识符 |
被检查的产品包含六个变体,并且上下文报告为 USD。上述观察表明,此路径确实从所选来源提取了真实的产品数据;但并不能证明每个 Shopify 店面都使用相同的主题或访问配置。
产品词汇有助于将身份与描述性属性同要约中会变化的商业字段区分开来。在将变体展平为仓库行时,保持这条边界。
使用 Scrapeless 开始抓取
使用 Scrapeless 加速你的网页抓取与自动化工作流!
立即注册即可获得 $5 免费额度 —— 无需信用卡。现在就在 Scrapeless 控制台 领取你的免费额度。
在不打破目录边界的前提下扩展发现范围
目录发现需要一个明确的停止规则和一组持久化的已访问 URL。
对于基于集合的抓取器,检查实际的下一页链接或店面已文档化的分页机制。保持集合发现与产品抽取分离。对规范化的产品路径进行去重,以免出现在多个集合中的同一商品变成多个产品。
记录一次运行是完整的已批准范围,还是部分样本。单个产品示例是一个样本;它无法确定商店的目录规模。变体数组限制、过滤后的集合或无法访问的页面,都应该在你的输出中作为覆盖范围限制体现出来。
为了抓取动态的 Shopify 产品页面,当结构化来源不足时,要保留精确的选项选择以及可见状态。不要将父级的最低价格复制到每一条变体行。当无法有把握地识别所选要约时,就应停止。
Playwright 的云扩展
Scrapeless Agent Browser 可以远程运行一个 Playwright 会话,而你的采集器保留其自身的校验逻辑。
当本地浏览器托管成为运维负担时,使用文档化的 Playwright 连接。SDK 会创建浏览器会话;Playwright 会附加到它返回的端点。
注意:以下云连接需要在
SCRAPELESS_API_KEY中提供有效的 Scrapeless 服务 API 密钥。本地采集示例可独立运行。这个依赖凭据的步骤必须在你的账号中完成后,才能部署云路径。
javascript
import { Scrapeless } from '@scrapeless-ai/sdk';
import { chromium } from 'playwright';
const client = new Scrapeless();
const { browserWSEndpoint } = await client.browser.create({
sessionName: 'shopify-public-product',
sessionTTL: 180,
proxyCountry: 'US'
});
const browser = await chromium.connectOverCDP(browserWSEndpoint);
try {
const page = await browser.newPage();
await page.goto('https://www.taylorstitch.com/', {
waitUntil: 'domcontentloaded'
});
console.log(await page.title());
} finally {
await browser.close();
}
在确认连接之后,在该会话中应用相同的发现与抽取流程。国家路由是一个浏览器参数;应验证店面的货币和本地化设置,而不是仅从代理设置推断。根据你的采集计划评估 Agent Browser 和 运行时定价。
在源边界处排查缺失数据
在缺失数据变成已存储产品记录之前就应诊断清楚。
| 症状 | 检查项 | 安全处置方式 |
|---|---|---|
| 没有产品锚点 | 规范路径和当前集合成员关系 | 标记发现不完整 |
| HTML 而非 JSON | 访问挑战或不同端点行为 | 暂停抽取 |
| 变体缺失 | 响应上限和所选产品 | 标记覆盖不完整 |
| 货币发生变化 | 本地化设置、cookie 和市场选择 | 启动一个独立的上下文序列 |
| 空价格 | 选定报价和来源字段语义 | 保持未知;切勿填入零值 |
在安排工作前先审查商店的访问规则。爬虫排除标准描述了机器人偏好,而条款和权限则约束更广泛的访问边界。
结论:将发现、上下文和输出放在一起
当产品发现使用稳定链接、变体提取使用经过检查的结构化来源时,基于 Playwright 的 Shopify 爬虫会更易于维护。保留浏览器上下文,使金额字段保持显式,并在存储结果前进行验证。只有在核实了目标店面的分页、访问限制和变体完备性后,才扩展超出示例的范围。
准备好搭建你的 Shopify 商品集合工作流了吗?
加入我们的社区获取免费方案,并与正在开发基于浏览器的电商采集的开发者交流:Discord · Telegram。
在 app.scrapeless.com 注册,并将经检查的抽取契约应用到你获批的产品来源上。
常见问题
问:抓取公开的 Shopify 产品数据合法吗?
答案取决于你所在的司法辖区、商店条款、访问方式以及预期用途。审阅这些条件,在需要时咨询律师。将工作流限制在公开且允许的产品页面表层。
问:构建 Shopify 爬虫需要代理吗?
经过检查的本地示例在无需代理的情况下读取可访问的公共来源。云端扩展将 proxyCountry 设为 US 以获得一致的路由;货币和市场选择仍然需要单独验证。
问:爬虫应如何处理 WAF 或 Access Denied 页面?
将结果视为未确认的集合,而非产品数据。对于已授权的云端工作流,先在同一会话中打开主页,然后等待真实的产品标记。当店面不允许所请求的访问时即停止。
问:当 Shopify 主题轮换其 DOM 类名时会有什么变化?
重新检查线上标记,并在可能的情况下保留稳定的产品 URL 或结构化来源。只有在确认选择器指向选定产品及其变体后,才更新选择器。
问:可以在没有 AI 代理的情况下运行吗?
可以。本地脚本可直接在 Node.js 下运行。代理可以帮助检查其他店面,但并非执行采集器的必需条件。
问:应如何处理并发和产品 URL?
先从每个商店一个工作线程开始,并使用其公共链接发现的规范 URL。如果商店允许并行采集,初始上限设置为每个主机三个工作线程或其更严格的限制。将变体选择与规范产品标识分开处理。
在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。



