scrapewright

io.github.Ozymandias-Owens-2由 GitHub 账号 Ozymandias-Owens-2 发布v1.0.3更新于 Oct 24, 2026

Give it a URL, get structured rows. A model writes the parser once; replays are free.

已验证Streamable HTTP可网页运行网页搜索与抓取数据与分析商业与电商

概览

AI 生成的概览

让助手把电商目录和自定义页面抓取为结构化数据,每个站点只需编译一次解析器,之后免费重放。

可以做什么
scrapewright 会识别 URL 背后的平台:Shopify 和 WooCommerce 目录通过其公开 API 确定性提取,存在 JSON-LD 时直接读取。对于自定义 HTML,它用一次 LLM 生成 CSS 选择器配方,按域名和 schema 缓存,之后用 BeautifulSoup 确定性重放。托管版工具为 detect_site、extract_page、crawl_site、crawl_status、account;本地版为 detect_site、scrape_catalog、extract_page、crawl_site、list_learned_sites。结果统一为 Product 结构或自定义字段 schema,可导出 CSV、XLSX 或 JSONL。
适用场景
当助手需要从店铺和其他结构化页面获取结构化商品或列表数据,而不想逐页阅读原始 HTML 时使用。适合对同一批站点反复抓取、能摊薄一次性编译成本的场景,以及 Shopify、WooCommerce 与自建站混合的情况。
运行要求
托管模式:远程端点,需要 X-API-Key 请求头,免费额度每月 1,000 行。本地模式:通过 uvx 或 pip 运行的 Python 包,仅对无已知平台的站点需要 ANTHROPIC_API_KEY,Shopify 和 WooCommerce 无需该密钥。JS 渲染(Playwright Chromium)、XLSX 和 MCP 服务器为可选依赖。SCRAPEWRIGHT_CACHE 指定配方存储位置,SCRAPEWRIGHT_OBEY_ROBOTS 默认遵守 robots.txt。需要网络访问。
安装前请注意
托管服务按量计费:编译新站点 300 积分,浏览器渲染 5 积分,每条记录 1 积分,付费套餐通过 Stripe 结账购买。本地服务器对自定义站点需要 ANTHROPIC_API_KEY,托管版需要 X-API-Key。把 SCRAPEWRIGHT_OBEY_ROBOTS 设为 0 会忽略 robots.txt,仅在你拥有或已获书面许可的站点上才合理。抓取会访问第三方页面,并把页面 HTML 发送给模型提供方用于合成。反爬墙无法突破,会如实返回未命中。

安装

在 SourceWeft 中

  1. 打开 控制台中的 scrapewright,将其添加到工作区。
  2. 连接 X-API-Key 凭据。它们会被私密存储,绝不会在公开页面上展示。
  3. 为需要使用其工具的对话启用该服务。

Web executable,通过 Streamable HTTP。 远程服务在工作区中配置后即可从网页运行时运行。

交给智能体安装

把这段提示词粘贴到 Claude Code、Codex、Cursor 等智能体,或 SourceWeft 对话中。智能体会读取该服务器的安装指南,先向你展示发布者、端点和登录方式,经你同意后再把它添加到你的客户端。密钥不会写进任何命令。

阅读 https://sourceweft.com/mcp/io.github.Ozymandias-Owens-2%2Fscrapewright/install.md,并按其中说明把这个 MCP 服务器接入我的客户端。修改任何配置之前,先告诉我它的发布者、端点和登录方式,等我确认。不要把 API 密钥写进命令或我会提交的文件;问我密钥应从哪里获取。

查看智能体所依据的安装指南

其他 MCP 客户端

把它添加到你客户端的 mcpServers 配置中。

{
  "mcpServers": {
    "scrapewright": {
      "type": "http",
      "url": "https://scrapewright.app/mcp",
      "headers": {
        "X-API-Key": "<YOUR_API_KEY>"
      }
    }
  }
}

README

scrapewright

[PyPI] [Python] [License: MIT]

Give it a URL. It writes the scraper.

