用 Python 抓取 Target.com:到 2026 年依然有效的 3 种方法

最后更新于 April 28, 2026
用 Python 抓取 Target.com:到 2026 年依然有效的 3 种方法

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。每次都是。

这不是你代码的 bug。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 构建的。当你在真实浏览器里打开商品页或搜索页时,流程是这样的:

  1. 服务器返回一个最小化的 HTML 空壳
  2. JavaScript 包加载并执行
  3. 前端调用 Target 内部的 Redsky API
  4. 商品数据(价格、评分、图片、库存)渲染进 DOM

如果你跳过第 2–4 步——而这正是 requests.get() 会做的——拿到的就是一个空页面。GroupBWT 做过基准测试:静态 HTTP 请求只能抓到 Target 上大约 30% 的可用数据。剩下的 70% 需要执行 JavaScript,或者直接访问 API。

搜索结果页更糟。初始 HTML 里只会出现少量商品,其余内容要等你滚动页面时才加载出来。

Target 的反爬防御:别再只会说“用代理”

大多数抓取教程谈到反爬时都很笼统,只会说“换代理就行了”。但 Target 的防护值得更具体地拆解。

TLS 指纹识别(最关键的一层)。 在 HTTPS 握手阶段,你的客户端会发送一个“Client Hello”包,里面包含 TLS 版本、密码套件、扩展、椭圆曲线等信息。这些内容会被哈希成一个 JA3 指纹。Python 的 requests 库会生成一个 固定且已知的哈希 —— 8d9f7747675e24454cd9b7ed35c58707 —— 反爬数据库会立刻把它识别出来。Chrome 会发送 16 个经过精心排序、带 GREASE 值的密码套件;Python 则会以非浏览器顺序发送 60 多个。封锁甚至会发生在任何 HTTP 内容交换之前。

IP 信誉评分。 Akamai 会把 IP 分到不同信任等级。根据 Scrapfly 的说法,机房 IP 会拿到“显著的负面信任分”,因为它们很可能被机器人使用。住宅 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 + BS4Selenium / PlaywrightRedsky 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__ 脚本标签中:

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__ 脚本
scripts = soup.find_all("script")
for script in scripts:
    if script.string and "__TGT_DATA__" in script.string:
        # 从脚本内容中提取 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 会每 500ms 轮询一次,直到元素出现或者超时。

第 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–300 像素最接近真人行为。

第 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 的方法:

  1. 在 Chrome 里打开任意 Target 商品页
  2. 打开 DevTools(F12)→ Network 标签页
  3. Fetch/XHR 过滤
  4. 刷新页面
  5. 找到发往 redsky.target.comredsky.a]target.com 的请求
  6. 点开一个——查看 Request URLHeaders

你会看到类似这样的地址:

https://redsky.target.com/redsky_aggregations/v1/web/pdp_fulfillment_v1?key=9f36aeafbe60771e321a7cc95a78140772ab3e96&tcin=12345678&store_id=2148&zip=55401

关键参数:

  • key —— API 密钥(静态,不轮换;不同端点使用不同密钥)
  • tcin —— Target.com 商品编号(8 位商品 ID)
  • store_id —— Target 门店编号
  • zip —— 用于履约数据的邮政编码

从请求头里把 API 密钥提取出来。它作为查询参数直接嵌在 URL 中。

第 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: {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 密钥是静态的,但和端点绑定。 不同的 Redsky 端点使用不同密钥。它们不会频繁轮换,但 Target 可能随时更改。
  • 这是一个未公开文档的内部 API。 Target 工程团队已经 确认它是有意面向公众开放的,这降低了法律风险,但它并不是带 SLA 的官方公共 API。
  • 商品变体(颜色、尺寸)各自有独立 TCIN。 你需要逐个变体查询。
  • 缺少 Sec-Fetch-* 请求头会立刻被拦截。 这是常见坑——一定要带上 Sec-Fetch-SiteSec-Fetch-ModeSec-Fetch-Dest

如何在大规模抓取 Target.com 时避免被封

这些做法适用于生产级规模,不管你用哪种方法。

使用住宅代理轮换,不要用机房代理

Target 的 Akamai 实现会一眼识别机房 IP 段。要持续抓取,住宅代理是必须的。价格差异很大——Smartproxy/Decodo 起价 $4.50/GBBright Data 起价 $5.04/GB,大流量时可降到 $3–4/GB。

每 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+ 星)支持从 chrome99chrome146 的 Chrome 版本,也支持 Safari、Edge 和移动端变体。同步模式下,它比 tls_client20–30%

