news 2026/9/19 4:03:43

sh-notice-search 实战指南:用 Node.js 直接查询首尔 SH 公社公开公告

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
sh-notice-search 实战指南:用 Node.js 直接查询首尔 SH 公社公开公告

sh-notice-search 实战指南:用 Node.js 直接查询首尔 SH 公社公开公告

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

本篇技术指南围绕sh-notice-search——k-skill 仓库中面向首尔住宅都市开发公社(SH,서울주택도시개발공사)的公开公告查询客户端展开,讲解如何通过其 JS API 与 CLI 直接抓取 SH 公开 HTML 公告板,完成公告列表检索、详情正文提取与附件元数据解析。读完本文,你将掌握该客户端的全部参数语义、分类别名映射、状态分类器原理、底层 HTML 解析机制及其明确的合规边界,可直接落地到 Agent 或自动化脚本中。

一、模块定位与能力边界

sh-notice-search是一个无需认证的公开 HTML 公告查询客户端,对应 k-skill 中的sh-notice-searchskill(技能清单见 sh-notice-search/skill.json,完整使用说明见 sh-notice-search/instruction.md)。它的设计目标非常克制:只读查询,不做任何事务性动作。

模块明确承诺的能力包括:

  • 按关键词检索 SH 公开公告/通知列表;
  • 按官方公告板分类(주택임대、주택분양、주택매입/주거복지、토지、상가/공장 等)筛选;
  • 从详情页提取正文、담당부서(负责部门)、등록일(登记日期)、조회수(浏览量)与真实附件文件名;
  • 解析附件的官方预览地址(preview_url)。

同时模块明确不做以下事情:청약 신청(认购申请)、登录、文书提交、支付、My Page 查询、通知发送等流程的自动化。数据源是 SH 的公开 HTML 页面,不需要代理、不需要 API Key、不需要任何密钥。这一点在 packages/sh-notice-search/README.md 的 Source 一节与 skill 的 instruction 中均有反复强调,也是后续所有设计(如不暴露直链下载地址)的出发点。

二、数据来源与关键发现

2.1 公开公告板的 URL 结构

模块直接抓取的列表/详情页地址格式为:

https://www.i-sh.co.kr/app/lay2/program/.../www/brd/.../{list,view}.do

以默认分类주택임대为例,src/index.js 中的CATEGORY_CONFIGS给出了精确路径:

  • 列表页:/app/lay2/program/S1T294C297/www/brd/m_247/list.do?multi_itm_seq=2
  • 详情页:/app/lay2/program/S1T294C297/www/brd/m_247/view.do?multi_itm_seq=2&seq=<seq>

其中multi_itm_seq=2是 주택임대 分类的官方标识;每个分类对应独立的板面路径与multi_itm_seq(详见第五节分类映射)。

2.2 srchWord 与 srchTp 的配套约束

这是 SH 公告板最重要的一个使用细节:SH 要求关键词搜索必须同时提供srchWordsrchTp两个参数,否则srchWord会被忽略。instruction.md 记录了一次真实冒烟测试(2026-05-15):仅带srchWord=행복주택而不带srchTp时返回的是整个 주택임대 板的全部公告数,加上srchTp=0后才收窄结果集。

因此客户端在存在关键词时总是携带srchTp,且默认按标题搜索:

搜索范围srchTpJS 侧写法
标题搜索(默认)0searchType: "title"/srchTp: "0"
正文搜索1searchType: "content"/srchTp: "1"

URL 构建逻辑见 buildSearchUrl:仅当keyword非空时才写入srchWordsrchTp。对应测试 test/index.test.js 验证了srchWordsrchTp=0multi_itm_seq=2三个参数同时出现。

三、环境要求与快速上手

sh-notice-search是一个 npm 包(package.json),发布版本 0.4.0,要求Node.js >= 18engines.node字段),标准入口为src/index.js,并暴露sh-notice-search的 bin 命令。

安装:

npm install sh-notice-search

依赖 Node 18+ 的原生fetch(代码通过global.fetch获取,也允许调用方注入自定义 fetcher,见第九节)。

最简用法——先搜列表,再取第一条的详情:

