🎯 カスタマイズ可能で検出回避型のクラウドブラウザ。自社開発のChromiumを搭載し、ウェブクローラーAIエージェント向けに設計されています。👉今すぐ試す
ブログに戻ります

Scrapeless Scraping BrowserでFacebook広告ライブラリをスクレイピングする方法

Sophia Martinez
Sophia Martinez

Specialist in Anti-Bot Strategies

29-Jun-2026

TL;DR:

  • Facebook広告ライブラリは公開された情報であり、ログインなしで広告を閲覧できます。 誰でもfacebook.com/ads/libraryを開き、広告主やキーワードを検索し、広告主名、広告文、各広告の出稿日を含む、ページが運用しているすべてのアクティブおよび非アクティブな広告を読むことができます。
  • ページは完全にクライアントサイドでレンダリングされるため、平文のHTTPでは空のシェルが返されます。 広告カードは初期応答の後にJavaScriptによって描画され、結果のグリッドはボット対策のチェックの背後にあるため、単純なrequests.getでは広告がまったく見えません。
  • Scrapeless Scraping Browserはページをクラウドサイドでレンダリングし、描画されたDOMを返します。 Chrome DevToolsプロトコルを使用して米国の居住者出口経路と温められたセッションで接続すると、完全にレンダリングされた結果のグリッドが返され、静的ページのように解析されます。
  • 各広告は安定したLibrary ID:テキストアンカーから発見します。 各広告カードにはLibrary ID: <digits>ラベルが付いており、そのアンカーから単一カードのコンテナに昇ることで、壊れやすいセレクタを壊すReactクラス名の変動に耐えられます。
  • キーワード検索は最初のレンダリングで27の広告カードを返し、グリッドを6回スクロールすると104になりました。 ここでのページネーションは無限スクロールです:グリッドはスクロールごとにカードを追加し、カウントが増えなくなるまで各スクロールの後にDOMを読みます。
  • 無料で始められます。 新しいScrapelessアカウントには、無料のScraping Browserランタイムが付属しています — app.scrapeless.comでサインアップしてください。

導入:どのブランドも運用している公的広告を読む

Facebook広告ライブラリはMetaの、FacebookとInstagramで運用されている広告の公的透明アーカイブです。すべてのアクティブ広告 — そして、社会問題、選挙、政治広告についてはすべての非アクティブ広告も — が広告主、クリエイティブ、運用日と共にリストされています。競合研究者、ブランドセーフティチーム、広告分析者は、ページが現在ユーザーの前に提示しているメッセージを正確に確認するためにこれを読みます。

摩擦はレンダリングにあり、アクセスにはありません。公共の広告を見るのにアカウントは必要ありませんが、ページは最初の応答の後にブラウザ内で結果のグリッド全体を構築し、グリッドは自動トラフィック防御によってゲートがかけられていますので、単純なHTTPクライアントは直ちにそれに引っかかります。URLをrequestsでダウンロードしても、広告カードは返されるバイトに含まれていません — それらは後でJavaScriptによって描画されますが、実行されません。レンダリングするマークアップは、回転するハッシュ化されたReactクラス名に依存しているため、クラス文字列に固定されたセレクタは次のフロントエンドデプロイで壊れます。

このガイドでは、Scrapeless Scraping Browserの上にPythonで抽出を構築します。これはページを米国の居住者出口経路でレンダリングし、完成したDOMを返すクラウドブラウザです。同じレンダリング→発見→抽出→ページネーションのループがすべてのスクレイパーを駆動しますが、1つの工夫があります:各カードに印刷された耐久性のあるLibrary ID:テキストに発見がアンカーしますので、解析はクラス名の変動に耐えます。その静的と動的レンダリングの分割は、JavaScriptではCheerioとPuppeteerのウォークスルーでカバーされています。


