🎯 一款可定制、具备反检测功能的云浏览器,由自主研发的 Chromium驱动,专为网页爬虫AI 代理设计。👉立即试用
返回博客

爬虫网页抓取:Python中的CSS和XPath选择器

Alex Johnson
Alex Johnson

Senior Web Scraping Engineer

23-Jul-2026

摘要:

  • parsel 使用 CSS 和 XPath 从 HTML 中选择数据,在同一个文档上 — 它是 Scrapy 使用的选择器引擎,作为独立库可用。
  • parsel 不执行抓取。 它没有 HTTP 客户端,也不运行 JavaScript;您提供标记,它构建一个可查询的树。
  • 这个差距出现在一个脚本中。 对于一个 JavaScript 渲染的演示页面,普通的 GET 请求给 parsel 0 个引用节点;通过 Scrapeless Universal Scraping API 使用 js_render 抓取同一 URL 则得到所有 10 个。
  • CSS 和 XPath,齐头并进。 实时运行通过两种方式提取相同的 10 个作者 — sel.css("small.author::text")sel.xpath("//small[@class='author']/text()") — 从渲染后的 HTML 中。
  • 这两层保持分开。 Scrapeless 负责抓取和渲染;parsel 进行选择。两者之间没有交集。
  • 可自由开始抓取一侧。app.scrapeless.com 创建您的 Scrapeless API 密钥。

parsel 是什么,以及它不是

parsel 是一个用于从 HTML 和 XML 中提取数据的 Python 库,使用 CSS 选择器和 XPath 表达式。它是构建 Scrapy 的选择器层,打包使您可以在任何地方使用,它在底层封装了 lxml,因此这两种选择器语言都会在相同的快速解析树上运行。选择它而不是仅 CSS 的解析器的原因是 XPath:当一个字段是通过其位置、文本或与邻居的关系而非类定义时,XPath 可以表达,而 CSS 不能。

它是一个选择器库,仅此而已。parsel 没有 HTTP 客户端,不持有会话,也不运行 JavaScript。给它一个字符串,它构建一个您可以查询的 Selector;让它去抓取一个 URL 时,没有相应的方法。因此,每个“parsel 网络抓取”设置是两层:返回真实 HTML 的内容和从中进行选择的 parsel。本指南使用 Scrapeless Universal Scraping API 作为第一层,因为普通的 HTTP 客户端在任何通过 JavaScript 构建内容的页面上返回的是预渲染的标记。更广泛的 Python 网络抓取教程 涵盖了周边生态。

安装

parsel 和 requests 是整个工具链。本指南是基于 parsel 1.11.0 编写的:

bash Copy
pip install "parsel==1.11.0" requests

将您的密钥保存在环境中,绝对不要在源代码中:

bash Copy
export SCRAPELESS_API_KEY="sk_your_scrapeless_key"

获取值得解析的 HTML

选择质量受抓取保真度的限制,因此从这里开始。在一个 JavaScript 渲染的页面上,普通 HTTP 客户端收到的标记不是读者所看到的标记:内容仅在脚本构建 DOM 之后到达,这个生命周期由 HTML 脚本规范 定义。一个脚本计算通过 parsel 自身的差异:

python Copy
# fidelity.py — parsel 看到的内容:普通 GET 与服务器端渲染
import os

import requests
from parsel import Selector

URL = "https://quotes.toscrape.com/js/"

plain = requests.get(URL, timeout=60).text
print("plain GET chars:", len(plain), "| quote nodes:", len(Selector(text=plain).css("div.quote")))

resp = requests.post(
    "https://api.scrapeless.com/api/v2/unlocker/request",
    headers={"Content-Type": "application/json", "x-api-token": os.environ["SCRAPELESS_API_KEY"]},
    json={"actor": "unlocker.webunlocker", "input": {"url": URL, "js_render": True}},
    timeout=120,
)
resp.raise_for_status()
rendered = resp.json().get("data", "")
print("rendered chars:", len(rendered), "| quote nodes:", len(Selector(text=rendered).css("div.quote")))

运行结果显示普通抓取的引用节点为 0,而渲染后的为 10:

text Copy
plain GET chars: 5806 | quote nodes: 0
rendered chars: 8940 | quote nodes: 10

渲染、解锁和代理路由都在服务器端的那一个 POST 中进行 — Universal Scraping API 是抓取层,而渲染后的标记是 parsel 应当选择的内容。

用 CSS 和 XPath 提取

通过真实的 HTML,parsel 完成提取。cssxpath 都返回一个 SelectorList; get 返回第一个匹配项,getall 返回每个匹配项。::text 伪元素和 text() 节点测试都可以提取文本,因此您可以通过两种方式读取相同字段 — CSS 遵循 W3C 选择器规范,而 XPath 遵循 W3C XPath 规范

python Copy
# extract.py — 抓取渲染后的页面,然后使用 CSS 和 XPath 选择
import os

import requests

从parsel导入选择器

