Target.com 看起来像是那种“很容易抓”的网站——直到你真的上手。你如果曾经用 Requests 和 BeautifulSoup 很快写过一段 Python 脚本,拿去抓 Target 商品页,结果价格字段返回 None,那你一点都不孤单。
我测试过主流零售网站的各种抓取方案,可以很负责地说:Target 一直都是最难啃的站点之一。它每月有 ,商品数据也很丰富——价格、评分、库存、评论一应俱全——但 Target 基于 React 的前端渲染,再加上 Akamai 的反爬检测,意味着最朴素的做法几乎会立刻翻车。不过,好消息是:确实有 3 种 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> 标签里,里面包含 __PRELOADED_QUERIES__ 的 __TGT_DATA__ 变量。这个 JSON 数据块里包括商品名称、描述、特征,有时在单品页里也会带价格。你还可以从搜索结果 HTML 中抓到商品标题和 URL。
第一步:配置 Python 环境
新建项目目录并安装依赖:
1mkdir target-scraper && cd target-scraper
2python -m venv venv
3source venv/bin/activate # Windows 上用:venv\activate
4pip install requests beautifulsoup4 curl_cffi
这里建议用 curl_cffi 替代标准 requests。它能伪装浏览器的 TLS 指纹,这是绕过 Target 封锁最关键的一步。,curl_cffi 的反爬绕过成功率达到 ,而标准 requests 只有 ——提升了 15 倍。
第二步:抓取 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### 第三步:从商品页中提取嵌入的 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### 第四步:处理分页
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)) # 友好一点
第五步:保存抓取结果
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 秒 vs. 28 秒)。这里我还是以 Selenium 为例,因为它社区更大、教程更多;但如果你是从零开始,Playwright 是更好的新选择。
第一步:安装 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)
第二步:加载 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 会每 500 毫秒轮询一次,直到元素出现或超时。
第三步:滚动页面以加载全部商品
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 像素,这样更像真人操作。
第四步:从渲染后的页面提取商品数据
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" |
第五步:抓取商品评论(附加项)
进入单个商品页,向下滚动到评论区域,然后提取评论数据:
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 前端会从内部 API redsky.target.com 拉取所有数据。你可以直接用 Python 调用它——不用解析 HTML,不用浏览器,也不用 JavaScript 渲染。返回的是干净的 JSON,包含 40 多个字段,覆盖价格、评分、评论、图片、库存、履约方式、规格和变体。对于批量商品数据来说,这毫无疑问是最快、最稳的方法。
第一步:用 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 key(固定,不会频繁轮换;不同端点使用不同 key)tcin—— Target.com Item Number,也就是 8 位商品 IDstore_id—— Target 门店编号zip—— 用于履约数据的邮编
API key 可以直接从请求 URL 中提取,它本身就是查询参数的一部分。
第二步:直接用 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。返回结果结构清楚、干净、速度快。
第三步:通过 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 里直接拿。
第四步:用并发请求提升规模
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 key 是固定的,但与端点相关。 不同的 Redsky 端点会用不同的 key。它们不常轮换,但 Target 随时可能修改。
- 这是一个未公开文档的内部 API。 Target 工程团队已经 ,这会降低法律风险,但它并不是带 SLA 的正式公开 API。
- 商品变体(颜色、尺码)通常各自对应唯一 TCIN。 你需要分别查询每个变体。
- 少了
Sec-Fetch-*头会直接被拦。 这是常见坑——一定要带上Sec-Fetch-Site、Sec-Fetch-Mode和Sec-Fetch-Dest。
如何大规模抓取 Target.com 又不容易被封
这些做法适用于生产环境,不管你用哪种方法都该遵守。
轮换住宅代理,不要用数据中心代理
Target 的 Akamai 会直接识别数据中心 IP 段。想长期稳定抓取,住宅代理几乎是必需品。价格差异很大——,,大批量时会降到每 GB $3–4 左右。
建议每 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 Suggest Fields” —— Thunderbit 会识别页面并自动建议列名(商品标题、价格、评分、图片 URL 等)
- 点击 “Scrape” —— 数据会在几秒内直接从渲染后的页面提取出来
不用配置代理。不用伪装 TLS 指纹。也不会再出现 None。
抓取 Target 商品列表和详情页
多页抓取是最实用的部分。你可以先抓 Target 搜索结果页拿到商品列表,再用 Subpage Scraping 自动访问每个商品 URL,把详情页数据补充到表格中——例如描述、完整评论、规格等——无需手写分页逻辑,也不用管理浏览器会话。
还能直接导出到 Excel、Google Sheets、Airtable 或 Notion。没有 csv.writer 的样板代码,也没有文件编码问题。
自动化定时抓取 Target.com
如果你需要持续监控价格或库存,Thunderbit 的 Scheduled Scraper 可以让你用自然语言描述计划,比如“每周一上午 9 点”。不用 cron、不要服务器、也不用在 VPS 上让 Python 脚本常驻运行。对做竞品价格监控的电商团队尤其有用——已经在使用自动化价格抓取,而价格情报的平均 ROI 高达 。
什么时候该用哪种方法抓 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 v. LinkedIn、Meta v. 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-* 请求头,并先访问首页预热会话。或者直接用像 这样的工具,让它自动处理反爬机制,无需手动配置。
了解更多