Firecrawlで100ページ超のサイトを丸ごとスクレイピングした手順と注意点

Written by
John Doe
公開日
2026-04-02

目次

Webサイトのデータをまとめて取得したい場面は意外と多い。競合分析、社内ドキュメントの移行、ナレッジベースの構築。BeautifulSoupやScrapyで地道にパーサーを書くのが王道だが、JavaScript描画のSPAが増えた今、従来のHTTPリクエスト+パースだけでは歯が立たないケースが出てきた。

Firecrawlはこの問題を正面から解決するツールだ。ブラウザレンダリング後のHTMLを取得し、Markdownに変換して返してくれる。LLMに食わせるデータの前処理がほぼ不要になる点が、他のスクレイピングツールとの最大の違いになる。

セットアップ: 5分で動く環境を作る

まずPython SDKをインストールする。

pip install firecrawl-py

APIキーはFirecrawlのダッシュボードで取得できる。無料プランでも月500クレジット使える。1ページのスクレイプで1クレジット消費するので、500ページ分だ。

from firecrawl import FirecrawlApp

app = FirecrawlApp(api_key='fc-xxxxxxxxxxxxxxxx')

環境変数で管理する場合は FIRECRAWL_API_KEY を設定しておけば、引数なしで初期化できる。

export FIRECRAWL_API_KEY=fc-xxxxxxxxxxxxxxxx
# 環境変数から自動で読み込む
app = FirecrawlApp()

単一ページの取得: scrape

まず1ページだけ取得してみる。scrape_urlメソッドにURLを渡すだけでいい。

result = app.scrape_url(
    'https://example.com/pricing',
    params={'formats': ['markdown', 'html']}
)

print(result['markdown'][:500])

返り値はこんな構造になる。

{
    'markdown': '# Pricing\n\nOur plans start at...',
    'html': '<h1>Pricing</h1><p>Our plans start at...</p>',
    'metadata': {
        'title': 'Pricing - Example',
        'description': 'Our pricing plans...',
        'language': 'en',
        'sourceURL': 'https://example.com/pricing',
        'statusCode': 200
    }
}

formatsパラメータで取得形式を指定できる。markdownだけで十分なケースがほとんどだが、DOM構造が必要ならhtmlも併せて取る。

SPAやログイン後ページの対処

JavaScriptで描画されるページも、デフォルトでブラウザレンダリングされるので特別な設定は不要。ただし、ログインが必要なページはheadersでCookieを渡す必要がある。

result = app.scrape_url(
    'https://app.example.com/dashboard',
    params={
        'formats': ['markdown'],
        'headers': {
            'Cookie': 'session_id=abc123def456'
        }
    }
)

サイト全体のクロール: crawl

本題のサイト全体取得。crawl_urlを使う。

crawl_result = app.crawl_url(
    'https://docs.example.com',
    params={
        'limit': 100,
        'scrapeOptions': {
            'formats': ['markdown']
        }
    },
    poll_interval=5
)

print(f"取得ページ数: {len(crawl_result['data'])}")
for page in crawl_result['data'][:3]:
    print(f"  - {page['metadata']['sourceURL']}")

出力例:

取得ページ数: 87
  - https://docs.example.com/
  - https://docs.example.com/getting-started
  - https://docs.example.com/api-reference

クロールの挙動を制御するパラメータ

crawl_result = app.crawl_url(
    'https://example.com',
    params={
        'limit': 200,
        'maxDepth': 3,
        'includePaths': ['/blog/*', '/docs/*'],
        'excludePaths': ['/admin/*', '/tag/*'],
        'allowBackwardLinks': False,
        'scrapeOptions': {
            'formats': ['markdown'],
            'onlyMainContent': True
        }
    }
)

onlyMainContent: Trueは地味だが重要。これを入れないと、全ページにヘッダー・フッター・サイドバーが重複して含まれる。LLMに渡すデータが無駄に膨らむ原因になる。

非同期クロール: 大規模サイト向け

100ページを超えるサイトでは、同期的に待つとタイムアウトのリスクがある。非同期APIを使うほうが安全だ。

crawl_job = app.async_crawl_url(
    'https://large-site.example.com',
    params={'limit': 500}
)

job_id = crawl_job['id']
print(f"ジョブID: {job_id}")

import time

while True:
    status = app.check_crawl_status(job_id)
    completed = status.get('completed', 0)
    total = status.get('total', 0)
    print(f"進捗: {completed}/{total}")

    if status['status'] == 'completed':
        break
    time.sleep(10)

pages = status['data']
print(f"取得完了: {len(pages)}ページ")

LLMで構造化データを抽出する: extract

Firecrawl v1で追加されたExtract機能を使うと、ページからLLMで構造化データを直接抽出できる。Pydanticのスキーマを定義して渡す。

from pydantic import BaseModel
from typing import List, Optional

class Product(BaseModel):
    name: str
    price: Optional[str]
    description: str
    features: List[str]

