cover_image

继续磨刀--爬取CME 黄金、原油等期货数据

静听烟雨任平生 静听烟雨任平生

磨刀不误砍柴工

从新浪爬取的最新期货数据(GC、CL、NQ、ES)只有前一天的结算价,芝加哥商业交易所(CME)官方网站可以提供过去五天的历史结算价。

昨天,套利程序的编程告一段落。我今天腾出手来,开始收拾之前遗漏的多个"未解之谜"。

鉴于 CME 网站极高的反爬机制与动态渲染技术,我在Gemini的帮助下,一次就搞定,当然也离不开TRAE打下的基础和我的耐心投喂。采用 Selenium 自动化 + 本地智能缓存的架构,确保数据的稳定获取和程序的鲁棒性,。

PS-再一次证明TRAE的无能,野花的芬芳

1. 为什么选择 Selenium 爬取 CME?

CME 网站的数据表格并非传统的静态 HTML 内容,而是通过现代前端 JavaScript 框架(例如 React/Angular)在页面加载完成后,异步请求后端 API 并动态渲染生成的。

这意味着,如果直接使用 Python 的 requests 库去请求网页的原始 HTML 源码,将只能得到一个不包含实际数据表格的“骨架”页面。

此外,CME 的后端 API 受到严格的 Web 应用防火墙(WAF,如 Cloudflare/Akamai)保护。任何直接针对 API 的请求都极易被识别为自动化机器人行为,并立即遭到拦截(通常表现为 HTTP 403 Forbidden 错误)。

解决方案:

为了有效应对这些挑战,选择使用 Selenium。Selenium 能够驱动真实的 Chrome 浏览器内核,完全模拟人类用户的正常访问行为。它会等待页面上的所有 JavaScript 脚本执行完毕,确保数据完全渲染到 DOM(文档对象模型)树中,然后再从这个完整的 DOM 树中提取所需的目标数据。这种方式极大地提高了数据获取的成功率和稳定性。

2. Selenium 的浏览器伪装与反爬策略

为了让 Selenium 驱动的浏览器行为更接近一个真实的普通用户,从而有效规避 CME 网站的反爬检测,在 get_cme_settlement 函数中进行了深度的环境伪装和策略优化。

2.1 浏览器启动参数伪装 (Headers & Options)

通过配置 Chrome 浏览器的启动选项,来模拟真实用户的环境特征,并隐藏自动化工具的痕迹。

from selenium.webdriver.chrome.options import Options

