使用Python Parsel的XPath爬取Medium返回空列表问题排查
Medium爬取XPath返回空列表的原因及解决办法
可能的原因
- 动态内容渲染差异:浏览器会执行页面中的JavaScript加载渲染内容,但
requests.get只能获取服务器返回的初始HTML源码,那些需要JS动态生成的元素在初始源码里不存在,自然用XPath查不到。Medium的热门内容多是异步加载的,这是最常见的问题。 - 绝对XPath的脆弱性:你用的是基于
id和多层级的绝对路径,这种路径极度依赖页面固定结构。服务器返回给爬虫的HTML结构可能和浏览器中看到的不一样(比如节点层级、id动态变化),导致路径失效;而其他h2节点刚好在初始HTML里存在,所以能查到。 - 反爬机制限制:Medium可能识别出请求来自爬虫而非真实浏览器,返回经过处理的HTML(比如隐藏目标元素、返回简化版页面),导致XPath无法匹配目标内容。
对应的解决办法
- 获取渲染后的HTML:使用Selenium、Playwright这类模拟浏览器行为的工具,加载页面并执行JS后再获取完整HTML解析。示例代码(Playwright):
from playwright.sync_api import sync_playwright from parsel import Selector def get_trending(): with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page(user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36") page.goto("https://medium.com") html = page.content() browser.close() selector = Selector(text=html) # 改用基于类名的稳定选择器 titles = selector.xpath("//h2[contains(@class, 'graf--title')]/text()").getall() print(titles) return titles get_trending()
- 使用更稳定的选择器:放弃绝对层级XPath,改用基于元素属性、类名的相对选择器。比如Medium文章标题通常带有特定class,用
//h2[contains(@class, 'graf--title')]定位,比依赖层级的路径可靠得多。 - 模拟浏览器请求头:给
requests.get添加浏览器的User-Agent等请求头,降低被反爬识别的概率:
import requests from parsel import Selector headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" } def get_trending(html): selector = Selector(text=html) titles = selector.xpath("//h2[contains(@class, 'graf--title')]/text()").getall() print(titles) return titles response = requests.get("https://medium.com", headers=headers) opponents = get_trending(response.text)
内容的提问来源于stack exchange,提问作者Karthik Bhandary
相关产品推荐
相关产品推荐

