一键导入
scrape
Scrape a website. Activates when user provides a URL to scrape, asks to extract data from a site, hits 403/blocking errors, or needs anti-bot evasion.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Scrape a website. Activates when user provides a URL to scrape, asks to extract data from a site, hits 403/blocking errors, or needs anti-bot evasion.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Reverse-engineer a website's internal APIs, encrypted endpoints, WebSocket streams, and obfuscated JavaScript. Activates when the target data isn't in the HTML, when the site uses encrypted CloudFront/CDN payloads, when real-time streaming data is needed, or when the scrape skill's Phase 0-1 finds API calls that are encrypted, signed, or behind a custom protocol. Escalation ladder from simple network capture to protobuf schema reconstruction.
Give AI agents their own email inboxes using the AgentMail API. Use when building email agents, sending/receiving emails programmatically, managing inboxes, handling attachments, organizing with labels, creating drafts for human approval, or setting up real-time notifications via webhooks/websockets. Supports multi-tenant isolation with pods.
Use Camoufox — a Firefox fork with C++-level fingerprint spoofing — for browser scraping when the user's real Chrome profile is not available. Activate on VMs, headless CI servers, remote machines without a GUI, or when torch needs multiple concurrent personas with rotated fingerprints. Detects via TORCH_CAMOUFOX_ENDPOINT env var. Drives from Node via playwright-core connecting to a Camoufox-hosted Playwright server.
Proven scraping playbook for amazon.com search result pages (/s?k=...). CloudFront + CAPTCHA wall blocks bare curl with HTTP 503, but a real Chrome session via the real Chrome debug port walks right through — no stealth, no proxy, no captcha. HTML is server-rendered; cheerio parses 22 results per page with stable `[data-component-type="s-search-result"]` blocks. Activate for any amazon.com /s search URL.
Proven scraping playbook for booking.com searchresults.html pages. CloudFront-fronted JS challenge blocks bare curl (HTTP 202 with a script-only interstitial), but a real Chrome session via the real Chrome debug port walks through on first navigation — no captcha, no proxy, no login. Listings are rendered client-side into [data-testid="property-card"] cards; pagination uses a "Load more results" button after the first 25. Prices only populate when the URL carries checkin/checkout/group_adults params. Activate for any booking.com /searchresults.html scrape.
Proven scraping playbook for costco.com category listings (e.g. /laptops.html, /computers.html). Akamai Bot Manager hard-blocks bare curl with HTTP 403 on the HTML pages, but Costco's internal catalog API at gdx-api.costco.com/catalog/search/api/v1/search is a public JSON POST endpoint with no auth, no cookies, and no anti-bot. Skip the browser entirely. Activate for any costco.com category listing scrape.
| name | scrape |
| description | Scrape a website. Activates when user provides a URL to scrape, asks to extract data from a site, hits 403/blocking errors, or needs anti-bot evasion. |
| metadata | {"author":"torch","version":"1.0.0"} |
You are a scout, not a crawler. On a site that does not already have a skill in skills/sites/<slug>/, the goal is to prove a strategy works on a small sample (5-20 items) and write a reusable playbook — NOT to extract every row on the site. Full crawls come on re-runs once the skill exists.
Run scrapers as background processes, not synchronous bash. You have pi-processes tools — use them. Synchronous bash node scrape.js blocks the turn, makes you blind to progress, and prevents steering. Background execution lets you watch logs, catch errors early, and kill a run once you have enough data.
Pattern:
scrape.js./output/<slug>.json, verify, write the skillDerive the slug ONCE at the start of the scrape from the brand/site name. Lowercase [a-z0-9-] only, no dots, no TLDs, no www. prefix. Examples: amazon, hackernews, mcmaster, digikey.
./output/<slug>.json — one file per scrape, no variants./skills/sites/<slug>/SKILL.md — directory name === slug === frontmatter name:_scratch_ so they're easy to deleteAlways follow this order. Each phase has an exit gate — skip ahead when possible.
curl -sL -D- <url> | head -300
From the response, determine:
strategies/framework-signatures.md/sitemap.xml, /sitemap_index.xml, Sitemap: in /robots.txtcf-ray / cf-mitigated headersGate A: All target data found in raw HTML + no protection → skip to Phase 3.
Based on framework detected in Phase 0, use the matching strategy from strategies/framework-signatures.md:
__NEXT_DATA__ JSON blob or /_next/data/ routes__NUXT__ / __NUXT_DATA__ payload/products.json, /collections.json, .json URL suffix/wp-json/wp/v2/ REST API/api/ or /graphql endpoints in source<script type="application/ld+json"> structured dataGate B: Clean API or JSON data source found → skip browser, go to Phase 3.
If Phase 0-1 found hints of an API (XHR URLs in the source, /api/ or /graphql endpoints, CloudFront domains, WebSocket URLs) but the responses are encrypted, signed, or behind auth — read the reverse-engineer skill and work through its levels. Often you can replay the API directly with fetch once you extract the right headers/tokens, completely skipping the browser.
A fetch call takes 50ms. A Puppeteer page load takes 3-10 seconds. If the data exists behind an API, reverse-engineering that API is the right move even if it takes 10 minutes of investigation — the resulting scraper will be 100x faster and more reliable than a DOM-based one.
Gate C: API replayed successfully with fetch → skip browser entirely, go to Phase 4.
Only when Phase 0-2 failed or when the site genuinely renders data client-side with no API:
http://127.0.0.1:${TORCH_CHROME_PORT ?? 9222} (via puppeteer.connect). If nothing's listening, launch it on demand: spawn $TORCH_CHROME_BIN with --remote-debugging-port=$TORCH_CHROME_PORT --user-data-dir=$TORCH_CHROME_PROFILE, poll the port until it answers, then connect. Torch clones the user's Chrome profile into $TORCH_CHROME_PROFILE on first run but never spawns Chrome itself — the torch command must not pop a browser window on startup. The on-demand launch from the scraper is the right place. See strategies/anti-blocking.md Layer 0 for the full snippet.process.env.TORCH_CAMOUFOX_ENDPOINT and connect via playwright-core's firefox.connect(ws://...) for the C++-level stealth fallback. See the camoufox skill for setup.puppeteer.launch() with the stealth plugin (reference/puppeteer-boilerplate.md). Disposable Chromium with zero history — fine for soft targets but almost guaranteed to trip bot scoring on hard sites.reverse-engineer skill to replay those APIs directly. The browser was just a recon tool; the actual scraper should use fetch against the discovered API whenever possible.If blocked, escalate through strategies/anti-blocking.md. Connecting to the real Chrome profile is the single biggest anti-blocking win — a fresh Chromium with stealth is a last resort, not a starting point.
import puppeteer from "puppeteer-core";
import puppeteerExtra from "puppeteer-extra";
import StealthPlugin from "puppeteer-extra-plugin-stealth";
import { spawn } from "node:child_process";
import { existsSync } from "node:fs";
const PORT = process.env.TORCH_CHROME_PORT ?? "9222";
const BROWSER_URL = `http://127.0.0.1:${PORT}`;
async function connectOrLaunchRealChrome() {
// (a) Already running — reuse it
try {
return await puppeteer.connect({ browserURL: BROWSER_URL });
} catch {}
// (b) Launch on demand against the cloned profile
const bin = process.env.TORCH_CHROME_BIN;
const profile = process.env.TORCH_CHROME_PROFILE;
if (!bin || !profile || !existsSync(bin) || !existsSync(profile)) return null;
const child = spawn(bin, [
`--remote-debugging-port=${PORT}`,
`--user-data-dir=${profile}`,
"--no-first-run",
"--no-default-browser-check",
"--restore-last-session=false",
], { stdio: "ignore", detached: true });
child.unref();
for (let i = 0; i < 40; i++) {
try { return await puppeteer.connect({ browserURL: BROWSER_URL }); } catch {}
await new Promise(r => setTimeout(r, 250));
}
return null;
}
let browser;
let kind;
let cleanup;
browser = await connectOrLaunchRealChrome();
if (browser) {
// Tier 1 — real Chrome (cloned profile, debug port)
kind = "real-chrome";
cleanup = async () => browser.disconnect(); // never close — leave the on-demand Chrome alive for reuse
} else if (process.env.TORCH_CAMOUFOX_ENDPOINT) {
// Tier 2 — Camoufox via playwright-core (VMs / headless servers)
const { firefox } = await import("playwright-core");
browser = await firefox.connect(process.env.TORCH_CAMOUFOX_ENDPOINT);
kind = "camoufox";
cleanup = async () => browser.close();
} else {
// Tier 3 — disposable Chromium + stealth (almost always detected on hard sites)
puppeteerExtra.use(StealthPlugin());
browser = await puppeteerExtra.launch({
headless: false,
args: ["--no-sandbox", "--disable-blink-features=AutomationControlled"],
});
kind = "disposable-chromium";
cleanup = async () => browser.close();
}
console.log(`[torch] using ${kind}`);
const page = await browser.newPage();
try {
await page.goto(url, { waitUntil: "networkidle2" });
// ... scrape ...
} finally {
await page.close();
await cleanup();
}
The disconnect() vs close() distinction matters: close() would kill the on-demand Chrome instance and force the next scrape in the same session to relaunch it. Always disconnect() on the real-Chrome tier so the cloned-profile Chrome stays warm for reuse.
JSON API (public or reverse-engineered) → Sitemap + fetch → Cheerio (static HTML) →
Browser as recon tool (capture API calls, then replay with fetch) →
Browser for DOM extraction (true last resort)
APIs are always the best scraping strategy — faster, more reliable, return more data, and don't trigger bot detection. If the API is hidden or encrypted, the reverse-engineer skill's 9-level ladder handles the discovery. A browser should be a recon tool for finding the API, not the extraction tool itself.
?page=, ?offset=, ?cursor=All pre-installed: puppeteer-extra, stealth plugin, adblocker plugin, cheerio. Use Node.js ES modules.
If a site requires login or rate-limits anonymous requests, use the agentmail skill to create a disposable email inbox for signups. Authenticated sessions often bypass rate limits and unlock more data.
scrape.js (Node.js ES modules)./output/<slug>.json — exactly one file, no variants[scraped] 5 items so far, [scraped] 20 items, donedisconnect() if using 127.0.0.1:9222)Before finalizing, delete any exploration artifacts:
*_debug.*, *-debug.*, *.debug.*<slug>_api.json, <slug>_dom.json, etc.)_scratch_Keep only the canonical ./output/<slug>.json and ./skills/sites/<slug>/SKILL.md.
Save the proven method as a per-site skill so the next run can invoke it directly instead of re-running reconnaissance. Create ./skills/sites/<slug>/SKILL.md where <slug> is a lowercase alphanumeric+hyphen identifier for the site (e.g. mcmaster, digikey, hackernews — no dots, no www. prefix).
Structure:
---
name: <slug>
description: Proven scraping playbook for <full domain>. <one-line architecture summary: framework, CDN, anti-bot, notable gotchas>. Activate whenever the target URL is on <domain>.
metadata:
author: torch
version: "1.0.0"
---
# <Site name> (<domain>)
> One-paragraph summary of what makes this site distinctive to scrape.
## Detection
Table of signals: CDN, framework, anti-bot, auth, robots.txt.
## Architecture
How the site loads data — SPA shell, API endpoints, frames, hydration, etc.
## Strategy used
Phase 0 (curl), Phase 1 (framework), Phase 2 (browser) — what worked, what was skipped.
## Stealth config that works
Exact puppeteer launch args, headers, UA — copy-pasteable.
## Extraction
Selectors, regexes, JSON paths, frame navigation. Include code snippets.
## Anti-blocking summary
Table: which of the 9 layers were needed, which were not, notes.
## Data shape
JSON example of one extracted record.
## Pagination / crawl architecture
Seed URLs, concurrency, checkpointing, priority ordering.
## Gotchas & lessons
Numbered list of anything surprising or fragile.
Naming rules (enforced by the skill loader):
name: frontmatter field must match exactly.a-z, 0-9, and -. No dots, no underscores, no consecutive hyphens, no leading/trailing hyphen.mcmaster over www-mcmaster-com.Once saved, pi-coding-agent will auto-discover the new skill on the next session. The scrape skill's reconnaissance phases can then be skipped for that domain.
Existing examples: see skills/sites/doordash/SKILL.md (SSR + Cloudflare) and skills/sites/nike/SKILL.md (pure JSON API, no browser) for full reference playbooks.
If the site didn't already have a skill before this run, open a pull request adding skills/sites/<slug>/SKILL.md to github.com/agentcomputer/torch so the next person scraping that site inherits the playbook instead of re-running recon. One site per PR. Tell the user once you've saved the skill locally that they should open the PR — see contributing for the general PR workflow and quality bar.