my-readurl-kit
my-readurl-kit 是一个独立的 Python 网页读取与内容提取库。当前的 read_url
实现迁移自 gede/gede/llm/tools/read_url_tool.py,但不依赖 gede 本身。
目标
- 获取给定 URL 的页面内容,支持返回原始 HTML 或可读文本。
- 通过
my-llmkit非流式调用 LLM,提取正文或与查询相关的内容。 - 同一套核心能力既可作为 Python 函数库使用,也可通过 CLI、MCP server 和 HTTP server 调用。
- 将抓取、解析、LLM 提取和传输协议分层,避免任一入口绑死核心库。
当前 Python API
网络 I/O 使用异步 API,成功结果通过 ReadUrlResult 返回;请求、状态码、响应
大小和内容类型错误会抛出 ReadUrlError 的具体子类,不会伪装成普通内容字符串。
import asyncio
from my_readurl_kit import read_url
async def main() -> None:
result = await read_url("https://example.com")
print(result.content)
print(result.final_url, result.status_code)
asyncio.run(main())
output_format 支持以下取值:
| 值 | 行为 |
|---|---|
raw |
返回解码后的原始响应正文,适用于 HTML、JSON 和 JavaScript |
text |
使用 BeautifulSoup 将 HTML 转为规范化纯文本,也是默认值 |
body |
通过 LLM 提取正文并剔除作者来源、推荐等非正文内容 |
relevant |
通过 LLM 提取与 query 直接相关的完整原文段落 |
relevant 传入非空 query 时提取相关段落;未传入或仅传入空白 query 时等同于
body。其他模式会忽略 query。body 和 relevant 只有在实际使用时才需要
LLM 配置;raw 和 text 不会创建或调用模型客户端。
调用方可以注入 httpx.AsyncClient、请求头和超时。默认跟随最多 10 次重定向,
接受文本、JSON、JavaScript 或 XML 响应,并将解压后的响应正文限制为 5 MiB。
LLM 配置
LLMConfig 接收 API key、API base URL、模型名称和客户端类型。
客户端支持 openai_compatible 和 claude,分别对应 my-llmkit 的
OpenAICompatibleChatCompletion 和 ClaudeChatCompletion。
未显式传入 llm_config 或 llm_client 时,body 和 relevant 会自动
读取以下环境变量:
export LLM_EXTRACT_API_KEY="your-api-key"
export LLM_EXTRACT_API_BASE="https://api.openai.com/v1"
export LLM_EXTRACT_MODEL="gpt-4.1-mini"
export LLM_EXTRACT_CLIENT="openai_compatible"
四个变量需要同时配置。库本身不保存密钥,raw 和 text 模式也不会
读取这些环境变量。
import asyncio
from my_readurl_kit import read_url
async def main() -> None:
body = await read_url(
"https://example.com/article",
output_format="body",
)
relevant = await read_url(
"https://example.com/article",
query="WebAssembly 的技术细节",
output_format="relevant",
)
print(body.content)
print(relevant.content)
asyncio.run(main())
高级调用方也可以通过 llm_client= 直接注入已构造的
my_llmkit.chat.LLMChatCompletion,此时不需要 llm_config,也不会创建
第二个模型客户端。也可显式构造 LLMConfig,或调用
LLMConfig.from_env() 读取上述变量。llm_config 与 llm_client 不能同时传入,
且它们的优先级都高于环境变量。
LLM 通过 LLMChatCompletion.run() 以非流式方式运行。未提供配置时,
body 返回 无法获取正文,relevant 返回 无相关内容。模型调用失败
或返回空内容时,body 回退到规范化后的网页全文,relevant 返回
无相关内容。
CLI
安装项目后可以使用 read-curl 读取网页。URL 是必需的位置参数,
--output-format 支持与 Python API 相同的 raw、text、body 和
relevant,默认值为 text。成功时标准输出只包含读取到的内容,便于通过
管道继续处理或重定向到文件。
read-curl "https://example.com"
read-curl "https://example.com" --output-format raw
body 和 relevant 会在发起网页请求前从四个 LLM_EXTRACT_* 环境变量
构建并校验 LLMConfig。这些变量缺失、不完整或无效时,命令会向标准错误
输出错误并以非零状态退出。raw 和 text 不读取或要求这些变量。
uv run --env-file .env read-curl \
"https://example.com/article" \
--output-format body
uv run --env-file .env read-curl \
"https://example.com/article" \
--output-format relevant \
--query "WebAssembly 的技术细节"
--query 在 --output-format relevant 模式下用于提取相关段落;未提供或仅包含
空白时,relevant 等同于 body。其他模式会忽略它。
LLM 模式会发起外部模型请求,可能产生费用。
计划中的其他入口
| 入口 | 用途 |
|---|---|
| MCP server | 向支持 MCP 的客户端暴露 read-url 工具 |
| HTTP server | 向其他进程或服务提供 HTTP API |
CLI、MCP 和 HTTP 层保持轻量,只负责参数转换、调用核心 API 以及输出结果。
设计边界
- 基础抓取与 HTML 解析不依赖 LLM,应可单独使用。
- LLM 提取建立在抓取与确定性文本解析结果之上,并通过
my-llmkit接入模型。 - 网络 I/O 以异步 API 为核心;如需同步 API,应由薄封装提供。
- 公共 API 使用明确的类型标注和结构化结果,区分成功结果与请求、解析、提取错误。
- 不在核心库中保存 API key 或其他密钥;配置由调用方显式传入或从
LLM_EXTRACT_*环境变量读取。
当前核心包结构:
src/my_readurl_kit/
├── cli.py # read-curl 命令行适配层
├── errors.py # 明确的错误类型
├── extract.py # 基于 my-llmkit 的非流式内容提取
├── fetch.py # 基于 httpx 的 HTTP 请求与响应处理
├── models.py # 公共结构化结果
├── parse.py # HTML 到可读文本的确定性转换
└── reader.py # read_url 高层 API
后续的协议入口会继续按上述边界拆分为独立模块。
开发环境
要求:
- Python 3.10 或更高版本
uv
安装项目及开发依赖:
uv sync
运行静态检查:
uv run pyright
运行不包含 LLM 的真实网站集成测试(会向微信和 Orchid Files 发起外部网络请求):
uv run pytest -m "network and not llm"
测试 body 模式(启动时从 .env 注入 LLM_EXTRACT_* 变量):
uv run --env-file .env pytest -s tests/test_read_url_network.py::test_read_url_body_with_llm
测试 relevant 模式(启动时从 .env 注入 LLM_EXTRACT_* 变量):
uv run --env-file .env pytest -s tests/test_read_url_network.py::test_read_url_relevant_with_llm
两个 LLM 测试都会抓取 Orchid Files 的公开文章并发起一次非流式模型
请求。测试函数会通过 LLMConfig.from_env() 构造配置,并将它显式传给
read_url(llm_config=...)。测试会输出提取结果,也可能产生模型调用费用。未注入完整环境
变量时,测试会显式跳过,不会误用回退文本当作模型输出。
构建发行包:
uv build
当前实现可用以下方式手工检查(会发起外部网络请求):
uv run python -c 'import asyncio; from my_readurl_kit import read_url; print(asyncio.run(read_url("https://example.com")).content)'
项目默认不要求为改动新增单元测试;需要验证行为时,应在变更说明中提供简短、 可复现的手工测试步骤。只有在明确要求时才新增单元测试。
初步迁移顺序
- 稳定当前 Python API 和错误模型(已完成)。
- 在核心 API 之上增加 CLI(已完成)。
- 后续增加 MCP server 和 HTTP server。
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file my_readurl_kit-0.0.3.tar.gz.
File metadata
- Download URL: my_readurl_kit-0.0.3.tar.gz
- Upload date:
- Size: 123.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.8.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f6b2811890f9a9832dffe64b96b42f76d61b8d3c4b164e2f8644c8e46ac831d
|
|
| MD5 |
f5941eec4915b8744a97c1769933478f
|
|
| BLAKE2b-256 |
14e243322c3822da12a62a653bee1e76bb6efe738bc571df8cc499c87e7a8d5d
|
File details
Details for the file my_readurl_kit-0.0.3-py3-none-any.whl.
File metadata
- Download URL: my_readurl_kit-0.0.3-py3-none-any.whl
- Upload date:
- Size: 14.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.8.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ad56dc72e7bb13e2f2f71e130673b24a82821ef8138889d4d7ed67e59f49b92d
|
|
| MD5 |
080249c57d9fc0b25c287c58663a7739
|
|
| BLAKE2b-256 |
5d4b182a71e37c563bff1c93917a553676d0e826cf3b2d8f96b6d193149e3a55
|