Skip to main content

lftr

独立的异步 Python LOFTER 抓取与解析库,Python 3.10+。安装名为 lftr,Python 导入名为 lofter。

这是非官方客户端,使用 LOFTER 网页及内部 DWR 标签搜索接口,与 LOFTER 官方无隶属关系。不依赖 AstrBot,也不包含订阅数据库、消息发送或统计条件管理。

安装

PyPI 安装名为 lftr。首次发行成功后可使用:

python -m pip install lftr

安装名与导入名不同:代码仍使用 from lofter import LofterClient,不要执行 pip install lofter。

从有访问权限的 GitHub 仓库安装开发版本:

python -m pip install "git+ssh://git@github.com/wdcyxxycdw/lofter.git@main"

正式接入其他项目时,建议将 main 换为已经验证的完整提交号。

从源码开发或构建 wheel:

uv sync --locked
uv build
python -m pip install dist/lftr-0.1.0-py3-none-any.whl

快速开始

import asyncio
import os

from lofter import LofterClient


async def main():
    async with LofterClient(os.environ["LOFTER_COOKIE"]) as client:
        posts = await client.fetch_tag_posts("原创", limit=20)
        for post in posts:
            print(post.post_id, post.title, post.url)


asyncio.run(main())

async with 会复用 HTTP 连接并在结束时关闭。也可手动调用 await client.close()。单个客户端的请求起始时间至少间隔 0.3 秒;连接错误、超时及 HTTP 429/500/502/503/504 最多尝试 3 次,其他 HTTP 错误直接抛出。

结构化抓取

方法 返回值 范围
fetch_tag_posts(tag, offset=0, limit=20, before=0) list[Post] 一页标签作品
fetch_tag_posts_paged(tag, total=...) list[Post] 最多请求 100 条,按 ID 去重
fetch_post(url) Post HTTPS 博主单帖详情
fetch_blog_posts(username, enrich=False) list[Post] 博主首页链接,可选逐篇补充详情
enrich_posts(posts, continue_on_error=True) list[Post] 按原顺序补充详情,保留原 post_id

分页示例:

first = await client.fetch_tag_posts("原创")
if first:
    before = min(post.publish_time_ms for post in first)
    second = await client.fetch_tag_posts("原创", offset=20, before=before)

分页同时使用位置 offset 和上一页最早的毫秒时间戳 before。包不维护订阅游标,不执行“补抓到上次已知帖子”的业务策略,不保证全量枚举,也不保证指定 total 条都为不同作品;可见范围受账号权限、接口分页和平台风控影响。

enrich_posts 默认在单篇失败时记录 warning 并保留原帖,不代表所有详情均成功。需要严格验证时传 continue_on_error=False。fetch_post 对无正文、摘要和图片的页面抛出 RuntimeError,不会把这样的登录页当成成功的帖子。

底层与离线解析 API

为便于迁移已有调用方,保留:

  • client.get(url, timeout=15):原始 HTML/text。
  • client.search_tag(tag, offset=0, limit=20, before=0):原始 DWR 文本。
  • client.search_tag_paged(tag, total):原始 DWR 分页文本列表,最多请求 100 条。
  • client.update_cookie(cookie):更新后续请求使用的 Cookie。

纯解析函数无需联网:

from lofter import (
    Post,
    build_tag_search_body,
    extract_lofter_username,
    parse_blog_posts,
    parse_dwr_response,
    parse_post_page,
)

Post 保留原插件的 11 个字段:post_id、title、summary、images、author、author_username、url、tags、publish_time、content、publish_time_ms。publish_time 仍使用本地时区格式化;需要机器游标时应使用 publish_time_ms。

HTML 纯解析函数保留原行为:不包含可识别作品的博主页可能返回空列表,单帖解析可能返回空字段;它们不是账号登录状态检测器。HTML 主题差异、登录墙、风控页或站点改版可能导致无法解析。DWR 解析会区分合法空列表、服务端异常、无效结构与非 DWR 响应。

