Target.com 表面上看起来像是那种很好抓的网站——直到你真的开始动手。如果你曾经用 Requests 和 BeautifulSoup 随手写过一段 Python 脚本,去请求 Target 的商品页,然后看到价格字段返回 None,那你绝不是一个人。
我测试过大多数主流零售网站的抓取方案,可以很明确地说:Target 一直是最难缠的几个之一。它每月有 ,商品数据也很丰富——价格、评分、库存、评论一应俱全——但 Target 基于 React 的前端渲染,加上 Akamai 的反爬检测,意味着最天真的做法几乎会立刻失败。不过,确实有三种 Python 方法是能用的。我会逐一讲解,说明为什么第一次尝试总会失效,并在最后给你一个不用写代码的快捷方案,适合 Python 不值得折腾的时候。
为什么你第一次用 Python 抓 Target.com 总是返回 None
先别急着看解决方案,先看看问题本身。大多数新手都会写出下面这段代码:
1import requests
2from bs4 import BeautifulSoup
3> This paragraph contains content that cannot be parsed and has been skipped.
4price = soup.select_one('[data-test="current-price"]')
5print(price) # None
输出是什么?None。每次都是。
这不是你代码的 bug。requests.get() 从 Target 拿到的 HTML,本质上只是一个空壳——一个 React 外壳,只是在告诉你:“嘿,去加载这段 JavaScript,真正的页面等会儿才渲染出来。”商品价格、评分、评论和库存信息,都是在初始页面加载之后由 JavaScript 再 注入进去的。而 Python 的 Requests 库不会执行 JavaScript,所以这些元素在响应里根本不存在。
论坛里到处都是撞上这堵墙的开发者。一个 说得很直接:“某个元素显示为 None,是因为它是用 Javascript 渲染的,而 requests 无法抓取由 Javascript 渲染的 HTML。” 也证实了这一点:“当你向 Target URL 发送 HTTP 请求时,返回的 HTML 响应缺少有意义的数据。”
而且就算你解决了 JavaScript 这层问题,还有第二层:Target 的 Akamai 反爬会先对你的 TLS 握手做指纹识别,在任何 HTML 字节传输之前就把 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 会发送 16 个经过精心排序、带 GREASE 值的密码套件;Python 则会以非浏览器顺序发送 60 多个。封锁甚至会发生在任何 HTTP 内容交换之前。
IP 信誉评分。 Akamai 会把 IP 分到不同信任等级。根据 ,机房 IP 会拿到“显著的负面信任分”,因为它们很可能被机器人使用。住宅 IP 则会获得正面评分。针对 Target,机房 IP 段往往会被立即标记。
JavaScript 指纹识别。 Akamai 会注入 JavaScript,收集你的 JS 引擎规格、硬件能力、操作系统信息、字体、插件,以及行为数据(打字速度、鼠标移动、点击时机)。这些信息会生成 _abck Cookie——一个有状态的指纹令牌。没有有效的 _abck,请求就会被拦截。
速率限制。 Target 大约会在每个 IP 每分钟 30–60 次请求时触发 429 错误。有些用户还报告过会收到 ,但内容其实是 “Pardon Our Interruption” 拦截页——这让自动化检测更难处理。
总体来说,。而 Akamai 绕过难度本身被 。
用 Python 抓 Target.com 的 3 种方法(横向对比)
网上很少有文章能把这三种可行方案放在一起比较。这里我直接给你摆出来,并老实评估:
This paragraph contains content that cannot be parsed and has been skipped.
下面我们逐个搭建。
方法 1:用 Python Requests 和 BeautifulSoup 抓 Target.com
这个方法拿不到搜索页里由 JavaScript 渲染出来的价格。不过它速度快、很轻量,而且只要你知道去哪里找,能提取的信息比你想象中更多。
关键技巧是:Target 会把部分商品数据嵌在 <script> 标签里,里面有一个 __TGT_DATA__ 变量和 __PRELOADED_QUERIES__。这个 JSON 数据块包含商品名称、描述、特性,有时甚至在单个商品页里也有价格。你还可以从搜索结果 HTML 中抓到商品标题和 URL。
第 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
这里建议用 curl_cffi 替代标准 requests。它可以伪装浏览器的 TLS 指纹,这是绕过 Target 封锁时最关键的因素。,curl_cffi 的反爬绕过成功率达到 ,而标准 requests 只有 ——提升了 15 倍。
第 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
4> This paragraph contains content that cannot be parsed and has been skipped.
5url = "https://www.target.com/s?searchTerm=bluetooth+headphones"
6resp = cureq.get(url, headers=headers, impersonate="chrome124")
7soup = BeautifulSoup(resp.text, "html.parser")
8> This paragraph contains content that cannot be parsed and has been skipped.
9你会拿到商品名称和 URL。价格呢?大概率不会出现在这个 HTML 里。这很正常。
10### 第 3 步:从商品页提取内嵌 JSON 数据
11单个商品页会把更丰富的数据嵌到 `__TGT_DATA__` 脚本标签中:
12```python
13import re, json
14product_url = "https://www.target.com/p/some-product/-/A-12345678"
15resp = cureq.get(product_url, headers=headers, impersonate="chrome124")
16soup = BeautifulSoup(resp.text, "html.parser")
17> This paragraph contains content that cannot be parsed and has been skipped.
18`__TGT_DATA__` 里的 JSON 结构包含商品名称、描述、特性,并且经常包含价格数据。具体嵌套层级会变,所以你需要先检查输出,再按实际结构去取值。
19### 第 4 步:处理分页
20Target 的搜索分页使用 `Nao` 参数。第 1 页是 `Nao=0`,第 2 页是 `Nao=24`,第 3 页是 `Nao=48`,以此类推(每页加 24):
21```python
22for page in range(0, 120, 24): # 前 5 页
23 paginated_url = f"https://www.target.com/s?searchTerm=bluetooth+headphones&Nao={page}"
24 resp = cureq.get(paginated_url, headers=headers, impersonate="chrome124")
25 # 解析并提取……
26 time.sleep(random.uniform(2, 5)) # 礼貌一点
第 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 其实是更好的选择。
第 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)
第 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 轮询一次,直到元素出现或者超时。
第 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
,如果滚动 10 次、每次停 1.5 秒,能拿到 8 个以上商品;不滚动的话通常只有 4–5 个。每次滚动 200–300 像素最接近真人行为。
第 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> This paragraph contains content that cannot be parsed and has been skipped.
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" |
第 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)
7> This paragraph contains content that cannot be parsed and has been skipped.
8评论由 Bazaarvoice 集成加载,支持分页(最多 51 页)、按最近排序,以及仅看图片的筛选。 [ScrapeOps 的基准测试](https://scrapeops.io/) 显示,Selenium 单个条目大约要 5.1 秒。
9别忘了处理完后关闭浏览器:
10```python
11driver.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
你会看到类似这样的地址:
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 商品编号(8 位商品 ID)store_id—— Target 门店编号zip—— 用于履约数据的邮政编码
从请求头里把 API 密钥提取出来。它作为查询参数直接嵌在 URL 中。
第 2 步:用 Python 直接请求 Redsky API
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"
6> This paragraph contains content that cannot be parsed and has been skipped.
7resp = cureq.get(url, headers=headers, impersonate="chrome124")
8data = resp.json()
9# 从 JSON 响应中提取商品详情
10product = data.get("data", {}).get("product", {})
11title = product.get("item", {}).get("product_description", {}).get("title", "N/A")
12price = product.get("price", {}).get("formatted_current_price", "N/A")
13rating = product.get("ratings_and_reviews", {}).get("statistics", {}).get("rating", {}).get("average", "N/A")
14print(f"{title} — {price} — Rating: {rating}")
完全不需要解析 HTML。响应结构清晰,速度也很快。
第 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 中提取(商品 URL 中会出现 /A-XXXXXXXX),也可以从 __TGT_DATA__ 内嵌 JSON 中拿。
第 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 的速率限制大约是 。
关于 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。
每 50–100 次请求轮换一次 IP;如果你的代理池支持,也可以每次请求都换。
用 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 和移动端变体。同步模式下,它比 tls_client 快 。
设置合理的请求节奏和请求头
- 随机延迟: 请求之间间隔 2–7 秒(不要固定间隔,随机性很重要)
- User-Agent 轮换: 准备 5–10 条真实浏览器 UA 并轮换使用
- 会话预热: 先访问
target.com首页,再请求商品页,以建立 Cookie - 请求头一致性: 你的
Sec-Ch-Ua必须和你声明的浏览器版本一致,Sec-Ch-Ua-Platform也必须和操作系统一致。不一致非常容易暴露 - 会话持久化: 同一个会话中尽量保持 Cookie 连续。 使用可持续 48 小时的会话稳定性,并配合轮换住宅代理
不写代码也能抓:用 Thunderbit 抓 Target.com(无代码方案)
说实话,Target.com 确实是最难用程序抓取的零售网站之一。JavaScript 渲染、Akamai 的 TLS 指纹、机房代理检测、ChromeDriver 版本问题——环节很多。如果你正在学 Python,这当然是很好的练习;但如果你是为了实际工作拿到 Target 商品数据,这种投入产出常常并不划算。
如果你只是想拿到数据,而不是做工程项目, 会自动帮你处理这些难点。
Thunderbit 如何应对 Target.com 的难点
Thunderbit 的 AI 网页爬虫运行在你的浏览器里,所以它天然就能渲染 JavaScript——不需要配置 Selenium,不需要无头浏览器设置,也不需要管理 ChromeDriver 版本。浏览器本身就是爬虫。
工作流程很简单:
- 安装 ,然后打开 Target 的商品页或搜索页
- 点击“AI 建议字段”——Thunderbit 会读取页面并推荐列名(商品标题、价格、评分、图片 URL 等)
- 点击“抓取”——数据会在几秒内直接从渲染后的页面提取出来
不需要配置代理,不需要伪装 TLS 指纹,也不会再出现 None。
抓取 Target 商品列表和详情页
多页抓取流程才是真正有意思的部分。你可以先抓 Target 搜索结果页,拿到商品列表,然后使用子页面抓取自动访问每个商品 URL,把详情页的数据(描述、完整评论、规格参数)补充进表格里,而不用自己写分页代码,也不用管理浏览器会话。
你可以直接导出到 Excel、Google Sheets、Airtable 或 Notion。没有 csv.writer 样板代码,也没有文件编码问题。
自动化定期抓取 Target.com
如果你要持续监控价格或库存,Thunderbit 的定时爬虫可以用自然语言描述计划(例如“每周一上午 9 点”)。不需要 cron 任务,不需要服务器配置,也不用在 VPS 上一直挂着 Python 脚本。这对跟踪的电商团队尤其有用——现在都在使用自动化价格抓取,而价格情报的平均 ROI 约为 。
用 Python 抓 Target.com 时该选哪种方法
给你一个快速决策框架:
This paragraph contains content that cannot be parsed and has been skipped.
如果你要搭建生产级数据管道,方法 3(Redsky API)在速度和稳定性上都最好。如果你只是做一次性研究,或者团队里没人懂 Python,Thunderbit 能省下好几个小时。而如果你正在学习网页抓取,那么从方法 1 → 方法 2 → 方法 3 逐步推进,是一条很自然、而且每一步都能学到真实东西的路线。
抓 Target.com 时的法律与伦理注意事项
这部分值得简单说明一下。Target 的 robots.txt 里大约有 120 多条 Disallow 规则,但明显没有屏蔽 /p/(商品页)和 /c/(分类页)——也就是说,商品页和分类页是明确允许抓取的。购物车、账户和结账页面则是受限的。
Target 的服务条款确实禁止自动化访问。不过,Redsky API 被 (Target 工程团队已证实),这在使用 API 进行数据采集时会降低法律风险。
你需要了解的关键判例有:
- (第九巡回法院,2022):抓取公开可访问的数据不违反 CFAA
- (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,反爬绕过能力可提升 - 必须使用住宅代理——机房 IP 会被立即标记
- 每次请求都带上
Sec-Fetch-*请求头——缺了就会立刻被拦 - 会话预热(先访问首页)能显著提高成功率
如果对你的场景来说,Python 的折腾成本不值得,可以自动处理 JavaScript 渲染、反爬措施和数据导出。试试,看看它能不能在几分钟内而不是几小时内帮你拿到想要的数据。
想看更多抓取指南和数据提取技巧,可以浏览 或我们的 。
常见问题
只用 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,或者使用像 这样从渲染后浏览器页面抓取的工具。
抓 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 指纹——这一个改动就能把成功率从 。除此之外:使用住宅代理(不要用机房代理)、轮换 User-Agent 字符串、请求之间加入随机 2–7 秒延迟、带上所有 Sec-Fetch-* 请求头,并通过先访问首页来预热会话。或者直接使用像 这样的工具,自动处理反爬措施,不需要任何配置。
了解更多