const { searchNotices, getNoticeDetail } = require("sh-notice-search") const list = await searchNotices({ keyword: "행복주택", category: "임대", page: 1 }) const detail = await getNoticeDetail({ seq: list.items[0].seq, category: "임대" })

注意list.items[0].seq是字符串类型的公告序号,详情查询直接复用即可。

四、JS API 参数详解

两个核心异步函数:searchNotices(options)getNoticeDetail(options)。参数经 normalizeSearchOptions / normalizeDetailOptions 统一归一化,支持多组别名。

4.1 searchNotices 参数

参数别名默认值说明
keywordq/query/srchWordnull搜索关键词,上限 100 字符,超长直接抛错
categorykind/noticeTyperent(주택임대)分类键或别名,见第五节
searchTypesrchTp/type有关键词时"0"标题(0)或正文(1)搜索
pagepageNo1页码,范围 1–1000,非数字抛错
pageSizelimit10返回行数,上限 10(SH 板每页固定 10 行)
statusnull状态筛选:open/진행closed/마감announced/당첨자
timeoutMs20000请求超时(毫秒),上限 120000
fetcherglobal.fetch自定义 fetch 实现(测试注入用)
signal外部 AbortSignal
includeHtmlfalsetrue时在结果中附带原始 HTML,便于诊断

非法输入均有明确报错:例如page: "abc"Provide valid page.,未知分类报Unsupported SH category: ...,未知状态报Unsupported SH status: ...(测试见 test/index.test.js)。

4.2 getNoticeDetail 参数

参数别名说明
seqnoticeSeq/id公告序号,必填,仅允许 1–20 位数字
categorykind/noticeType分类,缺省rent

此外同样支持timeoutMsfetchersignalincludeHtml

五、CLI 使用详解

CLI 入口为 src/cli.js,安装包后可直接调用sh-notice-search命令,输出为格式化 JSON。三种典型用法:

# 按关键词搜索列表 sh-notice-search 행복주택 --category 임대 --limit 5 # 分类 + 状态筛选 sh-notice-search 매입임대 --category 주거복지 --status 진행 # 按 seq 取详情 sh-notice-search --seq 304371 --category 임대

完整选项如下(--help可随时查看):

选项别名说明
--query <text>-q/--keyword关键词,存在时默认标题搜索
--search-type <type>--srch-tptitle/제목content/내용
--category <category>--kindallrent/임대sale/분양welfare/주거복지land/토지
--status <status>open/진행closed/마감announced/당첨자(标题分类器)
--page <number>页码,默认 1
--limit <number>--page-size返回行数,被 SH 固定页大小 10 截断
--seq <number>--id传该值则走详情查询
--include-html输出中附带原始 HTML

CLI 还支持detail <seq>的写法(parseArgs中检测detail/--detail标记后的数字参数)。出错时输出堆栈并设置进程退出码 1(见 run)。

六、返回字段详解

6.1 列表项字段

列表解析见 parseListRows,每行包含:

字段类型说明
seqstring公告序号(来自getDetailView('...')调用)
numberstring公告栏编号
titlestring标题(去掉 NEW 徽标文本)
departmentstring담당부서 负责部门
registered_datestring登记日期(如2026-05-14
viewsnumber浏览量
is_newboolean是否带 NEW 标记
category/category_namestring分类键 / 官方分类名
status/status_basisstring推断状态 / 恒为"title_text_classifier"
detail_urlstring官方详情页 URL

顶层返回结构还包含query(回显本次查询参数)、summarypagepage_sizereturned_counttotal_count)、sourcename: "sh-public-html"proxy: false、原始url)以及warnings数组,便于调用方自检。

6.2 详情字段

详情解析见 parseDetailHtml,getNoticeDetail返回{ notice, query, source },其中notice包含:

  • seqtitleregistered_dateviewsdepartmentcategorycategory_name
  • content_text:剥离脚本、样式与标签后的纯文本正文;
  • attachments:附件元数据数组;
  • detail_urlwarningsincludeHtml开启时还有html)。

6.3 附件元数据

每个附件包含:

