k-skill 之 SH Notice Search:基于公开 HTML 公告板实现的首尔住宅公社公告检索 Skill
2026/9/18 14:11:58 网站建设 项目流程

k-skill 之 SH Notice Search:基于公开 HTML 公告板实现的首尔住宅公社公告检索 Skill

【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill

导读

本文围绕 k-skill 仓库中的sh-notice-searchSkill(关联文档为 packages/k-skill-cli/skills/sh-notice-search/instruction.md)展开,讲解如何让 Agent 直接读取 서울주택도시개발공사(SH,首尔住宅城市开发公社,官网www.i-sh.co.kr)的公开公告/通知 HTML 公告板,将公告列表、详情正文与附件元数据整理为 JSON。读完本文,你将掌握该 Skill 的能力边界、公开访问路径的发现过程、分类别名映射、srchTp/page等关键参数的正确用法,以及底层 npm 包sh-notice-search的源码实现与测试验证细节,可直接用 Node.js 或 CLI 复现同样的检索流程。


一、Skill 定位:只读查询,不做业务自动化

sh-notice-search的核心职责是读取 SH 官方公开 HTML 公告板,把结构化信息交给 Agent 使用。根据 instruction.md 的定义,它支持:

  • 使用关键词检索 SH 公告/通知列表;
  • 选择官方公告板分类(주택임대、주택분양、주택매입/주거복지、토지、상가/공장 等);
  • 在详情页中提取正文、담당부서(负责部门)、등록일(登记日期)、조회수(浏览量)以及真实附件文件名;
  • 附件提取以实际带existFile('N')onclick 的附件锚点与downList元数据为准,而不是解析扩展名图标模板。

同时它的边界非常明确:执行 청약 신청(认购申请)、서류 제출(文件提交)、需要登录的 마이페이지 查询、결제(支付)或 알림 발송(通知发送)。这正是 skill.json 中description所指的"公开公告板直接查询"能力——即使它声明了profiles: ["proxy", "action:submission"],其查询本体仍是公开只读的 HTML 面。

典型使用场景

instruction.md 给出了几个可直接触发该 Skill 的 Agent 输入示例:

  • "SH 행복주택 공고 찾아줘"(帮我找 SH 幸福住宅公告)
  • "서울주택도시개발공사 매입임대 공고 보여줘"(给我看 SH 购租公告)
  • "SH 공고 seq 304371 상세와 첨부파일 알려줘"(告诉我 seq 304371 的详情与附件)
  • "SH 분양 공고 최신 목록 조회"(查询 SH 最新预售公告)

运行前置条件

条件说明
网络连接需要直接访问www.i-sh.co.kr
Node.js 18+运行时要求,见 packages/sh-notice-search/package.json 的engines字段
依赖来源本仓库的sh-notice-searchnpm 包,或逻辑等价的自有实现

二、公开访问路径的发现与规范化

这是整个 Skill 的基石:不经过 k-skill-proxy、不需要 API key。instruction.md 记录了 2026-05-15 的一次真实 smoke 探测结果——直接无认证抓取www.i-sh.co.kr即可返回列表/详情 HTML。

官方公开 HTML 公告板地址

  • 默认租赁(주택임대)列表:https://www.i-sh.co.kr/app/lay2/program/S1T294C297/www/brd/m_247/list.do?multi_itm_seq=2
  • 默认租赁详情:https://www.i-sh.co.kr/app/lay2/program/S1T294C297/www/brd/m_247/view.do?multi_itm_seq=2&seq=<seq>
  • 标题关键词搜索:追加srchWord=<keyword>&srchTp=0
  • 内容关键词搜索:追加srchWord=<keyword>&srchTp=1
  • 公告板固定每页 10 行,分页使用page参数

探测得出的两个关键结论

  1. 不带srchTpsrchWord=행복주택会返回完整的租赁公告板总数,而加上srchTp=0才能收窄结果集。因此只要存在关键词,客户端就强制携带srchTp,否则srchWord会被 SH 公告板忽略。
  2. 上游为公开源且无需 API key,所以不引入k-skill-proxy路由(对比本仓库其他依赖代理的 Skill,这是一个重要差异点)。

