AnyCrawl

クロール

サイト全体をクロールし、LLM 向けの構造化データに変換する

はじめに

AnyCrawl のクロール API は、シード URL から複数ページを発見・処理し、各ページに /v1/scrape と同じ抽出パイプラインを適用します。非同期です。すぐに job_id が返り、状態をポーリングして結果をページ単位で取得します。

主な特徴

  • 非同期ジョブ:クロールをキューに入れ、後から結果を取得
  • マルチエンジンauto(既定)、cheerioplaywrightpuppeteer
  • 柔軟な範囲制御strategymax_depthinclude_pathsexclude_paths
  • ページ単位のオプションscrape_options/v1/scrape のオプションを再利用
  • ページネーションskip で結果をストリームし、ペイロードサイズを制御

API エンドポイント

POST    https://api.anycrawl.dev/v1/crawl
GET     https://api.anycrawl.dev/v1/crawl/{jobId}/status
GET     https://api.anycrawl.dev/v1/crawl/{jobId}?skip=0
DELETE  https://api.anycrawl.dev/v1/crawl/{jobId}

使用例

クロールジョブを作成

curl -X POST "https://api.anycrawl.dev/v1/crawl" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://anycrawl.dev",
    "engine": "cheerio",
    "strategy": "same-domain",
    "max_depth": 5,
    "limit": 100,
    "exclude_paths": ["/blog/*"],
    "scrape_options": {
      "formats": ["markdown"],
      "timeout": 60000
    }
  }'

scrape_paths による選択的スクレイプ

scrape_paths を使うと、リンク発見のためにページを訪問しつつ、本文抽出を省略できます。コストとストレージを抑えられます。

curl -X POST "https://api.anycrawl.dev/v1/crawl" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://shop.example.com",
    "engine": "cheerio",
    "strategy": "same-domain",
    "max_depth": 5,
    "limit": 200,
    "include_paths": ["/*"],
    "scrape_paths": ["/products/*/details", "/products/*/reviews"],
    "scrape_options": {
      "formats": ["markdown", "json"]
    }
  }'