Most e-commerce catalog scraping splits into two worlds: sites on a known platform (Shopify, WooCommerce) that expose a clean JSON feed, and everything else — bespoke HTML where you hand-write a parser per site and re-write it every time the markup shifts. scrapewright collapses both into one call:

  1. Detect the platform behind a URL.
  2. For known platforms, extract deterministically from their public catalog API — free, stable, no LLM.
  3. For custom HTML, synthesize a reusable extractor once with an LLM, cache it, and replay it deterministically forever after.

The LLM is a compiler, not a runtime. It runs once per site to produce a recipe of CSS selectors; every page after that is parsed by plain BeautifulSoup at zero marginal cost. That is the whole cost-control story — no per-page model calls, no token bill that scales with your crawl.

                    ┌─────────────┐   store URL  ───▶  │   detect    │                    └──────┬──────┘        ┌──────────────────┼──────────────────┐        ▼                  ▼                   ▼    shopify            woocommerce         generic HTML   products.json      wc/store/products    (page mode)        │                  │                   │        │  deterministic   │                   ▼        │  (free)          │            cached recipe? ──yes──▶ replay (free)        └────────┬─────────┘                   │ no                 ▼                              ▼             Product{}  ◀───── selectors ── JSON-LD? ──yes──▶ Product{} (free)                 ▲                              │ no                 │                              ▼                 └──────── replay ◀── LLM synthesizes recipe ONCE ──▶ cache

Everything normalizes to one Product shape, so downstream code never knows or cares which path a record came from.

Install

bash
pip install scrapewright               # deterministic paths (Shopify, Woo, JSON-LD)pip install "scrapewright[llm]"        # + LLM recipe synthesis for custom HTMLpip install "scrapewright[llm,js,excel,mcp]"   # + JS rendering, XLSX, MCP serverplaywright install chromium                    # only needed for --js

Use it

python
from scrapewright import Scrapewright
sw = Scrapewright()
# Catalog mode — a whole Shopify/WooCommerce store, deterministicallyfor product in sw.scrape_catalog("https://shop.example.com", max_items=200):    print(product.brand, product.title, product.price, product.currency)
# Page mode — one custom-HTML product page.# First call: tries JSON-LD (free); if absent, the LLM writes a recipe once.# Every later call on that domain: replayed from the cached recipe, no LLM.item = sw.scrape_page("https://boutique.example.com/products/wool-coat")print(item.model_dump(exclude={"raw"}))
# Crawl mode — walk a WHOLE custom store from one listing/category URL.# The frontier discovers product pages (deterministic, free); the first page# pays the single synthesis cost, every other page replays the recipe.for product in sw.crawl("https://boutique.example.com/collection", max_items=100):    print(product.title, product.price)

CLI

bash
scrapewright detect https://shop.example.com          # platform + strategyscrapewright run    https://shop.example.com --max 50 # scrape a catalog → JSONLscrapewright crawl  https://boutique.example.com/collection -o products.xlsxscrapewright run    https://shop.example.com -o products.csv   # Excel-ready CSVscrapewright add    https://boutique.example.com/products/coat  # learn a sitescrapewright run    https://boutique.example.com/products/coat --no-llmscrapewright list                                     # cached recipe domains

-o writes .csv (Excel-ready, UTF-8 BOM), .xlsx (pip install scrapewright[excel]), or .jsonl; without it, products stream to stdout as JSONL.

Know what you are dealing with

detect answers the routing question before a job starts:

$ scrapewright detect https://some-store.comhttps://some-store.com  platform: bigcommerce  catalog:  -  strategy: crawl  note:     BigCommerce (Stencil) markup

Twelve platforms are recognized: Shopify and WooCommerce publish a free JSON catalog, so those route to catalog — deterministic, no LLM, no browser. Magento, BigCommerce, Salesforce Commerce Cloud, Squarespace, Wix, Webflow, PrestaShop, Shopware, Ecwid and OpenCart are recognized by fingerprint and route to crawl, where the recipe path handles them like any custom site — the point of naming them is knowing what you face, not writing twelve parsers. Wix and Ecwid render client-side, so detection says crawl+js up front.

A site behind an anti-bot wall reports strategy: blocked with the HTTP status, rather than pretending it found nothing.

Bring your own schema

