Skip to main content

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 返回解码后的原始响应正文;通过 Search1API 时返回服务提供的 Markdown/文本
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。

成功抓取的原始页面及其响应元数据会按请求 URL 和抓取来源分别缓存。普通 URL 优先读取 直抓缓存,其次读取之前成功回退的 Search1API 缓存;命中后不会再次请求源站或 Search1API。 Search1API 缓存还按 API base 区分。HTML 解析和 LLM 提取仍会按每次调用执行。 缓存默认存放在 /tmp/my-readurl-kit/caches,也可以通过 MY_READURL_KIT_CACHE_DIR 修改位置:

export MY_READURL_KIT_CACHE_DIR="/path/to/readurl-cache"

缓存没有自动过期时间;需要重新抓取时,删除该目录中对应的缓存文件或清空缓存目录。 缓存文件损坏或缓存目录不可写不会阻止正常抓取。

Search1API 站点解析

设置 SEARCH1API_API_KEY 后,Search1API 会处理 Wikipedia/Wikimedia URL,以及文档列出的 专用解析 URL 模式:LinkedIn 公开公司、职位、文章和个人资料页;Reddit 帖子、社区和用户页; X 帖子和文章;YouTube 视频和播放列表;GitHub 仓库 README 和文件;Stack Exchange 网络问题页; Hugging Face 模型、数据集和 Space 页面;微信公众号文章;Hacker News 页面;npm/PyPI 包页面; Bilibili 视频和 CSDN 博客文章。仅匹配文档支持的主机与路径才会优先调用专用解析器; Wikimedia/Wikipedia 使用 Search1API 通用 crawl。其他 URL 先直接抓取;如果源站返回 HTTP 状态错误(包括 400、403)或发生连接、超时等网络错误,会回退调用一次 Search1API /crawl。无效 URL、响应过大、内容类型不支持不会触发回退。

未设置 key 时,所有 URL 都使用原有直接抓取,失败时保留原错误。Search1API 请求失败会作为 Search1APIError 返回,不会自动再请求源站或重试。服务默认启用其内部 fallback。 每次实际调用 /crawl 消耗 1 credit;直抓失败后的回退调用也会消耗额度。发给 Search1API 的请求包含目标 URL,因此目标 URL 会由 Search1API 处理。 参考 额度说明 和 隐私政策。

export SEARCH1API_API_KEY="your-search1api-api-key"
# 可选;默认是 Search1API 官网 API 地址
export SEARCH1API_API_BASE="https://api.search1api.com"

SEARCH1API_API_BASE 和 Search1APIConfig(api_base=...) 都可用于切换兼容的 API 地址;默认值为 https://api.search1api.com。也可以在 Python API 中传入 Search1APIConfig。 search1api_client= 是单独注入的 httpx.AsyncClient,不会复用源站的 client=、请求头或 Cookie:

import asyncio

from my_readurl_kit import Search1APIConfig, read_url


async def main() -> None:
    result = await read_url(
        "https://www.reddit.com/r/python/comments/example/",
        search1api_config=Search1APIConfig(
            api_key="your-api-key",
            api_base="https://api.search1api.com",
        ),
    )
    print(result.content)


asyncio.run(main())

服务返回 Markdown/文本时,text、body 和 relevant 模式会直接使用该文本,不会按 HTML 解析;raw 返回该内容本身,不保证是 HTML。详细 URL 支持范围以 Search1API 专用解析器文档 为准。

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
read-curl "https://example.com" --log-level debug

--log-level 支持 debug、info、warning、error 和 critical,默认为 warning。日志写入标准错误,不会混入页面内容所在的标准输出。

CLI 启动时会从当前工作目录加载 .env;已经存在于进程环境中的变量优先级更高。 Python API 不会自动加载 .env,需要调用方自行设置环境变量或传入配置对象。

body 和 relevant 会在发起网页请求前从四个 LLM_EXTRACT_* 环境变量 构建并校验 LLMConfig。这些变量缺失、不完整或无效时,命令会向标准错误 输出错误并以非零状态退出。raw 和 text 不读取或要求这些变量。

uv run read-curl \
  "https://example.com/article" \
  --output-format body

uv run 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
└── search1api.py     # Search1API 路由与 crawl 集成

后续的协议入口会继续按上述边界拆分为独立模块。

开发环境

要求:

  • 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)'

项目默认不要求为改动新增单元测试;需要验证行为时,应在变更说明中提供简短、 可复现的手工测试步骤。只有在明确要求时才新增单元测试。

初步迁移顺序

  1. 稳定当前 Python API 和错误模型(已完成)。
  2. 在核心 API 之上增加 CLI(已完成)。
  3. 后续增加 MCP server 和 HTTP server。

Metadata

Release files for my-readurl-kit 0.0.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for my-readurl-kit 0.0.6
File Size Uploaded
my_readurl_kit-0.0.6.tar.gz 132.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for my-readurl-kit 0.0.6
File Interpreter ABI Platform
my_readurl_kit-0.0.6-py3-none-any.whl Python 3 none any Details

Total release size: 155.6 kB

Release files / my_readurl_kit-0.0.6.tar.gz

Download URL my_readurl_kit-0.0.6.tar.gz
Size 132.0 kB
Tags Source
SHA-256 checksum
How to use checksums
3bfe665b4eca61bf153ac278bcfe575ac728293f1da1acfe2399633c9daa732d
BLAKE2b-256 checksum
How to use checksums
da061c2ccec1cd614d749ad224bd4bdfd7cda3d65bd4ea4a09a55187544552f1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / my_readurl_kit-0.0.6-py3-none-any.whl

Download URL my_readurl_kit-0.0.6-py3-none-any.whl
Size 23.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e4fc27d1c53b5d4786601bba3a510105a08a4ee5d427f599a97bc0b8b56a3c81
BLAKE2b-256 checksum
How to use checksums
3adbc0e97055a8d9f7ddd4aa21cc36f1b4ebd87abd56b87dcaac1519bfb959db
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.0.9

2 release files

0.0.8

2 release files

This release

0.0.6 This release

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page