Mistral 网络抓取:读取隐藏在 HTML 属性中的数据
Scraping and Proxy Management Expert
TL;DR:
- Mistral读取的数据并不在可见文本中,实时运行证明了这一点。 给定一个渲染过的笔记本电脑目录页面,
mistralai/ministral-3b-2512返回了6个产品的完整、未截断的产品名称和一个数字评分——这两个值只存在于HTML属性中,而不在读者看到的文本中。 - 当前最便宜的Mistral层是专用的小型号系列,而不是缩小版的旗舰产品。 Ministral 3是Mistral专为高效而设计的家族;本指南检查了实时OpenRouter目录以获取当前最便宜的入口,而不是凭记忆重用旧的“Mistral 7B”名称。
- 该模型仍然无法抓取。 渲染、会话和访问挑战属于抓取层;本指南的每个页面都是通过Scrapeless Universal Scraping API进行一次POST请求。
- 仅属性数据是一个真正的提取陷阱。 页面可见的链接文本可以显示为“
Packard 255 G2...”,而真实的产品名称则完整地存在于几个字符远的title属性中——本指南的架构和提示指定了确切的读取位置。 - 还没有Mistral密钥?相同的请求可以通过OpenRouter运行。 本指南在
mistralai/ministral-3b-2512上实时执行,显示了捕获的输出。 - 抓取方面免费开始。 在 app.scrapeless.com 创建您的Scrapeless API密钥。
Mistral能抓取网站吗?
Mistral进行提取,而不是收集。发送页面文本和架构,它会返回您命名的字段——费用低廉,因为该模型家族的小层价格属于当前API中最低的。它无法执行的是检索URL、执行组装页面的JavaScript,或管理会话和抓取量的访问挑战。那些工作属于抓取层,故意与模型分开。
这是一个真实开放的表面:该网站上没有早期文章详细介绍如何将Mistral与实时抓取层配对进行网页提取。至于选择哪个模型家族的问题,LLM抓取器说明和LLM抓取器排名列表涵盖了这一类别。
安装
官方的Mistral SDK涵盖了原生路径,openai覆盖了OpenRouter路径,而requests涵盖了抓取层:
bash
pip install "mistralai==2.7.2" "openai==2.48.0" requests
配置
bash
export MISTRAL_API_KEY="your_mistral_key"
export SCRAPELESS_API_KEY="sk_your_scrapeless_key"
抓取真实数据隐藏在属性中的页面
演示目标是一个公共笔记本电脑目录沙盒,那里可见的产品链接被截断("Packard 255 G2..."),但完整名称在同一链接的title属性中,并且星级评分不是来自图标标记,而是来自包装元素上的data-rating值。对Universal Scraping API的一次POST请求返回渲染后的页面:
python
# fetch_laptops.py — 一次POST返回渲染的笔记本电脑目录页面
import os
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://webscraper.io/test-sites/e-commerce/static/computers/laptops",
"method": "GET",
"js_render": True,
},
},
timeout=120,
)
resp.raise_for_status()
html = resp.json().get("data", "")
print(f"抓取了 {len(html):,} 个字符")
print("产品卡片:", html.count('class="card thumbnail"'))
with open("page.html", "w", encoding="utf-8") as f:
f.write(html)
该运行从一个公共抓取练习网站上的实时目录页面打印出6个产品卡片——这是一个小巧、刻意限制的页面,而不是同一域名还提供的全多百项清单。
基本实施:将Mistral作为提取器
官方 SDK 的 chat.complete 方法采用 response_format={"type": "json_object"},该内容在 Mistral 的 JSON 模式参考 中有文档说明。当前最便宜的 Mistral 版本是 ministral-3b-2512——这是 Ministral 3 系列中最小的模型,而不是在旧教程中仍然流传的旧 Mistral 7B 名称。
注意:此模块需要一个带有信用的
MISTRAL_API_KEY——这是本指南未假定的唯一前提条件。下一部分通过 OpenRouter 运行相同的提取,并捕获输出。
python
# extract_mistral.py — 原生 Mistral 提取(需要 MISTRAL_API_KEY)
import json
import os
from mistralai.client import Mistral
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
page_html = open("page.html", encoding="utf-8").read()
response = client.chat.complete(
model="ministral-3b-2512",
temperature=0,
max_tokens=2000,
response_format={"type": "json_object"},
messages=[
{
"role": "system",
"content": '提取每一款笔记本电脑。仅用 JSON 回复: '
'{"laptops":[{"title":str,"price_usd":number,"rating":int,"review_count":int}]}. '
"title 是链接的 title 属性中的完整产品名称,而不是截断的可见文本。 "
"rating 为 1-5,从 data-rating 属性中读取。",
},
{"role": "user", "content": page_html},
],
)
data = json.loads(response.choices[0].message.content)
print(f"提取了 {len(data['laptops'])} 款笔记本电脑")
没有预填充技巧,也没有单独的模式对象——仅 response_format 就足以处理 Mistral 的 JSON 模式。
没有 Mistral 密钥?通过 OpenRouter 运行
由于这两种接口都遵循相同的 JSON 模式合同,因此聚合器变体仅因客户端和基础 URL 而有所不同。这是本指南实际执行的版本,用一个自包含的脚本进行提取:
python
# extract_openrouter.py — 通过 OpenRouter 执行相同的提取
import json
import os
import requests
from openai import OpenAI
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://webscraper.io/test-sites/e-commerce/static/computers/laptops",
"method": "GET",
"js_render": True,
},
},
timeout=120,
)
resp.raise_for_status()
page_html = resp.json().get("data", "")
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=os.environ["OPENROUTER_API_KEY"])
completion = client.chat.completions.create(
model="mistralai/ministral-3b-2512",
temperature=0,
max_tokens=2000,
response_format={"type": "json_object"},
messages=[
{
"role": "system",
"content": '提取每一款笔记本电脑。仅用 JSON 回复: '
'{"laptops":[{"title":str,"price_usd":number,"rating":int,"review_count":int}]}. '
"title 是链接的 title 属性中的完整产品名称,而不是截断的可见文本。 "
"rating 为 1-5,从 data-rating 属性中读取。",
},
{"role": "user", "content": page_html},
],
)
data = json.loads(completion.choices[0].message.content)
print(f"提取了 {len(data['laptops'])} 款笔记本电脑")
for row in data["laptops"]:
print(json.dumps(row, ensure_ascii=False))
实时运行返回了所有 6 款笔记本电脑,每个字段与源页面完全匹配:
text
提取了 6 款笔记本电脑
{"title": "Packard 255 G2", "price_usd": 416.99, "rating": 2, "review_count": 2}
{"title": "Aspire E1-510", "price_usd": 306.99, "rating": 3, "review_count": 2}
{"title": "ThinkPad T540p", "price_usd": 1178.99, "rating": 1, "review_count": 2}
{"title": "ProBook", "price_usd": 739.99, "rating": 4, "review_count": 8}
{"title": "ThinkPad X240", "price_usd": 1311.99, "rating": 3, "review_count": 12}
{"title": "Aspire E1-572G", "price_usd": 581.99, "rating": 1, "review_count": 2}
每个标题、价格、评分和评论数量都与源标记一一对应——模型从 title 属性中读取完整名称,而不是截断的链接文本,并从 data-rating 属性中读取,而不是计算图标标记。整个调用:28,797 个令牌,几乎全部为输入,因为抓取的页面携带着一整页的导航和脚本信息,围绕六个小产品卡片。
获取您的 API 密钥在免费计划上:app.scrapeless.com
高级模式
- 准确说明一个值在哪里,当它不在可见文本中时。 “从
title属性读取完整标题”和“从data-rating读取评分”是在这里产生正确输出的方式——一个仅仅说“提取标题”的提示会诱使模型返回它可以看到的截断字符串。 - 关注输入与输出的令牌比例。 这次运行在输入上耗费了 28,554 个令牌,而输出仅为 243 个——页面的周围元素,而不是六条记录,主导了费用。在发送之前修剪到产品网格容器会大幅减少这一点,而不会损失任何可提取内容。
- 提取时将温度保持为零。 Ministral 模型在结构化任务中对
temperature=0反应良好;任何更高都会重新引入改写风险,这会破坏诸如标题属性等确切匹配字段。 - 将较大的 Mistral 级别保留给真正困难的页面。 3B 级别在这里以零错误读取属性编码数据;仅在特定字段不断错误返回的情况下才升级,而其他输入是干净的。
故障排除
- 标题返回时被截断,仅匹配可见文本。 提示没有说明要读取属性。请精确命名来源(“链接的
title属性”),而不仅仅是字段。 - 评分错误或都相同。 值通常位于属性(
data-rating)中,而不是可计数的图标或文本中的纯数字——请说明它所在的位置,就像本指南的提示所做的那样。 - 笔记本电脑数量少于页面实际拥有的数量。 请先检查获取的 HTML 的卡片数量,就像上面的获取脚本所做的那样——短获取是渲染问题,而不是提取提示可以解决的。
- 输出在相同运行之间变化。 设置
temperature=0;提取是一项转录任务,而不是生成任务。
结论
Mistral 当前最便宜的级别干净地处理了一个真正的陷阱:一个截断的可见标题,一个隐藏在数据属性中的评分,它都仅用 response_format 标志和两句话告诉它真实值在哪里就正确读取了。模型仍然不能做的是首先获取该页面——通过专用的获取层进行的一个 POST 提供了 HTML,而上面的六行模式将其转换为带有选择器代码的键入记录。
准备好为 Mistral 提供真实页面?
此处的获取层是 Universal Scraping API——计划和请求量在 定价页面上,每个 unlocker.webunlocker 参数在 开发者文档中。您可以在 app.scrapeless.com 的免费计划上创建密钥,两个脚本按原样运行。
常见问题
问:Mistral 能否自行抓取网站?
不能。Mistral API 从您提供的文本中提取;它不能发出 HTTP 请求,渲染 JavaScript,或保持会话。每个正常工作的 Mistral 抓取设置都将其与返回忠实页面内容的获取层配对。
问:我应该选择哪个 Mistral 模型进行网络抓取提取?
ministral-3b-2512——Mistral 3 小模型系列中当前最便宜的级别,检查了实时的 OpenRouter 目录,而不是较旧的“Mistral 7B”名称。本指南中的实时运行使用了它,并返回了所有 6 条记录,每个字段与源页面完全匹配。
问:Mistral 如何处理隐藏在 HTML 属性中的数据,而不是可见文本?
当提示说明要查看的位置时,正确处理。本指南的系统消息为未截断的标题和数字评分命名了确切属性;没有该指令时,模型倾向于返回可视的截断文本。
问:通过 Ollama 本地使用的 Mistral 与 API —— 我该选择哪个来进行抓取提取?
指南中的 API 路径不需要本地 GPU 或模型下载,并在一次请求中返回结果;本地 Ollama 部署则以零每令牌成本和完整数据本地性交换了这种便利。两者将相同的获取-然后提取架构指向不同的执行环境——对于低量或突发工作选择 API,对于高量或数据敏感工作选择本地。
问:使用 Mistral 抓取是否合法?
提取层不更改收集规则。只获取公共页面,尊重网站条款和由机器人排除协议标准化的机器人指令,保持量的限制,并在适用法律下处理任何个人数据——此外,还有Mistral在模型方面的使用条款。
在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。



