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

pyquery网络爬虫:Python中的jQuery风格HTML解析

Alex Johnson
Alex Johnson

Senior Web Scraping Engineer

23-Jul-2026

TL;DR:

  • pyquery为Python提供了jQuery的API。 如果你知道$("div.quote").find("small.author").text(),那么你已经掌握了pyquery。
  • pyquery不执行抓取。 它封装了lxml用于解析和选择;它没有HTTP客户端,也不运行JavaScript。
  • 这个差异在一个脚本中显现。 对于一个JavaScript渲染的演示页面的普通GET请求,pyquery返回0个引用节点;而通过Scrapeless通用抓取API使用js_render的相同URL则返回全部10个引用。
  • 本指南中提取是真实的。 在线示例使用.items().find().text()从渲染的HTML中提取所有10个引用及其作者和标签。
  • 这两层保持分离。 Scrapeless负责抓取和渲染;pyquery负责选择。两者之间互不干扰。
  • 抓取侧免费开始。app.scrapeless.com 创建你的Scrapeless API密钥。

pyquery是什么,以及它不是什么

pyquery是一个Python库,它在lxml之上提供了jQuery风格的API。你将标记包装在一个PyQuery对象中—通常命名为d—然后使用前端开发者已经使用的相同调用进行选择和遍历:d("selector").find().eq().text().attr().items()。对于任何来自浏览器的人来说,这是从“我知道如何查询DOM”到“我可以用Python提取这个”的最短路径,因为它依赖于lxml,底层选择很快。

它只是一个解析和选择库,没别的。pyquery没有HTTP客户端,不保持会话,不运行JavaScript。给它一个字符串,它会构建一个可以查询的文档;如果你要求它抓取一个URL,尽管技术上它可以拉取一个,但它实际上使用普通请求并不进行渲染,这是处理任何通过脚本构建的页面的错误工具。因此,每个“pyquery网络抓取”设置由两个层次组成:一个返回真实渲染的HTML,另一个是从中选择的pyquery。本指南使用Scrapeless通用抓取API作为第一层。更广泛的Python网络抓取教程覆盖了周围的生态系统。

安装

pyquery和requests是整个工具链。编写本指南时的版本是pyquery 2.0.1:

bash Copy
pip install "pyquery==2.0.1" requests

将你的密钥存储在环境中,而不是源代码里:

bash Copy
export SCRAPELESS_API_KEY="sk_your_scrapeless_key"

获取值得解析的HTML

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

python Copy
# fidelity.py — pyquery所见:普通GET与服务器端渲染的对比
import os

import requests
from pyquery import PyQuery as pq

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

plain = requests.get(URL, timeout=60).text
print("普通GET字符数:", len(plain), "| 引用节点:", pq(plain, parser="html")("div.quote").length)

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("渲染字符数:", len(rendered), "| 引用节点:", pq(rendered, parser="html")("div.quote").length)

运行打印出现,对于普通抓取,引用节点为0,而对渲染页面为10:

text Copy
普通GET字符数: 5806 | 引用节点: 0
渲染字符数: 8940 | 引用节点: 10

渲染、解锁和代理路由都在那一次POST中发生——通用抓取API是抓取层,而渲染的标记是pyquery进行选择的基础。

使用jQuery API进行提取

拥有真实的HTML后,pyquery用jQuery的方式进行提取。.items()将选择转换为PyQuery对象的迭代器,.find()限制子选择器的作用域,.text()读取文本——选择器遵循W3C选择器规范,与jQuery使用的语法相同:

python Copy
# extract.py — 抓取渲染页面,然后使用jQuery API选择
import os

import requests
from pyquery import PyQuery as pq

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": "https://quotes.toscrape.com/js/", "js_render": True}},
timeout=120,
)
resp.raise_for_status()
d = pq(resp.json().get("data", ""), parser="html")

records = []
for quote in d("div.quote").items():
records.append({
"text": quote.find("span.text").text(),
"author": quote.find("small.author").text(),
"tags": [pq(tag).text() for tag in quote.find("a.tag").items()],
})
print("记录数:", len(records))
print("第一个作者:", records[0]["author"])
print("第一个标签:", records[0]["tags"])