响应 = requests.post(
"https://api.scrapeless.com/api/v2/unlocker/request",
headers={"Content-Type": "application/json", "x-api-token": os.environ["SCRAPELESS_API_KEY"]},
json={"actor": "unlocker.webunlocker", "input": {"url": "https://quotes.toscrape.com/js/", "js_render": True}},
timeout=120,
)
响应.raise_for_status()
sel = 选择器(文本=响应.json().get("data", ""))

css_nodes = sel.css("div.quote")
xpath_authors = sel.xpath("//div[@class='quote']//small[@class='author']/text()").getall()
print("css 引用节点:", len(css_nodes))
print("xpath 作者:", len(xpath_authors))

记录 = []
对于引用中的 css_nodes:
记录.append({
"text": 引用.css("span.text::text").get(),
"author": 引用.xpath(".//small[@class='author']/text()").get(),
"tags": 引用.css("a.tag::text").getall(),
})
print("第一位作者:", 记录[0]["author"])
print("第一位标签:", 记录[0]["tags"])

上述程序的实际运行选择了全部10条记录,CSS和XPath返回相同的作者:

text Copy
css 引用节点: 10
xpath 作者: 10
第一位作者: 阿尔伯特·爱因斯坦
第一位标签: ['变化', '深思', '思考', '世界']

这就是整个爬虫:一次POST请求以获取并渲染,一次选择器进行查询。对于简单字段使用CSS,对于位置字段使用XPath — quote.xpath("...")是相对于每个节点运行的 — 这种模式使得爬虫在页面变得复杂时仍然可读。

在免费计划中获取您的API密钥:app.scrapeless.com

高级模式

  • 当CSS不够时,选择XPath。 通过文本选择(//a[contains(text(), "Next")])、按位置选择((//tr)[2])或通过轴选择(following-sibling::td)都是XPath的领域;CSS没有等效的方式。
  • 相对于节点链选择器。 quote.css(...)quote.xpath(".//...")都是针对该节点的范围 — XPath中的前导.保持相对,这是“此引用中的作者”和“页面上的所有作者”之间的区别。
  • 使用get(default="")来避免None sel.css("span.missing::text").get(default="")返回一个空字符串而不是None,这防止了在不均匀记录的循环中因为奇特页面而崩溃。
  • 使用::attr()@来提取属性。 sel.css("a::attr(href)")sel.xpath("//a/@href")都读取一个属性;使用选择器其他部分已经使用的语言。

故障排除

  • 从您在浏览器中可以看到的页面中获得零节点。 内容是JavaScript渲染的,而您的获取返回了预渲染的HTML。以已知选择器计数,第一脚本的方法;几乎为空的树是获取问题,用js_render修复,而不是选择器问题。
  • get()返回None 选择器没有匹配到任何内容。检查已渲染的标记,确认类或路径,并传递一个default以免后续代码因为缺失而崩溃。
  • XPath返回元素时您想要文本。 在路径中添加/text()或在::text CSS选择器上使用.get();裸元素路径返回节点,而不是它的字符串。
  • 相对XPath抓取整个页面。//开头的路径是绝对的,即使在节点上调用。用.前缀 — .//small — 将其范围限制到当前元素。

结论

parsel凭借其作为选择层的地位,能够使用两种方言:CSS适用于常见情况,XPath适用于CSS无法到达的情况,通过一个基于lxml的树。决定这一切是否可行的层是获取 — 第一个脚本的0与10的计数决定了这一点 — 一次服务器渲染的POST弥补了差距。将两者连接起来,演示页面的十个引用以干净记录的形式到达,无论哪种方式更易于阅读。

创建一个免费的Scrapeless账户以获取API密钥,开发者文档涵盖了unlocker.webunlocker参数。当您计划定期作业时,请查看Scrapeless定价

常见问题

问:parsel能否独立抓取网站?

不。parsel从您已经拥有的HTML中选择数据;它没有HTTP客户端,也不运行JavaScript。将其与获取层配对 — 此处为Scrapeless通用抓取API,它在服务器端渲染页面 — 然后parsel处理从返回的标记中提取数据。

问:parsel和Scrapy选择器有什么区别?
在实践中,parsel是Scrapy使用的选择器引擎,作为一个独立的库发布。如果您在Scrapy爬虫中使用过response.cssresponse.xpath,那就是parsel。直接使用它可以让您保持选择器API,而不必采用整个框架。

问:我应该使用CSS还是XPath与parsel?

根据需要使用两者。CSS在基于类和标签的选择中更简洁;XPath处理文本、位置或轴的选择,而CSS无法表达。parsel在同一树上运行两者,因此可以根据字段混合使用。

问:parsel能处理JavaScript渲染的页面吗?

不能——它从不执行脚本。如果内容在初始HTML加载后才出现,简单的获取会给parsel一个空树。首先通过Scrapeless API以js_render获取页面,然后从渲染的HTML中选择,就像本指南所做的那样。

问:使用parsel进行爬取合法吗?

选择器库不会改变收集规则。仅获取公共页面,遵守网站条款以及机器人排除协议标准化的爬虫指令,保持爬取量的边界,并根据适用的法律处理任何个人数据。

在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。

最受欢迎的文章

目录