これを使ってできること

  • 競合のライブクリエイティブを追跡します。 ページが運用しているすべてのアクティブ広告を引き出し、現在のメッセージング、オファー、ランディング先を確認します。
  • クリエイティブトレンドデータセットを構築します。 カテゴリ内の多くの広告主から広告文を収集し、繰り返される言語、フック、形式を分析します。
  • キャンペーンのタイミングを監視します。 アクティブ広告の「開始日」と非アクティブなものの運用範囲を読み、キャンペーンが開始されて終了する時期をマッピングします。
  • ブランドプレゼンスを監査します。 どの広告が確認されたページに結びついているかを確認し、類似名の下で広告を運用している偽装者を捕まえます。
  • 広告インテリジェンスパイプラインに供給します。 描画された結果のグリッドを構造化された行に変換します — 広告主、ライブラリID、ステータス、日付 — これを下流分析やモデルが読み取ることができます。
  • 平文のHTTPでは到達できないグリッドに到達します。 広告ライブラリはボット対策チェックの背後でクライアントサイドでレンダリングされるため、クラウドブラウザに昇級し、静的ページで使用するいつもの解析コードを保持します。

なぜScrapeless Scraping Browserなのか

Scrapeless Scraping Browserは、ウェブクローラーやAIエージェント向けに設計されたカスタマイズ可能な、検出対策されたクラウドブラウザです。特に広告ライブラリ向けには、以下の機能を搭載しています:

  • クラウドサイドのJavaScriptレンダリング。 結果グリッドは初期応答の後に描画されます。クラウドブラウザがページを実行し、広告カードを既に含むDOMを返すので、BeautifulSoupはそれを静的HTMLのように解析します。
  • 195以上の国々における居住者プロキシ。 広告ライブラリは視聴者の国に応じてコンテンツを変化させるため、米国の居住者出口経路を設定すると、米国の訪問者が見るのと同じ広告が返されます。
  • アンチ検出フィンガープリンティング。 ページはボットチェックの背後にグリッドを構え、クラウドブラウザは一貫した人間のようなブラウザ表面を提供するため、グリッドがチャレンジの代わりにレンダリングされます。
  • 全体のための1つのAPIキー。 Python SDKは、CDP経由でPlaywrightに接続するためのbrowser_ws_endpointを生成し、同じキーがランタイムをカバーします。

無料プランでAPIキーを取得してください: app.scrapeless.com


前提条件

  • Python 3.10以上
  • scrapeless SDK、playwrightbeautifulsoup4
  • ScrapelessアカウントとAPIキー - app.scrapeless.com でサインアップ
  • ターミナルの基本的な知識

インストール

SDK、プロトコルクライアント、HTMLパーサーをインストールします:

bash Copy
pip install scrapeless playwright beautifulsoup4
playwright install chromium

playwright install chromiumは1回限りのローカルプロトコルクライアントをダウンロードします; 実際のレンダリングは依然としてScrapelessクラウドで行われます。scrapeless SDKはブラウザセッションを生成し、beautifulsoup4は返されたDOMを解析します。何かを実行する前にキーをエクスポートする必要があります: export SCRAPELESS_API_KEY=your_api_token_here


ステップ 1 — 広告ライブラリページをレンダリングし、広告が存在することを確認

スクレイピングはクリーンなレンダリングから始まります。広告ライブラリのURLはクエリパラメータで検索を行います - qがキーワード、countryが視聴者の地理、active_statusad_typeがフィルタです。クラウドブラウザに接続し、最初に公衆Facebookホームページでセッションをウォームアップさせて、リクエストが確立されたブラウザ表面を持つようにしてから、検索URLを読み込み、フィールドセレクターを書き込む前に広告カードをカウントします。

python Copy
import re
from scrapeless import Scrapeless
from scrapeless.types import ICreateBrowser
from playwright.sync_api import sync_playwright

URL = ("https://www.facebook.com/ads/library/"
       "?active_status=all&ad_type=all&country=US&q=nike")
LIB = re.compile(r"ライブラリID:\s*\d+")

