数か月前、あるエンジニアが週末に書いたPythonスクリプトを見せてくれました。Pinterestから商品リサーチ用のインスピレーション画像を集めるためのものだったのですが、実行結果は……たった16件のピン。2,000件以上あるボードの中から、です。彼は画面を見つめ、次に私を見て、こう言いました。「Pinterestにバカにされてる気がします」
彼だけではありません。これは、PythonでPinterestをスクレイピングしようとする開発者から最もよく聞く悩みです。requests と BeautifulSoup を使って Pinterest のURLにアクセスしても、返ってくるのは数件のデータか、空のHTMLの器だけ。なぜか? PinterestはJavaScriptで完全に描画されるシングルページアプリだからです。静的なHTTPリクエストでは、実際のコンテンツが見えません。このガイドでは、その理由、実際に使える方法(Playwright、内部APIのインターセプト、そして のようなノーコードツール)、さらにピン、ボード、ユーザープロフィール、無限スクロール、フル解像度画像を取得するためのステップごとのコードまで解説します。実運用レベルのスクレイパーを作りたい人も、手早くデータを取りたい人も、ここで必要な情報は一通りそろいます。
Pinterestスクレイピングとは?
Pinterestスクレイピングとは、Pinterest上のデータをプログラムで抽出することです。対象は、ピン画像、タイトル、説明文、ボード名、フォロワー数、URLなど。ひとつずつ手で保存する代わりに、コード(またはツール)を使って検索結果、ボード、プロフィールから構造化データをまとめて収集します。
と を抱えるPinterestは、Web上でも屈指のビジュアルデータソースです。企業にとってこのデータはまさに宝の山で、商品トレンドの把握、競合コンテンツの比較、インフルエンサー施策のリスト作成などに活用できます。
なぜPythonでPinterestをスクレイピングするのか?
Pinterestは、もはや結婚式プランナーの気分ボードだけではありません。れっきとしたビジネスインテリジェンスの場です。 がブランドのピンを見て何かを購入しており、 です。つまり、ユーザーは購入意欲を持って訪れている一方で、特定ブランドへのこだわりは強くないということ。これは発見されるチャンスが非常に大きいことを意味し、だからこそ多くのチームがPinterestの構造化データを欲しがるのです。
チームごとに見ると、こんな使い方があります。
| チーム | 必要なデータ | ビジネス価値 |
|---|---|---|
| EC運営 | 商品画像、価格、流行のテイスト | 競争力のある価格設計、トレンドを踏まえた在庫計画 |
| マーケティング | ボードの成果、ピンのエンゲージメント、競合コンテンツ | コンテンツ戦略、キャンペーン比較 |
| 営業 / リード獲得 | クリエイタープロフィール、フォロワー数、連絡先情報 | インフルエンサーへのアプローチ、提携候補の絞り込み |
| 不動産 | 物件のスタイリング例、インテリアトレンド、部屋のレイアウト | 物件写真の方向性、スタイリングの参考 |
| コンテンツ制作者 | 流行トピック、人気フォーマット、季節テーマ | コンテンツカレンダー、ビジュアル表現のリサーチ |
さらに大きなポイントがあります。Pinterestの公式APIはかなり制限が厳しいのです。ビジネスアカウントが必要で、申請にはアプリの動画デモまで求められ、アクセスできるのは自分のアカウントデータだけ。公開ボードや検索結果、競合プロフィールを見たいなら、現実的な選択肢はスクレイピングになります。そのため多くのチームがPythonを使い、セットアップを省きたい場合はThunderbitのようなノーコードツールに流れていくわけです。
BeautifulSoupだけではPinterestでうまくいかない理由(そして本当に使える方法)
requests + BeautifulSoup でPinterestをスクレイピングして、16件しか取れない、あるいは真っ白なページしか返ってこないなら、それは勘違いではありません。PinterestはReactで構築されており、コンテンツの100%をJavaScript経由で描画しています。通常のHTTPリクエストでPinterestのURLを取得すると、サーバーが返すのは最小限のHTMLだけです。数個の<link>や<script>タグと、Reactがアプリを差し込む空の<div>だけ。ピンカード、画像、タイトル、グリッドレイアウトは、ブラウザ上でJavaScriptが実行されたあとに挿入されます。
JavaScriptが動かない = ピンが見えない。
では、何が使えるのでしょうか? 主な方法を比較すると次の通りです。
| 方法 | JS対応 | フルデータ取得 | 難易度 | 向いている用途 |
|---|---|---|---|---|
requests + BeautifulSoup | いいえ | 約0〜16件 | 低 | Pinterestには不向き |
| Selenium / Playwright | はい | はい(スクロール処理が必要) | 中 | 自由度の高いPythonパイプライン |
| Pinterest内部APIのインターセプト | はい | はい(ページ分割JSON) | 高 | 最大量のデータ取得、ブラウザ不要 |
| サードパーティのスクレイピングAPI | はい | ケースによる | 低 | インフラなしでスケール |
| ノーコードツール(Thunderbit) | はい | AIで構造化 | 非常に低い | 非技術者、スピード重視 |
このチュートリアルでは、Pythonで使うなら Playwright をおすすめします。JavaScriptを描画でき、スクロールの再現も可能で、メンテナンスも活発(、求人掲載数は)。ベンチマークでもSeleniumより です。ノーコードで進めたい場合の方法も後半で紹介します。
Pinterest公式API vs. Pythonスクレイピング vs. ノーコード:どれを選ぶべき?
コードを書き始める前に、そもそも本当に書く必要があるのか考える価値があります。判断基準は次の通りです。
| 観点 | Pinterest API | Pythonスクレイピング | Thunderbit(ノーコード) |
|---|---|---|---|
| 事前承認 | ビジネスアカウント + 動画デモ | なし | なし |
| 公開ピン/ボードへのアクセス | 制限あり(自分のデータのみ) | フルアクセス | フルアクセス |
| 高解像度画像の取得 | ケースによる | 可能(URL解析が必要) | 可能(画像抽出機能) |
| 無限スクロール対応 | 該当なし | 可能(コードで制御) | 自動対応 |
| 保守コスト | 低 | 高(セレクタが壊れる) | なし(AIが適応) |
| Sheets/Airtableへの出力 | 手動 | 独自実装 | 標準機能 |
| セットアップ時間 | 数時間〜数日 | 30〜60分 | 2分 |
マーケター、EC運営担当、あるいはPythonスクリプトの作成や保守をせずにPinterestデータをスプレッドシートで欲しい人なら、 が最短ルートです。Pinterestのページを開いて「AI Suggest Fields」をクリックし、「Scrape」を押すだけで、Google Sheets、Excel、Airtable、Notionに直接エクスポートできます。サブページスクレイピング機能を使えば、個々のピンリンクをたどってデータを自動で補強することも可能です。コードを書いたことがないチームメンバーでも、3分以内に500件以上のピンをGoogle Sheetsへ取り込む様子を何度も見てきました。
自由度を重視したい人、Pythonパイプラインに組み込みたい人、あるいは自分で仕組みを作るのが好きな人は、このまま読み進めてください。
Pinterestスクレイピング用のPython環境を整える
- 難易度: 中級
- 所要時間: 約30〜60分(コーディングとテストを含む)
- 必要なもの: Python 3.9以上、Chromeブラウザ(テスト用)、ターミナル / コマンドラインアクセス
Playwrightと依存関係をインストールする
まず、プロジェクトフォルダを作成し、仮想環境をセットアップします。
1mkdir pinterest-scraper
2cd pinterest-scraper
3python -m venv venv
4source venv/bin/activate # Windowsの場合: venv\Scripts\activate
次にPlaywrightをインストールし、Chromiumのブラウザバイナリをダウンロードします。
1pip install playwright
2playwright install chromium
データの書き出しには、Python標準の json、os、csv モジュールも使います。追加インストールは不要です。
プロジェクトフォルダの構成
最初から整理しておくのがおすすめです。
1pinterest-scraper/
2├── scraper.py
3├── config.py
4├── output/
5│ ├── pins.json
6│ └── pins.csv
7└── images/
8 ├── board-name-1/
9 └── board-name-2/
config.py にはユーザーエージェント文字列を設定します。Pinterestはデフォルトのヘッドレスブラウザ署名をブロックするため、現実的なものを使いましょう。
1USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36"
ステップ1: Pinterest検索URLを作る
検索語をテンプレートに差し込んでURLを組み立てます。
1query = "mid century modern furniture"
2url = f"https://www.pinterest.com/search/pins/?q={query.replace(' ', '%20')}&rs=typed"
この形なら、どんな検索語にも対応できます。rs=typed パラメータは、検索語が候補ではなく「ユーザーが入力した語」であることをPinterestに伝えます。これが結果の関連性に影響することがあります。
ステップ2: ヘッドレスブラウザを起動してページを読み込む
Playwrightの基本セットアップは次の通りです。カスタムユーザーエージェントに注目してください。これがないと、Pinterestにブロックされるか、ログイン画面に飛ばされる可能性があります。
1import asyncio
2from playwright.async_api import async_playwright
3from config import USER_AGENT
4async def scrape_search(query, max_pins=100):
5 url = f"https://www.pinterest.com/search/pins/?q={query.replace(' ', '%20')}&rs=typed"
6 async with async_playwright() as p:
7 browser = await p.chromium.launch(headless=True)
8 page = await browser.new_page(
9 user_agent=USER_AGENT,
10 viewport={"width": 1920, "height": 1080}
11 )
12 await page.goto(url)
13 await asyncio.sleep(3) # JSが初期ピンを描画するのを待つ
このあと、通常は25〜50件程度の初期ピンが読み込まれます。
ステップ3: ページからピン情報を抽出する
Pinterestでは、各ピンが div[data-test-id='pinWrapper'] で囲まれています。その中に、ピンURLとタイトル(aria-label 経由)を持つリンク(<a>)と、サムネイルURLを持つ <img> があります。
1 results = []
2 pins = await page.query_selector_all("div[data-test-id='pinWrapper']")
3 for pin in pins:
4 link = await pin.query_selector("a")
5 if not link:
6 continue
7 title = await link.get_attribute("aria-label") or ""
8 href = await link.get_attribute("href") or ""
9 img = await pin.query_selector("img")
10 src = await img.get_attribute("src") if img else ""
11 results.append({
12 "title": title,
13 "url": f"https://www.pinterest.com{href}" if href.startswith("/") else href,
14 "image_url": src
15 })
この時点で results には、最初の表示領域に見えているピンが入っています。さらに取得するにはスクロールが必要です。ここが最重要ポイントです。
ステップ4: 結果をJSONまたはCSVに保存する
抽出したデータは、あとで使いやすいようにファイルへ書き出します。
1import json
2import csv
3def save_json(data, filepath="output/pins.json"):
4 with open(filepath, "w", encoding="utf-8") as f:
5 json.dump(data, f, ensure_ascii=False, indent=2)
6def save_csv(data, filepath="output/pins.csv"):
7 if not data:
8 return
9 with open(filepath, "w", newline="", encoding="utf-8-sig") as f:
10 writer = csv.DictWriter(f, fieldnames=data[0].keys())
11 writer.writeheader()
12 writer.writerows(data)
ExcelでCSVを開く予定なら、文字化け防止のために utf-8-sig を使うのがおすすめです。
Pinterestボード全体とユーザープロフィールをスクレイピングする
これは、既存チュートリアルに大きく欠けている部分です。ボードやプロフィールのスクレイピングを深掘りしている競合記事は、私はほとんど見つけられませんでした。でも、フォーラムでは非常に要望の多い機能です。ユーザーはボード内の全ピンをダウンロードし、画像をボードごとのフォルダに整理し、フォロワー数やボード一覧といったプロフィール情報も取り出したいのです。
ボードURLからすべてのピンを取得する
ボードURLは https://www.pinterest.com/{username}/{board-name}/ という形です。DOM構造は検索結果と似ていて、ピンは div[data-test-id='pinWrapper'] に包まれていますが、全件を読み込むにはスクロールが必要です。
1async def scrape_board(board_url, max_pins=500):
2 async with async_playwright() as p:
3 browser = await p.chromium.launch(headless=True)
4 page = await browser.new_page(user_agent=USER_AGENT, viewport={"width": 1920, "height": 1080})
5 await page.goto(board_url)
6 await asyncio.sleep(3)
7 seen_ids = set()
8 all_pins = []
9 for scroll_round in range(100): # 安全上の上限
10 pins = await page.query_selector_all("div[data-test-id='pinWrapper']")
11 new_count = 0
12 for pin in pins:
13 link = await pin.query_selector("a")
14 if not link:
15 continue
16 href = await link.get_attribute("href") or ""
17 if href in seen_ids:
18 continue
19 seen_ids.add(href)
20 new_count += 1
21 title = await link.get_attribute("aria-label") or ""
22 img = await link.query_selector("img")
23 src = await img.get_attribute("src") if img else ""
24 all_pins.append({
25 "title": title,
26 "url": f"https://www.pinterest.com{href}" if href.startswith("/") else href,
27 "image_url": src
28 })
29 print(f"スクロール {scroll_round + 1}: 取得済みのユニークピン {len(all_pins)} 件")
30 if new_count == 0 or len(all_pins) >= max_pins:
31 break
32 prev_height = await page.evaluate("document.body.scrollHeight")
33 await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
34 await asyncio.sleep(2.5)
35 curr_height = await page.evaluate("document.body.scrollHeight")
36 if curr_height == prev_height:
37 break # これ以上コンテンツがない
38 await browser.close()
39 return all_pins
注意点がひとつあります。ボードページには「More Ideas」タブが表示されることがあり、保存済みピンとアルゴリズムによるおすすめを分けています。ユーザーが実際に保存したピンだけ欲しいなら、その区切りが見えた時点でスクロールを止めましょう。
ユーザープロフィールをスクレイピングする:ボード、フォロワー数、ピン
プロフィールURLは https://www.pinterest.com/{username}/ の形です。プロフィールページからは次の情報が取れます。
- フォロワー / フォロー数:
div[data-test-id='follower-count']を確認 - ボード一覧: 各ボードは
/{username}/{board-name}/にリンクするカード - 総ピン数: プロフィールヘッダーに表示されることがある
1async def scrape_profile(username):
2 url = f"https://www.pinterest.com/{username}/"
3 async with async_playwright() as p:
4 browser = await p.chromium.launch(headless=True)
5 page = await browser.new_page(user_agent=USER_AGENT, viewport={"width": 1920, "height": 1080})
6 await page.goto(url)
7 await asyncio.sleep(3)
8 # フォロワー数を取得
9 follower_el = await page.query_selector("div[data-test-id='follower-count']")
10 followers = await follower_el.inner_text() if follower_el else "N/A"
11 # ボードリンクを取得
12 board_links = await page.query_selector_all("a[href*='/" + username + "/']")
13 boards = []
14 for bl in board_links:
15 href = await bl.get_attribute("href") or ""
16 name = await bl.get_attribute("aria-label") or href.split("/")[-2]
17 if href.count("/") >= 3 and href != f"/{username}/":
18 boards.append({"name": name, "url": f"https://www.pinterest.com{href}"})
19 await browser.close()
20 return {"username": username, "followers": followers, "boards": boards}
プロフィールのすべてのボードからピンを取得したい場合は、ボード一覧をループして scrape_board() を各ボードに対して呼び出します。ダウンロードした画像は、ボードごとのフォルダに自動で整理できます。
実運用向けの無限スクロール処理を作る
ここは、ただ動くだけのスクレイパーと本格運用ツールを分ける部分です。最大の悩みは、そして少なくとも十数件のフォーラムスレッドで見かけた悩みですが、スクレイパーが16〜25件しか返さないのは、スクロールが足りないか、for i in range(5): scroll() のような固定回数で運任せにしているからです。
そのやり方は不安定です。Pinterestはスクロールイベントをトリガーに、約25件ずつ新しいコンテンツを読み込みます。5回スクロールしたからといって、125件取れるとは限りません。通信が遅ければ75件しか出ないこともあるし、バッチが小さければ150件以上になることもあります。もっと賢いパターンが必要です。
新しいコンテンツがなくなるまでスクロールするパターン
以下は、ユニークなピンIDを追跡し、タイムアウトを設定し、リトライロジックを含め、進捗も表示する堅牢なスクロール関数です。
1import time
2import random
3async def scroll_and_collect(page, max_pins=1000, max_scrolls=200, scroll_pause=2.5):
4 seen_ids = set()
5 all_pins = []
6 no_new_count = 0
7 for i in range(max_scrolls):
8 pins = await page.query_selector_all("div[data-test-id='pinWrapper']")
9 new_this_round = 0
10 for pin in pins:
11 link = await pin.query_selector("a")
12 if not link:
13 continue
14 href = await link.get_attribute("href") or ""
15 if href in seen_ids:
16 continue
17 seen_ids.add(href)
18 new_this_round += 1
19 title = await link.get_attribute("aria-label") or ""
20 img = await link.query_selector("img")
21 src = await img.get_attribute("src") if img else ""
22 all_pins.append({
23 "title": title,
24 "url": f"https://www.pinterest.com{href}" if href.startswith("/") else href,
25 "image_url": src
26 })
27 print(f" スクロール {i+1}: 新規 {new_this_round} 件 | 合計ユニークピン {len(all_pins)} 件")
28 if len(all_pins) >= max_pins:
29 print(f" max_pins の上限 ({max_pins}) に達したため停止します。")
30 break
31 if new_this_round == 0:
32 no_new_count += 1
33 if no_new_count >= 3:
34 print(" 3回連続で新規ピンがなかったため、コンテンツの終端と判断します。")
35 break
36 else:
37 no_new_count = 0
38 prev_height = await page.evaluate("document.body.scrollHeight")
39 await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
40 await asyncio.sleep(scroll_pause + random.uniform(0.5, 1.5))
41 curr_height = await page.evaluate("document.body.scrollHeight")
42 if curr_height == prev_height and new_this_round == 0:
43 print(" ページ高さが変わらず新規ピンもないため、フィードの終端らしいです。")
44 break
45 return all_pins
この設計がうまくいく理由は次の通りです。
- hrefで重複排除: 各ピンのURLは一意なのでIDとして使えます。スクロール中にDOMが再描画されても、同じピンを二重に数えません。
- 3回連続ルール: 連続3回で新規ピンがゼロなら停止します。ページがまだ読み込み中でも、実データがなければ抜けられます。
- 待機時間にランダムな揺らぎ: スクロール間に0.5〜1.5秒のランダム遅延を入れると、人間らしい挙動になり、ボット対策を踏みにくくなります。
- max_scrollsの安全上限: 何かおかしくなっても無限ループを防げます。
例外ケースへの対応
- 「More Ideas」の境界: ボードページではPinterestが「More Ideas」セクションを挿入することがあります。ボードの保存済みピンだけ欲しいなら、その要素が表示された時点でスクロールを止められます。
- 長時間セッションでのレート制限: 数千件のピンがあるボードをスクロールしていると、Pinterestが徐々に制限をかけることがあります。新規ピンがときどきゼロになる程度なら問題ありませんが、3回続けてではない場合は、スクロール間隔を5秒以上に伸ばしてください。
Pinterest画像をフル解像度で取得する方法(サムネイルではなく)
これがまた厄介です。たくさんのピンをスクレイピングして画像をダウンロードしてみると、全部236pxの小さいサムネイルだった、ということがよくあります。フォーラムでは「画質がひどい、というか小さすぎる」と言われることもあります。解決策は、Pinterestの画像URL構造を理解することです。
Pinterestの画像URLパスを理解する
Pinterestの画像はすべて https://i.pinimg.com/{size}/{hash}.jpg で配信されています。{size} の部分が解像度を決めます。
| サイズパス | 寸法 | 用途 |
|---|---|---|
/236x/ | 幅236px | デフォルトのグリッド表示(初期状態で取得されるもの) |
/474x/ | 幅474px | 中解像度 |
/736x/ | 幅736px | ピン詳細 / 拡大表示 |
/originals/ | 元画像のサイズ | フル解像度 |
便利関数: 任意のPinterest画像URLを高解像度に変換する
以下の関数は、Pinterest画像URLを可能な限り高品質に書き換え、フォールバックも行います。
1import requests as req
2def upgrade_image_url(url, preferred_size="originals"):
3 """Pinterest画像URLを、利用可能な中で最高解像度に書き換える。"""
4 sizes = ["originals", "736x", "474x", "236x"]
5 if preferred_size not in sizes:
6 preferred_size = "originals"
7 for size in sizes[sizes.index(preferred_size):]:
8 upgraded = url
9 for s in sizes:
10 upgraded = upgraded.replace(f"/{s}/", f"/{size}/")
11 try:
12 resp = req.head(upgraded, timeout=5, allow_redirects=True)
13 if resp.status_code == 200:
14 return upgraded
15 except Exception:
16 continue
17 return url # すべて失敗したら元URLを返す
重要な注意点(2025年時点): /originals/ パスは、HTTP 403 Forbidden を返すことが増えています。 でも、2025年中頃時点でこの挙動が確認されています。安定して使える最大解像度は /736x/ です。この関数ではまず /originals/ を試し、失敗したら自動で /736x/ にフォールバックします。
画像を整理されたフォルダにダウンロードする
1import os
2import time
3def download_images(pins, folder="images/default", delay=1.5):
4 os.makedirs(folder, exist_ok=True)
5 for i, pin in enumerate(pins):
6 img_url = upgrade_image_url(pin.get("image_url", ""), preferred_size="736x")
7 if not img_url:
8 continue
9 filename = f"pin_{i+1}.jpg"
10 filepath = os.path.join(folder, filename)
11 try:
12 resp = req.get(img_url, timeout=15)
13 if resp.status_code == 200:
14 with open(filepath, "wb") as f:
15 f.write(resp.content)
16 print(f" {filename} をダウンロードしました ({len(resp.content) // 1024} KB)")
17 else:
18 print(f" {filename} に失敗: HTTP {resp.status_code}")
19 except Exception as e:
20 print(f" {filename} のダウンロード中にエラー: {e}")
21 time.sleep(delay + random.uniform(0.3, 0.8))
ダウンロード間には必ずレート制限用の待機を入れてください。私は1.5〜2.3秒程度にランダムな揺らぎを加えています。これがないと、数百リクエストの時点でPinterestにIPをブロックされる可能性があります。
スクレイピングしたPinterestデータを書き出す
CSVまたはJSONにエクスポートする
基本はすでに紹介しましたが、データが大きい場合(1万件以上のピンなど)は、1行1JSONオブジェクトのJSON Lines形式の方が扱いやすいです。ストリーミング処理にも向いています。
1def save_jsonl(data, filepath="output/pins.jsonl"):
2 with open(filepath, "w", encoding="utf-8") as f:
3 for item in data:
4 f.write(json.dumps(item, ensure_ascii=False) + "\n")
Google Sheets、Airtable、Notionに出力する
Pythonから直接Google Sheetsに送るなら、gspread ライブラリとGoogle Cloudのサービスアカウントが必要です。Airtableなら pyairtable、Notionなら notion-client を使います。どれもAPIキーの設定が必要で、パイプラインの複雑さは一気に増します。
あるいは——少し贔屓目ではありますが、実際かなり最速です—— を使えば、Pinterestをスクレイピングして、これらの宛先へワンクリックでエクスポートできます。APIキーもサービスアカウントも追加コードも不要です。 がエクスポートを標準でサポートしています。
Pinterestスクレイピングでブロックされにくくするコツ
ScrapeOpsによると、Pinterestのボット対策の回避難易度は です。簡単ではないものの、最難関でもありません。ブラウザフィンガープリント、挙動分析、IPベースのレート制限を使っています。効く対策は次の通りです。
- ユーザーエージェントをローテーションする: 実在するChromeのUAを複数用意し、セッションごとにランダムで使う。
- ランダムな遅延を入れる: スクロールやリクエストの間に2〜5秒、揺らぎ付きで待つ。プロキシなしの環境では10〜15秒に伸ばす。
- 現実的な画面サイズを使う:
viewport={"width": 1920, "height": 1080}を設定し、極端に小さいサイズや不自然なサイズは避ける。 - 大量取得ならプロキシを検討: 数千件のピンを取得するなら住宅用プロキシのローテーションが必要です。なしでやると、数百リクエスト後にIPブロックされる可能性があります。
- robots.txtを尊重する: Pinterestの
robots.txtは多くの自動クローラーをブロックしており、 があります。コンプライアンス上、意識しておきましょう。 - ログイン状態でのスクレイピングは避ける: ログアウトした状態で、公開されているコンテンツだけを扱いましょう。ログイン後の取得は、法的にも技術的にもリスクが上がります。
ThunderbitはAIエンジンで、ボット対策やCAPTCHAにも自動対応します。ノーコードで進めるなら、保守対象がひとつ減るのは大きなメリットです。
Pinterestスクレイピングに関する法的・倫理的な考慮事項
この記事の主題ではないので簡潔にしますが、重要な点です。
Pinterestの利用規約(Section 2a)では、「Pinterestからデータやコンテンツを、許可されていない方法でスクレイピング、収集、検索、コピー、その他アクセスしないこと(当社の明示的な事前許可なしに自動手段を使用することを含む)」に同意するよう定めています。一方で、裁判所は一般に、公開されているデータのスクレイピング自体は Computer Fraud and Abuse Act に違反しないと判断してきました。たとえば や Meta v. Bright Data(2024年1月)では、ログアウト状態で公開表示データをスクレイピングすることは合法と判断されています。
最低限のルールは次の通りです。
- ログアウトした状態で、公開表示のコンテンツだけをスクレイピングする
- スクレイピングしたデータをスパムやなりすましに使わない
- 画像の著作権を尊重する。可能ならメタデータだけを抽出し、著作権のある画像を無断で商用再配布しない
- 商用利用する予定があるなら、弁護士に相談する
法的な全体像を詳しく知りたい方は、 もご覧ください。
まとめ: 何を学んだか、次に何をするか
これで、なぜ静的スクレイピングがPinterestで失敗するのか(React製のSPAだからで、JavaScriptが動かなければデータも見えない)、Playwrightを使って検索結果・ボード・プロフィールをスクレイピングする方法、16件で止まらない実運用向け無限スクロール処理の作り方、そして小さなサムネイルではなく高解像度画像を取得する方法まで分かりました。
要点を振り返ると、次の通りです。
requests+ BeautifulSoup はPinterestでは機能しません。時間の無駄です。- Playwright はこの用途に最適なPythonツールです。高速で、サポートも充実しており、JS描画を自然に処理できます。
- 無限スクロール には、固定回数ではなく、重複排除ベースのスクロールループが必要です。
- 高解像度画像 を得るにはURLパスの書き換えが必要で、
/736x/を狙うのが現実的です(/originals/は403を返しやすい)。 - ボードとプロフィールのスクレイピング は既存チュートリアルでは手薄ですが、適切なセレクタがあれば難しくありません。
- 非エンジニアやスピード重視のチームには、 を使えば、Pinterestを2クリックでスクレイピングし、Google Sheets、Excel、Airtable、Notionへエクスポートできます。Pythonは不要です。 から無料で試せます。
Pythonパイプラインを組むなら、このガイドのコードはしっかりした土台になります。必要なのがデータだけなら、Thunderbitが近道です。どちらにせよ、もう16件のピンを前に途方に暮れることはありません。
スクレイピングやデータ抽出についてさらに知りたい方は、、、 もぜひご覧ください。 や、 のチュートリアルも参考になります。
FAQ
1. BeautifulSoupだけでPinterestをスクレイピングできますか?
単体では実用的ではありません。PinterestはJavaScriptで全コンテンツを描画するため、requests + BeautifulSoup では空のHTMLしか見えません。ページを描画するには、PlaywrightやSeleniumのようなヘッドレスブラウザが必要です。あるいは、JavaScript描画を自動で処理するThunderbitのようなノーコードツールを使う方法もあります。
2. 1回のセッションでPinterestから何件くらいスクレイピングできますか?
スクロールロジックとボット対策の扱い方次第です。このガイドの実運用向け無限スクロール処理(重複排除、タイムアウト、リトライロジック)を使えば、ボードや検索クエリごとに数百〜数千件を安定して取得できます。非常に大きなボードでは、収集に数分かかることを見込んでください。
3. スクレイピングしたPinterest画像が小さくなるのはなぜですか?
デフォルトでは、Pinterestはグリッド表示で /236x/ のサムネイルを返します。高解像度にしたい場合は、画像URLのパスを /736x/ または /originals/ に書き換えてください。ただし、2025年時点では /originals/ が403エラーを返すことが増えているため、安定して使えるのは /736x/ です。
4. Pinterestのスクレイピングは合法ですか?
公開データのスクレイピングは、hiQ v. LinkedIn や Meta v. Bright Data などの最近の裁判例では一般的に認められています。ただし、Pinterestの利用規約は無許可の自動アクセスを禁止しています。公開コンテンツだけを扱い、スパムに使わず、著作権を尊重し、商用利用では法務の確認を行ってください。
5. Pinterestをスクレイピングするのに最適なノーコード代替は何ですか?
なら、Pinterestのピンデータ(タイトル、画像、URL、説明文など)を2クリックで抽出でき、Google Sheets、Excel、Airtable、Notionへも標準でエクスポートできます。JavaScript描画、無限スクロール、ボット対策も自動で処理するため、コードを書く必要も保守する必要もありません。
詳しくはこちら