XPath只能解析静态HTML源码,无法提取JavaScript动态生成或隐藏在onclick/base64等属性中的链接;若源码无<a href=,必须改用Selenium等渲染工具。

为什么直接用 //table//a/@href 常常提取不到隐藏链接?
因为“隐藏”通常不是指 CSS 的 display: none 或 visibility: hidden,而是链接被包裹在 span、div 甚至文本节点里,或者 href 是通过 JavaScript 动态写入的。XPath 只能解析静态 HTML DOM,对 JS 渲染后的内容无能为力。如果你用 requests + lxml 直接解析响应体却没拿到链接,大概率是页面用了前端框架(如 Vue/React)或内联脚本拼接 href。
如何确认链接是否真在 HTML 源码中?
打开浏览器开发者工具 → 右键网页 →「查看页面源代码」(不是「检查元素」),然后搜索 <a 或关键词。如果源码里压根没有 <a href=,说明链接由 JS 注入,XPath 就无解——必须换方案。
- 若源码中存在但位置嵌套深(如
<td><span><a href="...">点击</a></span></td>),用//table//a[@href]/@href即可捕获 - 若链接藏在
onclick属性里(如onclick="location.href='xxx'"),XPath 可提取该属性值://table//td[contains(@onclick,'href')]/@onclick,再用正则解析 - 若 href 被 base64 编码或混淆(如
href="javascript:void(0)" data-url="base64:xxx"),需额外处理@data-url属性
用 lxml 提取时怎么避免漏掉“伪链接”?
常见陷阱是匹配到空 href、javascript:void(0)、# 等无效值。必须加过滤逻辑:
from lxml import html
import re
<p>tree = html.fromstring(html_content)
links = tree.xpath('//table//a[@href]/@href')
clean_links = [
link.strip()
for link in links
if link.strip()
and not re.match(r'^\s*(#|javascript:|void(0)|mailto:)', link.strip())
]
注意:XPath 中的 @href 不会自动补全相对路径,你得用 urllib.parse.urljoin(base_url, link) 手动处理。
立即学习“Python免费学习笔记(深入)”;
什么时候必须放弃 XPath 改用 Selenium?
当你发现目标链接只在点击某按钮、滚动到某区域、或等待几秒后才出现时,说明它依赖用户交互或异步加载。XPath 再精准也查不到尚未插入 DOM 的节点。
- 用 Selenium 启动浏览器后,先
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")触发懒加载 - 用
WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.XPATH, "//table//a")))等待元素就绪 - 再执行
driver.find_elements(By.XPATH, "//table//a")并取.get_attribute("href")—— 这才是 JS 渲染后的最终值
真实项目里,80% 的“隐藏链接”问题其实出在没分清静态 HTML 和动态 DOM 的边界;盲目调 XPath 表达式不如先看一眼原始 HTML 源码。


















