k-skill 实战:基于 RELAY_STORE 与 JSON-LD 构建 당근부동산(胡萝卜房地产)只读房源搜索技能
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
本指南以 k-skill 仓库中
daangn-realty-search技能文档为核心,完整讲解如何在 Agent 中通过公开网页数据表面实现韩国 당근부동산 的只读房源候选检索、区域解析与详情补全。读完本文,你将掌握window.RELAY_STORE(Relay 归一化 Store) 的双重解码解析技巧、application/ld+json结构化数据抽取方法,以及一套可直接复制运行的 Python 标准库命令行搜索方案,并理解其合规边界与失效模式。
技能定位:读什么、不做什么
daangn-realty-search是 k-skill 仓库中面向韩国房地产检索场景的技能,其核心定义在 docs/features/daangn-realty-search.md:
당근부동산 공개 웹 데이터 표면을 사용해 지역 기반 부동산 매물 후보를 검색하고, 상세 페이지의 공개 메타를 읽기 전용으로 확인하는 스킬입니다.
即:只用公开网页数据表面完成「按区域搜索房源候选」与「读取详情页公开元数据」两步,不做浏览器自动化、登录、聊天、询价、预约、签约等任何会对外部服务产生副作用的行为。这一点在 instruction.md 的 "When not to use" 中明确列出:需要登录、涉及聊天/收藏/交易提议/预约/签约/购买、需要绕过 CAPTCHA/反爬/登录墙的任务都明确不在本技能范围内。
典型使用场景(来自原文档):
- 「당근부동산 합정동 월세 매물 찾아봐」(帮我在合井洞找月租房源)
- 「마포구 전세 후보 당근에서 봐줘」(在 당근 上看麻浦区全租候选)
- 「이 당근부동산 URL 상세 요약해줘」(总结这个 당근부동산 URL 详情)
技能元数据(skill.json)标注category: real-estate、locale: ko-KR、phase: v1.5,主程序仅依赖 Python 3.9+ 标准库(urllib/json/re/argparse),不引入任何第三方包。
三个数据表面与 2026-06 域名迁移应对
实现全部基于浏览器公开渲染结果的只读抓取,instruction.md 将数据表面整理为三层:
| 表面 | 端点 | 用途 |
|---|---|---|
| 区域解析 | https://www.daangn.com/kr/api/v1/regions/keyword?keyword=<지역명> | 返回{"locations":[{id,name1,name2,name3,name*Id,depth}]} |
| 房源列表 | https://realty.daangn.com/map/{name1}/{name2}/{name3} | SSR 注入的window.RELAY_STORE |
| 房源详情 | https://realty.daangn.com/articles/<id> | application/ld+json与<title> |
需要特别注意的是旧版表面的废弃:原文档明确警告,旧版https://www.daangn.com/kr/realty/?_data=routes/kr.realty._index表面自 2026-06 起返回 HTTP 204(空响应),已彻底弃用、禁止再使用。源码 daangn_realty.py 的模块 docstring 也记录了这次域名迁移的应对:新表面统一指向realty.daangn.com/map/{name1}/{name2}/{name3}并解析其中的window.RELAY_STORE。
RELAY_STORE 解析路径:Card 优先的稳健策略
window.RELAY_STORE是页面 SSR 时注入的 Relay 归一化 Store。文档给出的理论解析路径为:
ArticleFeedConnection.edges → ArticleFeedEdge.node → ArticleFeedCard.article → Article但 daangn_relay_store.py 的实际实现采用了更稳健的策略:直接遍历 Store 中所有__typename == "ArticleFeedCard"的节点,再对article引用解引用(deref)。源码注释解释了原因:Store 中 Card 是直接平铺存放的,绕开 edges 链可以少走一层引用跳转,容错性更强。
双重 JSON 解码:字符串再字符串
这是本技能最值得注意的底层细节。window.RELAY_STORE在 HTML 中是以JS 转义字符串形式存在的,源码extract_relay_store用正则捕获后执行:
return json.loads(json.loads('"' + match.group(1) + '"'))即json.loads(json.loads(...))两级解码:第一级把转义字符串还原为原始 JSON 文本,第二级把文本解析为对象。仓库测试 test_daangn_realty.py 中构造测试 HTML 时同样用json.dumps(json.dumps(store))双层编码来模拟真实页面,验证了这条路径。若双层 JSON 解码失败,函数返回None,由上层按「页面结构变更或反爬」处理。
字段提取与价格单位
Article节点上关键字段(daangn_relay_store.py):
originalId→ 文章 ID,拼出详情 URLhttps://realty.daangn.com/articles/<id>area→ 面积(㎡),可能是字符串,必须float()转换(源码_parse_area对None/空串/非法值均安全降级)salesTypeV3→ 解引用后取type(如OFFICETEL)trades→ 引用数组,按__typename分派解析
价格单位 = 만원(万韩元),这是阅读输出时必须牢记的换算规则:
| 字段 | 含义 | 示例含义 |
|---|---|---|
deposit | 保证金/全租押金 | 2000 = 2,000만원(2千万韩元) |
monthlyPay | 月租金 | 100 = 100만원(100万韩元) |
price | 买卖价 | 28700 = 2억8,700만원 |
交易类型与평당(每坪)单价
三种交易类型由__typename区分(daangn_relay_store.py):
| 交易类型 | typename | 价格字段 | 평당单价 |
|---|---|---|---|
| 월세(月租) | MonthTrade | deposit + monthlyPay | monthlyPay / 평 |
| 매매(买卖) | BuyTrade | price | price / 평 |
| 전세(全租) | BorrowTrade | deposit | deposit / 평 |
其中평 = ㎡ / 3.305785,源码常量PY_PER_SQM = 3.305785。测试用例验证了换算正确性:33.05785㎡ → 10평,月租monthlyPay=50时 평당单价 = 5.0만원。salesType枚举取值包括APART、OFFICETEL、STORE、OPEN_ONE_ROOM、SPLIT_ONE_ROOM、TWO_ROOM、HOUSE等。
详情补全:JSON-LD 结构化数据抽取
列表页不含楼层、标题、地址,需要访问详情页https://realty.daangn.com/articles/<id>补全。解析逻辑在 daangn_detail_ld.py:
- 用正则抽取所有
<script type="application/ld+json">块 - 兼容
@graph数组/对象/单节点多种形态(_json_ld_nodes) - 从
@type == "Product"节点取name(标题)、releaseDate(发布日期, ISO 8601 UTC) - 从
@type == "Place"节点取name(地址) - 从
additionalProperty数组按name匹配提取floor、topFloor、nearbySubwayStation
楼层最终格式化为floor_label,例如floor="8.0"+topFloor="10"→"8층/10층"(源码会把.0后缀剥掉)。测试用例 test_daangn_realty.py 完整覆盖了该路径,包括@graph: null的容错、releaseDate 缺失默认None、以及后续 Product 节点优先覆盖 releaseDate 的行为。
命令行使用与参数详解
技能文档给出了三条核心命令,全部通过 k-skill CLI 执行:
# 基础搜索(补全前 5 条标题/楼层) npx -y @nomadamas/k-skill@0 exec daangn-realty-search scripts/daangn_realty.py -- search --region "합정동" --limit 5 # 组合过滤(用途 + 交易类型) npx -y @nomadamas/k-skill@0 exec daangn-realty-search scripts/daangn_realty.py -- search --region "합정동" --sales-type "STORE,OFFICETEL" --trade-type "MONTH" # 详情页解析 npx -y @nomadamas/k-skill@0 exec daangn-realty-search scripts/daangn_realty.py -- detail "https://realty.daangn.com/articles/..."search子命令的全部参数(daangn_realty.py):
| 参数 | 默认值 | 说明 |
|---|---|---|
--region | 必填 | 洞名(如 매교동、합정동) |
--sales-type | 无 | 用途过滤,逗号分隔,如STORE,OFFICETEL |
--trade-type | 无 | 交易过滤,逗号分隔:MONTH(月租)/BUY(买卖)/BORROW(全租) |
--limit | 20 | 最大返回条数 |
--titles | 5 | 用详情 JSON-LD 补全标题/楼层的前 N 条;0关闭(更快) |
--expand | 关 | 扩展到同区/市相邻洞搜索 |
--expand-max | 6 | 扩展时相邻洞最大数量 |
--keyword | 无 | 兼容旧 CLI 的关键字过滤(对返回物料的公开字段文本过滤) |
--only-verified | 关 | 兼容旧 CLI 的认证标记(公开 feed 不提供单独认证字段,仅兼容保留) |
其中--titles 0是追求速度的常用技巧——省去对每条候选发起详情页请求;而--expand则依赖区域解析的name2Id做兄弟洞聚合(见下节)。
区域解析与 effective_region
区域名通过 region API 解析为内部对象后填入地图 URL 路径:
합정동 → 서울특별시 마포구 합정동 → /map/서울특별시/마포구/합정동源码resolve_region(daangn_realty.py)的候选排序策略是:精确匹配 → 首尔 depth=3 → 首个候选。当存在同名洞时按此优先级消歧。响应中会包含effective_region(最终生效的完整行政区划名)。
--expand的实现思路是:用name2(구/시) 关键词再次请求 region API,收集同name2Id且depth == 3的相邻洞(find_sibling_regions),对每个洞分别抓取列表页后按article_id去重合并,最终按--limit截断。这正是「수원 팔달구 상가 매물 봐줘」这类跨洞检索诉求的支撑。
输出字段解读与判断要点
搜索输出为 JSON,单个物料字段如下(instruction.md):
article_id, salesType, area_sqm, area_pyeong, trades[{type,label,deposit_manwon,monthly_manwon,price_manwon,per_pyeong_manwon}], url, region, title, address, floor_label, nearby_subway, release_date原文档强调:做房地产判断时,实时状态、保证金/月租、管理费、面积、是否中介/直营是关键信息,因此输出会连同原始 URL 一起给出,供最终核实。此外:
release_date来自详情页 JSON-LD 的Product.releaseDate(ISO 8601, UTC),只对--titles补全范围内的物料填充;它究竟是首次登记日还是最近刷新/修改日,당근 侧并无公开规范,不能断定。- 顶层响应还包含
effective_region、expand、regions_searched、sources(每洞 URL+条数+note)、errors字段,便于审计搜索覆盖面。
失效模式与合规边界
原文档明确了三类失败模式,源码中均有对应处理:
RELAY_STORE 없음:页面结构再次变更或触发反爬,此时extract_relay_store返回None,上层在sources[].note中记录并不尝试绕过。- 시군구(name2) 落地页无物料:列表必须精确到洞(name3)层,SSR 才会填充 articleFeed;只到区/市层拿不到候选。
- 同洞歧义与详情失效:同洞名可能选中不同行政洞;删除/私密文章详情解析会失败(
parse_detail返回各字段为None的结构)。
合规层面必须遵守仓库内 DISCLAIMER.md 与 SKILL.md 的硬性规则:
- 公开信息自动收集仅限个人查询用途,禁止组织化、系统化、批量爬取及数据库构建/再分发;
- 禁止绕过登录墙、付费墙、CAPTCHA、访问控制、限流与 IP 封锁,检测到拒绝/封锁信号立即停止;
- 不执行任何支付、消息发送、最终提交、取消或公开发布动作,除非用户在操作前明确批准;
- 该技能与 당근 官方无任何从属、赞助或合作关系(非官方功能)。
最小运行条件与后续阅读
- 运行前提:联网 + Python 3.9+,且
npx可用(Node.js 18+);若npx不可用,可通过npx -y @nomadamas/k-skill@0 instruct daangn-realty-search获取完整说明。 - Windows 下中文/韩文 stdout 乱码防护:支持运行时中脚本会执行
sys.stdout.reconfigure(encoding='utf-8')(daangn_realty.py)。
想深入底层验证,可继续阅读:daangn_relay_store.py(Store 解析与交易分派)、daangn_detail_ld.py(JSON-LD 抽取)、test_daangn_realty.py(含 mock 的完整测试矩阵),或通过npx -y @nomadamas/k-skill@0 files daangn-realty-search查看随 CLI 分发的全部辅助文件。
【免费下载链接】k-skill한국인을 위한 스킬 모음집 - 에이전트를 한국인으로项目地址: https://gitcode.com/GitHub_Trending/ks/k-skill
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考