Repository navigation
接通抓取节点试抓与配置生成 - #101
接通抓取节点试抓与配置生成#101zzsky233 wants to merge 1 commit into
Conversation
JunieXD
left a comment
There was a problem hiding this comment.
感谢!试抓从头到尾接通了:带配置的直接读,不带的先通用解析,取不到条目才让模型生成配置,生成的配置在同一份页面上再解析一遍,请求照样走 Fetcher 和浏览器会话。在云南大学那页上实测了生成和采用,采用后再试抓得到同样的 29 条、不再调用模型,这一步做得很扎实。schema.py 里 title 被当成 JSON Schema 关键字删掉的问题也确实存在,修得对。我们拉到本地跑了 ./check.sh,267 个测试全部通过;把共享并发上限、title 的修复、任务隔离、停止时等待分别撤掉,都有测试失败。
剩下的问题主要是多了一些到不了的检查,失败原因的写法和正式抓取不一样,另外文档写得太长。
必须改
-
生成配置直接用
Selectors做输出结构(extraction/probe.py:19):GeneratedConfig.force_http只能是false,为了它多了一条提示词和一个测试用例。改成:response_schema(Selectors)、Selectors.model_validate_json(...),返回ListConfig(selectors=...);- 删掉
GeneratedConfig、提示词里force_http must be false那一行,以及测「模型擅自降级 HTTP」的用例;提示词里的selectors.items等写成items。
我们看了
response_schema(Selectors)的结果:是一个普通对象,date、next是string或null的anyOf,没有$ref,你们遇到的那个 400 来自可空的$ref,这里不会出现。我们没有调真实的模型,改完请拿probe.html实际调一次,结果写在 PR 描述里。 -
读列表的失败原因照
list_items的写法(crawl/probe.py:25-62,对照crawl/listing.py:19-22、:48-49):- 配置不合法写
配置不对: <字段位置>,和正式抓取一样取error.errors(include_input=False)[0]["loc"]; parse_listing抛的ValueError写str(error)。Pydantic 的ValidationError也是ValueError,单独那个except ValidationError(「列表条目不符合接口要求」)删掉;- 没有条目写
StepError("no_items"),不带说明,和一轮的写法一样; - 包着
ProbeReport(...)的 try(「试抓结果不符合接口要求」)和detail[:1000]删掉:一页列表到不了 5000 条,一次试抓最多一条用量,正式一轮也不截断原因。
- 配置不合法写
-
模型这边去掉到不了的检查,原因写成英文错误码(
extraction/probe.py):prepare里包着parse_webpage的 try 删掉:probe_source已经用同一份body和content_type调过parse_listing,它第一步就是parse_webpage,解析不了的页面走不到这里;- 构造
Usage的 try(invalid_usage)删掉,和Extract一样直接构造:模型名 100 个字符、token 数 21 亿的上限,模型的响应不会超过; - 原因照正式抽取的写法「试抓失败: 英文错误码」:
试抓失败: page_too_large、试抓失败: missing_usage,后面不加中文说明。
-
模型调用的日志挪进
Responses.generate:并发许可挪进Responses.generate以后,extract.py:142和probe.py:69量的耗时把排队等许可的时间也算进去了。用假时钟试过:排队 100 秒、调用 1 秒,日志记的是 101 秒。改成在generate里拿到许可后计时,响应通过校验后记一条日志(模型、状态、输入、输出和缓存命中的 token、耗时),Extract和ProbeConfig里那两段一样的日志删掉。哪次调用属于哪一轮、哪次试抓,日志里的source_id、probe_id已经能分开。 -
入口的
_probe照_crawl写(main.py:98-108):- 记成
logger.exception("Probe failed"),带上堆栈,和「Round failed」一样。预期的失败在probe_source里都转成了报告,走到这里的只有 bug 和上报失败,没有堆栈查不出来;logger.exception(..., exc_info=False)其实等于logger.error,读起来也容易误会。 current_probe.set(probe.id)挪到probe_source开头,像crawl_source设crawl_round那样,去掉token和reset:每个试抓在自己的 task 里跑,上下文不会串,_probe记的日志也带得上probe_id。- 入口测试里断言日志不含异常文字的部分改成断言「Probe failed」那条带
probe_id和堆栈,参照test_a_failing_round_leaves_the_others_and_stopping_waits_for_them。开发约定 §4 新加的那条改成「试抓的日志带probe_id」。
- 记成
-
文档只写现状和决定:
-
总体设计 §8.5 新加的「试抓配置生成实测」整节删掉:那是这个 PR 的验证记录,里面还有排错的过程(「修正……后」「首次定位问题的两次请求」)。§8.5 记的是选模型和门槛的实验,验证结果放在 PR 描述里就行,描述里已经有了。
-
开发约定 §2.2 只留规则,测试怎么写、Schema 转换的细节、「页面只是待分析的材料」这类 AGENTS.md 已经写了的,都不用重复。代码按第 1 条改完以后,可以写成:
### 2.2 试抓 - 只读列表第一页,不往回翻、不读正文和附件;结果交 `ProbeReport`,不写抓取记录。 - 带了配置就按配置读,空对象也算配置;没带配置先通用解析,取不到条目才让 LLM 按 `Selectors` 生成选择器,在同一份页面上再解析一遍,取到条目才交回生成的配置。 - `browser` 和 `force_http` 由管理员定,模型只生成选择器。 - 失败原因和一轮的写法相同,模型这一步写成「试抓失败: 错误码」;调用过模型的,失败时也交用量。 - 发给模型的 HTML 只清空脚本和样式的内容,元素的层级和位置不变,位置 XPath 照样能用;超过 20 万字符的页面直接失败,不截断。
-
-
删掉手工演示用的页面:
page2.html、notice/101.html至104.html、department/a/index.html、department/b/index.html没有测试读取,是为了手工翻页、点开正文搭的演示站,超出了 #46 的范围(试抓只读第一页,验收用保存的页面和固定的模型响应)。这七个文件删掉;README.md新加的两段只留第一句,也就是说明probe.html的那句。probe.html里指向page2.html的下一页链接留着没关系,测next选择器用不着文件真的存在。
建议一起改
- 按上面改完,为删掉的检查写的测试用例跟着删掉。
说明
- 云南大学那页的日期:年份写在分组标题里、行内只有日和中文月份,这是读列表的一种通用能力,不在这个 PR 里做,我们记到路线图上另做。
- 通用解析有结果但选错了栏目:照现在的做法交给管理员预览判断,和 #46 商定的一样。
- 改完转为待审,或者在这里说一声,我们就复审。推了新提交后 CI 要我们再批准一次才会跑,这是 fork 来的 PR 的正常情况。
关联
Closes #46
改了什么
管理员发起试抓后,抓取节点现在会处理
Work.probes,将条目、可采用的配置、失败原因和模型用量交回网站。title的问题,补充固定响应测试和完全虚构的本地示例页面。怎么验证的
./check.sh全部通过:生成类型一致性、格式、lint、Pyright 和 267 个测试。pnpm check全部通过:类型、lint、格式和 100 个测试;pnpm build、pnpm db:codegen --verify通过,重新生成 OpenAPI 后无差异。deepseek-flash:通用解析为空,生成配置后标题、链接、日期和下一页地址符合预期。此项页面获取和网站上报使用固定响应。已知限制与验证边界
影响
LLM_API_KEY,没有新增环境变量或依赖。