字段说明
filename真实文件名(如2025년 2차 행복주택 예비3차 계약결과.pdf
file_seqSH 侧文件序号
file_size字节数
file_type类型标识(如A
preview_url官方SH 预览/转换地址(/app/com/util/htmlConverter.do?...

刻意不暴露直接下载 URL(download_url):因为 SH 的文件下载行为可能与会话/策略相关,直链并不稳定,正确做法是把官方detail_url/preview_url交给用户浏览器处理。测试 test/index.test.js 专门断言download_url键不存在。

七、分类别名映射表

分类归一化由 normalizeCategory 完成,别名经CATEGORY_ALIAS索引映射到官方板面。完整配置见 CATEGORY_CONFIGS:

分类键官方板面multi_itm_seq支持别名
all전체multi_itm_seqs=1,2,4,8,16,32,64,128,256,512all전체
sale주택분양1sale분양주택분양분양주택
rent주택임대2rent임대주택임대임대주택
purchase주택매입512purchase매입주택매입매입임대welfare주거복지
movein입주안내4movein입주입주안내
land토지8land토지
commercial상가/공장16commercial상가공장
compensation보상/이주32compensation보상이주
design현상설계64design현상설계설계
etc기타256etc기타

重要提示주거복지并不是 SH 公告板的公开标签,而是面向用户的友好别名,当前映射到 SH 公开的주택매입板(multi_itm_seq=512)。使用该别名时,应在答复中向用户说明这一映射关系(instruction.md 明确要求如此)。

别名匹配前会先做归一化(normalizeToken:去掉所有空白、trim、转小写),因此" 임대 ""임대"等价。

八、状态分类器原理

SH 公开列表没有一等公民的状态字段(没有접수중/마감之类的列),因此模块采用保守的标题文本分类器(classifyNoticeStatus):

推断状态触发关键词(标题内出现)
announced당첨발표
closed마감계약결과결과완료종료
open모집공고입주자 모집신청접수공고
unknown以上均未命中

状态筛选在解析后进行(statusMatches),并把status_basis标记为"title_text_classifier"以便下游感知其推断性质;一旦传入了status参数,返回的warnings中也会追加说明。响应时务必向用户披露“状态系从标题推断,除非公告正文写明确切日期”。测试 test/index.test.js 验证了계약결과标题被归为closedopen筛选返回空的行为。

九、源码级实现原理

9.1 请求构建与容错

  • URL 构建buildSearchUrl用标准URL对象拼接base + config.path + "/list.do",再按需写入multi_itm_seq(s)pagesrchWordsrchTpbuildDetailUrl类似地拼view.doseq
  • fetch 注入fetchText优先使用调用方注入的fetcher,否则回退global.fetch;请求头固定携带user-agent: Mozilla/5.0 (compatible; k-skill/sh-notice-search)accept: text/html,...。测试验证了该 UA 头(test/index.test.js)。
  • 超时createTimeoutSignal在支持AbortSignal.timeout的运行时用其实现超时;默认 20 秒。
  • HTTP 错误:非 2xx 响应抛出带状态码与前 200 字节正文的错误信息。

9.2 HTML 解析的关键策略

  • 列表解析:优先定位<div id="listTb">内的<tbody>,逐<tr>提取。判定有效行的关键是找到getDetailView('seq')JS 调用;单元格不足 5 列的行跳过;标题会去掉NEW徽标。总数通过총 <strong>N</strong> 건正则提取。
  • 文本清洗stripTags先移除<script>/<style>再剥标签;decodeHtml完整处理数字实体(十进制/十六进制)与&amp;&lt;&gt;&quot;&nbsp;等常见实体。
  • 附件解析(parseAttachments)是本模块最有辨识度的部分,有两条严格规则:
    1. 只认带existFile('N')onclick 的真实附件锚点,并显式过滤.pdf/.hwp图标模板文本——测试 fixture 中专门把图标模板注释掉以验证其不会被误解析(test/index.test.js);
    2. 附件文件名、大小、类型优先来自页面内嵌的downListJSON 元数据,并与htmlConverter.do预览链接按file_seq配对。
  • 预览链接白名单normalizeAttachmentPreviewUrl只接受www.i-sh.co.kr同源且路径为/app/com/util/htmlConverter.do的链接,防止被注入外部地址——测试用evil.example替换后断言preview_url为空(test/index.test.js)。

9.3 异常面检测与 warnings

当响应缺少预期的列表/详情标记时,buildUnexpectedHtmlWarnings 会扫描页面中的NetFunnelcaptcha/보안문자로그인점검대기열차단等关键词,给出形如unexpected SH list HTML; possible block/maintenance markers: NetFunnel, 로그인, 점검的警告。测试用一份含“서비스 점검 안내 / NetFunnel 대기열 또는 로그인”的拦截页验证了该行为(test/index.test.js),它让调用方在遇到限流/维护页时能快速识别,而不是得到静默的空结果。

十、测试与质量保障

测试文件 test/index.test.js 使用 Node 内置node:test+node:assert/strictnpm test即运行(package.json 中scripts.testnode --test),覆盖:

  • 参数归一化:关键词默认标题搜索、韩英分类别名、状态别名、非法输入抛错;
  • URL 构建:hostname、路径、srchTpmulti_itm_seqseq参数;
  • 列表/详情 HTML 解析:总数、行字段、详情字段、附件配对;
  • 拦截页检测:列表与详情的 warnings;
  • 端到端:通过注入的假fetcher走通searchNoticesgetNoticeDetail
  • CLI:parseArgs解析与--help输出。

十一、失败模式与使用注意

综合 README 的 Boundaries、instruction 的 Failure modes 与源码实现,接入时需注意:

  1. SH 可能变更板面路径、表格标记、JS 函数或downList结构,导致解析部分失败或完全失败——务必把warnings纳入处理逻辑;
  2. IP 限流、NetFunnel 节流、维护页或临时 4xx/5xx会阻塞实时抓取;模块只做被动检测(warnings),不得绕过CAPTCHA、登录或排队保护;
  3. pageSize/limit超过 10 无意义,SH 板每页固定返回 10 行,追加结果请翻页(page);
  4. 关键词必须与srchTp成对出现,客户端已默认处理;
  5. 附件预览/下载受 SH 当前直链与下载策略约束,应把官方 URL 交还用户浏览器;
  6. 状态为标题推断结果,非官方字段,答复时需披露。

结语

sh-notice-search的价值在于把“SH 公开公告查询”这件容易被各种反爬策略与 HTML 结构变化困扰的事情,收敛为一个参数规范、边界清晰、可测试的 Node 客户端:官方 URL 直连、无需代理与密钥、srchWord+srchTp配套、真实附件锚点识别、拦截页警告,配合完整的单元测试与 skill 说明文档(sh-notice-search/instruction.md),无论是嵌入 Agent 技能还是独立脚本使用,都能在合规前提下稳定读取 SH 的 청약·주택 공고 信息。

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 4:03:08

读懂 Jest 社区生态:jest-community 组织与官方精选扩展项目指南

读懂 Jest 社区生态&#xff1a;jest-community 组织与官方精选扩展项目指南 【免费下载链接】jest Delightful JavaScript Testing. 项目地址: https://gitcode.com/gh_mirrors/je/jest 导读&#xff1a;Jest 官方文档中专门用一页介绍了一个由 Jest 维护者与协作者共同…

作者头像 李华
网站建设 2026/9/19 4:02:14

uniapp多平台打包配置:一套代码实现多地区多环境自动化构建

1. 项目整体设计与思路拆解1.1 这个项目到底在解决什么问题先说结论&#xff1a;uniapp 项目的打包配置&#xff0c;难的不是“能不能打”&#xff0c;而是“一套代码&#xff0c;怎么在十几个目标环境下各自长出正确的样子”。我接手这个项目的时候&#xff0c;团队已经有了一…

作者头像 李华
网站建设 2026/9/19 4:00:02

OpenClaw+腾讯云:构建广告营销Agent基础设施实战指南

这段时间我在帮一家广告营销公司搭企业级的Agent基础设施&#xff0c;最后跑的方案就是腾讯云加OpenClaw。很多人一听到OpenClaw&#xff0c;第一反应是“这不就是个开源的个人AI助理吗”&#xff0c;确实&#xff0c;它前身那套东西在开发者圈子里更多是被拿来接微信、Telegra…

作者头像 李华
网站建设 2026/9/19 3:59:48

豆包、DeepSeek、千问、智谱清言怎么选?普通人AI工具选择指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华