chrome_options = Options()
chrome_options.add_argument('--headless')  # 无头模式,后台静默运行,不弹出实体浏览器窗口
chrome_options.add_argument('--disable-gpu'# 禁用 GPU 硬件加速,在某些无头环境下可能需要
chrome_options.add_argument('--window-size=1920,1080'# 伪装真实桌面显示器分辨率,避免被识别为移动设备或异常尺寸

# 核心伪装:设置真实的 User-Agent,防止被识别为无头浏览器默认的 User-Agent
# 注意:User-Agent 应定期更新以保持其真实性,以应对网站可能的检测更新
chrome_options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36')

# --- 新增:高级反爬伪装配置 ---
# 1. 禁用浏览器正在被自动化程序控制的提示(例如 Chrome 顶部的"Chrome 正在受到自动化测试软件的控制")
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_experimental_option('useAutomationExtension'False)

# 2. 禁用 Blink 引擎中的自动化特征 (window.navigator.webdriver = true),这是许多 WAF 检测 Selenium 的关键点
chrome_options.add_argument('--disable-blink-features=AutomationControlled')

# 3. 避免加载图片,可以加快页面加载速度,并减少网络流量(可选,根据需求决定是否开启)
# chrome_options.add_argument('--blink-settings=imagesEnabled=false')

# 4. (可选) 配置代理,强烈建议用于访问海外网站
# chrome_options.add_argument('--proxy-server=http://127.0.0.1:10808')
chrome_options = Options()
chrome_options.add_argument('--headless')  # 无头模式,后台静默运行,不弹出实体浏览器窗口
chrome_options.add_argument('--disable-gpu'# 禁用 GPU 硬件加速,在某些无头环境下可能需要
chrome_options.add_argument('--window-size=1920,1080'# 伪装真实桌面显示器分辨率,避免被识别为移动设备或异常尺寸
# 核心伪装:设置真实的 User-Agent,防止被识别为无头浏览器默认的 User-Agent
chrome_options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36')

2.2 击穿 CDN 缓存 (_t 时间戳机制)

CME 的历史数据页面通常通过 URL Hash(例如 #tradeDate=04/10/2026)来切换日期。如果短时间内重复请求同一基础 URL,内容分发网络(CDN)可能会返回旧的缓存页面,导致数据不是最新的。

为了强制 CDN 穿透并触发真实的路由更新,在 URL 拼接时动态加入了一个毫秒级的时间戳参数。这使得每次请求的 URL 都是独一无二的,从而绕过 CDN 缓存。

import time

timestamp = int(time.time() * 1000# 获取当前时间的毫秒级时间戳
base_url = "https://www.cmegroup.com/markets/futures/..." # **请替换为实际的 CME 基础 URL**
target_date = "04/10/2026" # 示例目标日期,实际使用时应动态生成
url = f"{base_url}?_t={timestamp}#tradeDate={target_date}"

2.3 DOM 强刷防残留 (about:blank 技巧)

在单页应用(SPA)中,需要循环爬取不同日期的数据时,旧的表格 DOM 元素可能会残留在页面上。这会导致 Selenium 的 WebDriverWait 瞬间误判页面已加载完成,从而抓取到上一天的重复数据。

为了彻底销毁旧的 DOM 树,确保每次都是从一个干净的页面开始加载新数据,通过在每次请求真实 URL 前,先跳转一次空白页 (about:blank)。

from selenium import webdriver
from selenium.webdriver.chrome.options import Options # 导入 Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

# 假设 chrome_options 已按 2.1 节配置
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36')
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_argument('--disable-blink-features=AutomationControlled')
# ... 其他 chrome_options 配置 ...

driver = webdriver.Chrome(options=chrome_options)
wait = WebDriverWait(driver, 10# 10秒超时

# 示例 URL (请替换为实际的 CME 目标 URL)
example_url = "https://www.cmegroup.com/markets/futures/metals/gold/gold.html#tradeDate=04/10/2026"

# 强制清空上一次循环遗留的 DOM,防止旧表格干扰 Wait
driver.get("about:blank")
driver.get(example_url) # 加载新的目标 URL

# 优化后的等待:等待具有特定 class 或特定结构的表格元素,提高等待的精确度
# 假设 CME 的数据表格有一个 class 为 'cme-data-table'
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'table.cme-data-table')))
# 或者等待表格内的数据行渲染完成,确保数据已经加载
# wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'table.cme-data-table tbody tr')))

# 完成操作后,记得关闭浏览器
driver.quit()

2.4 未来扩展:引入 undetected-chromedriver

如果你发现使用原生的 Selenium + 配置项仍然偶尔遭遇 HTTP 403 或无尽的人机验证(Captcha),可以在文档的备选方案中考虑使用 undetected-chromedriver。这是一个经过特殊编译和优化的 WebDriver,专门用于绕过各类 WAF。

# 安装: pip install undetected-chromedriver
import undetected_chromedriver as uc

options = uc.ChromeOptions()
options.add_argument('--headless')
# undetected-chromedriver 默认已经处理了 webdriver 特征抹除,无需额外配置
# 它会自动管理 ChromeDriver,无需 webdriver-manager
driver = uc.Chrome(options=options)

# 完成操作后,记得关闭浏览器
driver.quit()

3. 本地智能缓存机制 (cme_history_cache.csv)

直接高频请求 CME 网站不仅速度极慢(受限于 Selenium 渲染速度),而且面临被封禁 IP 的高风险。为了解决这些问题,引入了 cme_history_cache.csv 文件作为轻量级的本地微型数据库。

3.1 缓存的工作流程

这个智能缓存机制旨在最大限度地减少对 CME 网站的实际访问,从而提高效率并降低风险:

  1. 读取比对: 程序启动时,首先会读取 cme_history_cache.csv 文件中已有的历史数据。然后,它会将当前需要获取的日期(例如,最近 5 天)与缓存中的日期进行比对,智能地识别出本地缺失的日期。
  2. 按需抓取(增量爬取): Selenium 驱动的浏览器只会针对那些在本地缓存中缺失的日期去 CME 网站进行请求和抓取。例如,如果昨天已经运行过代码并抓取了数据,今天再次运行时,程序只会去抓取最新的 1 天数据。
  3. 合并写入: 将新抓取到的数据与旧缓存数据进行合并。合并后的完整历史数据将重新覆盖写入 cme_history_cache.csv 文件。

3.2 缓存机制带来的三大核心优势:

这个本地智能缓存机制为工具带来了显著的性能和稳定性提升:

  • 🚀 极速执行: 如果所需历史数据已全部存在于本地缓存中,程序将无需启动浏览器访问 CME 网站。它会直接从 cme_history_cache.csv 载入数据,通常在 1 秒内即可完成运行并生成最终的对比报表。
  • 🛡️ 极致防封: 通过最大限度地降低对 CME 服务器的请求频次,大大减少了被识别为恶意爬虫并被封禁 IP 的概率,保障了爬虫的长期可用性。
  • 🔄 断点续传: 即使在爬取过程中遇到网络波动、程序中断等情况,已成功抓取的数据也会自动保存在缓存中。重新运行代码时,程序会自动跳过已抓取的部分,继续抓取剩余缺失的日期,避免了从头再来的痛苦。

4. 快速开始 (Quick Start)

如何快速部署和运行 CME 期货历史结算价爬虫与对比工具。

4.1 环境依赖

在运行本工具之前,请确保系统满足以下环境要求:

  • Python 版本: 确保已安装 Python 3.8 或更高版本。
  • 第三方库: 在终端或命令行中运行以下命令,安装所有必要的 Python 第三方库:
pip install requests beautifulsoup4 selenium webdriver-manager pandas
  • requests: 用于发送 HTTP 请求(例如,获取新浪财经数据)。

  • beautifulsoup4: 用于解析 HTML 内容(可能用于新浪财经数据)。

  • selenium: 驱动浏览器进行自动化操作。

  • webdriver-manager: 自动管理和下载 ChromeDriver,省去手动配置的麻烦。

  • pandas: 用于数据处理和 CSV 文件的读写(尤其在缓存机制中)。

  • 代理配置: 由于 CME 网站是海外网站,且反爬机制严格,强烈建议在运行环境配置代理。代码默认配置为 127.0.0.1:XXXX。请根据各自实际的代理服务地址和端口进行修改。

# 示例:在 Python 代码中设置代理
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

chrome_options = Options()
# ... 其他 chrome_options 配置,例如无头模式、User-Agent 等
chrome_options.add_argument('--proxy-server=http://127.0.0.1:XXXXX')

# driver = webdriver.Chrome(options=chrome_options)

4.2 运行程序

完成环境依赖的安装和代理配置后,你可以通过以下命令运行本工具:

python Test_sina_futures.py

程序运行结束后,将在当前目录下看到以下输出:

  • futures_data_YYYYMMDD.csv:这是一个对比报表文件,其中 YYYYMMDD 代表运行日期。该文件包含了从新浪财经获取的最新期货快照数据与从 CME 官方网站爬取到的历史结算价的交叉比对结果。
  • cme_history_cache.csv:本地智能缓存文件将被更新。如果程序抓取了新的 CME 历史数据,这些数据将被追加或更新到此文件中,以便下次运行时进行快速加载。

5. 总结与展望

这份说明文档不仅清晰地阐述了工具的实现细节,更重要的是,它深入解释了每个技术选择背后的原因,尤其是在应对 CME 网站复杂反爬机制方面的策略。本地智能缓存机制的设计,更是切中了爬取海外金融数据时对效率和稳定性的核心需求。

未来可能的扩展方向:

  • 数据持久化:目前数据主要存储在 CSV 文件中。未来可以考虑将数据对接至更强大的数据库系统,如 MySQL、PostgreSQL 或 SQLite,以实现更灵活的数据查询、管理和分析。
  • 资源优化:Selenium 在运行时可能会占用较多系统资源。可以进一步探索优化配置,例如更精细地控制浏览器行为、使用更轻量级的浏览器驱动(如 htmlunit 或 playwright 的无头模式,如果 CME 允许),或在资源受限环境下运行。
  • 错误处理与通知:增加更健壮的错误处理机制,例如在爬取失败、数据异常时自动重试,并通过邮件、微信或企业内部消息系统发送通知。
  • 可视化与报告:集成数据可视化库(如 Matplotlib, Seaborn, Plotly)生成更直观的对比图表和报告。

继续滑动看下一个
静听烟雨任平生
向上滑动看下一个