client = Scrapeless()  # 環境からSCRAPELESS_API_KEYを読み込む
session = client.browser.create(ICreateBrowser(proxy_country="US", session_ttl=240))

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(session.browser_ws_endpoint)
    ctx = browser.contexts[0] if browser.contexts else browser.new_context()
    page = ctx.pages[0] if ctx.pages else ctx.new_page()
    page.goto("https://www.facebook.com/", wait_until="domcontentloaded", timeout=60_000)
    page.wait_for_timeout(3_000)  # まず公衆のホームページでセッションをウォームアップ
    page.goto(URL, wait_until="domcontentloaded", timeout=60_000)
    page.wait_for_timeout(8_000)  # 結果のグリッドをクライアントサイドでレンダリングさせる
    html = page.content()
    browser.close()

print("htmlバイト数:", len(html), "| 最初のレンダリングでの広告カード:", len(LIB.findall(html)))

これにより、htmlバイト数: 1796025 | 最初のレンダリングでの広告カード: 27という行が出力されます - 最初のペイントで27枚の広告カードを持つ約1.8MBのレンダリングHTML(バイト数は異なるクリエイティブが読み込まれるため、実行ごとに変動します)。ページタイトルは広告ライブラリで、FacebookはURLを自社のデフォルト(search_type=keyword_unorderedmedia_type=allsort_dataブロック)を追加するように書き換えます。wait_until="domcontentloaded"と固定の待機は意図的です: 広告ライブラリは決してアイドル状態にならないアナリティクスとパーソナライズリクエストをストリーミングするため、ネットワークアイドルに待機するとタイムアウトまで停止してしまうでしょう。最初にホームページでウォームアップすることが、グリッドをアンチボットチャレンジの代わりにレンダリングするようになります。


ステップ 2 — 安定したアンカーから各広告を発見し、フィールドを抽出する

結果グリッドのReactクラス名はハッシュ化されており、デプロイメント間で回転するため、div.x1lliihqのようなセレクターはリスクがあります。耐久性のある信号は、各カードが印刷するテキスト: ライブラリID: <数字> です。そのラベルでのアンカー発見し、まだ正確に1つのライブラリIDを包んでいる最大の祖先に登ります — そのコンテナが1つの広告カードです — その内部のテキストとリンクからフィールドを読み取ります。

python Copy
from bs4 import BeautifulSoup

soup = BeautifulSoup(html, "html.parser")  # ステップ1のhtml

def single_card(node):
    # まだ正確に1つの"ライブラリID:"を保持している最大の祖先。
    best, n = node.parent, node.parent
    while n is not None and len(LIB.findall(n.get_text(" ", strip=True))) == 1:
        best, n = n, n.parent
    return best

def fields(card):
    txt = card.get_text("\n", strip=True)
    lib = re.search(r"ライブラリID:\s*(\d+)", txt)
    status = "アクティブ" if re.search(r"\bアクティブ\b", txt) else (
        "非アクティブ" if "非アクティブ" in txt else None)
    started = re.search(r"([A-Z][a-z]+ \d{1,2}, \d{4})に開始", txt)
    ran = re.search(r"([A-Z][a-z]+ \d{1,2}, \d{4}) - ([A-Z][a-z]+ \d{1,2}, \d{4})", txt)
    advertiser = None
    for a in card.find_all("a", href=True):
        if re.match(r"https://www\.facebook\.com/[^/?#]+/?$", a["href"]) and a.get_text(strip=True):
python Copy
advertiser = a.get_text(strip=True)
            break
    return {
        "library_id": lib.group(1) if lib else None,
        "advertiser": advertiser,
        "status": status,
        "started_running": started.group(1) if started else None,
        "active_range": list(ran.groups()) if ran else None,
    }

seen, cards = set(), []
for node in soup.find_all(string=LIB):
    card = single_card(node)
    if id(card) not in seen:
        seen.add(id(card))
        cards.append(card)