凭据与边界

  • 只使用自己有权访问的内容和 Cookie;本包不自动登录或绕过访问限制。
  • Cookie 放在进程环境或仅本机可读、被 Git 忽略的 .env.test 中,不写入源码、日志、fixtures 或命令历史。
  • 低层 get() 会向传入的 URL 发送 Cookie;仅向可信目标调用它。优先使用校验目标格式的高层抓取方法。
  • DWR 解析会用 dukpy 执行响应脚本,只应处理可信来源的 LOFTER 响应,不应当作通用的不可信 JavaScript 沙箱。
  • 图片字段是 URL,不下载图片文件;不包含移动端 API,也不承诺作品总量统计。

测试

默认只运行合成样本与本地 HTTP/TLS 服务,不访问真实 LOFTER:

uv run --locked pytest -q

显式开启真实网络测试:

cp .env.test.example .env.test
# 仅在本机填写 Cookie 和至少有两页作品的标签
uv run --locked pytest -q --live tests/test_live.py

--live 要求 LOFTER_COOKIE 和 LOFTER_TAG;缺少时直接报错,不静默跳过。LOFTER_POST_URL 和 LOFTER_BLOG 是可选的已知样本覆盖,未填写时会从标签搜索结果自动选择单帖 URL 和博主用户名。在线测试会验证真实 DWR 标签分页、HTML 单帖解析、博主主页解析和至少一篇详情 enrich;只请求 LOFTER,不调用 AstrBot 或 QQ。

GitHub Actions 的 Live LOFTER E2E 工作流只支持手动触发,使用仓库 Secret LOFTER_COOKIE 和手动输入的标签,不会在普通 push/PR CI 中运行。 CI 在 Python 3.10 和 3.13 上运行离线测试、构建 sdist/wheel,再将 wheel 安装至仓库外的干净虚拟环境重新运行测试,验证没有依赖原插件或源码路径。

发布到 PyPI

发布工作流为 .github/workflows/publish.yml,仅在 GitHub Release 正式发布时触发,不在提交或 PR 时上传。

在 PyPI 的账户 Publishing 页面添加 pending trusted publisher:

字段 值
PyPI project name lftr
Repository owner wdcyxxycdw
Repository name lofter
Workflow filename publish.yml
Environment 留空

工作流合并到 main 后,为对应提交创建 v0.1.0 Release。工作流会检查标签与 pyproject.toml 的版本一致,运行离线测试、构建 lftr 的 wheel/sdist,再通过 OIDC Trusted Publishing 上传。构建任务没有发布权限,发布任务不需要长期 API Token 或 LOFTER Cookie。

发布会公开安装包及其中的源码;GitHub 仓库本身无需改为公开。PyPI 的同名版本文件不能覆盖,后续发行必须更新版本及锁文件,再创建对应的新 Release。

来源与许可证

提取自 wdcyxxycdw/astrbot_plugin_lofter,基准提交 3606b03e4c1689fd5bbff87b3b857a5ba2674a3f。原插件 metadata 标注作者为 yamanashi,保留原作者及贡献者归属,详见 NOTICE。

沿用原项目 README 中的 AGPLv3 声明,本包为 AGPL-3.0-only,完整文本见 LICENSE。本次独立包提取不修改原插件的部署或数据库;插件侧依赖迁移另行进行。

Metadata

Release files for lftr 0.1.0

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

Source distribution (sdist)

Source distribution for lftr 0.1.0
File Size Uploaded
lftr-0.1.0.tar.gz 28.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lftr 0.1.0
File Interpreter ABI Platform
lftr-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 55.0 kB

Release files / lftr-0.1.0.tar.gz

Download URL lftr-0.1.0.tar.gz
Size 28.0 kB
Tags Source
SHA-256 checksum
How to use checksums
64918d0dfc1c55e01fabfac76170cbd4bd9f07180d746fafd0672a0af3d92094
BLAKE2b-256 checksum
How to use checksums
fa0e5579d51b0c06057e617cf9bce27d21ab144c9c96d35b03376e5b2b45b864
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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 / lftr-0.1.0-py3-none-any.whl

Download URL lftr-0.1.0-py3-none-any.whl
Size 27.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bf82792fd8476c09d1ebf7dcd0311924db285d73a712490cb3af0819d0332417
BLAKE2b-256 checksum
How to use checksums
892cddff23699202db93bfea5943220b9eae1a7c0662e4bc9c12f81e1742254f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","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.2.0

2 release files

This release

0.1.0 This release

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