Target.com 是那種看起來很簡單、實際上卻不太好抓的網站。您如果曾經寫過一個用 Requests 和 BeautifulSoup 的 Python 小腳本,丟到 Target 的商品頁,結果價格欄位回傳 None,其實一點也不孤單。
我測試過多數大型零售網站的抓取方式,可以很肯定地說:Target 一直都屬於最棘手的那一類。它每月有 2.08 億到 2.8 億次造訪,商品資料像是價格、評分、庫存、評論都很有價值,但 Target 結合了 React 的前端渲染與 Akamai 的機器人偵測,讓天真的抓取方法幾乎一開始就失敗。不過,確實有三種 Python 方法真正可行。我會逐一說明,解釋為什麼第一種嘗試通常會失敗,並示範一個不用寫程式的替代方案,讓您在 Python 不值得折騰時也有退路。
為什麼您第一次用 Python 抓 Target.com 會回傳 None
先別急著想解法,先看問題出在哪。這是多數初學者會寫的程式:
import requests
from bs4 import BeautifulSoup
url = "https://www.target.com/p/some-product/-/A-12345678"
response = requests.get(url, headers={"User-Agent": "Mozilla/5.0"})
soup = BeautifulSoup(response.text, "html.parser")
price = soup.select_one('[data-test="current-price"]')
print(price) # None
輸出是什麼?None。每次都一樣。
這不是您程式的錯。requests.get() 從 Target 拿回來的 HTML 幾乎只是個骨架——一個 React 外殼,意思是「嘿,載入這段 JavaScript 來渲染真正的頁面吧」。商品價格、評分、評論與可用性,都是在初始頁面載入之後才由 JavaScript 注入。因為 Python 的 Requests 不會執行 JavaScript,所以那些元素根本不會出現在回應裡。
論壇上到處都是撞上這堵牆的開發者。某篇 ScrapeOps 分析說得很直接:「元素會顯示為 None,是因為它是由 Javascript 渲染,而 requests 無法抓取由 Javascript 渲染的 HTML。」Crawlbase 教學也證實:「當您向 Target URL 發送 HTTP 請求時,HTML 回應中沒有什麼有用的資料。」
而且就算您解決了 JavaScript 問題,還有第二層:Target 的 Akamai 機器人偵測會先對您的 TLS 握手做指紋辨識,在傳送任何一個 HTML 位元組之前,就先把 Python 的 requests 庫標記出來。這點我等一下會再講。
為什麼 Target.com 這麼難用 Python 抓
Target 不只是「一個用了 JavaScript 的網站」。它其實是一套分層防禦系統——了解每一層,才能選對抓取方法。
JavaScript 渲染的商品資料
Target.com 是用 React 建置的。當您在真正的瀏覽器裡打開商品頁或搜尋頁時,流程是這樣:
- 伺服器送出最小化的 HTML 骨架
- JavaScript bundle 載入並執行
- 前端呼叫 Target 內部的 Redsky API
- 商品資料(價格、評分、圖片、可用性)渲染進 DOM
如果您跳過第 2 到第 4 步——也就是 requests.get() 做的事——您拿到的就只是一個空頁面。GroupBWT 有做過基準測試,靜態 HTTP 請求在 Target 上大約只能抓到 30% 的可用資料。其餘 70% 需要 JavaScript 執行或 API 存取。
搜尋結果頁更糟。初始 HTML 只會出現少數幾個商品;其餘商品得等您往下捲動才會載入。
Target 的反機器人防禦:不只是泛泛地說「用代理」
多數抓取教學對反機器人措施都只會輕描淡寫地說「用代理就好」。但 Target 的防線其實更值得細講。
TLS 指紋辨識(最關鍵的一層)。 在 HTTPS 握手期間,您的客戶端會送出一個「Client Hello」封包,裡面會暴露您的 TLS 版本、cipher suites、擴充套件與橢圓曲線。這些資訊會被雜湊成一個 JA3 指紋。Python 的 requests 會產生一個 固定且已知的雜湊值——8d9f7747675e24454cd9b7ed35c58707——反機器人資料庫會立刻標記它。Chrome 會送出 16 個經過仔細排序、包含 GREASE 值的 cipher suites;Python 則會以非瀏覽器順序送出 60 多個。封鎖會在任何 HTTP 內容交換之前就發生。
IP 信譽評分。 Akamai 會把 IP 分到不同信任等級。資料中心 IP 在 Scrapfly 的說法裡會得到「顯著的負面信任分數,因為它們很可能被機器人使用」。住宅 IP 則會得到正面分數。以 Target 來說,資料中心 IP 範圍幾乎會立刻被標記。
JavaScript 指紋辨識。 Akamai 會注入 JavaScript,收集您的 JS 引擎規格、硬體能力、作業系統資料、字型、外掛程式與行為資料(打字速度、滑鼠移動、點擊時間)。這會生成 _abck cookie——一種有狀態的指紋權杖。沒有有效的 _abck,請求就會被封鎖。
速率限制。 Target 大約每個 IP 每分鐘觸發 30 到 60 次請求時就會出現 429 錯誤。有些使用者回報會收到看起來像 200 OK 的回應,但實際內容是「Pardon Our Interruption」封鎖頁,這讓自動化偵測更棘手。
整體來看,ScrapeOps 給 Target 的難度評分是 7/10。而 Akamai 繞過部分則被評為 9/10。
用 Python 抓 Target.com 的 3 種方法(並排比較)
外面沒有一篇文章會把這三種可行方法完整放在一起比較。下面我直接整理給您:
| 比較項目 | Requests + BS4 | Selenium / Playwright | Redsky API |
|---|---|---|---|
| 支援 JS 渲染 | ❌ 否 | ✅ 是 | ✅ 是(JSON) |
| 每筆速度 | ⚡ 約 0.5–1 秒 | 🐢 約 5–10 秒 | ⚡ 約 0.5–1 秒 |
| 反機器人風險 | ⚠️ 高(TLS 指紋) | ⚠️ 中 | ⚠️ 中(驗證金鑰可能變動) |
| 設定複雜度 | 低 | 中 | 中高(逆向工程) |
| 資料完整度 | 約 30%(僅靜態 HTML) | 約 95%(整頁) | 約 90%(結構化 JSON) |
| 最適合用途 | 靜態中繼資料、__TGT_DATA__ | 完整商品頁、評論 | 大量商品資料擷取 |
接下來我們一種一種來做。
方法 1:用 Python Requests 和 BeautifulSoup 抓 Target.com
這個方法抓不到搜尋頁上由 JavaScript 渲染的價格。不過它速度快、很輕量,而且如果您知道該看哪裡,其實能抓到比您想像更多的資料。
關鍵在於:Target 會在 <script> 標籤裡嵌入一部分商品資料,裡面有個 __TGT_DATA__ 變數,含有 __PRELOADED_QUERIES__。這個 JSON 區塊會包含商品名稱、描述、特色,有時候甚至會在單一商品頁中包含價格。您也可以從搜尋結果 HTML 中擷取商品標題與 URL。
步驟 1:設定 Python 環境
建立專案資料夾並安裝相依套件:
mkdir target-scraper && cd target-scraper
python -m venv venv
source venv/bin/activate # Windows 上:venv\Scripts\activate
pip install requests beautifulsoup4 curl_cffi
這裡建議用 curl_cffi 取代標準 requests。它會偽裝瀏覽器的 TLS 指紋,這是避免被 Target 擋下來最重要的一步。基準測試顯示 curl_cffi 的反機器人繞過成功率有 92%,而標準 requests 只有 12%——大約提升 15 倍。
步驟 2:抓取 Target 搜尋結果
Target 的搜尋 URL 格式很直接:https://www.target.com/s?searchTerm={keyword}
from curl_cffi import requests as cureq
from bs4 import BeautifulSoup
import time, random
headers = {
"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",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
}
url = "https://www.target.com/s?searchTerm=bluetooth+headphones"
resp = cureq.get(url, headers=headers, impersonate="chrome124")
soup = BeautifulSoup(resp.text, "html.parser")
# 商品卡片使用這個 data-test 屬性
cards = soup.find_all("div", {"data-test": "@web/site-top-of-funnel/ProductCardWrapper"})
for card in cards:
link_tag = card.find("a")
title = link_tag.get_text(strip=True) if link_tag else "N/A"
href = "https://www.target.com" + link_tag["href"] if link_tag and link_tag.get("href") else "N/A"
print(f"{title} — {href}")
您會拿到商品名稱和 URL。價格呢?多半不會出現在這份 HTML 裡。這很正常。
步驟 3:從商品頁擷取嵌入的 JSON 資料
單一商品頁會把更豐富的資料嵌在 __TGT_DATA__ script tag 裡:
import re, json
product_url = "https://www.target.com/p/some-product/-/A-12345678"
resp = cureq.get(product_url, headers=headers, impersonate="chrome124")
soup = BeautifulSoup(resp.text, "html.parser")
# 找出 __TGT_DATA__ script
scripts = soup.find_all("script")
for script in scripts:
if script.string and "__TGT_DATA__" in script.string:
# 從 script 內容中擷取 JSON
match = re.search(r'__TGT_DATA__\s*=\s*({.*?});?\s*$', script.string, re.DOTALL)
if match:
tgt_data = json.loads(match.group(1))
# 導向 JSON 結構找商品細節
queries = tgt_data.get("__PRELOADED_QUERIES__", {})
# 商品資料被巢狀包在裡面——實際結構會因頁面而異
print(json.dumps(queries, indent=2)[:500]) # 預覽結構
__TGT_DATA__ 裡的 JSON 結構包含商品名稱、描述、特色,以及常常會有價格資料。實際巢狀層級會變動,所以您需要先看輸出,再決定怎麼走。
步驟 4:處理分頁
Target 的搜尋分頁使用 Nao 參數。第 1 頁是 Nao=0,第 2 頁是 Nao=24,第 3 頁是 Nao=48,以此類推(每次加 24):
for page in range(0, 120, 24): # 前 5 頁
paginated_url = f"https://www.target.com/s?searchTerm=bluetooth+headphones&Nao={page}"
resp = cureq.get(paginated_url, headers=headers, impersonate="chrome124")
# 解析與擷取...
time.sleep(random.uniform(2, 5)) # 禮貌一點
步驟 5:儲存抓到的資料
import csv
with open("target_products.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["title", "url", "price", "description"])
writer.writeheader()
for product in products:
writer.writerow(product)
您能拿到什麼: 商品標題、URL、描述與嵌入式中繼資料。您不容易穩定拿到什麼: 搜尋結果頁的動態價格和評分。這些通常要靠方法 2 或 3。
方法 2:用 Selenium 或 Playwright 抓 Target.com
無頭瀏覽器會渲染 JavaScript、載入動態內容,並模擬真實使用者行為。這就是能抓到價格、評分與評論的方法。
關於 Selenium 和 Playwright 的選擇:Playwright 的採用率已經超過 Selenium——2026 年是 45.1% 對 22.1%——而且基準測試顯示它還 快 2.5 倍(20 頁 11 秒對 28 秒)。我這裡會示範 Selenium,因為它社群更大、教學更多;但如果您是從零開始,Playwright 其實是更好的選擇。
步驟 1:安裝 Selenium 與 ChromeDriver
pip install selenium webdriver-manager
webdriver-manager 會自動處理 ChromeDriver 版本,從此不用再煩惱「ChromeDriver 版本不相符」:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1920,1080")
options.add_argument("--disable-blink-features=AutomationControlled")
options.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")
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
步驟 2:載入 Target 頁面並等待內容出現
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver.get("https://www.target.com/s?searchTerm=bluetooth+headphones")
# 等待商品卡片渲染完成(顯式等待 > time.sleep)
WebDriverWait(driver, 15).until(
EC.presence_of_element_located((By.CSS_SELECTOR, '[data-test="product-title"]'))
)
顯式等待很重要。time.sleep(10) 對載入快的頁面是在浪費時間,對載入慢的頁面又不夠久——兩頭都吃虧。WebDriverWait 會每 500 毫秒輪詢一次,直到元素出現或逾時。
步驟 3:捲動頁面以載入全部商品
Target 會隨著您捲動才懶載入商品。不捲動的話,您可能只會看到 4 到 5 個商品,而不是整頁:
import time
last_height = driver.execute_script("return document.body.scrollHeight")
for _ in range(10):
driver.execute_script("window.scrollBy(0, 300);")
time.sleep(1.5)
new_height = driver.execute_script("return document.body.scrollHeight")
if new_height == last_height:
break
last_height = new_height
ScrapeOps 的測試證實,10 次捲動、每次延遲 1.5 秒,可抓到 8 個以上商品;不捲動則只有 4 到 5 個。每次捲動建議 200 到 300px,比較像真人行為。
步驟 4:從渲染後的頁面擷取商品資料
products = []
cards = driver.find_elements(By.CSS_SELECTOR, '[data-test="@web/site-top-of-funnel/ProductCardWrapper"]')
for card in cards:
try:
title = card.find_element(By.CSS_SELECTOR, '[data-test="product-title"]').text
except:
title = "N/A"
try:
price = card.find_element(By.CSS_SELECTOR, '[data-test="current-price"]').text
except:
price = "N/A"
try:
link = card.find_element(By.CSS_SELECTOR, 'a[href*="/p/"]').get_attribute("href")
except:
link = "N/A"
products.append({"title": title, "price": price, "link": link})
for p in products:
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" |
步驟 5:抓取商品評論(加分項)
前往單一商品頁,捲動到評論區,然後擷取評論資料:
from bs4 import BeautifulSoup
driver.get("https://www.target.com/p/some-product/-/A-12345678")
# 往下捲動以載入評論
for _ in range(5):
driver.execute_script("window.scrollBy(0, 500);")
time.sleep(2)
soup = BeautifulSoup(driver.page_source, "html.parser")
reviews = soup.find_all("div", {"data-test": "review-card--text"})
for review in reviews:
print(review.get_text(strip=True)[:100])
評論透過 Bazaarvoice 整合載入,支援分頁(最多 51 頁)、依最近排序,以及只看圖片的篩選。 ScrapeOps 基準測試顯示,Selenium 每筆大約要 5.1 秒。
做完記得關閉瀏覽器:
driver.quit()
方法 3:用 Redsky API 抓 Target.com
Target 前端會從 redsky.target.com 這個內部 API 抓取所有資料。您可以直接用 Python 呼叫它——不用解析 HTML、不用瀏覽器、不用 JavaScript 渲染。回傳的是乾淨的 JSON,裡面有 40 多個資料欄位,涵蓋價格、評分、評論、圖片、可用性、履約、規格與變體。若是大量商品資料,這是最快也最可靠的方法,差距非常明顯。
步驟 1:用 Chrome DevTools 找出 Redsky API
多數教學會直接跳過這步,但其實您自己也能找到 API:
- 用 Chrome 打開任一 Target 商品頁
- 開啟 DevTools(F12)→ Network 分頁
- 用 Fetch/XHR 篩選
- 重新整理頁面
- 找到對
redsky.target.com或redsky.a]target.com的請求 - 點進去看 Request URL 與 Headers
您會看到類似這樣的網址:
https://redsky.target.com/redsky_aggregations/v1/web/pdp_fulfillment_v1?key=9f36aeafbe60771e321a7cc95a78140772ab3e96&tcin=12345678&store_id=2148&zip=55401
關鍵參數:
key— API 金鑰(固定,不會輪替——不同端點會用不同的 key)tcin— Target.com Item Number(8 位數商品 ID)store_id— Target 門市代碼zip— 用於履約資料的郵遞區號
把 API key 從 request headers 裡擷取出來。它其實已經作為 query parameter 寫在網址中了。
步驟 2:直接用 Python 呼叫 Redsky API
from curl_cffi import requests as cureq
import json
API_KEY = "9f36aeafbe60771e321a7cc95a78140772ab3e96" # 從 DevTools 擷取
TCIN = "12345678"
url = f"https://redsky.target.com/redsky_aggregations/v1/web/pdp_fulfillment_v1?key={API_KEY}&tcin={TCIN}&store_id=2148&zip=55401"
headers = {
"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",
"Accept": "application/json",
"Origin": "https://www.target.com",
"Referer": "https://www.target.com/",
"Sec-Fetch-Site": "same-site",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Dest": "empty",
}
resp = cureq.get(url, headers=headers, impersonate="chrome124")
data = resp.json()
# 從 JSON 回應擷取商品資料
product = data.get("data", {}).get("product", {})
title = product.get("item", {}).get("product_description", {}).get("title", "N/A")
price = product.get("price", {}).get("formatted_current_price", "N/A")
rating = product.get("ratings_and_reviews", {}).get("statistics", {}).get("rating", {}).get("average", "N/A")
print(f"{title} — {price} — 評分:{rating}")
不用解析 HTML。回應結構清楚、乾淨又快速。
步驟 3:透過 API 抓取商品搜尋結果
product_summary_with_fulfillment_v1 端點一次可以接受多個 TCIN:
tcins = ["12345678", "23456789", "34567890"]
tcin_str = ",".join(tcins)
search_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"
resp = cureq.get(search_url, headers=headers, impersonate="chrome124")
results = resp.json()
for item in results.get("data", {}).get("product_summaries", []):
title = item.get("title", "N/A")
price = item.get("price", {}).get("formatted_current_price", "N/A")
print(f"{title} — {price}")
要取得 TCIN,可以從搜尋頁 HTML 中擷取(商品 URL 會顯示成 /A-XXXXXXXX),或者從內嵌的 __TGT_DATA__ JSON 裡拿。
步驟 4:用並行請求擴大規模
from concurrent.futures import ThreadPoolExecutor
import time, random
def fetch_product(tcin):
url = f"https://redsky.target.com/redsky_aggregations/v1/web/pdp_fulfillment_v1?key={API_KEY}&tcin={tcin}&store_id=2148&zip=55401"
time.sleep(random.uniform(2, 5))
resp = cureq.get(url, headers=headers, impersonate="chrome124")
return resp.json()
tcin_list = ["12345678", "23456789", "34567890", "45678901"]
with ThreadPoolExecutor(max_workers=3) as executor:
results = list(executor.map(fetch_product, tcin_list))
並行數量要保守——3 到 5 個執行緒,外加 2 到 5 秒的隨機延遲。Target 的速率限制大約是每個 IP 每分鐘 30 到 60 次請求。
關於 Redsky API 的重要注意事項
在您把這個做成正式生產管線之前,先留意幾件事:
- API key 雖然固定,但只對特定端點有效。 不同 Redsky 端點會使用不同的 key。它們不常輪替,但 Target 隨時可能改。
- 這是未公開文件的內部 API。 Target 的工程團隊已經確認它是刻意公開可用,這降低了法律風險,但它並不是有 SLA 的官方公開 API。
- 商品變體(顏色、尺寸)各自有獨立的 TCIN。 您需要逐一查詢每個變體。
- 少了
Sec-Fetch-*headers 會立刻被擋。 這是常見陷阱——務必包含Sec-Fetch-Site、Sec-Fetch-Mode和Sec-Fetch-Dest。
如何在大規模抓取 Target.com 時避免被封鎖
以下做法適用於正式規模,不論您用哪一種方法。
使用住宅代理輪換,不要用資料中心代理
Target 的 Akamai 會一眼認出資料中心 IP 範圍。要長期抓取,住宅代理幾乎是必需品。價格差異很大——Smartproxy/Decodo 起價每 GB 4.50 美元,Bright Data 每 GB 5.04 美元,大量使用時可降到每 GB 3 到 4 美元。
每 50 到 100 次請求輪換一次 IP;如果您的代理池支援,也可以每次請求都換。
用 curl_cffi 偽裝 TLS 指紋
這是您能做的、影響最大的單一改動。直接替換 requests:
from curl_cffi import requests as cureq
# 標準 requests —— 在受保護網站上的成功率約 12%
# resp = requests.get(url, headers=headers)
# curl_cffi —— 成功率約 92%
resp = cureq.get(url, headers=headers, impersonate="chrome124")
curl_cffi(GitHub 8,200+ 星)支援從 chrome99 到 chrome146 的 Chrome 版本,也支援 Safari、Edge 與行動裝置變體。在同步模式下,它比 tls_client 快 20% 到 30%。
設定合理的請求節奏與標頭
- 隨機延遲: 每次請求間隔 2 到 7 秒(不要固定間隔——隨機性很重要)
- User-Agent 輪換: 維護 5 到 10 組真實瀏覽器 User-Agent 字串並輪替使用
- Session 預熱: 先拜訪
target.com首頁,再進入商品頁,以建立 cookie - 標頭一致性: 您的
Sec-Ch-Ua必須和您聲稱的瀏覽器版本一致。Sec-Ch-Ua-Platform也必須和您聲稱的作業系統一致。不一致很容易被看穿。 - Session 持續性: 在同一個 session 裡保留 cookies。Scraperly 建議搭配輪換住宅代理,維持 48 小時的 session 穩定度。
不想寫程式?用 Thunderbit 抓 Target.com(無程式碼替代方案)
說真的,Target.com 是程式化抓取零售網站中比較難的一種。JavaScript 渲染、Akamai 的 TLS 指紋辨識、資料中心代理偵測、ChromeDriver 版本問題——每一項都會卡您。如果您是在學 Python,這很適合作為練習;但如果您是為了實際工作要拿到 Target 商品資料,投入產出常常不划算。
如果您想直接拿到資料、不想碰工程細節,Thunderbit 會自動處理最難的部分。
Thunderbit 如何處理 Target.com 的難題
Thunderbit 的人工智慧網頁爬蟲是在瀏覽器中執行,這代表它會自然渲染 JavaScript——不用裝 Selenium,不用設定無頭瀏覽器,也不用管 ChromeDriver 版本。瀏覽器本身就是爬蟲。
流程如下:
- 安裝 Thunderbit Chrome 擴充功能 並前往 Target 的商品頁或搜尋頁
- 點擊「AI Suggest Fields」——Thunderbit 會讀取頁面並提出欄位名稱建議(商品標題、價格、評分、圖片 URL 等)
- 點擊「Scrape」——資料會在幾秒內直接從渲染後的頁面擷取出來
不用設定代理。不用偽裝 TLS 指紋。不會再看到 None。
抓取 Target 商品列表與詳細頁
多頁工作流程最有意思。您可以先抓 Target 搜尋結果頁拿到商品清單,再用 子頁面抓取 自動逐一拜訪每個商品 URL,並把詳細頁資料豐富到表格中——像是描述、完整評論、規格等,不必自己寫分頁程式,也不用管理瀏覽器 session。
可以直接匯出到 Excel、Google Sheets、Airtable 或 Notion。沒有 csv.writer 的樣板程式,也沒有檔案編碼問題。
自動化重複性的 Target.com 抓取
如果您要持續監控價格或追蹤庫存,Thunderbit 的 排程爬蟲 可以讓您用自然語言描述排程(例如「每週一上午 9 點」)。不用 cron job、不用架伺服器、不用在 VPS 上維持 Python 腳本一直跑。這對追蹤競品價格的電商團隊特別有用——81% 的美國零售商現在都在使用自動化價格抓取,而價格情報的平均投資報酬率高達 27:1。
用 Python 抓 Target.com 時,什麼情況該用哪一種方法
給您一個快速決策框架:
| 你的情況 | 建議方法 |
|---|---|
| 在學 Python、小型專案 | 方法 1:Requests + BS4(靜態資料與 __TGT_DATA__) |
| 需要完整商品頁,包含價格與評論 | 方法 2:Selenium / Playwright |
| 大量擷取商品資料 | 方法 3:Redsky API |
| 需要快速拿資料,不想寫程式 | Thunderbit(無程式碼) |
| 需要持續監控價格 | Thunderbit 排程爬蟲或 Redsky API + cron |
| 一次性的研究專案,團隊非技術背景 | Thunderbit——老實說是最快的路徑 |
如果您是在打造正式的資料管線,方法 3(Redsky API)在速度和穩定性上表現最好。如果您只是做一次性研究,或團隊沒有 Python 能力,Thunderbit 可以幫您省下好幾個小時。如果您是在學網頁抓取,方法 1 → 方法 2 → 方法 3 是很自然的學習路線,而且每一步都能學到真正有用的東西。
抓取 Target.com 的法律與倫理考量
這部分值得簡短說明。Target 的 robots.txt 大約有 120 多條 Disallow 路徑,但明顯沒有封鎖 /p/(商品)或 /c/(分類)——商品頁與分類頁都明確允許爬取。購物車、帳號與結帳頁則是受限制的。
Target 的服務條款確實禁止自動化存取。不過,Redsky API 被刻意設計成對外可見(Target 工程團隊已確認),這會降低以 API 為基礎進行資料收集的法律風險。
您應該知道的主要法律判例:
- hiQ v. LinkedIn(第九巡迴法院,2022):抓取公開可得資料不違反 CFAA
- Meta v. Bright Data(2024):Meta 敗訴——法院認定抓取公開資料不構成 CFAA 違反
如果是大規模商業抓取,請諮詢法律顧問。若是市場研究、價格比較或個人專案,且使用的是公開資料,基本上站得住腳。務必尊重速率限制,不要讓 Target 的伺服器負載過高。
結論與重點整理
Target.com 的難度名符其實。天真的 Requests + BeautifulSoup 做法會失敗,是因為 Target 透過 JavaScript 渲染商品資料,而且 Akamai 甚至會在您拿到回應前就先對 TLS 握手做指紋辨識。不過,選對方法後,抓取其實並不複雜。
三種方法的可靠度排名:
- Redsky API —— 速度最快、最適合大量資料、回傳乾淨 JSON。需要先用 DevTools 逆向找出 API 端點。
- Selenium / Playwright —— 能處理 JavaScript 渲染,可以拿到頁面上的所有內容。速度較慢,但資料完整。
- Requests + BeautifulSoup —— 只適用於靜態 HTML 與嵌入的
__TGT_DATA__JSON。速度快,但不完整。
最大的技術重點:
- 用
curl_cffi取代標準requests,反機器人繞過能力可提升 15 倍 - 住宅代理是必要條件——資料中心 IP 會立刻被標記
- 每次請求都加入
Sec-Fetch-*headers——少了會立刻被擋 - session 預熱(先拜訪首頁)會明顯提高成功率
如果對您的用途來說,Python 抓取太麻煩,Thunderbit 的 Chrome 擴充功能會自動處理 JavaScript 渲染、反機器人措施與資料匯出。試試看免費方案,看看能不能在幾分鐘內而不是幾小時內拿到您要的資料。
想看更多抓取教學和資料擷取技巧,可以參考 Thunderbit 部落格 或我們的 YouTube 頻道。
常見問題
我可以只用 Python Requests 和 BeautifulSoup 抓 Target.com 嗎?
部分可以。您可以從商品頁的 __TGT_DATA__ script tag 擷取商品標題、URL 和一些嵌入式 JSON 資料。但搜尋結果頁上的價格、評分、評論與可用性都是 JavaScript 渲染的,靜態 HTTP 請求看不到。要拿完整資料,請用 Selenium/Playwright 或 Redsky API。
為什麼我的 Target.com 爬蟲價格欄位會回傳 None?
Target 會在初始頁面載入後再用 JavaScript 載入價格資料。當您使用 requests.get() 時,拿到的是尚未渲染完成的 HTML 骨架——也就是 JavaScript 執行、把商品資料注入 DOM 之前的版本。價格元素在回應裡根本不存在。請改用能渲染 JavaScript 的無頭瀏覽器(Selenium 或 Playwright)、直接呼叫 Redsky API 取得 JSON,或者使用像 Thunderbit 這類會從渲染後瀏覽器頁面擷取資料的工具。
抓取 Target.com 合法嗎?
依照目前美國的判例,抓取公開可得資料通常是被允許的(hiQ v. LinkedIn、Meta v. Bright Data)。Target 的 robots.txt 也允許商品頁與分類頁被爬取。不過,Target 的服務條款禁止自動化存取,所以仍有灰色地帶。若是用公開資料做市場研究或價格比較,法律上通常較站得住腳;若是大規模商業運作,請諮詢律師。
Target 的 Redsky API 是什麼?我要怎麼存取?
Redsky 是 Target 的內部 API,負責前端商品資料。它不是那種您註冊帳號、拿 API 文件與 API key 的公開 API;它是 React 應用在渲染商品頁時所呼叫的後端。您可以打開 Chrome DevTools,切到 Network 分頁並用 XHR/Fetch 篩選,找出對 redsky.target.com 的請求來發現端點。API key 會以 query parameter 的形式嵌在 request URL 中。Target 工程團隊已確認這個 API 是刻意對外可見的。
我要怎麼避免在抓 Target.com 時被封鎖?
最有影響的一個改動,就是用 curl_cffi 取代標準 Python requests,偽裝瀏覽器的 TLS 指紋——單這一步就能把成功率從 12% 提升到 92%。除此之外:使用住宅代理(不要用資料中心)、輪換 User-Agent 字串、每次請求之間加入 2 到 7 秒隨機延遲、加入所有 Sec-Fetch-* headers,並先拜訪首頁做 session 預熱。或者,直接用像 Thunderbit 這樣會自動處理反機器人措施的工具,完全不用設定。
延伸閱讀