records = [fields(c) for c in cards]
print("広告レコードを抽出しました:", len(records))
for r in records[:4]:
    print(r)

これで27件のレコードが抽出されました。最初のいくつかは実際の広告主や日付を表示します — {'library_id': '1869276447125570', 'advertiser': 'ナイキ', 'status': 'アクティブ', 'started_running': '2026年3月17日', 'active_range': None}。二つの日付形式に注目してください:アクティブな広告は「<日付>に開始」と表示され、一方で非アクティブな広告は代わりに「<開始> - <終了>」の実行範囲を表示します。そのため、パーサーは両方を読み取り、もう一方のフィールドを None にします。広告主は、カード内に最も安定して表示される場所である、空のページURL(facebook.com/<page>/)を指す最初のリンクから取得されます。

無料プランでAPIキーを取得する: app.scrapeless.com


ステップ3 — 結果グリッドをスクロールして広告を追加読み込み

広告ライブラリはページ番号でページネーションを行わず、スクロールすることでカードを追加します。信頼できるパターンは、グリッドをスクロールして新しいカードがレンダリングされるのを待ち、DOMを再度読み取り、カードの数が増えなくなるまで繰り返します。各スクロール後の Library ID: の出現回数を数えることで、その検索に対するグリッドが枯渇した時点を知ることができます。

python Copy
session = client.browser.create(ICreateBrowser(proxy_country="US", session_ttl=240))

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(session.browser_ws_endpoint)
    ctx = browser.contexts[0] if browser.contexts else browser.new_context()
    page = ctx.pages[0] if ctx.pages else ctx.new_page()
    page.goto("https://www.facebook.com/", wait_until="domcontentloaded", timeout=60_000)
    page.wait_for_timeout(3_000)
    page.goto(URL, wait_until="domcontentloaded", timeout=60_000)
    page.wait_for_timeout(8_000)

    def card_count():
        return len(LIB.findall(page.content()))

    counts = [card_count()]
    for _ in range(6):  # スクロールするとグリッドがより多くのカードを読み込みます
        page.mouse.wheel(0, 6_000)
        page.wait_for_timeout(2_500)
        counts.append(card_count())
    browser.close()

print("各スクロール後のカード数:", counts)

これは 各スクロール後のカード数: [27, 37, 47, 66, 75, 85, 104] と表示されます — グリッドは最初の描画時に27枚のカードから6回のスクロール後に104枚に増えました。2回連続して数が一致する時、グリッドの読み込みがそのクエリのために停止したことを意味し、スクロールを止めることができます。スクロールステップを控えめに保ち、新しいカードが描画されるのに十分な時間を置かないと、グリッドが追いつく前にカウントを読み取ることになります。


ステップ4 — 構造化された出力を書く

ステップ2の records リストはすでに一貫したキーを持つ辞書のリストなので、CSVまたはJSONに書き込むのは数行で済みます。スキーマを前もって決めてください — 各行に同じキーが含まれ、不在のフィールドが None になるようにします。

python Copy
import csv
import json

