2,000件を超えるピンがびっしり並んだボード。それを相手に走らせた週末製のPythonスクリプトが、最終的に拾えたのはたった16件でした。あるエンジニアが商品リサーチ用のインスピレーション画像を集めようとして書いたものです。彼はしばらく結果を見つめたあと、こちらを振り向いてこぼしました。「Pinterestに小馬鹿にされてる気がします」
この嘆き、彼に限った話ではありません。PythonでPinterestをスクレイピングしようとする開発者から、私がいちばん耳にする悩みがまさにこれです。requests と BeautifulSoup でPinterestのURLを叩いても、戻ってくるのはわずか数件か、中身のない空っぽのHTMLだけ。理由ははっきりしています。PinterestはJavaScriptで丸ごと描画されるシングルページアプリで、静的なHTTPリクエストには本来のコンテンツが映らないのです。本ガイドでは、その仕組みから、実戦で通用する手段(Playwright、内部APIの傍受、そして Thunderbit に代表されるノーコードツール)、さらにピン・ボード・ユーザープロフィール・無限スクロール・フル解像度画像を取りにいくコードまで、順を追って解説します。本番運用のスクレイパーを組みたい人にも、とにかく早くデータが欲しい人にも、必要な材料は一通り揃います。
Pinterestスクレイピングとは?
Pinterestスクレイピングとは、Pinterest上のデータをプログラムで抜き出す行為を指します。狙う対象は、ピン画像、タイトル、説明文、ボード名、フォロワー数、URLなど。一枚ずつ手作業で保存する代わりに、コード(あるいはツール)に任せて、検索結果・ボード・プロフィールから構造化データを一気にかき集めます。
ピンは 2025年後半時点で2,400億超、月間アクティブユーザーは6億1,900万人。これだけの規模を抱えるPinterestは、Web上でも有数のビジュアルデータの宝庫です。企業からすればこのデータは金脈そのもので、商品トレンドの把握、競合コンテンツとの突き合わせ、インフルエンサー施策の候補リスト作成などに役立ちます。
なぜPythonでPinterestをスクレイピングするのか?
Pinterestはもはや、結婚式の準備をする人がムードボードを作る場所、という枠には収まりません。立派なビジネスインテリジェンスの舞台です。週1回以上Pinterestを使う人の85%はブランドのピンをきっかけに何かを買っており、上位検索の96〜97%にはブランド名が含まれません。要は、ユーザーは買う気で訪れているのに、特定ブランドへの執着は薄いということ。裏を返せば、発見してもらえる余地が桁外れに大きいわけで、だからこそ各チームが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を描画でき、スクロールの再現もこなし、開発も活発に続いています(GitHubスター78,600超、求人掲載は前年比180%増)。ベンチマーク上でもSeleniumを35〜45%上回る速さです。コードを書かずに進めたい人向けの段取りは、記事の後半で扱います。
Pinterest公式API vs. Pythonスクレイピング vs. ノーコード:どれを選ぶべき?
コードに手をつける前に、そもそも自分で書く必要があるのかを一度立ち止まって考える価値があります。比べる軸は以下のとおりです。
| 観点 | Pinterest API | Pythonスクレイピング | Thunderbit(ノーコード) |
|---|---|---|---|
| 事前承認 | ビジネスアカウント + 動画デモ | なし | なし |
| 公開ピン/ボードへのアクセス | 制限あり(自分のデータのみ) | フルアクセス | フルアクセス |
| 高解像度画像の取得 | ケースによる | 可能(URL解析が必要) | 可能(画像抽出機能) |
| 無限スクロール対応 | 該当なし | 可能(コードで制御) | 自動対応 |
| 保守コスト | 低 | 高(セレクタが壊れる) | なし(AIが適応) |
| Sheets/Airtableへの出力 | 手動 | 独自実装 | 標準機能 |
| セットアップ時間 | 数時間〜数日 | 30〜60分 | 2分 |
PythonスクリプトをわざわざこしらえずにPinterestデータをスプレッドシートで受け取りたい――そんなマーケターやEC運営担当には、ThunderbitのAI Web Scraper が最短距離です。Pinterestのページを開いて「AI Suggest Fields」を押し、続けて「Scrape」を押すだけ。出力先はGoogle Sheets、Excel、Airtable、Notionへ直接です。サブページスクレイピング機能を併用すれば、個々のピンのリンクをたどってデータを自動で肉付けすることもできます。コードに触れたことのないメンバーが、500件超のピンを3分とかからずGoogle Sheetsへ流し込む光景を、私は何度となく目にしてきました。
逆に、細かく作り込みたい人、Pythonパイプラインに組み込みたい人、あるいは自分の手で仕組みを編むのが好きな人は、このまま先へ進んでください。
Pinterestスクレイピング用のPython環境を整える
- 難易度: 中級
- 所要時間: 約30〜60分(コーディングとテストを含む)
- 必要なもの: Python 3.9以上、Chromeブラウザ(テスト用)、ターミナル / コマンドラインアクセス
Playwrightと依存関係をインストールする
はじめに、プロジェクト用のフォルダを切って、仮想環境を立ち上げます。
mkdir pinterest-scraper
cd pinterest-scraper
python -m venv venv
source venv/bin/activate # Windowsの場合: venv\Scripts\activate
続いてPlaywrightを入れ、Chromiumのブラウザバイナリを落とします。
pip install playwright
playwright install chromium
データの書き出しには、Python標準の json、os、csv を使うので、ここでの追加インストールは不要です。
プロジェクトフォルダの構成
着手の段階で整えておくと、あとが楽になります。
pinterest-scraper/
├── scraper.py
├── config.py
├── output/
│ ├── pins.json
│ └── pins.csv
└── images/
├── board-name-1/
└── board-name-2/
config.py にはユーザーエージェント文字列を書いておきます。Pinterestは既定のヘッドレスブラウザの署名を弾くため、いかにも本物らしい値を選びましょう。
USER_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を組み立てます。
query = "mid century modern furniture"
url = f"https://www.pinterest.com/search/pins/?q={query.replace(' ', '%20')}&rs=typed"
この形にしておけば、どんな検索語でも使い回せます。末尾の rs=typed は、その語が候補からの選択ではなく「ユーザーが打ち込んだ語」であることをPinterestに知らせるパラメータで、結果の関連度に効いてくることがあります。
ステップ2: ヘッドレスブラウザを起動してページを読み込む
Playwrightの最小構成は次のとおりです。カスタムのユーザーエージェントに目を向けてください。これを欠くと、Pinterestに弾かれるか、ログイン画面へ飛ばされる恐れがあります。
import asyncio
from playwright.async_api import async_playwright
from config import USER_AGENT
async def scrape_search(query, max_pins=100):
url = f"https://www.pinterest.com/search/pins/?q={query.replace(' ', '%20')}&rs=typed"
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
page = await browser.new_page(
user_agent=USER_AGENT,
viewport={"width": 1920, "height": 1080}
)
await page.goto(url)
await asyncio.sleep(3) # JSが初期ピンを描画するのを待つ
ここまでで、おおむね25〜50件ほどの初期ピンが読み込まれます。
ステップ3: ページからピン情報を抽出する
Pinterestでは、各ピンが div[data-test-id='pinWrapper'] で囲われています。その内側に、ピンURLとタイトル(aria-label 経由)を持つリンク(<a>)と、サムネイルURLを抱えた <img> が入っています。
results = []
pins = await page.query_selector_all("div[data-test-id='pinWrapper']")
for pin in pins:
link = await pin.query_selector("a")
if not link:
continue
title = await link.get_attribute("aria-label") or ""
href = await link.get_attribute("href") or ""
img = await pin.query_selector("img")
src = await img.get_attribute("src") if img else ""
results.append({
"title": title,
"url": f"https://www.pinterest.com{href}" if href.startswith("/") else href,
"image_url": src
})
この時点の results には、最初の表示領域に映っているピンだけが収まっています。それ以上を取りにいくにはスクロールが要ります。ここが、いちばんの勘どころです。
ステップ4: 結果をJSONまたはCSVに保存する
抜き出したデータは、後で扱いやすいようファイルへ書き出しておきます。
import json
import csv
def save_json(data, filepath="output/pins.json"):
with open(filepath, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def save_csv(data, filepath="output/pins.csv"):
if not data:
return
with open(filepath, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.DictWriter(f, fieldnames=data[0].keys())
writer.writeheader()
writer.writerows(data)
ExcelでそのCSVを開くつもりなら、文字化けを避けるために utf-8-sig を選んでおくのが無難です。
Pinterestボード全体とユーザープロフィールをスクレイピングする
ここは、既存チュートリアルがごっそり取りこぼしている領域です。ボードやプロフィールのスクレイピングを腰を据えて扱った競合記事を、私はほとんど見かけませんでした。ところがフォーラムでは、これこそ要望の多い機能なのです。ボード内のピンを全部ダウンロードし、画像をボードごとのフォルダに振り分け、さらにフォロワー数やボード一覧といったプロフィール情報まで欲しい――そういう声がよく上がります。
ボードURLからすべてのピンを取得する
ボードURLは https://www.pinterest.com/{username}/{board-name}/ の形をとります。DOM構造は検索結果とよく似ていて、ピンは div[data-test-id='pinWrapper'] に包まれていますが、全件を読み込ませるにはやはりスクロールが要ります。
async def scrape_board(board_url, max_pins=500):
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
page = await browser.new_page(user_agent=USER_AGENT, viewport={"width": 1920, "height": 1080})
await page.goto(board_url)
await asyncio.sleep(3)
seen_ids = set()
all_pins = []
for scroll_round in range(100): # 安全上の上限
pins = await page.query_selector_all("div[data-test-id='pinWrapper']")
new_count = 0
for pin in pins:
link = await pin.query_selector("a")
if not link:
continue
href = await link.get_attribute("href") or ""
if href in seen_ids:
continue
seen_ids.add(href)
new_count += 1
title = await link.get_attribute("aria-label") or ""
img = await link.query_selector("img")
src = await img.get_attribute("src") if img else ""
all_pins.append({
"title": title,
"url": f"https://www.pinterest.com{href}" if href.startswith("/") else href,
"image_url": src
})
print(f"スクロール {scroll_round + 1}: 取得済みのユニークピン {len(all_pins)} 件")
if new_count == 0 or len(all_pins) >= max_pins:
break
prev_height = await page.evaluate("document.body.scrollHeight")
await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
await asyncio.sleep(2.5)
curr_height = await page.evaluate("document.body.scrollHeight")
if curr_height == prev_height:
break # これ以上コンテンツがない
await browser.close()
return all_pins
ひとつ気をつけたい点があります。ボードページには「More Ideas」タブが現れることがあり、ユーザーが保存したピンとアルゴリズム由来のおすすめを切り分けています。本人が実際に保存したピンだけが欲しいなら、その区切りが目に入った段階でスクロールを切り上げましょう。
ユーザープロフィールをスクレイピングする:ボード、フォロワー数、ピン
プロフィールURLは https://www.pinterest.com/{username}/ という形です。プロフィールページからは、こんな情報が拾えます。
- フォロワー / フォロー数:
div[data-test-id='follower-count']を確認 - ボード一覧: 各ボードは
/{username}/{board-name}/にリンクするカード - 総ピン数: プロフィールヘッダーに表示されることがある
async def scrape_profile(username):
url = f"https://www.pinterest.com/{username}/"
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
page = await browser.new_page(user_agent=USER_AGENT, viewport={"width": 1920, "height": 1080})
await page.goto(url)
await asyncio.sleep(3)
# フォロワー数を取得
follower_el = await page.query_selector("div[data-test-id='follower-count']")
followers = await follower_el.inner_text() if follower_el else "N/A"
# ボードリンクを取得
board_links = await page.query_selector_all("a[href*='/" + username + "/']")
boards = []
for bl in board_links:
href = await bl.get_attribute("href") or ""
name = await bl.get_attribute("aria-label") or href.split("/")[-2]
if href.count("/") >= 3 and href != f"/{username}/":
boards.append({"name": name, "url": f"https://www.pinterest.com{href}"})
await browser.close()
return {"username": username, "followers": followers, "boards": boards}
プロフィール内の全ボードからピンをかき集めたいときは、取得したボード一覧をループで回し、各ボードに対して scrape_board() を呼び出します。落としてきた画像は、ボードごとのフォルダへ自動で仕分けられます。
実運用向けの無限スクロール処理を作る
ここが、ただ動くだけのスクレイパーと本番で使える道具とを分ける分水嶺です。最大のつまずきは――フォーラムでも少なく見積もって十数本のスレッドで見かけました――スクレイパーが16〜25件で止まってしまう現象で、原因はスクロール不足か、for i in range(5): scroll() のように回数を決め打ちして運任せにしていることにあります。
その流儀は、とにかく安定しません。Pinterestはスクロールイベントを合図に、ひとかたまり約25件ずつ新しいコンテンツを継ぎ足していきます。5回スクロールしたから125件、とはならないのです。回線が細ければ75件で止まることもあれば、バッチが小さければ150件超になだれ込むこともあります。もっと利口なやり方が要ります。
新しいコンテンツがなくなるまでスクロールするパターン
以下は、ユニークなピンIDを追い、タイムアウトを構え、リトライを織り込み、進捗まで吐き出す堅牢なスクロール関数です。
import time
import random
async def scroll_and_collect(page, max_pins=1000, max_scrolls=200, scroll_pause=2.5):
seen_ids = set()
all_pins = []
no_new_count = 0
for i in range(max_scrolls):
pins = await page.query_selector_all("div[data-test-id='pinWrapper']")
new_this_round = 0
for pin in pins:
link = await pin.query_selector("a")
if not link:
continue
href = await link.get_attribute("href") or ""
if href in seen_ids:
continue
seen_ids.add(href)
new_this_round += 1
title = await link.get_attribute("aria-label") or ""
img = await link.query_selector("img")
src = await img.get_attribute("src") if img else ""
all_pins.append({
"title": title,
"url": f"https://www.pinterest.com{href}" if href.startswith("/") else href,
"image_url": src
})
print(f" スクロール {i+1}: 新規 {new_this_round} 件 | 合計ユニークピン {len(all_pins)} 件")
if len(all_pins) >= max_pins:
print(f" max_pins の上限 ({max_pins}) に達したため停止します。")
break
if new_this_round == 0:
no_new_count += 1
if no_new_count >= 3:
print(" 3回連続で新規ピンがなかったため、コンテンツの終端と判断します。")
break
else:
no_new_count = 0
prev_height = await page.evaluate("document.body.scrollHeight")
await page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
await asyncio.sleep(scroll_pause + random.uniform(0.5, 1.5))
curr_height = await page.evaluate("document.body.scrollHeight")
if curr_height == prev_height and new_this_round == 0:
print(" ページ高さが変わらず新規ピンもないため、フィードの終端らしいです。")
break
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を手に入る範囲で最高画質に置き換え、ダメなら段階的にフォールバックします。
import requests as req
def upgrade_image_url(url, preferred_size="originals"):
"""Pinterest画像URLを、利用可能な中で最高解像度に書き換える。"""
sizes = ["originals", "736x", "474x", "236x"]
if preferred_size not in sizes:
preferred_size = "originals"
for size in sizes[sizes.index(preferred_size):]:
upgraded = url
for s in sizes:
upgraded = upgraded.replace(f"/{s}/", f"/{size}/")
try:
resp = req.head(upgraded, timeout=5, allow_redirects=True)
if resp.status_code == 200:
return upgraded
except Exception:
continue
return url # すべて失敗したら元URLを返す
重要な注意点(2025年時点): /originals/ パスは、HTTP 403 Forbidden を返すケースが目立ってきました。gallery-dl の既知の issue でも、2025年の半ばにこの挙動が報告されています。腰を据えて頼れる最大解像度は /736x/ です。この関数はまず /originals/ を試み、しくじったら自動で /736x/ に切り替えます。
画像を整理されたフォルダにダウンロードする
import os
import time
def download_images(pins, folder="images/default", delay=1.5):
os.makedirs(folder, exist_ok=True)
for i, pin in enumerate(pins):
img_url = upgrade_image_url(pin.get("image_url", ""), preferred_size="736x")
if not img_url:
continue
filename = f"pin_{i+1}.jpg"
filepath = os.path.join(folder, filename)
try:
resp = req.get(img_url, timeout=15)
if resp.status_code == 200:
with open(filepath, "wb") as f:
f.write(resp.content)
print(f" {filename} をダウンロードしました ({len(resp.content) // 1024} KB)")
else:
print(f" {filename} に失敗: HTTP {resp.status_code}")
except Exception as e:
print(f" {filename} のダウンロード中にエラー: {e}")
time.sleep(delay + random.uniform(0.3, 0.8))
ダウンロードの合間には、必ずレート制限よけの待機を挟んでください。私は1.5〜2.3秒くらいの幅でランダムに揺らしています。これを怠ると、数百リクエストを投げたあたりでPinterestにIPを締め出される恐れがあります。
スクレイピングしたPinterestデータを書き出す
CSVまたはJSONにエクスポートする
基本形はすでに示しましたが、データが膨らんできた場合(ピン1万件超など)は、1行に1つのJSONオブジェクトを並べるJSON Lines形式のほうが扱いやすいです。ストリーミング処理とも相性が良いです。
def save_jsonl(data, filepath="output/pins.jsonl"):
with open(filepath, "w", encoding="utf-8") as f:
for item in data:
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キーの設定が前提で、パイプラインの込み入り具合は一気に跳ね上がります。
それとも――いくらか身びいきは入りますが、実際これが最速です――Thunderbit なら、Pinterestを取得して、これらの宛先へワンクリックで書き出せます。APIキーもサービスアカウントも追加コードも一切不要です。Thunderbit Chrome拡張機能 が、エクスポートを最初から面倒見てくれます。
Pinterestスクレイピングでブロックされにくくするコツ
ScrapeOpsの評価では、Pinterestのボット対策をかいくぐる難しさは10段階中6。楽勝ではないものの、手も足も出ないというほどでもありません。向こうはブラウザフィンガープリント、挙動分析、IP単位のレート制限を駆使しています。これに効く打ち手は次のとおりです。
- ユーザーエージェントをローテーションする: 実在するChromeのUAを複数用意し、セッションごとにランダムで使う。
- ランダムな遅延を入れる: スクロールやリクエストの間に2〜5秒、揺らぎ付きで待つ。プロキシなしの環境では10〜15秒に伸ばす。
- 現実的な画面サイズを使う:
viewport={"width": 1920, "height": 1080}を設定し、極端に小さいサイズや不自然なサイズは避ける。 - 大量取得ならプロキシを検討: 数千件のピンを取得するなら住宅用プロキシのローテーションが必要です。なしでやると、数百リクエスト後にIPブロックされる可能性があります。
- robots.txtを尊重する: Pinterestの
robots.txtは多くの自動クローラーをブロックしており、約180のDisallowルール があります。コンプライアンス上、意識しておきましょう。 - ログイン状態でのスクレイピングは避ける: ログアウトした状態で、公開されているコンテンツだけを扱いましょう。ログイン後の取得は、法的にも技術的にもリスクが上がります。
ThunderbitはAIエンジンで、ボット対策やCAPTCHAにも自前で立ち向かいます。ノーコードで進めるなら、保守しなくて済む対象がひとつ減るのは大きな利点です。
Pinterestスクレイピングに関する法的・倫理的な考慮事項
本記事の主題ではないので手短に済ませますが、軽んじてよい話ではありません。
Pinterestの利用規約(Section 2a)は、「Pinterestからデータやコンテンツを、許可されていない方法でスクレイピング、収集、検索、コピー、その他アクセスしないこと(当社の明示的な事前許可なしに自動手段を使用することを含む)」への同意を求めています。その一方で裁判所は、公開されているデータのスクレイピング自体は Computer Fraud and Abuse Act に反しない、という立場を概ね取ってきました。hiQ v. LinkedIn や Meta v. Bright Data(2024年1月)では、ログアウト状態で公開表示のデータを取得する行為は適法と判断されています。
最低限守りたい線引きは、以下のとおりです。
- ログアウトした状態で、公開表示のコンテンツだけをスクレイピングする
- スクレイピングしたデータをスパムやなりすましに使わない
- 画像の著作権を尊重する。可能ならメタデータだけを抽出し、著作権のある画像を無断で商用再配布しない
- 商用利用する予定があるなら、弁護士に相談する
法的な全体像をもっと詳しく押さえたい方は、web scraping legal implications guide もあわせてご覧ください。
まとめ: 何を学んだか、次に何をするか
ここまでで、なぜ静的スクレイピングがPinterestで通用しないのか(React製のSPAで、JavaScriptが回らないとデータも姿を現さないから)、Playwrightで検索結果・ボード・プロフィールを取りにいく方法、16件で頭打ちにならない本番向けの無限スクロール処理の組み方、そして小さなサムネイルではなく高解像度画像を手にする方法まで、ひととおり押さえられました。
要点をざっと振り返ります。
requests+ BeautifulSoup はPinterestでは機能しません。時間の無駄です。- Playwright はこの用途に最適なPythonツールです。高速で、サポートも充実しており、JS描画を自然に処理できます。
- 無限スクロール には、固定回数ではなく、重複排除ベースのスクロールループが必要です。
- 高解像度画像 を得るにはURLパスの書き換えが必要で、
/736x/を狙うのが現実的です(/originals/は403を返しやすい)。 - ボードとプロフィールのスクレイピング は既存チュートリアルでは手薄ですが、適切なセレクタがあれば難しくありません。
- 非エンジニアやスピード重視のチームには、 Thunderbit を使えば、Pinterestを2クリックでスクレイピングし、Google Sheets、Excel、Airtable、Notionへエクスポートできます。Pythonは不要です。Chrome拡張機能 から無料で試せます。
Pythonでパイプラインを築くなら、本ガイドのコードは確かな足場になります。欲しいのがデータだけなら、Thunderbitが近道です。どちらを選んでも、16件のピンを前に立ち尽くす日々とはお別れです。
スクレイピングやデータ抽出をさらに掘り下げたい方は、PythonでWebスクレイピングする方法、おすすめの自動Webスクレイピングツール、WebサイトからExcelへデータをスクレイピングする方法 もどうぞ。Thunderbitの料金ページ や、Thunderbit YouTubeチャンネル のチュートリアルもヒントになります。
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をスクレイピングするのに最適なノーコード代替は何ですか?
ThunderbitのAI Web Scraper を使えば、Pinterestのピンデータ(タイトル、画像、URL、説明文など)を2クリックで抜き出せて、Google Sheets、Excel、Airtable、Notionへも標準でエクスポートできます。JavaScript描画も無限スクロールもボット対策も自動でさばくので、コードを書く必要も保守する必要もありません。
詳しくはこちら