この例では:

  • /* に一致するページはすべて訪問(リンク発見)されます
  • /products/*/details または /products/*/reviews に一致するページだけ本文が抽出・保存されます
  • カテゴリやナビゲーションなどはクロールのみでスクレイプされず、クレジットとストレージを節約します
const start = await fetch("https://api.anycrawl.dev/v1/crawl", {
    method: "POST",
    headers: {
        Authorization: "Bearer YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    body: JSON.stringify({
        url: "https://anycrawl.dev",
        engine: "cheerio",
        strategy: "same-domain",
        max_depth: 5,
        limit: 100,
        exclude_paths: ["/blog/*"],
        scrape_options: { formats: ["markdown"], timeout: 60000 },
    }),
});
const startResult = await start.json();
const jobId = startResult.data.job_id;

状態のポーリング

curl -H "Authorization: Bearer <YOUR_API_KEY>" \
  "https://api.anycrawl.dev/v1/crawl/7a2e165d-8f81-4be6-9ef7-23222330a396/status"

結果の取得(ページネーション)

curl -H "Authorization: Bearer <YOUR_API_KEY>" \
  "https://api.anycrawl.dev/v1/crawl/7a2e165d-8f81-4be6-9ef7-23222330a396?skip=0"

ジョブのキャンセル

curl -X DELETE -H "Authorization: Bearer <YOUR_API_KEY>" \
  "https://api.anycrawl.dev/v1/crawl/7a2e165d-8f81-4be6-9ef7-23222330a396"

リクエストパラメータ

パラメータ必須既定説明
urlstringはい-クロール開始のシード URL
template_idstringいいえ-このクロールで使うテンプレート ID
variablesobjectいいえ-テンプレート変数(template_id 指定時のみ)
engineenumいいえautoページ単位のエンジン:autocheerioplaywrightpuppeteer
exclude_pathsarray of stringいいえ-除外パス(glob 風)、例:/blog/*
include_pathsarray of stringいいえ-クロールに含めるパス(除外の後に適用)
scrape_pathsarray of stringいいえ-本文抽出対象のパス。一致した URL のみ内容を保存。未設定なら含まれる URL のみスクレイプ
max_depthnumberいいえ10シードからの最大深さ
strategyenumいいえsame-domain範囲:allsame-domainsame-hostnamesame-origin
limitnumberいいえ100クロールする最大ページ数
max_agenumberいいえ-キャッシュ最大経過時間(ms)。0 でキャッシュ読み取りをスキップ。省略時はサーバー既定
store_in_cachebooleanいいえtrueページ単位のスクレイプで Page Cache に書き込むか
retrybooleanいいえfalse失敗時に再試行するか
proxystring (URI)いいえ-任意のプロキシ URL
formatsarray of enumいいえ["markdown"]ページ単位の出力形式
timeoutnumberいいえ60000リクエスト単位のタイムアウト(ms)
wait_fornumberいいえ-抽出前の待機(ms)。ブラウザエンジンのみ
wait_untilenumいいえ-ブラウザの待機条件:loaddomcontentloadednetworkidlecommit
wait_for_selectorstring, object, or arrayいいえ-セレクタ待ち(ブラウザのみ)。wait_for より優先。
include_tagsarray of stringいいえ-一致する CSS セレクタの要素のみ含める
exclude_tagsarray of stringいいえ-一致する CSS セレクタの要素を除外
only_main_contentbooleanいいえtrue本文のみ抽出(ヘッダー・フッター・ナビ等を除く)
json_optionsobjectいいえ-構造化 JSON 抽出(schema、user_prompt、schema_name、schema_description)
extract_sourceenumいいえmarkdownJSON 抽出元:markdown(既定)または html
ocr_optionsbooleanいいえfalseMarkdown 画像のみ OCR 強化。html/rawHtml には影響しない。
scrape_optionsobjectいいえ-ページ単位オプション(/v1/scrape と同じ。トップレベル url/engine を除く)

scrape_options のフィールド

| フィールド | 型 | 既定 | 備考 | | ------------------- | ------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | formats | array of enum | ["markdown"] | 出力:markdownhtmltextscreenshotscreenshot@fullPagerawHtmljsonsummarylinks | | timeout | number | 60000 | リクエスト単位タイムアウト(ms) | | wait_for | number | - | 抽出前待機(ms)。ブラウザのみ。wait_for_selector より下位 | | wait_for_selector | string, object, or array | - | ブラウザのみ。CSS 文字列、オブジェクト { selector: string, state?: "attached" | "visible" | "hidden" | "detached", timeout?: number }、または配列。順に待機。wait_for より優先。 | | include_tags | array of string | - | 一致する CSS セレクタの要素のみ | | exclude_tags | array of string | - | 一致する CSS セレクタを除外 | | only_main_content | boolean | true | 本文のみ抽出 | | proxy | string (URI) | - | 任意のプロキシ URL | | json_options | object | - | 構造化 JSON 抽出(schema、user_prompt、schema_name、schema_description) | | extract_source | enum | markdown | JSON 抽出元:markdown(既定)または html | | ocr_options | boolean | false | Markdown 画像のみ OCR 強化。html/rawHtml には影響しない。 | | max_age | number | - | キャッシュ最大経過時間(ms)。0 で読み取りスキップ。省略時はサーバー既定 | | store_in_cache | boolean | true | ページ単位で Page Cache に書き込むか |

キャッシュの挙動

  • クロールは現状 Page Cache を読み取りませんが、書き込みは行われる場合があります。
  • scrape_options を渡す場合は、その中でキャッシュ制御を設定してください。

レスポンス形式

1) 作成(HTTP 200)

{
    "success": true,
    "data": {
        "job_id": "7a2e165d-8f81-4be6-9ef7-23222330a396",
        "status": "created",
        "message": "Crawl job has been queued for processing"
    }
}

想定エラー

  • 400 バリデーションエラー
{
    "success": false,
    "error": "Validation error",
    "message": "Invalid enum value...",
    "data": {
        "type": "validation_error",
        "issues": [
            { "field": "engine", "message": "Invalid enum value", "code": "invalid_enum_value" }
        ],
        "status": "failed"
    }
}
  • 401 認証エラー
{ "success": false, "error": "Invalid API key" }

2) 状態(HTTP 200)

{
    "success": true,
    "message": "Job status retrieved successfully",
    "data": {
        "job_id": "7a2e165d-8f81-4be6-9ef7-23222330a396",
        "status": "completed",
        "start_time": "2025-05-25T07:56:44.162Z",
        "expires_at": "2025-05-26T07:56:44.162Z",
        "credits_used": 0,
        "total": 120,
        "completed": 30,
        "failed": 2
    }
}

3) 結果ページ(HTTP 200)

{
    "success": true,
    "status": "pending",
    "total": 120,
    "completed": 30,
    "credits_used": 12,
    "next": "https://api.anycrawl.dev/v1/crawl/7a2e165d-8f81-4be6-9ef7-23222330a396?skip=100",
    "data": [
        {
            "url": "https://anycrawl.dev/",
            "title": "AnyCrawl",
            "markdown": "# AnyCrawl...",
            "timestamp": "2025-05-25T07:56:44.162Z"
        }
    ]
}

想定エラー

  • 400 無効なジョブ ID / 見つからない
{ "success": false, "error": "Invalid job ID", "message": "Job ID must be a valid UUID" }

4) キャンセル(HTTP 200)

{
    "success": true,
    "message": "Job cancelled successfully",
    "data": { "job_id": "7a2e165d-8f81-4be6-9ef7-23222330a396", "status": "cancelled" }
}

想定エラー

  • 404 見つからない
  • 409 ジョブは既に完了
{
    "success": false,
    "error": "Job already finished",
    "message": "Finished jobs cannot be cancelled"
}

ベストプラクティス

  • 単一ページはコスト削減のため /v1/scrape、サイト全体は /v1/crawl
  • strategymax_depth、パスルールで範囲とコストを調整。
  • scrape_paths でナビ/カテゴリは訪問のみ、本文ページだけスクレイプする。
  • formats は必要なものだけに絞る。
  • skip でページングし、巨大なレスポンスを避ける。

scrape_paths によるコスト最適化

scrape_paths なし(既定)

  • 含まれるページすべて → 訪問+本文抽出+保存

scrape_paths あり

  • include_paths に含まれるが scrape_paths にないページ → 訪問のみ(リンク発見)
  • include_pathsscrape_paths の両方に一致 → 訪問+本文抽出+保存

例:

  • EC:カテゴリはすべてクロールし、商品詳細だけスクレイプ
  • ドキュメント:ナビはすべて巡回し、記事本文だけ抽出
  • ニュース:セクションは閲覧し、全文記事だけ保存

エラー処理の例

async function fetchAllResults(jobId) {
    let skip = 0;
    while (true) {
        const res = await fetch(`https://api.anycrawl.dev/v1/crawl/${jobId}?skip=${skip}`);
        if (!res.ok) {
            const err = await res.json().catch(() => ({}));
            throw new Error(err.message || `HTTP ${res.status}`);
        }
        const page = await res.json();
        // handle page.data here
        if (!page.next) break;
        const next = new URL(page.next);
        skip = Number(next.searchParams.get("skip") || 0);
    }
}

高同時実行の利用

クロールキューは複数ジョブの同時実行に対応します。複数のシードを投入し、それぞれ独立にポーリングします。

const seeds = ["https://site-a.com", "https://site-b.com", "https://site-c.com"];
const jobs = await Promise.all(
    seeds.map((url) =>
        fetch("https://api.anycrawl.dev/v1/crawl", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ url, engine: "cheerio" }),
        }).then((r) => r.json())
    )
);

FAQ

/v1/crawl/v1/scrape の違いは?

/v1/scrape は単一 URL を同期で取得し、すぐ内容を返します。/v1/crawl はシードから複数ページを発見し、非同期で処理します。

範囲とコストを抑えるには?

strategymax_depthlimit、パスルール(include_pathsexclude_pathsscrape_paths)を使います。

include_pathsscrape_paths の違いは?

  • include_paths:どの URL を訪問するか(リンク発見)
  • scrape_paths:どの URL を本文抽出・保存するか

scrape_paths 未設定なら、含まれる URL はすべてスクレイプされます(後方互換)。

コストを下げるには?

ナビ/カテゴリはリンク発見のためだけ訪問し、本文は scrape_paths に合うページだけ保存します。

結果のページネーションは?

skip クエリを使います。レスポンスに next があれば、次のページ取得に従います。

なぜ html/markdown が返らないジョブがある?

scrape_options.formats に必要な形式が含まれ、かつ選んだエンジンがそれをサポートしているか確認してください。

状態の値

ジョブの status はジョブモデルで定義された値に従います。

状態意味
pendingキュー待ちまたは実行中(未完了)
completed正常完了
failedエラーで終了
cancelledキャンセル済み。以降の処理は実行されない

OpenAPI(自動生成)

自動生成ドキュメントは API リファレンスを参照してください。

  • POST /v1/crawl
  • GET /v1/crawl/{jobId}/status
  • GET /v1/crawl/{jobId}
  • DELETE /v1/crawl/{jobId}