Products are just the built-in default. Declare the fields you want and the same compile-once/replay-free loop works on any structured page — job posts, listings, registry records:

bash
scrapewright run https://jobs.example.com/p/123 -f title -f company -f salary:number -f tags:list --schema-name job
python
from scrapewright import Scrapewright, Schema
job = Schema.from_names(["title", "company", "salary:number", "tags:list"], name="job")record = Scrapewright().extract("https://jobs.example.com/p/123", job)print(record.data)   # {'title': ..., 'company': ..., 'salary': ..., 'tags': [...]}

Field kinds are text (default), number, url, and list. A number field is parsed out of whatever the page wrote — "£51.77", "1.250,00", "€72,000" all become numbers — and when the page said more than the digits, the original is kept next to it as <field>_text:

json
{ "price": 51.77, "price_text": "£51.77" }

A value that holds no number at all ("call us") is left as the page wrote it rather than dropped. Recipes are cached per site and per schema, so one domain can be compiled against several field sets without them overwriting each other.

Use it from an AI agent (MCP)

scrapewright speaks MCP, so an agent can call it as a tool instead of reading raw HTML itself. Two ways in.

Hosted, nothing to install. Point the client at the service with a key from scrapewright.app (1,000 free rows a month; an account that has never been topped up compiles one new site and renders twenty pages a day):

json
{  "mcpServers": {    "scrapewright": {      "url": "https://scrapewright.app/mcp",      "headers": { "X-API-Key": "sw_..." }    }  }}

Tools: detect_site, extract_page, crawl_site, crawl_status, account. Paid from the same credit balance as the REST API; no model key of your own is needed.

Local, your own model key. Run the server on your machine:

bash
pip install "scrapewright[mcp,llm]"scrapewright mcp

Point any MCP client at that command and the agent gains five tools: detect_site, scrape_catalog, extract_page, crawl_site, and list_learned_sites.

Drop this into your client's config — Claude Desktop, Cursor, or anything else that speaks MCP:

json
{  "mcpServers": {    "scrapewright": {      "command": "uvx",      "args": ["--from", "scrapewright[mcp,llm]", "scrapewright", "mcp"],      "env": { "ANTHROPIC_API_KEY": "sk-ant-..." }    }  }}

The key is only needed for sites on no known platform, where a recipe has to be written once. Shopify and WooCommerce stores work without it.

The economics are the point. An agent that reads pages itself pays model tokens per page, forever. These tools pay once per site — an agent crawling 500 pages spends one synthesis, not five hundred, and platform stores (Shopify, WooCommerce) cost nothing at all.

Run it as a service

The same core behind an HTTP API, with keys, quotas, metering and background jobs:

bash
pip install "scrapewright[service,llm]"scrapewright keys create --label alice --plan freescrapewright serve --port 8000
bash
curl -X POST localhost:8000/v1/extract   -H "X-API-Key: sw_..." -H "Content-Type: application/json"   -d '{"url": "https://shop.example.com/products/coat"}'
EndpointPurpose
POST /v1/detectplatform + strategy (cheap)
POST /v1/extractone page -> structured record
POST /v1/crawla whole site -> job id (crawls outlive a request)
... modelinks follow a listing into item pages · rows read the listing's own cards · like one item page -> every page shaped like it
GET /v1/jobs/{id}poll a crawl
GET /v1/jobs/{id}/download?format=csv|xlsx|jsonlthe finished crawl as a file
GET /v1/usagewhat this key has consumed, against its plan

Or skip the API: scrapewright.app/account takes a listing URL and a field list, runs the crawl, and hands back the spreadsheet.

Prepaid credits, no subscription

One action costs real money: compiling a new site, a single LLM pass over a page, measured at $0.02 on a small product page and $0.15 on a heavy rendered one. Everything after that is BeautifulSoup — the ten-thousandth record from a compiled site is free to serve. So credits are priced off that one action, and everything else is denominated relative to it:

ActionCredits
1 record delivered1
1 browser render5
1 new site compiled300
page fetches, detectfree
$ scrapewright planspack         credits   price   $/credit   marginstarter       10,000     $10    0.00100    80.0%growth        50,000     $40    0.00080    75.0%scale        250,000    $150    0.00060    66.7%
Free: 1,000 credits a month, resetting.

