使用requests与lxml爬取JS渲染页面表格无输出问题求助
排查无输出的核心原因
- 静默无输出的本质逻辑:你写的xpath规则在requests获取到的静态页面源码里完全匹配不到目标元素,
tree.xpath()最终返回空列表,后续的for循环没有可遍历对象,直接跳过执行流程,既不会输出内容,也不会触发报错——这是Python遍历空列表的正常表现。 - 目标元素不在静态源码中:你想匹配的
id="eventTabDiv_history_0"节点,根本不是初始页面加载时就存在的内容。这个节点的表格数据是页面加载完成后,通过JS发异步请求拉取数据,再动态插入到DOM里的,直接请求静态页面当然找不到对应元素。 - 请求头不满足反爬要求:你当前用的Chrome 54版本UA是2016年的旧版本,也缺少AJAX请求必备的校验字段,服务器很可能直接返回反爬验证页,就算元素存在也拿不到有效数据。
- 内容提取写法错误:对嵌套了大量子标签的表格节点直接取
.text属性,只会拿到节点本身直接包裹的纯文本,子标签里的表格行、单元格内容完全取不到,就算匹配到节点,输出也会是空的。
轻量化实现方案(不引入Selenium,保持requests+lxml的低延迟优势)
不用启动浏览器等待JS渲染,直接定位动态数据对应的后端异步接口,拿到嵌好表格结构的HTML片段后直接用lxml解析,完全贴合你之前选择轻量化技术栈的初衷:
- 补全满足反爬校验的请求头,重点加上AJAX请求必需的
X-Requested-With、Referer字段,把UA替换为当前主流的Chrome版本 - 跳过无关的整页静态HTML请求,直接POST请求承载历史表格数据的接口,减少冗余传输
- 修正文本提取逻辑,用xpath遍历单元格内的所有文本节点,避免嵌套标签导致的内容遗漏
可直接运行的修正代码
from lxml import html import requests # 补全反爬校验必需的请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Referer': 'https://www.investing.com/economic-calendar/unemployment-rate-301', 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json, text/javascript, */*; q=0.01' } # 直接请求历史数据的异步接口 api_url = 'https://www.investing.com/economic-calendar/Service/getCalendarFilteredData' post_params = { 'action': 'getHistoricalData', 'eventId': '301', 'dateFrom': '2015-01-01', 'dateTo': '2024-06-01', 'limit_from': '0' } # 发请求获取接口返回内容,加异常判断避免静默无输出 resp = requests.post(api_url, headers=headers, data=post_params) resp.raise_for_status() resp.encoding = 'utf-8' # 接口返回JSON格式数据,其中data字段是拼接好的表格HTML片段 table_html = resp.json()['data'] tree = html.fromstring(table_html) # 匹配所有带有效数据的表格行 data_rows = tree.xpath('//tr[contains(@id, "eventRowId")]') for row in data_rows: # 提取行内所有单元格的纯文本,过滤空字符串 row_content = [cell.strip() for cell in row.xpath('.//td//text()') if cell.strip()] print(row_content)
实用优化提示
- 这种方案的请求速度比Selenium快10倍以上,完全符合轻量化的技术选型要求
- 如果需要拉取更多历史数据,只要修改
post_params里的limit_from参数值,和前端滚动加载的逻辑保持一致即可,不需要额外渲染页面 - 提取嵌套结构的文本时不要用节点的
.text属性,用.xpath('.//text()')结合空值过滤的方式,才能拿到完整的单元格内容 - 所有请求都加上状态码校验,避免服务器返回错误页、验证页的时候程序静默执行,排查问题的时候可以先把响应内容打印出来,确认拿到的是有效数据再做解析
内容的提问来源于stack exchange,提问作者Sayed Gouda
相关产品推荐
相关产品推荐