result = app.scrape_url(
    'https://example.com/product/widget-pro',
    params={
        'formats': ['extract'],
        'extract': {
            'schema': Product.model_json_schema()
        }
    }
)

product = result['extract']
print(f"商品名: {product['name']}")
print(f"価格: {product['price']}")
print(f"特徴: {', '.join(product['features'])}")

出力例:

商品名: Widget Pro
価格: $49/month
特徴: リアルタイム同期, APIアクセス, カスタムダッシュボード

スキーマを渡さずにプロンプトだけで指示することもできる。

result = app.scrape_url(
    'https://example.com/about',
    params={
        'formats': ['extract'],
        'extract': {
            'prompt': '会社の設立年、従業員数、所在地を抽出してください'
        }
    }
)

実際にやってみて気づいたこと

クレジット消費に注意

無料プランの500クレジットは、テスト段階で使い切りやすい。limitパラメータを設定せずにクロールすると、リンクをたどれるだけたどってしまう。最初はlimit: 10で動作確認し、徐々に増やすのが無難だ。

robots.txtは尊重される

Firecrawlはデフォルトでrobots.txtに従う。クロール対象サイトのrobots.txtでDisallowされているパスは取得できない。自社サイトのデータ移行で使う場合は、一時的にrobots.txtを調整する必要があるかもしれない。

レート制限

無料プランでは1分あたり10リクエストの制限がある。大量のページを取得する場合は、crawl_urlで一括取得するほうが、scrape_urlをループで回すより効率的だ。クロール機能は内部でレート制限を管理してくれる。

セルフホスト

APIキーの管理やデータの外部送信が気になる場合は、セルフホストもできる。

git clone https://github.com/mendableai/firecrawl.git
cd firecrawl
docker compose up -d

セルフホスト版はAPIキー不要で、レート制限もない。ただし、ブラウザレンダリング用のChromiumが必要になるため、メモリは最低4GB確保しておく。

料金体系(2026年3月時点)

プラン月額クレジット/月レート制限
Free$050010req/min
Hobby$163,00020req/min
Standard$83100,000100req/min
Growth要問合せカスタムカスタム

個人プロジェクトや小規模な用途ならHobbyで十分。月3,000ページあれば、中規模サイトを数回クロールできる。

Scrapy・Playwrightとの使い分け

要件推奨ツール
細かいパーサーのカスタマイズが必要Scrapy
ブラウザ操作(ログイン、クリック等)が必要Playwright
サイト全体をMarkdownで一括取得Firecrawl
LLMに渡す構造化データが欲しいFirecrawl(Extract)
無料で大量ページを取得したいScrapy(セルフ管理)

Firecrawlの強みは「とにかく早くきれいなデータが欲しい」場面にある。パーサーを書かなくていいのは、プロトタイプやPoCのスピードを上げる上で大きい。本番のパイプラインに組み込むなら、コストとレート制限を考慮して判断するのがいい。

まとめ

Firecrawlは「Webスクレイピングの面倒な部分を全部やってくれるAPI」と考えるとわかりやすい。JSレンダリング、Markdown変換、構造化抽出の3つが1つのAPIで完結する。

手を動かすなら、まず無料プランでscrape_urlを試すところから始めるのがいい。1ページの取得結果を見れば、自分の用途に合うかどうかすぐ判断できる。

Relation

関連記事

This is some text inside of a div block.

広告費の「見えない穴」を塞いだら、月100万円の無駄が見つかった話

This is some text inside of a div block.
7 min read
GTMのdataLayerでUTMパラメータをフォーム送信に紐付けるデータフロー図
This is some text inside of a div block.

GTMのdataLayerでUTMパラメータをフォーム送信に紐付ける方法

This is some text inside of a div block.
7 min read
This is some text inside of a div block.

Lステップに月2万円払い続ける必要、本当にある?OSSの「LINE Harness」をCloudflare Workersにデプロイして無料LINE CRMを手に入れた話

This is some text inside of a div block.
7 min read
This is some text inside of a div block.

YouTube運用にAIを導入して分かったこと――効率化できる部分と、できない部分の境界線

This is some text inside of a div block.
7 min read
This is some text inside of a div block.

WordPressからWebflowに移行して半年。正直な感想と移行判断のチェックリスト

This is some text inside of a div block.
7 min read
This is some text inside of a div block.

Webflowとは何か — 使い始めた初日に知りたかった10のこと

This is some text inside of a div block.
7 min read

現在【毎月先着5社様】限定無料相談受付ます

大変申し訳ありません。私たちのリソースには限りがあり、一社一社に質の高いサービスを提供するため、現在【毎月先着5社様】限定で、この特別な条件(全額返金保証+無料相談)でのご案内とさせていただいております。

さらに、今このページをご覧のあなただけに、無料相談へお申し込みいただいた方限定で、通常5万円相当の【競合サイト分析&改善提案レポート】を無料でプレゼントいたします。

枠がすぐに埋まる可能性がありますので、お早めにお申し込みください。

プライバシーポリシーに同意し、まずは無料相談をおこないます
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.