Margin is measured on compiling a site, because that is the only step that costs anything; a test fails if a price edit drops any pack below 60%. A free account can cost us at most $0.20 a month, even if every free credit goes to the most expensive action there is.

Credits are a ledger, not a counter — every grant and every charge is a row, so a disputed bill can be reconstructed line by line, and a replayed payment webhook cannot double-credit (grants take an idempotency key). Running out returns 402 with the balance and what to do about it; a crawl is capped by the credits on hand, so a job stops at what the caller can pay for instead of overdrawing.

bash
scrapewright credits grant <key_id> --pack starter --idempotency <payment_id>scrapewright credits balance <key_id>
Taking payment

Stripe is wired in and turned on by environment, not by a code change:

bash
pip install "scrapewright[service,stripe]"export STRIPE_SECRET_KEY=sk_test_...      # absent -> nothing is for saleexport STRIPE_WEBHOOK_SECRET=whsec_...    # absent -> webhooks are refusedscrapewright serve
EndpointPurpose
GET /v1/credits/packsthe price list — public, no key needed
POST /v1/credits/checkoutstart a purchase, returns a Stripe Checkout URL
POST /v1/webhooks/stripepayment notifications from Stripe

The webhook endpoint takes no API key — Stripe is the caller, so the signature is the credential, and an unverified endpoint would be a free credit printer for anyone who guessed the URL. Three rules hold the integration up:

  • Verify every signature. No signing secret configured means webhooks are refused outright, rather than accepted unverified.
  • Never trust an amount off the wire. The event names a pack; how many credits that pack is worth is looked up from our own price list, so a tampered payload buys exactly what it paid for or nothing at all.
  • Grant idempotently, keyed on the Checkout session. Stripe retries deliveries, and one payment can produce several event types — the session id is what identifies the money that actually moved.

examples/stripe_smoke_test.py runs the whole path against Stripe's test mode with the 4242 card. Any other provider plugs into the same two-method BillingProvider protocol in scrapewright.service.billing; without one, the service simply runs free, which is the right default for a demo or a self-hosted instance.

Docker:

bash
docker build -t scrapewright .                       # static pathsdocker build -t scrapewright --build-arg WITH_JS=1 . # + headless Chromiumdocker run -p 8000:8000 -v sw-data:/data scrapewright

Client-side-rendered stores

Add --js (or Scrapewright(js=True)) and pages that render their catalog in the browser become extractable:

bash
scrapewright run https://spa-store.example.com/products/x --page --jsscrapewright crawl https://spa-store.example.com/shop --js -o products.xlsx

Rendering stays rare by construction: the static fetch runs first, and Chromium is only started when the static HTML is an empty client-side shell or extraction on it fails. A recipe learned from rendered HTML is tagged needs_js, so later runs on that site skip the wasted static hop. The browser starts at most once per run and is reused for every page.

The Product shape

python
url: str            # canonical product URLtitle: strbrand: str | Noneprice: Decimal | None   # parsed from "1,250.00" / "1.250,00" / "€1290" alikecurrency: str | Noneavailable: bool | Noneimages: list[str]       # absolute URLssizes: list[str]description: str | Nonesku: str | Nonesource_platform: str    # shopify | woocommerce | json-ld | selector

A record is usable when it carries a title, a price, and a URL. The validator (scrapewright.coverage) reports the usable ratio across a batch — the number a recipe is trusted on before it's cached.

How the pieces fit

ModuleRole
detectPlatform registry: free-catalog probes, then fingerprints for 12 platforms; returns the strategy to use
extract/shopify, extract/woocommerceDeterministic catalog extractors
extract/jsonldschema.org/Product from <script type="application/ld+json"> — free, ~common
extract/llmSynthesizes a SelectorRecipe from HTML — the one-time compile step
extract/selectorsReplays a recipe with BeautifulSoup — the deterministic runtime
schemaSchema/Field — declare what to extract; PRODUCT_SCHEMA is the built-in default
service/FastAPI app: API keys (stored hashed), record-based quotas, cost metering, background crawl jobs, pluggable billing
service/creditsCredit prices, packs, and the free allowance
service/stripe_billingStripe Checkout + signature-verified webhook
service/pricingMeasured unit costs and the margin each pack clears
mcp_serverFive MCP tools so AI agents can call scrapewright directly
fetchStaticFetcher (plain HTTP) and BrowserFetcher (headless Chromium), plus the shell heuristic that decides when a render is worth paying for
crawlFrontier: turns one listing URL into product URLs (pattern match + card-template fallback + pagination) — deterministic, no LLM
cachePersists recipes keyed by domain, so the compile happens once
validateField-coverage scoring
exportBatch → .csv / .xlsx / .jsonl
pipelineOrchestrates detect → extract → validate → cache → heal

