court-auction-notice-search 完整解析:3 层防护下的法院拍卖公告结构化查询客户端
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
大法院运营的拍卖门户 courtauction.go.kr 没有任何公开 Open API,IP 级机器人拦截又凶得很。k-skill 仓库里的court-auction-notice-search直接复用该站内部的 WebSquare JSON XHR 接口,把 매각공고(不动产拍卖公告)与案件数据转换成 Agent 可直接消费的 JSON,设计哲学一句话:慢即是稳。
一个没有 API 的政府站,逼出了什么方案
你面对的目标是这样的:想查"某天某个法院有哪些拍卖",官网上只有搜索表单;表单背后是 WebSquare 前端框架发出的 JSON POST 请求,没有文档、没有 SDK、没有公开端点列表。换句话说,唯一的数据获取方式就是精确模仿浏览器里那个搜索按钮的行为——包括请求体结构、Referer、会话 Cookie。
第二个约束更硬:站点按 IP 做激进拦截,实测大约 30 秒内 16 次请求就会触发约 1 小时的封禁。一个常规的"并发抓全量"脚本,在这里活不过半分钟。
第三个约束是合规:拍卖数据用于参考,真正的投标必须由人在法院完成。这个包因此被明确定位为 read-only 客户端——它只读,不写,不提交,不填表。
这三条约束(无 API、强反爬、合规红线)直接决定了后面所有设计决策的走向。
架构解析:为什么主通道是裸 HTTP,浏览器只是备胎 🧭
整体数据流只有一条主线:你的调用 → index.js 的参数归一化 → http.js 的CourtAuctionHttpClient.postJson→ 站点内部 5 个 endpoint → normalize.js 的字段翻译 → 结构化 JSON 返回。
决策一:默认传输用直接 HTTP,不碰浏览器。公告列表、公告详情、案件直查三条主路径在真实浏览器里验证过,裸 HTTP 完全走得通。省掉浏览器意味着省掉登录态依赖、省掉启动开销、省掉被封后需要"人肉接管"的复杂局面。落到代码上,http.js里ENDPOINT_PATHS集中了全部 5 个 POST 路径,每个请求都带X-Requested-With: XMLHttpRequest、韩语Accept-Language和按 endpoint 分配的Referer,发送前先对对应入口页做一次 warmup GET 来拿到会话 Cookie。
决策二:浏览器只给自由检索兜底。Workflow C(物件自由条件检索)走的 endpoint 被站点 WAF 盯得更紧,裸 HTTP 有时会收到 WAF 型 400。playwright.js 的CourtAuctionPlaywrightClient就为此而生,优先级是:用户已打开的 Runtime 浏览器(Aside → BrowserOS → Chrome CDP)→ 全部够不到才chromium.launch本地启动。注意顺序的意义:优先复用用户自己的浏览器,是因为那台浏览器的指纹与会话更接近"真人",被拦概率最低。
决策三:2 秒最小间隔 + 1 秒 jitter。对照"30 秒 16 次就封"的实测红线,默认minDelayMs: 2000、jitterMs: 1000意味着相邻两次调用间隔 2000~2999ms 不等,随机增量把固定节奏的"机器味"打散。
决策四:每会话 10 次调用预算。ensureBudget在每次postJson前检查计数,超了直接抛BUDGET_EXCEEDED。这不是 bug 是保险丝:它保证即使你的循环写错了,一个客户端也烧不出 16 次/30 秒的量。
按日期查拍卖公告:公告列表与案件展开
回答"今天/明天哪里有不动产拍卖"这类问题,用searchSaleNotices。
| 参数 | 必填 | 取值 | 说明 |
|---|---|---|---|
date | 是 | YYYY-MM/YYYYMM/YYYY-MM-DD/YYYYMMDD | 站点的搜索按钮只按月发请求,给特定日期时先查整月再在本地按日过滤(见 index.js 的toNoticeSearchDate) |
courtCode | 否 | 如B000210(首尔中央地方法院) | 空字符串 = 全部法院 |
bidType | 否 | date/period/기일입찰/기간입찰/000331/000332 | 别名、中文名、代码都收;空 = 两种都查 |
返回items[]里每条公告都带caseNumber无关字段——重点是有judgeDeptCode(即jdbnCd,法院返回的加密令牌)、saleDate、saleTimes、correctionCount、cancellationCount,以及原样透传的raw。
const { searchSaleNotices, getSaleNoticeDetail } = require("court-auction-notice-search"); async function run() { const ann = await searchSaleNotices({ date: "2026-04-27", courtCode: "B000210", bidType: "date" }); // 逐条展开公告,取出案件号/用途/地址/评估价/最低价 const view = await getSaleNoticeDetail(ann.items[0]); // 错误处理:catch BLOCKED 后停止 for (const row of view.items) { console.log(row.caseNumber, row.address, row.appraisedPrice, row.minimumSalePrice); } }展开公告时最容易踩的点是:getSaleNoticeDetail需要jdbnCd,而这个令牌只存在于列表响应的raw里,外部无法凭空构造。所以最简单的姿势就是把列表返回的items[i]整个对象原样塞回去,构造逻辑会自动从raw里抽取cortOfcCd、dspslDxdyYmd、jdbnCd、bidDvsCd。
案件号直查:三步拿到完整进展
回答"2024타경100001 这个案子走到哪一步了",用getCaseByCaseNumber,只需两个入参:
| 参数 | 必填 | 说明 |
|---|---|---|
courtCode | 是 | ^B\d{6}$强校验,格式不对直接本地报错 |
caseNumber | 是 | 推荐2024타경100001;2024-100001、2024_100001会被normalizeCaseNumber自动补成标准形态 |
found: true时返回一份很完整的档案:caseInfo(案件名、受理日、请求金额、裁判部、进行状态)、items[](拍卖目的物地址与分配请求终期)、schedule[](每个拍卖日的最低价/评估价/结果)、claimDeadline、relatedCases、appeals、stakeholders。这些子表在 normalize.js 的normalizeCaseDetailResponse里逐一展开,足以支撑"上次拍卖流拍了吗""上诉结果如何"这类追问。found: false / status: 204时别当成故障——案件不存在或非公开,应当让用户核对案件号和法院是否匹配。
自由条件筛物件:区域、用途、价格、流拍次数
回答"江南区 5 亿以下、流拍 1 次以上的公寓",用searchProperties。
| 参数 | 必填 | 约束 |
|---|---|---|
region: {sido, sigungu, dong} | 否 | sido 可传代码或韩语名;시군구/읍면동 无静态表,直接传 raw 代码(如11680) |
usage: {large, medium, small} | 否 | 5 位代码(건물=20000)或大分类韩语名 |
priceRange/appraisedPriceRange | 否 | 韩元{min, max},允许小数 |
saleDate | 否 | {from, to} |
flbdCount | 否 | {min, max}仅整数 |
area | 否 | ㎡{min, max} |
pageSize | 否 | 只能10/20/50/100(默认 10),其他值上游会回 400,本地直接拒绝 |
一个隐含行为:传了region就走 지번주소 检索(请求体里cortStDvs:"2"),不传则切公告模式(cortStDvs:"1"),切换逻辑在 index.js 的buildPropertySearchBody里。返回items[]的 raw 列做了英文键翻译,常用的几组:saNo→caseNumber、gamevalAmt/minmaePrice→appraisedPrice/minimumSalePrice、yuchalCnt→flbdCount、boCd/jiwonNm→courtCode/courtName。
代码表方面有两个值得学的取舍。其一,getUsageCodes()只固化了 4 个大分类和部分中/小分类,getRegionCodes()只固化 19 个 시도——시군구级联 XHR 不稳定,索性不做静态表,未知值一律 fail-open 透传,让错误在源头可见而不是被静默改写成"看起来对"的代码。其二,codetables/index.js 的resolveUsageCode有同名保护:"아파트"在大/中/小多层都存在,若按名字跨层匹配就会把错误的代码写进请求体,所以指定了层级却匹配不上时,宁可原样透传。
使用前必须交代的合规红线 🚨
这一节的内容要在每次使用场景中告诉用户,没有例外。
诚实声明清单(原文照发,不用"建议您"):
- 数据是法원경매정보站公开信息的原样搬运,投标前必须回法院原始公告复核;
- 该站对自动化极度敏感,快速连续查询会封 IP 约 1 小时,封后同一 IP 需等约 1 小时;
- 价格、拍卖日期、场所均以公告时点为准,可能因
correctionCount/cancellationCount反映的正正·撤回·延期而变动; - 本客户端 read-only,绝不自动投标。
⚠️封禁(data.ipcheck === false)不要自动重试。站点把重试流量视为持续攻击,只会延长封禁;正确动作是立即停止、把封禁事实与等待指引交给用户,等约 1 小时或换网络。
⚠️pageSize只有 4 个合法值。传1、5之类"看起来合理"的值,本地校验直接抛错;这是被上游 400 打疼后加上的前置拦截。
⚠️连到用户自己的浏览器时只清 page/context/tab。Runtime 浏览器属于用户,fallback 结束只断开 automation client,绝不关闭 BrowserOS/Aside/Chrome 的 profile;只有本地 launch 的浏览器才整体关闭。
fail-open 与 fail-closed 的分界也在这里:未知代码、未知法院代码是 fail-open(透传,让上游裁决);而PLAYWRIGHT_UNAVAILABLE(fallback 模块缺失)和错误的 provider 名是 fail-closed(立即抛错,绝不静默降级成一个更脆弱的通道)。
限流参数怎么调:默认值与更保守的客户端 🔧
默认节流四件套:调用间 ≥2000ms + 0~1000ms 随机增量、每会话 10 次预算、单次超时 15s、封禁即停。两个调节面:
- CLI 全局标志:
--min-delay-ms 3000拉大间隔、--max-calls 5收紧预算、--timeout-ms改超时(见 cli.js)。 - 注入自造客户端:
CourtAuctionHttpClient的minDelayMs/jitterMs/maxCallsPerSession/timeoutMs全部可 override,传进任意主函数的client参数即可。
const { CourtAuctionHttpClient, searchSaleNotices } = require("court-auction-notice-search"); const cautious = new CourtAuctionHttpClient({ minDelayMs: 3000, jitterMs: 2000, maxCallsPerSession: 5, timeoutMs: 30_000 }); const first = await searchSaleNotices({ date: "2026-05", client: cautious });预算用尽后,开一个新客户端实例即重置计数;要更高 burst,间隔拉到 3~5 秒并换客户端。自由检索在裸 HTTP 遇到 WAF 型 400 时会自动走浏览器 fallback;想彻底关掉就传fallback: false,而BLOCKED默认中止,只有显式fallbackOnBlocked: true才会再试一次。
实战演练:从安装到第一条数据 🛠️
# 安装(npm 包名与 CLI 同名;也可 clone 仓库后在 packages/ 下构建) npm install court-auction-notice-search # 1) 先拿法院代码表,确认目标法院的 B000xxx court-auction-notice-search codes courts --pretty | head -40 # 2) 查某月某法院的公告列表 court-auction-notice-search notices --date 2026-04 --court-code B000210 --bid-type date --pretty # 3) 案件号直查 court-auction-notice-search case --court-code B000210 --case-number "2024타경100001" --prettysearch子命令还支持--region <시도[:시군구[:읍면동]]>、--usage <대[:중[:소]]>、--appraised-min/max、--flbd-min/max、--page-size 10|20|50|100等,完整用法见court-auction-notice-search -h或 README.md。Node.js 侧的最小集成就是前面 Query A 的示例:主调用两行,catch里判断error.code === "BLOCKED"分支单独提示即可。
排错速查:5 类错误各给一个 30 秒动作 🩺
| 错误码 | 触发条件 | 30 秒自助动作 |
|---|---|---|
BLOCKED | 响应data.ipcheck === false | 停止一切重试,告知用户约 1 小时后或换 IP 再试 |
BUDGET_EXCEEDED | 会话调用超预算 | 新建一个CourtAuctionHttpClient实例,或调大maxCallsPerSession |
UPSTREAM_ERROR | 站点返回通用错误 | 最常见是会话过期或jdbnCd失效,丢弃旧客户端从 warmup 重来;看error.upstreamMessage定位 |
NETWORK_ERROR | 超时/连接失败 | 核对timeoutMs与网络;原始异常在error.cause里 |
PLAYWRIGHT_UNAVAILABLE | 想用浏览器 fallback 但模块没装 | npm i rebrowser-playwright(或playwright-core)后重试 |
收尾:这套保守客户端范式能迁移到哪
三句话总结设计哲学:传输上保守(默认裸 HTTP,浏览器只在被 WAF 打脸时出场)、输出上结构化(raw 列全量翻译为语义键并保留raw兜底)、边界上清晰(read-only、封禁即停、预算保险丝、同名代码保护)。
延伸阅读按这个顺序走:instruction.md 是技能视角的完整工作流 → README.md 的 Endpoints used 表记录了每个请求体的 canonical 键 → index.js 看参数归一化与 fallback 判定 → http.js 看节流与错误构造 → 最后翻 test/fixtures/ 的响应夹具,其中canonical-search-body.json由 capture-pgj151-submit.cjs 从真实浏览器提交捕获,是理解请求结构的最佳样本。
凡是"无公开 API + 强反爬 + 合规敏感"的政务/金融站点查询,"慢即是稳 + 分层 fallback + 预算即停"这套组合都可以原样搬过去。
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考