
本文介绍使用 selenium 自动化操作 jodi 数据网站表格时,如何通过模拟水平滚动行为,持续捕获全部动态渲染的列标题(如 jan2002、feb2002…),解决因懒加载/虚拟滚动导致仅获取前14列的问题。
本文介绍使用 selenium 自动化操作 jodi 数据网站表格时,如何通过模拟水平滚动行为,持续捕获全部动态渲染的列标题(如 jan2002、feb2002…),解决因懒加载/虚拟滚动导致仅获取前14列的问题。
JODI 数据平台(JODI Data)采用动态列加载机制:初始仅渲染可视区域内的列(通常为前14列),其余列需向右水平滚动后才生成对应 <th> 元素。直接调用 <code>find_elements 无法一次性获取全部列名,必须结合滚动控制与增量采集策略。
核心思路是:识别水平滚动控件 → 循环触发右移 → 每次滚动后捕获新出现的最右列标题 → 直至滚动条宽度归零(即无更多列可加载)。
该网站的滚动条容器位于 <td id="hScrollTD"> 内,其中第5个 <code><img> 元素(索引为 [5])是“向右滚动”按钮,而第4个 <img>([4])的 width 属性实时反映剩余可滚动宽度(单位:px)。当 width="0" 时,表示已到达最右端。
以下是完整、健壮的实现代码(含显式等待与异常防护):
from selenium import webdriver
from selenium.webdriver import ChromeService
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# 配置驱动(请替换为你的 chromedriver 路径)
chrome_driver_path = r'C:\Users\user\Downloads\chromedriver-win64\chromedriver-win64\chromedriver.exe'
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--start-maximized')
# 可选:禁用图片提升加载速度(不影响列名抓取)
# chrome_options.add_argument('--blink-settings=imagesEnabled=false')
service = ChromeService(executable_path=chrome_driver_path)
driver = webdriver.Chrome(service=service, options=chrome_options)
wait = WebDriverWait(driver, 30)
try:
driver.get('https://www.php.cn/link/612ba8e9bd06486a09b88ffc221c8738')
# 等待首屏列头出现
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, 'th.TVItemColHeader')))
# 初始化:获取初始可见列
header_texts = [el.text.strip() for el in driver.find_elements(By.CSS_SELECTOR, 'th.TVItemColHeader')]
# 定位滚动控制区
scroll_td = driver.find_element(By.XPATH, "//td[@id='hScrollTD']")
while True:
# 获取当前剩余滚动宽度(字符串,如 "120")
scroll_img_4 = scroll_td.find_element(By.XPATH, "./img[4]")
width_attr = scroll_img_4.get_attribute('width')
# 若宽度为 "0" 或无法解析,退出循环
if not width_attr or width_attr == "0":
break
try:
# 点击右滚动按钮(img[5])
scroll_right_btn = scroll_td.find_element(By.XPATH, "./img[5]")
scroll_right_btn.click()
# 等待新列加载(关键!避免读取旧DOM)
wait.until(lambda d: len(d.find_elements(By.CSS_SELECTOR, 'th.TVItemColHeader')) > len(header_texts))
# 获取最新一列(最右侧新增列)
new_header = driver.find_elements(By.CSS_SELECTOR, 'th.TVItemColHeader')[-1].text.strip()
if new_header and new_header not in header_texts:
header_texts.append(new_header)
except Exception as e:
print(f"滚动或采集时发生异常: {e}")
break
print(f"成功获取 {len(header_texts)} 列标题:")
print(header_texts)
finally:
driver.quit()
✅ 关键注意事项:
-
必须添加显式等待:
wait.until(...)确保新列 DOM 已渲染完成,否则易读取到重复或空值; -
避免
time.sleep()替代等待:页面加载耗时不固定,硬等待效率低且不可靠; -
检查重复项:尽管逻辑上每次取
last-child,但网络波动可能导致重复点击,建议加入if new_header not in header_texts去重; -
滚动控件稳定性:若网站未来更新 DOM 结构(如修改
id或img索引),需同步调整 XPath 表达式; - 反爬提示:该站点未设强反爬,但高频请求可能触发限流,生产环境建议添加随机延时与 User-Agent 轮换。
通过上述方法,你将稳定获取全部年月列名(实测可达 200+ 列),为后续全量数据抓取奠定准确的列结构基础。