Design notes

  • Deterministic paths run first. Shopify JSON, the WooCommerce Store API, and JSON-LD cover a large share of real stores for free. The LLM is only ever reached for genuinely custom HTML.
  • Self-healing. When a cached recipe stops producing usable products — the site changed its DOM — the page falls through to the free JSON-LD path and, failing that, a fresh synthesis replaces the stale recipe. A broken site heals on the next run instead of silently returning empty fields.
  • Bounded model spend. Batch and crawl runs cap LLM calls at max_synth_per_run (default 3) — a site that resists synthesis cannot burn one model call per page. The bill is bounded no matter how large the crawl.
  • Provider-configurable. The LLM extractor takes a model and works with any injected client; the default targets Anthropic's Claude via the official SDK.

Testing

The deterministic paths are fully covered by offline fixtures — no network, no model calls — so CI is green without an API key:

bash
pip install "scrapewright[dev]"pytest

Status

v0.9 (alpha). Implemented and tested: an HTTP service with API keys, prepaid credits (priced off the one action that costs money, on an auditable ledger) and Stripe checkout with a signature-verified webhook, cost metering and background jobs; platform detection across 12 storefronts with a recommended strategy per site, catalog extraction (Shopify, WooCommerce), page extraction (JSON-LD, LLM-synthesized selectors), recipe caching, self-healing re-synthesis with a bounded per-run model budget, a crawl frontier (one listing URL → the whole site), JS rendering via an optional Playwright fetcher with automatic escalation, schema-agnostic extraction (bring your own fields), an MCP server for AI agents, coverage validation, and CSV / XLSX / JSONL export. 141 offline tests.

Known limit, stated plainly: it does not defeat anti-bot walls — deliberately out of scope. Sites behind Akamai/Fastly-style challenges return an honest miss.

Roadmap: pagination strategies for infinite-scroll listings, and a deployed instance of the service.

License

MIT — see LICENSE.

来源:README.md,提交 9c05167

工具

5

detect_site

Report what platform a site runs on and which strategy to use. Cheap; call it before a large job.

输入参数url必填

extract_page

Extract structured data from ONE page. ``fields`` declares your own schema, e.g. ["title", "salary:number", "tags:list"]; omit it for the product schema. First call on a new site compiles a recipe (300 credits); later calls replay it for 1 credit per row. A site nobody could read is remembered for a week and answered without calling the model, so re-reading a dead page costs nothing. `retry` asks anyway.

输入参数jsurl必填retryfields

crawl_site

Walk a site and extract every item. Waits up to four minutes; a longer crawl returns a job_id to pass to crawl_status. `mode` picks how the items are found. "rows" (the default) reads every card on the listing itself and follows its pagination -- right for search results, categories and tables. "links" follows each card into its own page, for sites where an item has one. "like" starts from one example item page, given as `like_page`, and finds every other page shaped like it; pass `listing_url` as well when you know the stock or results page, because inferring it is the part that fails.

输入参数jsmodefieldsscrolllike_pagemax_itemslisting_url必填

crawl_status

Fetch a crawl that outlived its call.

输入参数job_id必填

account

Credits left and this month's usage for the key in use.

无需输入参数。

版本历史

3
  1. v1.0.3最新Sep 29, 2026
  2. v1.0.2Sep 20, 2026
  3. v1.0.1Sep 16, 2026

评分

已满足适用检查项的 100%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通必需25 分
  • 已声明认证方式必需10 分
  • 已列出工具必需15 分
  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分