设置合理的请求节奏和请求头

  • 随机延迟: 请求之间间隔 2–7 秒(不要固定间隔,随机性很重要)
  • User-Agent 轮换: 准备 5–10 条真实浏览器 UA 并轮换使用
  • 会话预热: 先访问 target.com 首页,再请求商品页,以建立 Cookie
  • 请求头一致性: 你的 Sec-Ch-Ua 必须和你声明的浏览器版本一致,Sec-Ch-Ua-Platform 也必须和操作系统一致。不一致非常容易暴露
  • 会话持久化: 同一个会话中尽量保持 Cookie 连续。Scraperly 建议 使用可持续 48 小时的会话稳定性,并配合轮换住宅代理

不写代码也能抓:用 Thunderbit 抓 Target.com(无代码方案)

说实话,Target.com 确实是最难用程序抓取的零售网站之一。JavaScript 渲染、Akamai 的 TLS 指纹、机房代理检测、ChromeDriver 版本问题——环节很多。如果你正在学 Python,这当然是很好的练习;但如果你是为了实际工作拿到 Target 商品数据,这种投入产出常常并不划算。

如果你只是想拿到数据,而不是做工程项目,Thunderbit 会自动帮你处理这些难点。

Thunderbit 如何应对 Target.com 的难点

Thunderbit 的 AI 网页爬虫运行在你的浏览器里,所以它天然就能渲染 JavaScript——不需要配置 Selenium,不需要无头浏览器设置,也不需要管理 ChromeDriver 版本。浏览器本身就是爬虫。

工作流程很简单:

  1. 安装 Thunderbit Chrome 扩展,然后打开 Target 的商品页或搜索页
  2. 点击“AI 建议字段”——Thunderbit 会读取页面并推荐列名(商品标题、价格、评分、图片 URL 等)
  3. 点击“抓取”——数据会在几秒内直接从渲染后的页面提取出来

不需要配置代理,不需要伪装 TLS 指纹,也不会再出现 None

抓取 Target 商品列表和详情页

多页抓取流程才是真正有意思的部分。你可以先抓 Target 搜索结果页,拿到商品列表,然后使用子页面抓取自动访问每个商品 URL,把详情页的数据(描述、完整评论、规格参数)补充进表格里,而不用自己写分页代码,也不用管理浏览器会话。

你可以直接导出到 Excel、Google Sheets、Airtable 或 Notion。没有 csv.writer 样板代码,也没有文件编码问题。

自动化定期抓取 Target.com

如果你要持续监控价格或库存,Thunderbit 的定时爬虫可以用自然语言描述计划(例如“每周一上午 9 点”)。不需要 cron 任务,不需要服务器配置,也不用在 VPS 上一直挂着 Python 脚本。这对跟踪竞争对手价格的电商团队尤其有用——81% 的美国零售商现在都在使用自动化价格抓取,而价格情报的平均 ROI 约为 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 诉 LinkedIn(第九巡回法院,2022):抓取公开可访问的数据不违反 CFAA
  • Meta 诉 Bright Data(2024):Meta 败诉——法院认定抓取公开数据不构成 CFAA 违反

如果是大规模商业抓取,建议咨询法律顾问。若是市场研究、价格对比或个人项目,并且只使用公开可获取的数据,你的立场通常是比较稳妥的。记得始终尊重速率限制,不要给 Target 服务器造成过载。

结论与要点回顾

Target.com 确实配得上它的难度评分。天真的 Requests + BeautifulSoup 方案会失败,是因为 Target 用 JavaScript 渲染商品数据,而且 Akamai 会在你拿到响应前就对 TLS 握手做指纹识别。不过,只要方法对,数据提取其实并不复杂。

