JMESPath网络抓取:声明性查询JSON API
Advanced Data Extraction Specialist
TL;DR:
- jmespath 以声明方式查询 JSON。 一个表达式将嵌套的 API 响应重塑为扁平记录——没有循环,没有手动字典遍历。
- JSON API 是最干净的抓取目标。 许多网站从后端 JSON 端点构建其页面;获取该 JSON 数据,数据已结构化。
- jmespath 不执行获取操作。 它没有 HTTP 客户端,也不解析 HTML;你将解码后的 JSON 对象交给它,它会进行查询。
- 通过 Scrapeless 进行抓取,当 API 受到保护时。 实时运行通过 Scrapeless Universal Scraping API 获取了产品 API,然后使用 jmespath 选择、过滤和排序结果。
- 一行中进行过滤和投影。
products[?price < \50`].title返回了六个低于 $50 的产品;sort_by(products, &price)[0]` 返回了最便宜的产品。 - 抓取方面免费开始。 在 app.scrapeless.com 创建你的 Scrapeless API 密钥。
jmespath 是什么,以及它不是
jmespath 是一种用于 JSON 的查询语言。你编写一个描述你想要的形状的表达式,库会遍历文档并返回结果——投影从列表的每个元素中提取字段,过滤器仅保留匹配条件的元素,多重选择哈希将每个元素重建为更小的记录。它是 AWS CLI 用于其 --query 标志的相同表达语言,由 JMESPath 规范 标准化,并作为一个小型 Python 库提供。
它是一种查询语言,而不是爬虫。jmespath 没有 HTTP 客户端,不获取 URL,也不解析 HTML——它在你已经解码的 JSON 值上操作,由 JSON 数据交换标准 定义。因此,"jmespath 网页抓取" 设置包括两个层次:返回 JSON 的东西,以及重塑它的 jmespath。这很重要,因为现代网站上的大量数据是由页面在后台调用的后端 JSON API 提供的;直接访问该端点可以完全跳过 HTML 解析。当端点受到地理限制或限制速率时,本指南通过 Scrapeless Universal Scraping API 获取它。对于 HTML 抓取,Python 网页抓取教程 中介绍了选择器。
安装
jmespath 和 requests 是整个工具链。本指南编写时使用的版本是 jmespath 1.0.1:
bash
pip install "jmespath==1.0.1" requests
将你的密钥保存在环境中,绝不要在源代码中:
bash
export SCRAPELESS_API_KEY="sk_your_scrapeless_key"
通过 Scrapeless 获取 JSON API
获取层返回原始 JSON。由于端点提供 JSON,而不是渲染的页面,因此 js_render 保持关闭;Scrapeless API 处理请求、代理路由和端点前方的任何访问控制,并返回由 HTTP 语义标准 定义的正文。解码一次后,jmespath 接管:
python
# fetch.py — 通过 Scrapeless 拉取 JSON API,然后查询它
import json
import os
import jmespath
import requests
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://dummyjson.com/products?limit=10", "js_render": False}},
timeout=120,
)
resp.raise_for_status()
payload = json.loads(resp.json()["data"])
print("页面中的产品数量:", jmespath.search("length(products)", payload))
print("首个标题:", jmespath.search("products[0].title", payload))
print("总可用数量:", jmespath.search("total", payload))
运行没有一条循环就读取了响应的形状:
text
页面中的产品数量: 10
首个标题: Essence Mascara Lash Princess
总可用数量: 194
Scrapeless 调用是获取层——Universal Scraping API 返回 JSON 正文,payload 现在是 jmespath 可以查询的普通 Python 对象。
使用 jmespath 重塑和过滤
jmespath 的重点在于将冗长的响应转换为你想要的确切记录。使用多重选择哈希的投影重建每个产品;过滤表达式仅保留匹配项;sort_by 对它们进行排序——所有这些都是表达式,而不是过程代码:
python
# query.py — 在三个表达式中重塑、过滤和排序
import json
import os
import jmespath
import requests
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://dummyjson.com/products?limit=10", "js_render": False}},
timeout=120,
)
resp.raise_for_status()
payload = json.loads(resp.json()["data"])
records = jmespath.search("products[].{title: title, price: price, rating: rating}", payload)
under_50 = jmespath.search("products[?price < `50`].title", payload)
cheapest = jmespath.search("sort_by(products, &price)[0].{title: title, price: price}", payload)
print("记录数:", len(records))
print("第一条记录:", json.dumps(records[0], ensure_ascii=False))
print("低于$50的产品数:", len(under_50))
print("最便宜的产品:", json.dumps(cheapest, ensure_ascii=False))
每一行都是一个查询,完成循环的工作:
text
记录数: 10
第一条记录: {"title": "精华睫毛膏 公主假睫毛", "price": 9.99, "rating": 2.56}
低于$50的产品数: 6
最便宜的产品: {"title": "红色指甲油", "price": 8.99}
这就是整个提取器:一次 POST 请求获取 JSON,三条表达式进行整形。多重选择哈希 {title: title, price: price} 是工作马,它丢弃不需要的字段并重命名保留的字段,因此您存储的正是所请求的内容。
获取您的免费计划 API 密钥:app.scrapeless.com
高级模式
- 在投影之前过滤。
products[?rating > \4.5`].{title: title}` 首先保留匹配项,然后对其进行重塑;以这种方式排列管道可保持表达式的可读性和结果的简洁。 - 使用
[]扁平化嵌套列表。 当记录嵌套自己的列表时,products[].reviews[].rating将每个产品的每条评论评分扁平化为一个列表——扁平操作符的作用相当于双重循环。 - 用
|管道表达式。products | length(@)和sort_by(@, &price) | [0]将一个结果链接到下一个表达式;@是当前节点,这就是如何将一个查询的输出馈送到另一个查询。 - 防止缺少键。 jmespath 对不存在的路径返回
None而不是引发错误,因此只在某些记录中存在的字段不会导致查询崩溃——存储时检查None。
故障排除
json.loads在响应时引发异常。 端点返回了 HTML,而不是 JSON——通常是错误或阻止页面。确认 URL 是 JSON API 而不是调用它的 HTML 页面,并确保获取成功后再解码。- 投影返回空列表。 路径与文档形状不匹配。打印顶层键并逐级检查;JSON API 嵌套其数组在
products或results等键下,而不是在根级别。 - 过滤器匹配不到任何内容。 过滤器中的数字和字符串需要使用反引号——
price < \50`, 而不是price < 50`。没有反引号时,值被读取为字段名称。 - 结果保持了您不想要的字段。 您使用了无包装投影
products[]而不是多重选择哈希。添加.{title: title, price: price}以仅选择要保留的字段。
结论
jmespath 通过不使用过程代码的层,成功地将 JSON 响应转换为记录:投影、过滤和排序作为单一表达式。获取 JSON 的层是获取——后端 API 是最干净的来源,而一次 Scrapeless POST 在跨越任何防护后返回其主体。将两者连在一起,一个冗长的产品馈送变成了您实际存储的四个字段。
创建一个免费的 Scrapeless 帐户以获取 API 密钥, 开发者文档 涵盖了 unlocker.webunlocker 参数。当您计划定期作业时,请查看 Scrapeless 定价。
常见问题解答
问:jmespath 可以独立抓取网站吗?
不可以。jmespath 查询您已经拥有的 JSON 值;它没有 HTTP 客户端,也不获取 URL 或解析 HTML。将它与获取层配对——这里是 Scrapeless 通用抓取 API,返回 JSON 主体——并且 jmespath 将其重新整形为记录。
问:为什么抓取 JSON API 而不是 HTML 页面?
因为数据已经结构化地到达。许多页面从它们在后台调用的后端 JSON 端点渲染;访问该端点完全跳过了 HTML 解析和选择器维护,而 jmespath 将响应转化为您想要的记录。
问:jmespath 与 jsonpath 有什么不同?
两个查询 JSON,但 jmespath 有一个正式的规范和一个紧凑的表达语言,具有投影、过滤器、函数和重塑输出的多选哈希。它的多选语法——在查询中重命名和删除字段——使其成为提取的良好选择。
问:如果网站没有 JSON API,该怎么办?
那么就解析 HTML:通过 Scrapeless 获取渲染页面,并使用选择器库。jmespath 仅在源是 JSON 时适用;这两种方法涵盖了数据的两种形态。
问:抓取 JSON API 合法吗?
查询语言并未改变收集规则。仅抓取公共端点,遵循网站条款以及由 机器人排除协议 标准化的 robots 指令,保持抓取量在合理范围内,并根据适用的法律处理任何个人数据。
在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。