# recordsはステップ2で構築された辞書のリストです(最終スクロールの後にパースを再実行して、
# 読み込まれたすべてのカードをキャプチャします)。
fieldnames = ["library_id", "advertiser", "status", "started_running", "active_range"]
with open("facebook_ads.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    for row in records:
        writer.writerow({**row, "active_range": json.dumps(row["active_range"])})

with open("facebook_ads.json", "w", encoding="utf-8") as f:
    json.dump(records, f, ensure_ascii=False, indent=2)

print("facebook_ads.csv と facebook_ads.json に", len(records), "行を書き込みました")

これで全体のループが完成です:クラウドブラウザでグリッドをレンダリングし、Library ID: アンカーから各カードを発見し、広告主や日付を抽出し、さらに読みこむためにスクロールし、保存します。URL内の qcountry パラメーターを入れ替えることで、広告ライブラリが提供する任意の広告主、キーワード、または地域を指し示すことができます。


広告データを責任を持って取り扱う

広告ライブラリは公共の透明性を提供する場であり、そこにある広告はどのブランドにも誰でも見えるように公開されていますが、レコードは依然として特定の広告主に結びついているため、注意深く収集してください:

  • 公共の場に留まること。 このガイドに記載されているすべては、訪問者が見る同じ匿名の、ログイン不要の結果グリッドを読み取ります。ゲートを拒むビューにアクセスするために認証を行ったり、公共ページに表示されない情報を引き出したりしないでください。
Copy
- **広告および広告主データを収集し、個人データは収集しないこと。** ここで役立つフィールドは、広告主ページ、クリエイティブ、実行日です。コメント投稿者の名前、反応、または広告に付随する個人情報を収集することは避けてください。
- **目的を最小限にし、維持すること。** 分析に必要なフィールドだけを引き出し、それ以上は取らず、ユースケースが必要とする限り保持してください。
- **プラットフォームの利用規約およびレート制限を尊重すること。** Metaのサービス利用規約および<a href="https://datatracker.ietf.org/doc/html/rfc9309" rel="nofollow"><strong>ロボット排除プロトコル</strong></a>を遵守し、リクエストボリュームは控えめに保ち、出口を固定し、同時実行数を制限することで、アクセスを集中させないようにしてください。

商業用途やコンプライアンスに敏感な作業に関しては、該当するプラットフォームの規約を確認し、定期的なパイプラインを構築する前に法律の専門家に相談してください。

---

## 戻り値

パース後、各広告カードは一貫したスキーマの1つのフラットレコードに減少します:

```json
[
  {
    "library_id": "1869276447125570",
    "advertiser": "Nike",
    "status": "アクティブ",
    "started_running": "2026年3月17日",
    "active_range": null
  },
  {
    "library_id": "308819044896583",
    "advertiser": "Nike",
    "status": "非アクティブ",
    "started_running": null,
    "active_range": ["2023年8月15日", "2025年7月24日"]
  }
]
// スキーマは、ステップ2が出力する内容を正確に反映しています。フィールド値は例示的なサンプルです。

実際に期待されるいくつかのポイント:

  • カードのカウントは実行間で変動する。 同じクエリが、どの広告がライブであるかやグリッドの読み込み具合によってわずかに異なるカード数を返す可能性があるため、カウントはスナップショットとして扱い、固定の合計としないでください。
  • 2つの日付形式、1つのスキーマ。 アクティブな広告は単一の「開始日」を持ち、非アクティブな広告は実行範囲を持っています。パーサーはどちらかの値が存在する場合にその値を埋め込み、もう一方には「None」を残します。
  • テキストに基づき、ドリフトを再確認する。 「ライブラリID:」ラベルはハッシュ化されたReactクラスよりもずっと耐久性がありますが、Metaは定期的にカードレイアウトを変更します — マークアップが変わった際には、ディスカバーおよびフィールドパターンを再確認してください。
  • 出口を固定する。 proxy_country="US"を指定することで、返された広告が米国の視聴者に合わせて一貫性を持たせます;必要な地域に合わせて国コードを変更してください。広告ライブラリは国によって結果が異なります。

結論:広告ライブラリの抽出パイプラインをスケールする

Facebook広告ライブラリの読み取りは、4つの動作に簡略化されます。クラウドブラウザで結果グリッドを表示し、安定した「ライブラリID:」アンカーから各広告を発見し、広告主と実行日を抽出し、カウントが落ち着くまでスクロールして読み込みます。正しいHTTPセマンティクスを用いた単純なHTTP部分では、クライアントサイドのグリッドをボット防止チェックの背後で実行することができず、Scrapeless Scraping Browserにエスカレートし、ページをレンダリングして、あなたのパーサーがすでに理解しているDOMを返します。

ここから、すべての生産スクレイパーがスケールする方法でスケールします:最も耐久性のあるフックにセレクタを固定し、レイアウトが変わった際には再確認し、広告がターゲットとしている視聴者に合わせて米国の出口を固定し、欠落しているフィールドをnullableとして扱い、ホストごとに同時実行数を控えめに保つこと。JavaScriptで同じレンダースプリットについては、CheerioとPuppeteerのガイドがNode.jsにおける静的と動的の決定を説明しており、SDKおよびCLIの表面はdocs.scrapeless.comで文書化されています。実行時のオプションを確認するには、価格ページで確認してください。


AI駆動のデータパイプラインの構築を準備していますか?

無料プランを取得し、広告インテリジェンスパイプラインを構築している開発者と接続するために、コミュニティに参加してください:Discord · Telegram

app.scrapeless.comで無料のスクレイピングブラウザランタイムにサインアップし、上記のパターンを広告主、キーワード、あなたの広告ライブラリ研究に必要な地域に適応させてください。


FAQ

Q: Facebook広告ライブラリをスクレイピングすることは合法ですか?
広告ライブラリは公共の透明性アーカイブであり、そこにある広告はログインなしで公開されているため、一般的には制限されるデータよりも確固たる基盤にあります。ただし、規則は法域によって異なり、Metaのサービス利用規約によっても異なるため、プラットフォームの規約を確認し、個人情報ではなく広告主および広告データを収集し、商業利用に関しては法律の専門家に相談してください。

Q: 広告ライブラリを読むためにログインする必要がありますか?
いいえ。広告ライブラリの公共広告は匿名の訪問者に表示され、このガイドは同じ無ログインの表面を読み取ります。公共ページに表示されないビューにアクセスするために認証しないでください。

Q: プロキシは必要ですか?
はい。広告ライブラリは視聴者の国によって結果が異なり、ボットチェックの背後にグリッドをゲートするため、住宅プロキシを通してルーティングし、proxy_countryで国を固定して、ページが地域の訪問者が見る広告を返すようにします。スクレイピングブラウザのセッションには、その出口が含まれています。

Q: ページが広告の代わりにチャレンジや空のグリッドを表示します — クリンレンダーを取得するにはどうすればよいですか?
それはリクエストに対するボット防止チェックです。米国の住宅出口を固定した状態でScrapeless Scraping Browserを通じてレンダリングし、広告ライブラリのURLに移動する前に同じセッションでfacebook.comを最初に読み込んでセッションを温め、グリッドが読み込まれるときに確立された人間のようなブラウザ表面を持つリクエストになるようにします。

Q: レイアウト変更の後にセレクターが機能しなくなりました — どうすればいいですか?
広告ライブラリのReactクラス名はハッシュ化されて回転するため、それに固定しないでください。各カードが印刷するLibrary ID:テキストでのアンカーディスカバリーに基づき、テキストパターンとベアページリンクからフィールドを読み取ります。メタがカードレイアウトを変更した場合、クラス文字列を追いかけるのではなく、テキストパターンを再確認してください。

Q: 最初の画面以上の広告をページを切り替えてどうやって取得しますか?
グリッドは無限スクロールを使用しており、ページ番号ではありません。ページをスクロールし、新しいカードが描画されるのを待ち、DOMを再読み取りし、Library ID:のカウントが増えなくなるまで繰り返します — 一度の実行で27カードから104カードに6回のスクロールで増えました。

Q: 同時にいくつの検索を実行できますか?
同時実行は控えめに保ってください — ホストごとに約3セッションが合理的な上限です — そうすることで礼儀正しいリクエストレートの範囲内に収まります。クラウドブラウザセッションはHTTPリクエストよりも重いため、静的フェッチよりも厳しく制限してください。

Q: AIエージェントなしでこれを行うことはできますか?
はい。上記のPythonとSDKフローはそのままでエンドツーエンドで実行されます。エージェントはその上にある便利なレイヤーです;レンダー → ディスカバー → 抽出 → スクロールループは、直接スケジュールできる通常のコードです。

Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。

最も人気のある記事

カタログ