三种方法,按可靠性排序:

  1. Redsky API——最快、最适合批量数据,也最稳定,返回干净的 JSON。需要先通过 DevTools 逆向分析 API 端点。
  2. Selenium / Playwright——能处理 JavaScript 渲染,能拿到页面上的所有内容。速度慢一些,但最全面。
  3. Requests + BeautifulSoup——只能处理静态 HTML 和内嵌的 __TGT_DATA__ JSON。速度快,但不完整。

最关键的技术收益:

  • curl_cffi 替代标准 requests,反爬绕过能力可提升 15 倍
  • 必须使用住宅代理——机房 IP 会被立即标记
  • 每次请求都带上 Sec-Fetch-* 请求头——缺了就会立刻被拦
  • 会话预热(先访问首页)能显著提高成功率

如果对你的场景来说,Python 的折腾成本不值得,Thunderbit 的 Chrome 扩展可以自动处理 JavaScript 渲染、反爬措施和数据导出。试试免费方案,看看它能不能在几分钟内而不是几小时内帮你拿到想要的数据。

想看更多抓取指南和数据提取技巧,可以浏览 Thunderbit 博客或我们的 YouTube 频道

常见问题

只用 Python Requests 和 BeautifulSoup 能抓 Target.com 吗?

可以,但只能抓一部分。你能从商品页的 __TGT_DATA__ 脚本标签里提取商品标题、URL 和部分嵌入的 JSON 数据。不过,搜索结果页上的价格、评分、评论和库存信息都是由 JavaScript 渲染的,静态 HTTP 请求拿不到。要完整数据,请用 Selenium/Playwright 或 Redsky API。

为什么我的 Target.com 爬虫价格总是返回 None?

因为 Target 会在初始页面加载后通过 JavaScript 再加载价格数据。当你使用 requests.get() 时,你拿到的是 JavaScript 执行之前的 HTML 外壳——商品数据还没有注入 DOM。价格元素在响应里根本不存在。你可以使用能渲染 JavaScript 的无头浏览器(Selenium 或 Playwright),直接调用 Redsky API 获取 JSON,或者使用像 Thunderbit 这样从渲染后浏览器页面抓取的工具。

抓 Target.com 合法吗?

根据当前美国判例,抓取公开可访问的数据通常是允许的(hiQ 诉 LinkedIn、Meta 诉 Bright Data)。Target 的 robots.txt 也允许抓取商品页和分类页。不过,Target 的服务条款禁止自动化访问,所以法律上存在灰色地带。用于市场研究和价格对比的公开数据抓取,通常法律风险相对可控;但如果是大规模商业运营,建议先咨询律师。

Target 的 Redsky API 是什么?怎么访问?

Redsky 是 Target 的内部 API,负责前端商品数据。它不是那种你注册后能拿到文档和 API Key 的公共 API,而是 React 应用在渲染商品页时调用的后端接口。你可以通过打开 Chrome DevTools,在 Network 标签页按 XHR/Fetch 过滤,查找发往 redsky.target.com 的请求来发现它。API Key 会作为查询参数直接嵌在请求 URL 里。Target 工程团队已经确认该 API 是有意面向公众开放的。

怎么避免抓 Target.com 时被封?

最有效的单点改动,是用 curl_cffi 替代标准 Python requests 来伪装浏览器 TLS 指纹——这一个改动就能把成功率从 12% 提升到 92%。除此之外:使用住宅代理(不要用机房代理)、轮换 User-Agent 字符串、请求之间加入随机 2–7 秒延迟、带上所有 Sec-Fetch-* 请求头,并通过先访问首页来预热会话。或者直接使用像 Thunderbit 这样的工具,自动处理反爬措施,不需要任何配置。

了解更多

Ke
Ke
Thunderbit 首席技术官 | 高级数据科学家与机器学习专家 Ke Shen 拥有近十年的机器学习和数据科学经验,毕业于哥伦比亚大学,曾任 Walmart Labs 高级数据科学家。他在 Python、R、Java 和统计学方面拥有深厚且备受同行认可的专业能力,并分享如何将复杂的 AI 算法从理论落地到生产级架构的实战经验。
目录

只要开口,就能抓取网页

用简单英文说出你的需求。或者更简单,什么都不用说。

试用 Thunderbit 免费
使用 AI 提取数据
轻松将数据传输到 Google Sheets、Airtable 或 Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week