Target.comは、一見するとスクレイピングしやすそうに見えるサイトです。ところが、実際にやってみると話は別。Requests と BeautifulSoup で軽く Python スクリプトを書き、Target の商品ページに投げてみたら、価格欄が None になっていた——そんな経験があるなら、あなたはまったくの少数派ではありません。
主要な小売サイトの多くでスクレイピング手法を試してきた立場から言うと、Target はかなり手強い部類に入ります。月間 という膨大なアクセスを集めるこのサイトには、価格、評価、在庫、レビューなど、商品データの宝庫が詰まっています。ただし、React ベースのクライアントサイド描画と Akamai のボット検知が組み合わさっているため、素朴な方法はほぼ即座に失敗します。とはいえ、Python で本当に使える方法は3つあります。この記事ではそれぞれを順番に解説し、最初の試みがなぜ必ず崩れるのかを説明し、Python が割に合わないときに使えるノーコードの近道も紹介します。
なぜ最初のTarget.comスクレイピングは None になるのか
解決策の前に、まず問題点から。初心者がよく書くコードはだいたいこんな感じです。
1import requests
2from bs4 import BeautifulSoup
3url = "https://www.target.com/p/some-product/-/A-12345678"
4response = requests.get(url, headers={"User-Agent": "Mozilla/5.0"})
5soup = BeautifulSoup(response.text, "html.parser")
6price = soup.select_one('[data-test="current-price"]')
7print(price) # None
出力は? None です。毎回そうなります。
これはコードのバグではありません。Target から requests.get() で返ってくる HTML は、実質的には骨組みだけです。「JavaScript を読み込んで、実際のページを描画してね」という React の外枠にすぎません。商品価格、評価、レビュー、在庫状況などは、初回のページ読み込み後に JavaScript によって差し込まれます。Python の Requests ライブラリは JavaScript を実行しないため、そうした要素はレスポンス内に存在しません。
フォーラムでも、この壁にぶつかった開発者の話があふれています。ある は率直にこう述べています。「要素が None として表示されるのは、JavaScript で描画され、requests では JavaScript によって描画された HTML を取得できないからだ」。 でも、「Target の URL に HTTP リクエストを送ると、HTML レスポンスには意味のあるデータが含まれていない」と確認されています。
しかも、JavaScript の問題を解決してもまだ終わりではありません。Target は Akamai のボット検知で TLS ハンドシェイクの特徴を見分け、HTML が1バイトもやり取りされる前に Python の requests ライブラリをブロックします。この点はあとで詳しく触れます。
Target.com が Python スクレイピングで難しい理由
Target は単に「JavaScript を使っているサイト」ではありません。層になった防御システムです。そして、各層を理解することが、適切なスクレイピング手法を選ぶ鍵になります。
JavaScript で描画される商品データ
Target.com は React で構築されています。実際のブラウザで商品ページや検索ページを開くと、次の流れで処理が進みます。
- サーバーが最小限の HTML 骨組みを返す
- JavaScript のバンドルが読み込まれて実行される
- フロントエンドが Target 内部の Redsky API を呼び出す
- 商品データ(価格、評価、画像、在庫状況)が DOM に描画される
この 2〜4 を飛ばすと、つまり requests.get() で取得するのはまさにそこです——空のページしか返ってきません。 によると、静的な HTTP リクエストで取得できるのは Target 上の利用可能データの約 にすぎません。残りの 70% には JavaScript の実行か API アクセスが必要です。
検索結果ページはさらに厳しく、初期 HTML に表示される商品はごく一部で、残りはスクロールに応じて読み込まれます。
Target のボット対策:ありきたりな「プロキシを使え」では足りない
多くのスクレイピング解説は、ボット対策を「とりあえずプロキシを使えばいい」で済ませがちです。ですが、Target の防御はもう少し具体的に理解する必要があります。
TLS フィンガープリント(最大の難関)。 HTTPS ハンドシェイクの際、クライアントは「Client Hello」パケットを送り、TLS バージョン、暗号スイート、拡張、楕円曲線などの情報を明かします。これらは JA3 フィンガープリントとしてハッシュ化されます。Python の requests ライブラリは — 8d9f7747675e24454cd9b7ed35c58707 — を生成し、ボット対策のデータベースに即座に検知されます。Chrome は GREASE 値を含む16個の暗号スイートを慎重に並べて送りますが、Python はブラウザとは異なる順序で60個以上を送ります。ブロックは HTTP コンテンツが交換される前に起こります。
IP レピュテーション判定。 Akamai は IP を信頼度ごとに分類します。 を借りると、データセンター IP は「ボットに使われる可能性が高いため、強いマイナス評価」を受けます。住宅回線の IP はプラス評価です。Target では特に、データセンター系の IP 帯が即座にフラグされます。
JavaScript フィンガープリント。 Akamai は JavaScript を注入し、JS エンジンの仕様、ハードウェア性能、OS 情報、フォント、プラグイン、さらには入力速度・マウスの動き・クリック間隔といった行動データまで収集します。これにより _abck クッキーという状態付きのフィンガープリントトークンが生成されます。有効な _abck がなければ、リクエストはブロックされます。
レート制限。 Target では、おおむね IP あたり毎分30〜60リクエスト程度で 429 エラーが発生します。中には、見かけ上は 実際には「Pardon Our Interruption」のブロックページを含んでいる、という報告もあり、自動判定を難しくしています。
しています。Akamai の回避に限ると、 です。
PythonでTarget.comをスクレイピングする3つの方法(比較)
3つすべての実用的な手法を、1つの場所で横並びに比較した記事は意外と多くありません。そこで、率直に評価してみます。
| 比較項目 | Requests + BS4 | Selenium / Playwright | Redsky API |
|---|---|---|---|
| JavaScript描画への対応 | ❌ いいえ | ✅ はい | ✅ はい(JSON) |
| 1件あたりの速度 | ⚡ 約0.5〜1秒 | 🐢 約5〜10秒 | ⚡ 約0.5〜1秒 |
| ボット検知リスク | ⚠️ 高い(TLS フィンガープリント) | ⚠️ 中程度 | ⚠️ 中程度(認証キーが変わる可能性) |
| 構築の難易度 | 低 | 中 | 中〜高(リバースエンジニアリングが必要) |
| データの網羅性 | 約30%(静的HTMLのみ) | 約95%(ページ全体) | 約90%(構造化JSON) |
| 向いている用途 | 静的メタデータ、__TGT_DATA__ | 商品ページ全体、レビュー | 大量の商品データ収集 |
では、1つずつ作っていきましょう。
方法1:Pythonの Requests と BeautifulSoup でTarget.comをスクレイピングする
この方法では、JavaScript で描画される検索ページの価格までは取れません。ただし軽量で高速ですし、どこを見るかを知っていれば予想以上の情報を拾えます。
ポイントは、Target が一部の商品データを <script> タグ内に埋め込んでいることです。そこには __PRELOADED_QUERIES__ を含む __TGT_DATA__ 変数があります。この JSON ブロックには、商品名、説明、特徴、そして個別商品ページでは価格までも含まれていることがあります。検索結果の HTML から商品タイトルと URL を取ることもできます。
Step 1: Python 環境を整える
作業フォルダを作成し、依存関係をインストールします。
1mkdir target-scraper && cd target-scraper
2python -m venv venv
3source venv/bin/activate # Windows の場合: venv\Scripts\activate
4pip install requests beautifulsoup4 curl_cffi
ここでは標準の requests より curl_cffi を使います。ブラウザの TLS フィンガープリントを偽装できるため、Target でブロックを避けるうえで最も大きな差が出ます。 curl_cffi は を示し、標準の requests の と比べて 15倍の改善です。
Step 2: Target の検索結果を取得する
Target の検索URLはシンプルです。https://www.target.com/s?searchTerm={keyword}
1from curl_cffi import requests as cureq
2from bs4 import BeautifulSoup
3import time, random
4headers = {
5 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
6 "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
7 "Accept-Language": "en-US,en;q=0.9",
8}
9url = "https://www.target.com/s?searchTerm=bluetooth+headphones"
10resp = cureq.get(url, headers=headers, impersonate="chrome124")
11soup = BeautifulSoup(resp.text, "html.parser")
12# 商品カードはこの data-test 属性を使う
13cards = soup.find_all("div", {"data-test": "@web/site-top-of-funnel/ProductCardWrapper"})
14for card in cards:
15 link_tag = card.find("a")
16 title = link_tag.get_text(strip=True) if link_tag else "N/A"
17 href = "https://www.target.com" + link_tag["href"] if link_tag and link_tag.get("href") else "N/A"
18 print(f"{title} — {href}")
これで商品名と URL は取れます。価格は? この HTML からはたぶん取れません。想定どおりです。
Step 3: 商品ページに埋め込まれた JSON を取り出す
個別の商品ページには、__TGT_DATA__ スクリプトタグ内により豊富なデータが入っています。
1import re, json
2product_url = "https://www.target.com/p/some-product/-/A-12345678"
3resp = cureq.get(product_url, headers=headers, impersonate="chrome124")
4soup = BeautifulSoup(resp.text, "html.parser")
5# __TGT_DATA__ スクリプトを探す
6scripts = soup.find_all("script")
7for script in scripts:
8 if script.string and "__TGT_DATA__" in script.string:
9 # スクリプト本文から JSON を抽出
10 match = re.search(r'__TGT_DATA__\s*=\s*({.*?});?\s*$', script.string, re.DOTALL)
11 if match:
12 tgt_data = json.loads(match.group(1))
13 # 商品情報の JSON 構造へたどる
14 queries = tgt_data.get("__PRELOADED_QUERIES__", {})
15 # 商品データはその中にネストされている。構造はページによって変わる
16 print(json.dumps(queries, indent=2)[:500]) # 構造を確認
__TGT_DATA__ の中の JSON 構造には、商品名、説明、特徴、そして多くの場合価格データが含まれています。ネストの仕方はページごとに違うため、出力を確認しながら目的の値へたどってください。
Step 4: ページネーションに対応する
Target の検索結果ページは Nao パラメータでページ送りされます。1ページ目は Nao=0、2ページ目は Nao=24、3ページ目は Nao=48……という具合に、24ずつ増えていきます。
1for page in range(0, 120, 24): # 最初の5ページ
2 paginated_url = f"https://www.target.com/s?searchTerm=bluetooth+headphones&Nao={page}"
3 resp = cureq.get(paginated_url, headers=headers, impersonate="chrome124")
4 # 解析して抽出...
5 time.sleep(random.uniform(2, 5)) # 相手に配慮
Step 5: スクレイピングしたデータを保存する
1import csv
2with open("target_products.csv", "w", newline="", encoding="utf-8") as f:
3 writer = csv.DictWriter(f, fieldnames=["title", "url", "price", "description"])
4 writer.writeheader()
5 for product in products:
6 writer.writerow(product)
この方法で取れるもの: 商品タイトル、URL、説明、埋め込みメタデータ。安定して取れないもの: 検索結果ページの動的価格や評価。そうした情報が必要なら、方法2か3を使ってください。
方法2:Selenium または Playwright でTarget.comをスクレイピングする
ヘッドレスブラウザなら JavaScript を描画でき、動的コンテンツも読み込み、実際のユーザー操作に近い挙動を再現できます。価格、評価、レビューを取りたいなら、この方法です。
Selenium と Playwright のどちらを使うべきかについては、 — 2026年時点で です。さらにベンチマークでは (20ページで11秒対28秒)とされています。ここではコミュニティが大きくチュートリアルも多い Selenium を紹介しますが、新しく始めるなら Playwright の方が有力です。
Step 1: Selenium と ChromeDriver をインストールする
1pip install selenium webdriver-manager
webdriver-manager を使えば ChromeDriver のバージョン管理を自動化でき、「ChromeDriver のバージョン不一致」という面倒ごとを避けられます。
1from selenium import webdriver
2from selenium.webdriver.chrome.service import Service
3from selenium.webdriver.chrome.options import Options
4from webdriver_manager.chrome import ChromeDriverManager
5options = Options()
6options.add_argument("--headless=new")
7options.add_argument("--window-size=1920,1080")
8options.add_argument("--disable-blink-features=AutomationControlled")
9options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36")
10driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
Step 2: Target のページを開き、コンテンツの読み込みを待つ
1from selenium.webdriver.common.by import By
2from selenium.webdriver.support.ui import WebDriverWait
3from selenium.webdriver.support import expected_conditions as EC
4driver.get("https://www.target.com/s?searchTerm=bluetooth+headphones")
5# 商品カードが描画されるのを待つ(time.sleep より明示的な待機が重要)
6WebDriverWait(driver, 15).until(
7 EC.presence_of_element_located((By.CSS_SELECTOR, '[data-test="product-title"]'))
8)
明示的な待機は非常に重要です。time.sleep(10) だと、速く読み込めたときは無駄に待ち、遅いときは足りない——最悪の組み合わせになります。WebDriverWait は、要素が現れるかタイムアウトするまで 500ms ごとに確認します。
Step 3: ページをスクロールして全商品を読み込む
Target はスクロールに応じて商品を遅延読み込みします。スクロールしないと、ページ全体ではなく4〜5件しか取れません。
1import time
2last_height = driver.execute_script("return document.body.scrollHeight")
3for _ in range(10):
4 driver.execute_script("window.scrollBy(0, 300);")
5 time.sleep(1.5)
6 new_height = driver.execute_script("return document.body.scrollHeight")
7 if new_height == last_height:
8 break
9 last_height = new_height
、1.5秒の待機を挟みながら10回スクロールすると、スクロールなしの4〜5件に対して8件以上の商品が取れることが確認されています。1回のスクロール量は、人間らしく見せるため 200〜300px 程度にしましょう。
Step 4: 描画済みページから商品データを抽出する
1products = []
2cards = driver.find_elements(By.CSS_SELECTOR, '[data-test="@web/site-top-of-funnel/ProductCardWrapper"]')
3for card in cards:
4 try:
5 title = card.find_element(By.CSS_SELECTOR, '[data-test="product-title"]').text
6 except:
7 title = "N/A"
8 try:
9 price = card.find_element(By.CSS_SELECTOR, '[data-test="current-price"]').text
10 except:
11 price = "N/A"
12 try:
13 link = card.find_element(By.CSS_SELECTOR, 'a[href*="/p/"]').get_attribute("href")
14 except:
15 link = "N/A"
16 products.append({"title": title, "price": price, "link": link})
17for p in products:
18 print(f'{p["title"]} — {p["price"]}')
Target で使える主要な data-test セレクター(2026年時点で確認済み):
| データ項目 | セレクター |
|---|---|
| 商品カード | data-test="@web/site-top-of-funnel/ProductCardWrapper" |
| 商品タイトル | data-test="product-title" |
| 現在価格 | data-test="current-price" |
| 評価値 | data-test="rating-value" |
| 評価件数 | data-test="rating-count" |
Step 5: 商品レビューを取得する(おまけ)
個別の商品ページへ移動し、レビュー欄までスクロールして、レビュー情報を取り出します。
1from bs4 import BeautifulSoup
2driver.get("https://www.target.com/p/some-product/-/A-12345678")
3# 下までスクロールしてレビューを読み込む
4for _ in range(5):
5 driver.execute_script("window.scrollBy(0, 500);")
6 time.sleep(2)
7soup = BeautifulSoup(driver.page_source, "html.parser")
8reviews = soup.find_all("div", {"data-test": "review-card--text"})
9for review in reviews:
10 print(review.get_text(strip=True)[:100])
レビューは Bazaarvoice 連携で読み込まれ、ページネーション(最大51ページ)、新着順ソート、写真のみフィルタにも対応しています。 では、Selenium で 1件あたり約 5.1 秒です。
終わったらブラウザを閉じるのを忘れずに。
1driver.quit()
方法3:Redsky API を使ってTarget.comをスクレイピングする
Target のフロントエンドは、redsky.target.com にある内部 API から情報を取得しています。これを Python から直接呼び出せば、HTML パースもブラウザも JavaScript 実行も不要です。レスポンスは整った JSON で、価格、評価、レビュー、画像、在庫、配送、仕様、バリエーションなど40項目以上のデータを含みます。大量の商品データを扱うなら、これが圧倒的に最速かつ最も安定した方法です。
Step 1: Chrome DevTools で Redsky API を見つける
多くのチュートリアルは、この工程を丸ごと省略します。自分で API を見つける手順は次のとおりです。
- Chrome で任意の Target 商品ページを開く
- DevTools(F12)→ Network タブを開く
- Fetch/XHR で絞り込む
- ページを再読み込みする
redsky.target.comまたはredsky.a]target.comへのリクエストを探す- 1つクリックし、Request URL と Headers を確認する
すると、こんなURLが見つかります。
1https://redsky.target.com/redsky_aggregations/v1/web/pdp_fulfillment_v1?key=9f36aeafbe60771e321a7cc95a78140772ab3e96&tcin=12345678&store_id=2148&zip=55401
主なパラメータは次の通りです。
key— APIキー(固定で、頻繁には変わらないが、エンドポイントごとに異なる)tcin— Target.com Item Number(8桁の商品ID)store_id— Target の店舗IDzip— 配送・受け取り情報用の郵便番号
APIキーはリクエストヘッダーからではなく、URL のクエリパラメータとして埋め込まれています。
Step 2: Redsky API に Python から直接リクエストする
1from curl_cffi import requests as cureq
2import json
3API_KEY = "9f36aeafbe60771e321a7cc95a78140772ab3e96" # DevTools から取得
4TCIN = "12345678"
5url = f"https://redsky.target.com/redsky_aggregations/v1/web/pdp_fulfillment_v1?key={API_KEY}&tcin={TCIN}&store_id=2148&zip=55401"
6headers = {
7 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
8 "Accept": "application/json",
9 "Origin": "https://www.target.com",
10 "Referer": "https://www.target.com/",
11 "Sec-Fetch-Site": "same-site",
12 "Sec-Fetch-Mode": "cors",
13 "Sec-Fetch-Dest": "empty",
14}
15resp = cureq.get(url, headers=headers, impersonate="chrome124")
16data = resp.json()
17# JSON レスポンスから商品情報を取り出す
18product = data.get("data", {}).get("product", {})
19title = product.get("item", {}).get("product_description", {}).get("title", "N/A")
20price = product.get("price", {}).get("formatted_current_price", "N/A")
21rating = product.get("ratings_and_reviews", {}).get("statistics", {}).get("rating", {}).get("average", "N/A")
22print(f"{title} — {price} — Rating: {rating}")
HTML のパースは不要です。レスポンスは構造化されていて、きれいで、速いです。
Step 3: API で検索結果をまとめて取得する
product_summary_with_fulfillment_v1 エンドポイントは、複数の TCIN を一度に受け付けます。
1tcins = ["12345678", "23456789", "34567890"]
2tcin_str = ",".join(tcins)
3search_url = f"https://redsky.target.com/redsky_aggregations/v1/web/product_summary_with_fulfillment_v1?key={API_KEY}&tcins={tcin_str}&store_id=2148&zip=55401"
4resp = cureq.get(search_url, headers=headers, impersonate="chrome124")
5results = resp.json()
6for item in results.get("data", {}).get("product_summaries", []):
7 title = item.get("title", "N/A")
8 price = item.get("price", {}).get("formatted_current_price", "N/A")
9 print(f"{title} — {price}")
TCIN を取得するには、検索結果ページの HTML から取る方法と、__TGT_DATA__ に埋め込まれた JSON から取る方法があります。商品 URL には /A-XXXXXXXX の形で現れます。
Step 4: 並列リクエストでスケールさせる
1from concurrent.futures import ThreadPoolExecutor
2import time, random
3def fetch_product(tcin):
4 url = f"https://redsky.target.com/redsky_aggregations/v1/web/pdp_fulfillment_v1?key={API_KEY}&tcin={tcin}&store_id=2148&zip=55401"
5 time.sleep(random.uniform(2, 5))
6 resp = cureq.get(url, headers=headers, impersonate="chrome124")
7 return resp.json()
8tcin_list = ["12345678", "23456789", "34567890", "45678901"]
9with ThreadPoolExecutor(max_workers=3) as executor:
10 results = list(executor.map(fetch_product, tcin_list))
並列数は控えめにしましょう。3〜5スレッドで、2〜5秒のランダム遅延を入れるのが無難です。Target のレート制限は、IP あたり 前後です。
Redsky API を使う際の重要な注意点
本番のパイプラインにする前に、いくつか注意があります。
- APIキーは固定ですが、エンドポイントごとに異なります。 Redsky の各エンドポイントは別々のキーを使います。頻繁には変わりませんが、Target がいつ変更してもおかしくありません。
- これは文書化されていない内部 API です。 ただし Target のエンジニアリングチームは、 。法的リスクは下がりますが、SLA のある正式な公開 API ではありません。
- 商品バリエーション(色・サイズ)ごとに TCIN は別です。 各バリエーションを個別に取得する必要があります。
Sec-Fetch-*ヘッダーが欠けると即ブロックされます。 ここはよくある落とし穴です。必ずSec-Fetch-Site、Sec-Fetch-Mode、Sec-Fetch-Destを含めてください。
ブロックされずにTarget.comを大規模スクレイピングするコツ
ここからは、手法を問わず本番運用で役立つポイントです。
データセンターではなく住宅プロキシを使う
Target の Akamai 実装は、データセンター IP 帯を見つけ次第フラグします。持続的なスクレイピングには住宅プロキシが必須です。価格はかなり幅がありますが、、 からで、ボリュームが増えると $3〜4/GB 程度まで下がることもあります。
IP の切り替えは50〜100リクエストごと、あるいはプロキシプールが対応しているなら毎回でも構いません。
curl_cffi で TLS フィンガープリントを偽装する
これは最も効果の大きい変更です。requests の置き換えとしてそのまま使えます。
1from curl_cffi import requests as cureq
2# 標準 requests — 保護されたサイトでの成功率は12%
3# resp = requests.get(url, headers=headers)
4# curl_cffi — 成功率 92%
5resp = cureq.get(url, headers=headers, impersonate="chrome124")
は GitHub スター数 8,200超で、chrome99 から chrome146 までの Chrome 版に加え、Safari、Edge、モバイル版にも対応しています。同期モードでは です。
現実的なリクエスト間隔とヘッダーを設定する
- ランダム遅延: リクエスト間は2〜7秒(固定間隔ではなく、ランダム性が重要)
- User-Agent のローテーション: 実在するブラウザの User-Agent を5〜10種類用意して回す
- セッションのウォームアップ: 商品ページへ行く前に
target.comのトップページを開いてクッキーを確立する - ヘッダーの整合性:
Sec-Ch-Uaは、名乗っている User-Agent のブラウザ版と一致している必要があります。Sec-Ch-Ua-Platformも OS と一致していなければなりません。矛盾はすぐ見抜かれます。 - セッション維持: リクエスト間でクッキーを保持する。 ローテーションする住宅プロキシを使った48時間のセッション安定性を推奨しています。
コードを書かずにTarget.comをスクレイピングする:Thunderbit という選択肢
Target.com は、プログラムで扱うには本当に難しい小売サイトのひとつです。JavaScript の描画、Akamai の TLS フィンガープリント、データセンタープロキシの検知、ChromeDriver のバージョン問題……やることが多いです。Python 学習の題材としては良いですが、実務で Target の商品データが必要なだけなら、費用対効果が合わないことも少なくありません。
工程を増やさずにデータだけ欲しいなら、 が厄介な部分を自動で処理してくれます。
Thunderbit が Target.com の難題をどう解決するか
Thunderbit の AI Web Scraper はブラウザ内で動くため、JavaScript を自然に描画します。Selenium のセットアップも、ヘッドレスブラウザの設定も、ChromeDriver のバージョン管理も不要です。ブラウザそのものがスクレイパーになります。
流れは次の通りです。
- をインストールし、Target の商品ページまたは検索ページを開く
- 「AI Suggest Fields」 をクリック — Thunderbit がページを読み取り、列名を提案します(商品名、価格、評価、画像URL など)
- 「Scrape」 をクリック — 描画済みページから数秒でデータを抽出
プロキシ設定は不要。TLS フィンガープリントの偽装も不要。None の結果に悩まされることもありません。
Target の商品一覧ページと詳細ページをまとめて取得する
複数ページ対応のワークフローこそ、Thunderbit の真価が出る場面です。Target の検索結果ページをスクレイピングして商品一覧を取得し、さらに Subpage Scraping を使って各商品のURLへ自動で移動し、詳細ページのデータ——説明、レビュー全文、仕様など——を、ページネーション用コードを書いたりブラウザセッションを管理したりせずに、表へ追加できます。
Excel、Google Sheets、Airtable、Notion に直接エクスポートできます。csv.writer の定型コードも、文字コードの悩みも不要です。
定期的なTarget.comスクレイピングを自動化する
価格監視や在庫追跡を継続的に行うなら、Thunderbit の Scheduled Scraper を使えば、スケジュールを自然な言葉で指定できます(例:「毎週月曜の午前9時」)。cron ジョブも、サーバー構築も、VPS 上で Python スクリプトを動かし続ける必要もありません。これは、 する eコマースチームに特に便利です。現在、 自動価格スクレイピングを利用しており、価格インテリジェンスの ROI は平均で とされています。
PythonでTarget.comをスクレイピングするなら、どの方法を使うべきか
簡単な判断表をまとめます。
| あなたの状況 | 推奨方法 |
|---|---|
| Python学習中、小規模プロジェクト | 方法1: Requests + BS4(静的データと __TGT_DATA__ 向け) |
| 価格やレビューを含む完全な商品ページが必要 | 方法2: Selenium / Playwright |
| 大量の商品データを一括抽出したい | 方法3: Redsky API |
| コードを書かずに素早くデータが欲しい | Thunderbit(ノーコード) |
| 価格を定期監視したい | Thunderbit Scheduled Scraper または Redsky API + cron |
| 単発のリサーチ案件で、非技術チームが使う | Thunderbit — 正直これが一番早いです |
本番向けのデータパイプラインを作るなら、速度と信頼性の面で方法3(Redsky API)が最も優れています。単発の調査や、チームに Python の専門知識がないなら、Thunderbit で何時間も節約できます。そして Web スクレイピングを学びたいなら、方法1 → 方法2 → 方法3 という流れは、各段階で本物の学びがある自然なステップアップです。
Target.comをスクレイピングするときの法的・倫理的な注意点
ここは簡潔に触れておきます。Target の robots.txt にはおよそ120以上の Disallow パスがありますが、/p/(商品)や /c/(カテゴリ)は明示的にはブロックされていません。つまり、商品ページとカテゴリページはクロール対象として許可されています。一方で、カート、アカウント、チェックアウト関連のページは制限されています。
Target の利用規約では、自動アクセスは禁止されています。ただし、Redsky API が ことが Target のエンジニアリング側から確認されているため、API ベースのデータ収集では法的リスクが下がります。
知っておくべき主な判例は次の通りです。
- (第9巡回区、2022年):公開データのスクレイピングは CFAA に違反しない
- (2024年):Meta は敗訴し、公開データのスクレイピングで CFAA 違反はないと判断された
大規模な商用スクレイピングを行う場合は、必ず法務に相談してください。市場調査、価格比較、個人プロジェクトなど、公開データを使う範囲であれば、法的には比較的安全圏にあります。ただし、レート制限は守り、Target のサーバーに過負荷をかけないようにしましょう。
まとめと重要ポイント
Target.com が難しいサイトであるのには理由があります。素朴な Requests + BeautifulSoup の方法が失敗するのは、Target が商品データを JavaScript で描画し、さらに応答が返る前に Akamai が TLS ハンドシェイクの特徴を見ているからです。とはいえ、適切な方法を選べば、抽出自体は難しくありません。
信頼性順の3手法:
- Redsky API — 大量データに最速・最安定。きれいな JSON が返る。DevTools で API エンドポイントを逆算する必要あり。
- Selenium / Playwright — JavaScript 描画に対応し、ページ上の情報をほぼすべて取得できる。遅いが網羅的。
- Requests + BeautifulSoup — 静的 HTML と埋め込み
__TGT_DATA__JSON に限定。速いが不完全。
技術面で特に効く対策:
- 標準の
requestsではなくcurl_cffiを使うと、 が見込める - 住宅プロキシは必須。データセンター IP は即フラグされる
- すべてのリクエストで
Sec-Fetch-*ヘッダーを入れる。欠けると即ブロック - セッションのウォームアップ(先にホームページへ訪問)で成功率がかなり上がる
そして、あなたの用途に対して Python の手間が見合わないなら、 が JavaScript 描画、ボット対策、データ出力まで自動で処理してくれます。 を試して、数時間かかる作業が数分で済むか確かめてみてください。
さらにスクレイピングの実践ガイドやデータ抽出のコツは、 や でも紹介しています。
FAQ
Python の Requests と BeautifulSoup だけで Target.com をスクレイピングできますか?
部分的には可能です。商品ページの __TGT_DATA__ スクリプトタグから、商品タイトル、URL、埋め込み JSON の一部は取得できます。ただし、検索結果ページの価格、評価、レビュー、在庫状況は JavaScript で描画されるため、静的な HTTP リクエストでは出てきません。完全なデータが必要なら、Selenium/Playwright か Redsky API を使ってください。
Target.com のスクレイパーで価格が None になるのはなぜですか?
Target は、初回ページ読み込み後に JavaScript で価格データを読み込みます。requests.get() を使うと、JavaScript が実行されて商品データが DOM に挿入される前の、描画前 HTML しか受け取れません。つまり、価格要素はレスポンス内にそもそも存在しないのです。JavaScript を描画できるヘッドレスブラウザ(Selenium や Playwright)を使うか、JSON を直接返す Redsky API を呼び出すか、あるいは描画済みブラウザページから取得する のようなツールを使ってください。
Target.com をスクレイピングするのは合法ですか?
現在の米国判例法(hiQ v. LinkedIn、Meta v. Bright Data)では、公開データのスクレイピングは一般的に認められています。Target の robots.txt でも商品・カテゴリページのクロールは許可されています。ただし、Target の利用規約では自動アクセスが禁止されているため、グレーゾーンは残ります。公開データを使った市場調査や価格比較なら、法的には比較的妥当な範囲です。大規模な商用運用を考えているなら、弁護士に相談してください。
Target の Redsky API とは何ですか? どうやってアクセスしますか?
Redsky は、Target のフロントエンドが商品データを取得するために使っている内部 API です。公開 API のようにドキュメントや登録手順があるわけではなく、React アプリが商品ページ描画のために呼び出すバックエンドです。Chrome DevTools を開き、Network タブで XHR/Fetch に絞って redsky.target.com へのリクエストを探すと、エンドポイントを見つけられます。API キーはリクエスト URL のクエリパラメータに埋め込まれています。Target のエンジニアリングは、この API が意図的に外部公開されていることを認めています。
Target.com でブロックされないようにするにはどうすればいいですか?
最も効果が大きいのは、標準の Python requests ではなく curl_cffi を使ってブラウザの TLS フィンガープリントを偽装することです。これだけで成功率は 上がります。加えて、住宅プロキシを使う、User-Agent をローテーションする、リクエスト間に2〜7秒のランダム待機を入れる、Sec-Fetch-* ヘッダーをすべて付ける、最初にホームページへアクセスしてセッションを温める、といった対策が有効です。あるいは、ボット対策を自動で処理してくれる を使えば、面倒な設定は不要です。
さらに詳しく