Copy
实时运行选择了所有10条记录,标签和所有信息:

```text
记录数: 10
第一个作者: 阿尔伯特·爱因斯坦
第一个标签: ['变化', '深思', '思考', '世界']

这就是整个爬虫:一次POST请求以获取和渲染,一次PyQuery对象进行查询。.items()迭代器是值得记住的pyquery习惯——它将选择转换为每个记录对象,你可以在上面使用.find(),与jQuery的.each()直接对应。

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

高级模式

  • 始终使用.items()进行迭代。 直接循环遍历PyQuery选择将产生原始lxml元素,而不是PyQuery对象,因此.find()会失效。.items()为你提供每个对象的完整API。
  • 使用.attr()读取属性。 quote.find("a::attr(href)")不是pyquery语法;请使用quote.find("a").attr("href"),与jQuery完全一样。
  • 真实页面传递parser="html" 它选择lxml宽容的HTML解析器,可以处理真实网站发送的格式错误的标记;默认的可能比你想要的更严格。
  • 链式调用,不要重复查询。 d("div.quote").eq(0).find("small.author")将每一步的范围限制在最后一步,这样更快,更接近你已经熟悉的jQuery的阅读方式。

故障排除

  • 从可以在浏览器中看到的页面获取零节点。 内容是通过JavaScript渲染的,而你的获取返回的是预渲染的HTML。像第一个脚本一样计算已知选择器;几乎空的文档是抓取问题,通过js_render解决,而不是选择器问题。
  • .find()引发AttributeError 你直接迭代选择而不是使用.items(),因此得到了一个裸lxml元素。将循环切换为for x in sel.items():
  • .text()返回所有文本连接在一起。 pyquery连接后代文本。用更具体的选择器缩小范围,或使用.eq(0).text()读取单个节点。
  • 编码看起来不正确。parser="html"优先传递响应文本给pyquery;当解析器是HTML时,lxml读取文档声明的编码。

结论

pyquery赢得了作为选择层的地位,能够使用jQuery:使用你在浏览器中熟知的相同.find().text().items()调用,基于快速的lxml树。决定是否可以这样做的层是抓取——第一个脚本的0与10之比较量确定了这一点——并且一次服务器渲染的POST填补了这个差距。将两者连接起来,快照页面的十个引用以清晰的记录呈现,以你已经想到的方式进行选择。

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

常见问题

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

其实不能。pyquery可以拉取一个URL,但它是通过普通请求而没有JavaScript渲染,因此在现代页面上它从空的标记中选择。将其视为解析器:与抓取层配对——这里是Scrapeless Universal Scraping API,它在服务器端渲染页面——然后pyquery处理从返回的HTML中选择。

问:pyquery和jQuery一样吗?

它在Python中镜像jQuery的API——d("selector").find().text().attr().items()——但它在lxml的服务端运行,而不是在浏览器中,因此它从静态标记中选择,而不执行脚本或处理事件。选择语法可以转移;运行时则不可以。

问:pyquery还是BeautifulSoup?

偏好选择。pyquery的阅读方式类似于jQuery,如果你来自前端工作,它是一个自然的选择;BeautifulSoup则拥有更Pythonic的API。两者都解析相同的HTML,并且都需要一个单独的抓取层用于JavaScript渲染的页面。

问:pyquery处理JavaScript渲染的页面吗?
不单独使用——它从不执行脚本。如果内容在初始 HTML 加载后才出现,普通的 fetch 会给 pyquery 提供一个空文档。首先通过 Scrapeless API 使用 js_render 抓取页面,然后从渲染的 HTML 中选择,就像本指南所示。

问:使用 pyquery 抓取数据合法吗?

选择库并不改变收集规则。仅抓取公共页面,尊重网站条款和由 机器人排除协议 标准化的机器人指令,保持抓取量的界限,并根据适用的法律处理任何个人数据。

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

最受欢迎的文章

目录