朝鲜王朝实录(조선왕조실록)搜索技能实战:基于 k-skill 的 sillok_search.py 官方站点抓取方案
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
本技术指南围绕 k-skill 开源仓库中的joseon-sillok-search技能展开,讲解如何借助 @nomadamas/k-skill CLI 与随附的 sillok_search.py 脚本,直接读取韩国国史编纂委员会(국사편찬위원회)官方实录网站sillok.history.go.kr的公开 HTML,完成关键词检索、王名/年份过滤、结果摘要与文章详情(국역/원문)抽取。读完本文,你将掌握该技能的全部 CLI 参数语义、端到端工作流、底层 HTML 解析原理、分页与过滤算法,以及通过单元测试验证的工程细节,可直接复制命令用于真实的历史文献检索场景。
一、技能定位:面向 Agent 的官方实录检索器
joseon-sillok-search是 k-skill 技能集(仓库目录joseon-sillok-search/,同时以packages/k-skill-cli/skills/joseon-sillok-search/形式随 CLI 分发包捆绑)中的一个history 类别技能,其skill.json中的 profile 为lookup(查询类),面向ko-KR韩国语场景,版本标记为 v1。
它的核心定位在 instruction.md 中说得非常直白:不依赖任何第三方 API、不维护本地索引,而是直接对官方站点的「搜索结果显示页 HTML」和「文章详情页 HTML」进行抓取与解析。v1 的能力范围被刻意收敛为:
- 关键词检索(keyword search)
- 可选的按王名过滤(
--king) - 可选的按公元纪年过滤(
--year) - 整理检索结果的标题 / 摘要 / 原文链接
- 从文章详情页抽取 국역(韩文翻译)与 원문(汉文原文)摘要
该技能适合以下典型用户请求(文档「When to use」章节给出的示例):
- "조선왕조실록에서 훈민정음 찾아줘"(在实录里找训民正音)
- "세종 때 실록에서 측우기 관련 기사 검색해줘"(检索世宗时期实录中测雨器相关条目)
- "1443년 조선왕조실록 기록 찾아줘"(查找 1443 年的实录记录)
- "정조실록에서 수원 관련 기록 몇 개 보여줘"(从正祖实录中展示几条水原相关记录)
这些自然语言请求由 Agent 理解后,转换为下文将详述的 CLI 调用。
二、运行前提与输入参数
2.1 前提条件(Prerequisites)
按官方文档,运行本技能只需满足:
- 可用的互联网连接(所有数据均实时抓取自官方站点);
python3运行环境;- 无需任何 API Key,这也是该方案最大的易用性来源;
- helper 脚本
sillok_search.py已随@nomadamas/k-skillCLI 打包,也可直接阅读仓库源码 joseon-sillok-search/scripts/sillok_search.py。
脚本对第三方依赖的要求极低:requests属于可选依赖(源码第 17-20 行以 try/except 方式导入,缺失时自动退化为标准库urllib),因此即便在无pip install的裸环境里也能运行。
2.2 输入参数总览
文档「Inputs」章节与源码 parse_args 函数 共同定义了完整的命令行参数面:
| 参数 | 必选 | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
--query | ✅ 必选 | str | 无 | 发送给实录站点的检索关键词,缺省直接报错 |
--king | 可选 | str | 无 | 王名过滤,如세종、정조、세종실록,自动做别名规范化 |
--year | 可选 | int(正整数) | 无 | 公元纪年过滤,如1443;源码用positive_int校验,传入0或负数会抛出ArgumentTypeError(有对应测试test_rejects_non_positive_year) |
--limit | 可选 | int(正整数) | 5(源码DEFAULT_LIMIT) | 返回结果条数上限 |
--type | 可选 | k或w | k | k= 국역(韩文翻译)检索,w= 원문(汉文原文)检索 |
--timeout | 可选 | int(正整数) | 30秒(源码DEFAULT_TIMEOUT) | HTTP 超时时间 |
需要特别说明--type的语义:官方站点对「翻译文本」与「原文文本」分别建了检索通道,选择w意味着直接在汉文原文里匹配关键词(例如检索「임진왜란/壬辰倭乱」的古文写法时更精确)。
三、端到端工作流
文档「Workflow」章节给出了 5 步标准流程,与源码 search_sillok 函数 的实现一一对应:
- 发起官方检索:通过 CLI 调用
npx -y @nomadamas/k-skill@0 exec joseon-sillok-search scripts/sillok_search.py -- --query "...",向官方检索 endpoint 发送 POST 请求; - 解析结果页:从返回的搜索 HTML 中解析出结果总数、按王分类的统计(왕별 분류)、文章链接与摘要;
- 收窄结果:按需追加
--king、--year再次过滤(这一步既可以放在请求时,也可以依赖脚本内置过滤); - 逐篇抓详情:对选中的每篇文章,打开
/id/<article_id>详情页,抽取 국역 与 원문 摘要; - 结构化输出:将所有数据组装为结构化的 JSON 返回给调用方。
3.1 CLI 实战命令
文档「CLI examples」提供的四条命令可直接复制运行,覆盖了参数组合的主要形态:
# 基础关键词检索(默认检索 국역、最多 5 条) npx -y @nomadamas/k-skill@0 exec joseon-sillok-search scripts/sillok_search.py -- --query "훈민정음" # 组合过滤:王名 + 公元年份 + 条数 npx -y @nomadamas/k-skill@0 exec joseon-sillok-search scripts/sillok_search.py -- --query "훈민정음" --king "세종" --year 1443 --limit 3 # 王名别名自动规范化("세종실록" 会被归一为 "세종") npx -y @nomadamas/k-skill@0 exec joseon-sillok-search scripts/sillok_search.py -- --query "측우기" --king "세종실록" --limit 5 # 切换到汉文原文检索通道 npx -y @nomadamas/k-skill@0 exec joseon-sillok-search scripts/sillok_search.py -- --query "임진왜란" --type w --limit 5命令语法说明:exec joseon-sillok-search scripts/sillok_search.py指示 k-skill CLI 在技能目录内执行scripts/sillok_search.py,双横线--之后是传给该脚本的自身参数。若不用 npx,也可直接以python3 scripts/sillok_search.py --query "훈민정음"的方式运行仓库内的源码。
四、源码深度解析:抓取与解析原理
4.1 网络层:浏览器式请求头 + 双客户端容错
源码顶部常量定义了三个核心 URL(与文档「Notes」章节完全一致):
- 官方主页:
https://sillok.history.go.kr - 检索 endpoint:
https://sillok.history.go.kr/search/searchResultList.do(POST) - 文章详情:
https://sillok.history.go.kr/id/<article_id>(GET)
DEFAULT_HEADERS模拟了真实浏览器的请求特征:Accept-Language: ko-KR、Referer: https://sillok.history.go.kr/main/main.do,以及一个完整的 Chrome/136 macOS UA 串。这类「以公开 HTML 表面为数据源」的方案必须携带贴近真实浏览器的头,以提高请求被站点正常响应的概率。
HTTP 客户端采用双通道策略(build_http_client / fetch_text):
- 优先使用
requests库(若已安装)发起 POST/GET; - 若
requests不可用、或抛出传输类异常(RequestException、OSError),自动回退到标准库urllib的OpenerDirector,其内部挂载了HTTPCookieProcessor(CookieJar 维持会话)与HTTPSHandler(默认 TLS 校验); - 值得强调的是:HTTP 错误(HTTPError)不会触发回退而是直接抛错,TLS 校验始终开启(
ssl.create_default_context()),这一点被测试test_build_opener_keeps_default_tls_verification和test_fetch_text_keeps_requests_tls_verification_enabled显式锁定,防止未来改动意外关闭证书校验。
4.2 检索请求体构造
build_search_payload(源码 L423-L431)构造的 POST 表单字段为:
| 字段 | 值 | 含义 |
|---|---|---|
topSearchWord | 用户关键词 | 检索词 |
pageIndex | 页码(从 1 开始) | 分页游标 |
initPageUnit | 0 | 初始化分页单元 |
type | k/w | 检索通道 |
sillokType | S | 实录类型 |
topSearchWord_ime | <span class="newbatang">…</span> | 高亮样式用的 HTML 回显 |
4.3 搜索结果页解析
parse_search_results(源码 L334-L378)通过三组正则从结果页 HTML 中抽取信息:
- 结果总数:优先读取隐藏字段
totalCount,失败则回退到页面文本「검색결과N개」; - 分通道计数:按
--type选择隐藏字段countK(국역)/countW(원문)/countM/countC中的对应项,用于后续分页总数计算; - 王别分类统计:匹配
class="cate-item"的链接(其 href 为javascript:searchCategory('...')),解析出形如「세종 (5)」的分类标签与命中数,存入categories列表; - 结果条目:匹配
class="result-box"区块,从中提取goView('<article_id>', n)跳转函数里的文章 ID、class="subject"的标题、class="text"的摘要,并拼接出https://sillok.history.go.kr/id/<article_id>形式的正式链接。
4.4 标题元数据解析:王名规范化与纪年换算
这是本技能最具历史领域特色的部分。实录条目标题形如:
세종실록 102권, 세종 25년 12월 30일 경술 2번째기사 / 훈민정음을 창제하다
parse_result_title_metadata(源码 L312-L331)要做三件事:
- 拆分文章标题:以
/为界,取后半段作为article_title(如「훈민정음을 창제하다」); - 解析王名与纪年:用正则捕获「XX N년」或「XX 즉위년」(即位年),即位年视为第 1 年;
- 换算公元年份:查
KING_ACCESSION_YEARS表拿到该王即位时的公元年,再执行gregorian_year = accession_year + regnal_year(即位年特殊处理为直接取即位年)。该表覆盖从 태조(1392) 到 순종(1907)、순종부록(1910) 的全部 25 位王及增修/修正本。
王名别名规范化由 normalize_king_name 与KING_ALIASES表实现:用户输入「세종실록」「정조실록」「연산군일기」等带后缀写法时,会被统一映射为 canonical 王名(如세종、연산군),而「선조수정실록」→「선조수정」、「순종실록부록」→「순종부록」等特殊卷也都有独立条目。这正好印证了文档「Response policy」中「输入的王名稍有不同也能归一到 canonical 王名」的承诺。
4.5 过滤与分页算法
filter_results(源码 L381-L397)在客户端侧做二次过滤:王名用规范化后的名称做精确匹配,年份则按换算出的公元年份做精确相等比较。
search_sillok中的分页主循环(源码 L471-L487)是关键设计点:
- 从
pageIndex=1开始逐页抓取; - 首页返回后,用
type_count / 每页条数向上取整算出总页数,并受MAX_PAGES = 20硬上限保护; - 当过滤条件命中率低时,会继续向后翻页,直到累计过滤结果达到
limit或当前页为空——测试test_search_continues_to_later_pages_for_filtered_matches专门验证了「第 1 页全是非目标王条目、第 2 页才命中」时会正确请求第 2 页; - 最终只对
filtered_results[:limit]中的文章逐篇抓取详情页。
4.6 详情页解析与文本清洗
parse_detail_page(源码 L400-L420)从/id/<article_id>页面提取:
- header:
class="title"中的日期行(含「세종실록102권, 세종 25년 12월 30일 경술 2/2 기사 / 1443년 명 정통(正統) 8년」这种中韩历法对照信息); - title:
<h3>中的文章标题; - 국역 文本:
class="view-item left"区块内class="view-text"; - 원문 文本:
class="view-item right"区块内class="view-text"; - 분류(分类):
class="view_font02"的列表项(如「어문학-어학(語學)」)。
清洗层(clean_text/clean_article_text,源码 L203-L219)依次执行:剔除 HTML 注释 → 将<br>转成换行 → 剥离标签 → 反转义实体 → 压缩空白。clean_article_text还会用DETAIL_FOOTER_PATTERN精确裁剪掉文末的版本注记与版权行——形如【태백산사고본】 33책 102권 42장 A면 【국편영인본】 4책 533면的书志学信息以及ⓒ 세종대왕기념사업회版权脚注,避免污染正文;测试test_strips_bibliographic_and_copyright_footer_from_article_text验证了该行为。
4.7 结构化 JSON 输出
脚本最终输出(search_sillok 返回值)的顶层结构为:
{ "query": "훈민정음", "type": "k", "filters": { "king": "세종", "year": 1443, "limit": 3 }, "total_results": 21, "type_count": 11, "returned_count": 3, "categories": [ { "label": "세종", "count": 5, "token": "..." } ], "items": [ { "article_id": "kda_12512030_002", "url": "https://sillok.history.go.kr/id/kda_12512030_002", "title": "세종실록 102권, ... / 훈민정음을 창제하다", "article_title": "훈민정음을 창제하다", "summary": "이달에 임금이 친히 언문 28자를 지었다.", "king": "세종", "regnal_year": 25, "gregorian_year": 1443, "detail": { "header": "...", "title": "...", "translated_text": "...", "original_text": "...", "classification": "어문학-어학(語學)" }, "excerpt": "국역正文前 280 字符(无 국역 时退化为 summary 前 280 字符)" } ] }excerpt字段的设计值得一提:它取详情页 국역 正文的前 280 字符作为可读摘要,若详情缺失则回退到结果页 summary,保证 Agent 始终有可供引用的文本。任何网络/解析异常都会以{"error": "..."}JSON 形式输出到 stderr 并返回退出码 1(main 函数)。
五、测试验证:工程可靠性的保障
仓库在 scripts/test_sillok_search.py 中提供了完整的unittest测试套件(与 scripts/test_sillok_search.py 同源,构建了含真实 DOM 结构的样例 HTML),覆盖以下关键行为:
- 标题元数据解析:
세종 25년→ 纪年 25、公元 1443;문종 즉위년→ 纪年 1、公元 1450(即位年特殊规则); - 结果页解析:总数 21、국역 计数 11、分类「전체/세종/정조」及其计数、条目 ID 与 URL 拼接;
- 王/年过滤:
king="세종", year=1443后只保留目标条目kda_12512030_002; - 详情页解析:국역/원문/분류 三字段抽取,以及书志脚注与版权行的剥离;
- 网络回归:默认 TLS 校验保持开启、
requests传输失败时回退urllib、构建客户端时 opener 始终可用; - 跨页翻页:过滤命中在第 2 页时能正确继续抓取;
- 参数校验:
--year 0被拒绝。
这些测试既可作为回归保护,也是理解各解析函数输入输出契约的最佳样例——例如 SAMPLE_DETAIL_HTML 中「훈민정음을 창제하다」对应的 원문 为「○是月, 上親制諺文二十八字。」,完整示范了韩汉对照的抽取结果。
六、响应策略与完成标准
文档「Response policy」与「Done when」明确了 Agent 的行为边界,也是使用本技能时应遵循的规则:
- 回答内容:以官方实录站点确认的「文章标题 + 链接 + 摘要 + 详情摘要」为核心,链接统一整理为
https://sillok.history.go.kr/id/...格式; - 年份语义:
--year一律按公元纪年过滤(源码中的gregorian_year字段即由此而来); - 王名归一:
세종、세종실록等输入差异会自动归一化; - 不做过度推断:v1 明确不实现semantic search、embedding、大规模索引构建(源码也确无相关代码),只做公开 HTML 表面抓取;
- 空结果如实报告:命中 0 条时不得臆造内容,原样返回空结果;
- 判定完成的条件:官方站点真实检索到 ≥1 条结果、必要时王/年过滤已生效、至少包含 1 条文章详情摘要、链接均为官方
/id/格式。
七、适用边界与注意事项
- 该方案依赖官方站点的公开 HTML 结构与接口稳定性,站点改版可能导致解析正则失效,届时需同步更新 sillok_search.py 中的正则常量;
- 抓取属于对公网资源的轻量访问,脚本已内置 30 秒超时、20 页翻页上限,建议按需控制
--limit,避免大规模并发请求; - 数据版权归国史编纂委员会所有,文中链接指向官方页面,适合学术检索与个人研究用途;
- 若使用 CLI 分发版本,指令以
npx -y @nomadamas/k-skill@0 instruct joseon-sillok-search输出的最新内容为准;helper 文件清单可通过npx -y @nomadamas/k-skill@0 files joseon-sillok-search查看(见 SKILL.md)。
从「关键词 → 王/年过滤 → 详情抽取 → 结构化 JSON」的完整链路看,joseon-sillok-search是一个无密钥依赖、工程化程度高(正则解析、纪年换算、双客户端容错、280 字符摘要、跨页翻页均有源码与测试支撑)的领域查询技能,可作为 Agent 在历史文献检索场景中的开箱即用组件。
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考