源码侧,packages/sh-notice-search/src/index.js 的buildSearchUrl/buildDetailUrl完整体现了上述 URL 构造逻辑:按分类配置取path,设置multi_itm_seq/multi_itm_seqs,再追加pagesrchWordsrchTp等参数。normalizeSearchType(index.js)实现了srchTp的规范化:无关键词时为null,有关键词时默认"0"(标题),同时接受title/제목/0content/본문/내용/1两组别名。


三、分类别名与官方 Tab 映射

SH 公告板按官方 Tab 划分,而用户习惯用自然语言别名。instruction.md 给出了完整的别名表,这也是 Agent 应答时必须遵循的映射:

输入别名官方 Tab
rent임대주택임대주택임대(multi_itm_seq=2
sale분양주택분양주택분양(multi_itm_seq=1
purchase매입매입임대welfare주거복지주택매입(multi_itm_seq=512
land토지토지
commercial상가공장상가/공장
compensation보상이주보상/이주
design현상설계현상설계
etc기타기타
all전체전체

需要特别说明:주거복지并不是 SH 公告板的公开 Tab 名,而是为提升用户体验增加的别名,当前映射到 SH 公开的주택매입Tab。Agent 在回答时必须向用户披露这一映射关系,避免产生"SH 存在同名 Tab"的误解。

源码中的完整分类配置

npm 包在 src/index.js 的CATEGORY_CONFIGS中定义了比文档别名表更完整的配置,包括每个分类的官方 board pathmultiItmSeq

key官方名称board path(相对www.i-sh.co.krmultiItmSeq
all전체/app/lay2/program/S1T294C295/www/brd/m_2411,2,4,8,16,32,64,128,256,512multi_itm_seqs
sale주택분양/app/lay2/program/S1T294C296/www/brd/m_2441
rent주택임대/app/lay2/program/S1T294C297/www/brd/m_2472
purchase주택매입/app/lay2/program/S1T294C3379/www/brd/m_247512
movein입주안내/app/lay2/program/S1T294C298/www/brd/m_2484
land토지/app/lay2/program/S1T294C299/www/brd/m_2558
commercial상가/공장/app/lay2/program/S1T294C300/www/brd/m_25616
compensation보상/이주/app/lay2/program/S1T294C301/www/brd/m_25732
design현상설계/app/lay2/program/S1T294C302/www/brd/m_25864
etc기타/app/lay2/program/S1T294C304/www/brd/m_260256

别名通过CATEGORY_ALIAS(index.js)反查得到内部 key,normalizeCategory遇到未知分类会抛出Unsupported SH category错误。注意all分类使用的是复数参数multi_itm_seqs,其他分类使用单数multi_itm_seq——这是 URL 构造(buildSearchUrl)中需要区分的关键细节,测试用例 index.test.js 也专门验证了분양→sale 与welfare→purchase 的路径和参数映射。


四、工作流:三步完成公告检索

步骤 1:检索公告列表

以 npm 包方式调用:

const { searchNotices } = require("sh-notice-search") const result = await searchNotices({ keyword: "행복주택", category: "임대", page: 1, limit: 5 }) console.log(result.items)

CLI 方式:

node packages/sh-notice-search/src/cli.js 행복주택 --category 임대 --limit 5 node packages/sh-notice-search/src/cli.js 매입임대 --category 주거복지 --status 진행

列表项返回字段:

  • seq
  • title
  • department
  • registered_date
  • views
  • is_new
  • categorycategory_name
  • statusstatus_basis
  • detail_url

参数规范化细节(来自 normalizeSearchOptions):

  • keyword兼容q/query/srchWord写法,长度上限 100 字符(超出抛错);
  • category兼容kind/noticeType,内部归一到分类 key;
  • page默认 1,合法范围 1–1000;
  • pageSize/limit默认 10,最大值被钳制为 10MAX_PAGE_SIZE = 10),因为 SH 公告板固定每页 10 行,传更大的值不会让 SH 返回更多行;
  • timeoutMs默认 20000ms,上限 120000ms;
  • srchTp兼容searchType/type写法;
  • 支持注入fetchersignal(便于测试与取消)。

返回结构上,parseListHtml(index.js)会给出query(回显查询条件)、summarypagepage_sizereturned_counttotal_count)、sourcename: "sh-public-html"、官方 URL、proxy: false)、warningsitemstotal_count通过正则匹配页面上的총 <strong>N</strong> 건文本得到(extractTotalCount)。测试 index.test.js 验证了 95 条总数、2 行返回、seq/title/views/is_new/category_namedetail_url的解析结果。

步骤 2:获取详情与附件

const { getNoticeDetail } = require("sh-notice-search") const detail = await getNoticeDetail({ seq: "304371", category: "임대" }) console.log(detail.notice.content_text) console.log(detail.notice.attachments)

CLI:

node packages/sh-notice-search/src/cli.js --seq 304371 --category 임대

附件字段:

  • filename
  • file_seq
  • file_size
  • file_type
  • preview_url(SH 官方 preview/converter 地址)

刻意不返回直接下载链接:SH 的文件下载行为可能依赖会话与站点策略,因此应把detail_urlpreview_url交给用户浏览器处理。

详情解析的源码实现(parseDetailHtml):

  • 标题:匹配detailTable/firgs0401Table容器内的<caption>colspan=2<th>
  • 登记日期:匹配<strong>등록일 : </strong>后的YYYY[-.]MM[-.]DD,并归一为-分隔;
  • 浏览量:匹配<strong>조회수 : </strong>后的数字;
  • 负责部门:匹配personInfo列表中的담당부서 : ...(extractDepartment);
  • 正文:匹配class="cont"<td>,去除 script/style 与标签后得到content_text(stripTags);
  • 状态:同样基于标题文本分类(见下一节)。

附件提取的严谨性是本文档强调的重点。实现位于 parseAttachments 与 parseAttachmentDownList:

  1. 先从页面内嵌脚本中解析downList(JS 对象/数组字面量)元数据,按fileSeq建立索引;
  2. 定位첨부(파일)表头行,遍历该单元格内的<a>锚点;
  3. 只认可同时满足以下条件的锚点为真实附件classbtnAttachonclick形如existFile('<数字>')、且锚点文本不是纯扩展名标签(如.pdf.hwp这类图标模板,由isAttachmentIconLabel过滤);
  4. preview_url只接受 SH 本域且路径为/app/com/util/htmlConverter.do的链接(normalizeAttachmentPreviewUrl),防止外部伪造域混入。

测试用例 index.test.js 专门验证了:HTML 注释中的图标模板.pdf/.hwp不会被当作真实附件,而两个带existFile('0')/existFile('1')的锚点会与downList中的fileSize/oriFileNm/fileTp合并,产出filenamefile_seqfile_sizefile_typepreview_url,且不包含download_url字段;同文件 L194-L206 还验证了外部域evil.example/htmlConverter.do会被丢弃。

步骤 3:保守地解读"状态"

SH 公开公告板列表没有一等公民的状态字段(如접수중/마감)。包虽然支持status过滤,但它是一个标题文本分类器

  • open/진행:标题含 모집공고、입주자 모집、신청、접수、공고
  • closed/마감:标题含 마감、계약결과、결과、완료、종료
  • announced/당첨자:标题含 당첨、발표

源码实现为 classifyNoticeStatus:按"당첨/발표 → 마감/계약결과/결과/완료/종료 → 모집공고/입주자 모집/신청/접수/공고"的优先级正则判定,否则返回unknown;过滤逻辑 statusMatches 会对closedannounced做精确匹配。状态别名表 STATUS_ALIASES 支持韩英两组写法,如진행/모집중/공고중 → open마감/종료/결과 → closed당첨/발표 → announced

instruction.md 明确要求:应答时必须披露"状态是从标题推断的",除非详情正文给出了确切的日期。测试 index.test.js 也验证了status: "closed"只返回含계약결과的行、status: "open"在该样本下返回 0 行,印证分类器的保守性。


五、CLI 与参数总览

CLI 入口为 packages/sh-notice-search/src/cli.js,同时是 npm bin(见 package.json 的bin字段,安装后可直接使用sh-notice-search命令)。其帮助信息给出完整选项:

Usage: sh-notice-search [keyword] [options] Search public SH notices: sh-notice-search 행복주택 --category 임대 --limit 5 sh-notice-search 매입임대 --category 주거복지 --status 진행 Fetch one detail: sh-notice-search --seq 304371 --category 임대 Options: -q, --query <text> Keyword. Defaults to title search when present. --search-type <type> title/제목 or content/내용. --category <category> all, rent/임대, sale/분양, welfare/주거복지, land/토지, etc. --status <status> open/진행, closed/마감, announced/당첨자 (title classifier). --page <number> Page number (default: 1). --limit <number> Returned rows; capped at SH fixed page size 10. --seq <number> Fetch detail by SH notice seq. --include-html Include raw HTML in output for diagnostics.

参数解析器(parseArgs)支持--query/-q/--keyword--category/--kind--status--page--limit/--page-size--srch-tp/--search-type--seq/--id--include-html--help/-h;首个裸参数会被当作 keyword,detail/--detail后跟数字可指定 seq。CLI 输出为标准 JSON(JSON.stringify(result, null, 2)),--include-html可在诊断时保留原始 HTML。测试 index.test.js 验证了参数解析与--help输出。


六、完成标准(Done when)

根据 instruction.md,一次合格的 SH 公告查询应满足:

  • 直接从用户机器查询 SH 官方列表/详情 URL;
  • 关键词搜索包含srchTp,避免srchWord被忽略;
  • 分页使用page,并识别公告板固定每页 10 行的限制;
  • 附件取自真实的existFile()锚点与downList元数据,而非扩展名图标模板;
  • 展示公开来源 URL,避免登录/申请类自动化。

七、失败模式与处理原则

instruction.md 明确列出的失败场景,也是 Agent 运行时需要向用户说明的情况:

  • 上游结构变更:SH 可能调整公告板路径、表格标记、JavaScript 函数或downList结构,导致解析部分失败或整体失败;
  • 访问受限:IP 限速、NetFunnel 节流、维护页面或临时 4xx/5xx 会阻断实时抓取;不得绕过CAPTCHA/登录/排队保护;
  • 搜索参数陷阱srchWord不带srchTp会被 SH 公告板忽略,必须带srchTp=0(标题)或srchTp=1(内容);
  • 分页陷阱pageSize大于 10 不会让 SH 返回更多行,追加结果必须用page
  • 附件策略:附件预览 URL 可能需要浏览器接力,并受 SH 当前直链/下载策略约束;
  • 状态推断局限:公开列表无显式状态列,status只能从标题文本推断。

源码中的防御性设计值得借鉴:fetchText(index.js)设置user-agent: Mozilla/5.0 (compatible; k-skill/sh-notice-search)accept头,使用AbortSignal.timeout实现超时,非 2xx 响应会抛出含状态码与前 200 字符的异常;buildUnexpectedHtmlWarnings(index.js)通过findUpstreamBlockMarkers检测页面是否含NetFunnelcaptcha/보안문자로그인/login점검/maintenance대기열/queue차단/block等特征词,并在warnings中提示"可能被拦截或处于维护"。测试 index.test.js 与 L185-L192 分别用模拟的维护页验证了列表/详情场景下会输出NetFunnel로그인점검警告。


八、边界与安全准则

  • 只读查询:本 Skill 仅做查找,不做任何写操作;
  • 无代理、无 API key、无密钥:所有请求直接发往 SH 公开面,这也是它在设计上与依赖代理的 Skill 的最大区别;
  • 禁止自动化敏感流程:不得自动化 청약 신청、로그인、서류 제출、결제 或 마이페이지 流程;
  • 保持来源透明:应答时展示 SH 官方 URL 与source.name = "sh-public-html",让用户可自行核实。

九、快速上手指南

  1. 在仓库根目录安装/链接sh-notice-search包(Node.js 18+,参考 packages/sh-notice-search/package.json);
  2. 先做一次冒烟查询验证网络可达:node packages/sh-notice-search/src/cli.js 행복주택 --category 임대 --limit 5
  3. 观察返回 JSON 的summary.total_countsource.urlwarnings,确认命中正常公告板标记而非维护页;
  4. --seq <编号> --category <分类>拉取详情,核对content_textdepartmentregistered_dateviewsattachments
  5. 需要内容关键词检索时显式加--search-type 내용(等价srchTp=1);
  6. 需要排查解析问题时加--include-html输出原始 HTML 定位结构变化。

更多实现细节可继续阅读 packages/sh-notice-search/README.md、src/index.js、src/cli.js 与测试 test/index.test.js,以及 Skill 元数据 packages/k-skill-cli/skills/sh-notice-search/skill.json。

【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询