海外租房作业包:初始化 → 抓取 → 核验 → 通勤 → 问询 → 收信 → 谈判授权 → 资料付款 → 合同与面谈风险 → 门禁
这个技能解决什么
手工找房的四个典型浪费(均来自真实踩坑):
| 浪费 | 根因 | 对应模块 |
|---|---|---|
| 代租时署名/口吻/邮箱全乱,家人还不知道进度 | 假设"租客=用户=同一人" | 初始化访谈层 |
| 一条条点开,一半是旧消息、假消息 | 把索引页的字段当成了事实 | 核验层 |
| 房子"都在同一个城市",结果一套步行 8 分钟、一套 40 分钟 | 只看城市,不看通勤 | 通勤层 |
| 同一个问题(最短租期、杂费、能不能看房)一遍遍问 | 问询拆成很多轮 | 问询层 |
| 跟同一个房东/中介重复谈同一件事 | 没有台账,记不住问过什么 | 收信闭环层 |
| 房东回信了,还得人工读、人工判断、人工录 | 回复没有回流到台账 | 收信闭环层 |
| 谈判时每次都重新想"我到底能退到哪" | 没有授权表,边界每次现想 | 谈判授权层 |
| 到了签约/办居留才发现材料没准备、钱打不过去 | 前置条件没有提前暴露 | 资料付款层 + 门禁层 |
| 语言不熟,合同自己看不懂,看到"押金不退"也不知道 | 没有合同风险扫描 | 合同与面谈风险层 |
| 换个国家就得从头再来 | 没有国家包,各国规则混在一起 | 国家包层 |
什么时候用
- 用户或用户的家人要租房(尤其学生房、交换生、短租 4–6 个月)
- 父母/亲友给子女或他人代租(发信人≠租客,需要署名与抄送)
- 需要判断一批房源里哪些是真的、现在还能租
- 需要知道每套房离学校/公司多远、要多久(通勤排序)
- 需要给房东/中介发问询,或准备议价、签约
- 需要看懂一份外语合同有没有坑(退租通知期、押金、注册),或为当面看房/签约做准备
- 需要梳理签约前必须办完的事(税号、押金、担保、合同注册、IBAN)
- 需要换到另一个国家(意大利 → 法国/德国/西班牙/荷兰…)继续找房
工作流
第 0 步:初始化访谈(先问清"谁住、谁办、用什么邮箱、要不要抄送")
不要假设"租客=用户=同一人"。 大量真实场景是父母给子女租、朋友代办。
一旦代租,署名、口吻、发件箱、进度同步全都变了。所以在抓任何一条房源之前,
先把这四组问题问完:
python scripts/onboard.py init --out renter.json # 生成模板
python scripts/onboard.py check --file renter.json # 校验必填与常见错误
python scripts/onboard.py show --file renter.json # 打印"谁—发—收—抄"一张表
| 组 | 问什么 | 决定什么 |
|---|---|---|
| A 谁住 | 租客姓名(护照拼写)、签证类型、性别、学校院系、常用语言 | 合同主体(拼错会一路炸) |
| B 谁办 | 自租还是代租?代办人与租客的关系 | sender_name 与署名口吻 |
| C 邮件链路 | 发件箱用哪个、署名写什么、回复收哪、要不要抄送第三人 | 见下方专节 |
| D 目标 | 城市 + 邮编、工作/学习地点(给全地址)、预算(月总成本)、租期起止、性别/身份限制 | 喂给核验层 + 通勤层 |
口吻规则(容易混):邮件类(问询/追问)用委托人第一人称("我代 XX 写信");
群帖/私信/站内信是租客本人发的,用租客第一人称。两套不能混用。
发件箱与抄送(本步的重点)
- 发件箱用委托人的,不要用租客的。 理由:① 不打扰本人 ② 口吻对
③ 所有回复集中一处便于追踪 ④ 租客邮箱常不常用。 - 抄送必须挂在核验门禁后面,不能无脑抄。 三档策略:
| 档位 | 行为 | 用什么时候 |
|---|---|---|
none |
不抄送 | 自租,或不需要别人知道 |
confirmed_only |
只把【已核实可租】的房源抄给第三人 | 默认推荐 |
all |
每条原始房源都抄 | ⚠️ 会把假消息/僵尸房源一起抄过去,重演"浪费家人时间" |
技术上强制:outreach.py 在 cc_mode=confirmed_only 下必须显式加 --confirmed
才会抄送,否则只发房东、不抄送,并打印原因。这条设计就是为了防止
"抓来一堆没核实的房源直接转发给家人"——那等于把筛选成本转嫁给收件人。
详细访谈提纲与常见错误见 references/onboarding.md。
其余目标配置
整理一份目标配置:目标城市与允许的邮编、工作/学习地点(学校主楼 / 院系 / 实习公司,
给全地址)、预算(按月总成本算)、需要的租期(起止)、性别限制、通勤方式偏好、渠道清单。
邮编很关键:房源板常常覆盖整个省,只按城市名搜会把 25 公里外的房子混进来。
工作/学习地点同样关键:它决定每条房源能不能算出通勤。只报城市 = 无法区分
"步行 8 分钟"和"步行 40 分钟"的两套房(详见第 2.5 步与 references/commute.md)。
第 0.5 步:谈判授权表(先定边界,再出去谈)
出去谈之前先把"哪些能松、哪些不能松"问清楚,落成一张表。否则每谈一次就要重新想一遍,
而且没有边界的时候最容易一路让步。
python scripts/mandate.py init --out mandate.json # 生成模板
# AI 按 references/negotiation_mandate.md 的四条线访谈用户后逐项填写
python scripts/mandate.py check --file mandate.json # 校验漏填与自相矛盾
python scripts/mandate.py brief --file mandate.json # 输出授权简报(可直接贴给 AI)
四条线:A 硬约束(越线即放弃)/B 可让(有弹性,按梯度让)/
C 筹码(拿去换条件)/D 红线(遇到就退出,AI 不得放宽)。
brief 的输出就是谈判的授权边界。超出这个边界的话术,不许写进发给房东的消息里。
第 1 步:抓取
按渠道类型分别抓,每条房源必须带一个能回到详情页/原帖的 URL。
渠道清单做成平台登记表(assets/sources.json),这样换城市/换国家时渠道是现成的:
python scripts/sources.py init # 首次生成(含种子平台)
python scripts/sources.py list [--country IT] # 看已登记的渠道
python scripts/sources.py add --id x --country IT --city Trento \
--type classified --url https://... --access unknown # 扩充新平台
python scripts/sources.py probe --all # 实测可达性(直连/登录墙/被墙)
python scripts/sources.py report # 汇总健康度
probe 会把实测结果写回登记表,并把 access 校正为 direct / login / proxy / blocked——
这一步决定"这个平台到底能不能抓",比人工猜测准。
默认直连优先,直连失败才降级走代理(理由与参数见下文「隐私与数据落盘」第三节)。
(被墙的平台挂代理,登录墙走 edge-cdp-scrape 带登录态。)
- 房源板 / 分类广告站:静态优先用 curl;有登录墙或 JS 渲染的用
edge-cdp-scrape技能
(Windows 上用系统 Edge 的调试端口 + Node 内置 WebSocket,零安装、带登录态) - 社交群组:用
[role="article"]抓帖,按帖子 ID 去重,清掉?__cft__之类的追踪串 - 抓完立刻把原始 HTML/文本存盘(
_raw_html/)——核验要反复读,别只读一次
抓取层写成一个 extract_<站点>.py,输出统一的 listings.json。
样板见 scripts/extract_example_operauni.py(学生房源板详情页 → 标准 JSON),
照它的字段结构写别的站点即可。
平台自我学习:每抓一个新平台,字段名大概率跟标准字段对不上。跑一次:
python scripts/sources.py learn --scrape listings.json --source <平台id>
它会把未知字段记进登记表的 learned_fields,并给出归类建议。
下次遇到同一个平台就不用再猜字段——学的对象是「平台 × 字段模式」,是记录,不是玄学。
归好类的字段写进 aliases,之后所有平台的抓取结果都能自动对齐到同一套标准字段。
第 2 步:核验(本技能的核心)
先导入台账,再跑核验规则引擎:
python scripts/housing_db.py --db rental/housing.db import listings.json
python scripts/verify.py --db rental/housing.db --city Trento --caps 38121,38122,38123,38100 --budget 500
python scripts/verify.py --db rental/housing.db --explain 12 # 看某条的判定理由
六个必读字段(完整规则见 references/verification_rules.md):
- 地址 + 邮编 + 城市 —— 不在目标城市的一律另列,不混进主推
- 起租日(
Libera da/Available from)—— 晚于今天=现在不能住;
早于今天要算空置月数,超过 6 个月=僵尸房源(房东早就不看这块了) - 每月杂费(
Spese extra)—— 报价必须写「租金 + 杂费 = 月总成本」 - 最短租期(
Permanenza minima)—— 标 12 个月的不得直接推给租客 - 性别限制 —— 同公寓多间房要逐间标空/占
- 押金 + 合同类型 —— 优先找
contratto transitorio(临时性质租赁合同)
核验输出五档判定:direct(可直接联系)/ inquiry(先问再推)/
nearby(不在目标城市)/ unverified(没读过详情页)/ excluded(已排除)。
只有 direct 档能直接交给租客。 其余一律先问、或剔除。
第 2.5 步:通勤("能不能租"之后,回答"值不值")
核验只解决及格线;能不能选它,看它到你每天要去的地方多远。同一座城市里两套都"在 Trento"
的房子,可能一套步行 8 分钟、一套 43 分钟。只报城市,等于没筛。
# 给台账里所有房源算通勤并写回(--to 用初始化访谈里的工作/学习地点)
python scripts/commute.py enrich --db rental/housing.db \
--to "Via Sommarive 9, Trento" --to-city Trento
python scripts/commute.py show --db rental/housing.db # 按通勤分排序
python scripts/commute.py show --db rental/housing.db --links # 附每条房源导航链接
python scripts/commute.py map --db rental/housing.db --out commute_map.html # 生成整张地图
python scripts/commute.py geocode --address "Via Verdi 8" --city Trento # 单点自测
数据全部来自免费无 Key 的服务(Photon / Nominatim / OSRM / Overpass),路由与抓取层一致:
直连优先,失败才降级走代理。
地图连接器:房源站本身不给地图,所以本层补两种——① show --links 给每条房源的
导航链接(按地址拼装,不联网);② map 生成一张自包含 HTML 地图(目标地 + 各房源 + 连线,
点左侧清单可定位)。地图库与瓦片都做了多路 CDN 兜底,瓦片全挂时仍可用点与清单。
实测:国内网络下 osm.de/osm.fr-hot/Esri 直连可用,官方 osm.org 直连常超时——
所以别给地图页面硬套代理。
三条必须如实告知的边界(详见 references/commute.md):
1. 不算真实公交/地铁乘车时长(那需要 GTFS 时刻表)——只给步行/骑行/驾车时间 + 最近站点距离。
2. 步行/骑行时间是估算(按路网距离 ÷ 速度,步行 4.5 km/h、骑行 15 km/h),够排序、不够承诺。
3. 地址做城市校验:同名街道跨城、省名含城市名(Provincia di Trento 含 Trento)都会误配,
所以只取结构化结果的 city/town/village 字段、用全等/前缀匹配,跨城的不硬算。
呈现时给一条"通勤"行:步行 8 分钟(0.6 km)|骑行 3 分钟|最近公交站 120 m。
排序:先按核验档位,再按通勤分;步行超 25 分钟的单独归"偏远可考虑"。
第 3 步:问询(一封问完)
先查台账"这条问过没有",再出文案:
python scripts/housing_db.py --db rental/housing.db fresh # 还没问过的候选
python scripts/outreach.py --profile renter.json --stage first_contact --lang it
# 已在核验层确认可租 → 加 --confirmed 才会抄送第三人(cc_mode=confirmed_only 时)
python scripts/outreach.py --profile renter.json --stage first_contact --lang it --confirmed
首轮问询必须一次包含 8 个决定性问题(清单与意/英原文见 references/question_pack.md):
是否还空着 → 能否 4–6 个月 → 不行是否接受转租(subentro) → 月总成本与押金 →
这周能否看房 → 有无性别/身份限制 → 合同类型与是否注册 → 其他硬性条件。
身份三张牌(每封都放):身份可信(学校/院系)+ 租期可连续住满(房东最怕中途空房)
+ 付得起(有税号、可签正规合同、可立刻付押金)。把"我能住满整段"放在最前面。
发出后立刻记台账:
python scripts/housing_db.py --db rental/housing.db contact \
--listing 12 --channel email --target landlord@example.com --questions "1,2,3,4,5,6,7"
房东异议("只租 12 个月"、"要担保人"、"还有别人在看"、"不签合同"…)的应对
见 references/objection_handling.md —— 先给房东一个"你不会空房"的方案,再谈自己的需求。
第 4 步:收信闭环(把回复读进来、分类、匹配台账)
前面只能把问询发出去;发 20 封就有 20 封回复,收回来这一段才是量最大的。
python scripts/inbox.py --mail smtp.json # 读+分类
python scripts/inbox.py --mail smtp.json --db rental/housing.db --mandate mandate.json
python scripts/inbox.py --mail smtp.json --writeback # 判定写回台账
python scripts/inbox.py --mail smtp.json --json replies.json # 结构化输出
它做四件事,并且每一件都可解释:
- 分类:
unavailable(已租掉)/declined(明确拒绝)/negotiating(在谈)/
available(可租)/bounce(退信)/needs_review(未命中规则)。
每条判定都会打印命中了哪几个关键词(多语言规则,见脚本内RULES)。 - 抽要素:押金 / 租金 / 租期(含"没有押金"这类否定语境的处理)。
- 匹配台账:按发件邮箱对上哪条房源、哪一轮问询;匹配不到的标"陌生来信"。
- 授权表判定:给了
--mandate就对照 A/B 线,输出
"可自动处理(授权范围内)" 还是 "需人工决策",并说明越了哪条线。
纪律:分类不靠猜。没命中任何规则的标 needs_review 交给人看,绝不硬猜;
默认只读,不加 --writeback 不改台账;推送给家人(抄送)仍走 outreach.py 的 --confirmed 门禁,本工具不做。
python scripts/housing_db.py --db rental/housing.db due --hours 48 # 该追的
python scripts/housing_db.py --db rental/housing.db asked # 已问过(别重复问)
python scripts/housing_db.py --db rental/housing.db report # 汇总
节奏:发出后 48 小时未回 → 发一次简短追问(不重发首轮);5 天仍无回 → 标"无响应",停止投入。
对方明确回复"不可租" → 标 excluded,以后不再出现。
自动化的边界(哪些能自动、哪些必须人来)
谈判可以大部分自动化,因为 mandate.json 就是谈判代理权:授权范围之内,
AI 可以自己回信、按让步梯度出牌、婉拒、标记排除。但有三条不能越:
| 级别 | 动作 | 谁做 |
|---|---|---|
| ✅ 可自动 | 问询、追问、澄清条件、授权范围内的让步与出牌、婉拒、标 excluded | AI |
| ⚠️ 必须人确认 | 超出授权表、承诺具体日期或金额、换房源、接受合同条款 | 人 |
| ⛔ 绝不能自动 | 付款、签字、提交证件 | 只能人 |
判定原则很简单:一旦某封信会让你们承担新的义务或付出钱,就必须停下来问人。
inbox.py 已经把"需人工决策"单独分桶输出,就是为这条服务的。
第 5 步:前置门禁(每一步之前先查)
python scripts/prep_check.py # 总进度 + 下一步该补什么
python scripts/prep_check.py --stage before_signing
python scripts/prep_check.py --mark codice_fiscale done
清单按五阶段推进(全部条目与理由见 assets/checklist.json):
阶段0 现在(税号、证件、付款方式、担保方案、居留材料)→
阶段1 找房前(渠道账号、专用邮箱、加群/学联/国际办公室、预算口径、话术)→
阶段2 看房前(看房工具包、必查 10 项、陪同人)→
阶段3 签约前(合同 8 项、税务注册、房东与产权核验、IBAN 核验、收据)→
阶段4 入住后(家具清单、水电登记、合同存档、退租通知期)。
有 blocking=true 的项没做完时脚本以退出码 1 退出——不要跳过它继续下一步。
第 5.5 步:资料与付款(防止"到了那一步才发现没做")
完整内容见 references/document_prep.md,三个要点:
- 资料包:身份类(护照/签证/录取证明/税号/证件照)+ 财务类(卡/资金来源/押金备足)
+ 证明类(学校介绍信——最有效也最便宜)+ 一段标准化自述。 - 填注册表:各国字段名不同但要的东西是同一批,用
document_prep.md的字段对照表;
姓名与护照完全一致,不确定的字段写"待确认"而不是瞎填。 - 付押金前必跑 IBAN 核验:
python scripts/iban_check.py "IT60 X054 2811 1010 0000 0123 456"
python scripts/iban_check.py --file accounts.txt --expect-name "Mario Rossi"
它做三件事:格式与 mod-97 校验位 → 国家长度与是否 SEPA/欧元区 → 收款人姓名一致性提示。
格式校验只能挡住"抄错/乱编",挡不住"假房东"——最后一步必须在银行转账页
核对收款人姓名与合同主体一致。 这一步工具替不了人。
第 5.7 步:合同风险 + 面对面谈判(AI 到不了现场的两步)
前面所有工作都是为了走到这两步,而它们有个共同点:AI 到不了现场、也签不了字。
所以只能提前把"看什么、防什么、怎么谈"交到人手里。详见 references/contract_risk.md。
① 拿到合同文本 → 逐条扫(PDF 先转文本):
python scripts/contract_check.py --file contratto.txt
python scripts/contract_check.py --file contratto.txt --json out.json
它定位 13 类关键条款(退租通知期/押金/合同类型/是否注册/租期/租金/杂费/转租/维修/违约金/
税号/产权/付款),命中给原文摘录,并把合同里没找到的条款单列(= 要去问房东);
再命中常见红旗表述(不退押金、只收现金、通知期 6 个月、注册义务推给租客…)。
纪律:找不到就明说找不到(标 ✖),绝不因正则没匹配就假装没问题;
每条附原文便于人工复核。它是筛子不是律师——合法性存疑时,找学校国际办或
当地租客协会(意大利 SUNIA / UDI,多数对学生会免费)复核。
② 去看房/签约前 → 把一套提示写在手机上(references/contract_risk.md 完整版):
- 交付物三件:授权底线(
mandate.py brief输出)、合同必查项、"当场绝不能做"三件事。 - 现场高频风险:被催付定金(没看房不付)、房东不签合同(没注册合同不入住)、
押金要现金不给收据、口头承诺不入合同(尤其 subentro / 通知期 / 押金退还)、费用口径模糊。 - 现场技巧:先给房东"你不会空房"的方案;只对授权表内的条件当场松口,超出的一律
"我回去确认一下";入住前把房屋瑕疵全拍下来;带一个能看合同的人。 - ⛔ 当场三不做:不签字(条款未核完)、不付款(未看房未核房东)、不交证件原件。
渠道可达性(决定"能不能真的联系上")
| 渠道 | 联系方式在哪 | 邮件能直达吗 | 该怎么做 |
|---|---|---|---|
| 学校/官方学生房源板 | 常在登录墙后;详情页可能只暴露板子自己的邮箱 | ❌ 多数不能 | 注册账号后走站内联系表单。注意:有的板子要勾"本人系本校在读学生"声明 + 验证码,只能学生本人注册,不得代注册代勾 |
| 分类广告站(subito 类) | 列表页无电话,详情页才有 | ❌ 无邮箱 | 站内信,或取电话由人拨打 |
| 社交群组(Facebook/微信/QQ) | 只能私信发帖人 | ❌ | 只草拟文案由人粘贴,不要自动发(风控/封号)。进群/发帖见 references/group_and_network.md |
| 中介 / 公寓运营公司 | 公开邮箱 | ✅ | 一封邮件可问多套,回复率最高,优先发 |
| 学校国际办公室 / 学生会 / 学联 | 公开邮箱 | ✅ | 请求转发求助帖、出学生身份介绍信、协助复核合同 |
先分诊再动手:能发邮件的一批发出去;发不到的按上表走对应渠道。
不要把所有房源都当成"可以发邮件问"——那样会一批都发不出去,还查不出原因。
换国家:国家包(从意大利扩到全欧)
本技能分两层,核心引擎国家无关,国家包按国家替换(详见 references/country_packs.md):
- 核心引擎:抓取 → 核验六字段 → 一次问全 → 台账 → 授权表 → 门禁。任何国家都成立。
- 国家包六槽位:① 身份号 ② 担保机制 ③ 押金惯例 ④ 合同类型 ⑤ 登记义务 ⑥ 主要平台。
已覆盖:意大利/法国/德国/西班牙/荷兰(详表)+比利时/葡萄牙/奥地利/波兰/捷克/爱尔兰/英国(简表)。
一条跨国通用规律:每个国家都有一个"必须先办的号"和"一套让你被信任的机制"。
顺序永远是 先办号 → 再建立可信度 → 才有资格谈房源;搞反就会出现"到了那一步才发现没做"。
所以 prep_check.py 的阶段 0 在任何国家都是第一优先。
换国家五步:① 替换 checklist 阶段 0 的号/担保叫法 → ② sources.py add 补该国平台
→ ③ 写 extract_<站点>.py → ④ 加该国语言模板 → ⑤ 补字段名对应。
做完第 ① 步先跑 prep_check.py --stage now 确认阶段 0 没有空槽位。
脚本
| 脚本 | 作用 |
|---|---|
scripts/onboard.py |
初始化访谈:谁住 / 谁办 / 发件箱 / 收件箱 / 抄送策略。init / check / show |
scripts/housing_db.py |
台账(SQLite):导入 / 列表 / 记问询 / 记回复 / 待追 / 已问 / 待问 / 汇总 |
scripts/verify.py |
核验规则引擎:六字段判定 + 五档分流 + --explain 讲理由 |
scripts/commute.py |
通勤计算 + 地图:房源 → 工作/学习地的步行/骑行/驾车距离与时间、最近公交火车站;show --links 给导航链接,map 生成自包含地图 HTML(geocode / route / enrich / show / map / cache,免费无 Key,直连优先) |
scripts/outreach.py |
按模板生成问询文案(意/英/中,含群申请与发帖),支持抄送门禁;不加 --send 只打印 |
scripts/inbox.py |
收信闭环:IMAP 读回复 → 分类(可解释)→ 抽要素 → 匹配台账 → 对照授权表给判定 |
scripts/prep_check.py |
前置准备门禁:按阶段列出必办项,阻塞项未完成则退出码 1 |
scripts/mandate.py |
谈判授权表:init 生成 / check 校验矛盾 / brief 出授权简报与让步梯度 |
scripts/sources.py |
平台登记表:add 扩充 / probe 实测可达性(直连优先,失败才降级走代理)/ learn 记录未知字段(自我学习)/ report 汇总 |
scripts/iban_check.py |
IBAN 格式与校验位核验 + SEPA/币种提示(付押金前必跑) |
scripts/contract_check.py |
合同风险扫描:13 类条款定位 + 红旗表述命中,逐条附原文;找不到的明说找不到;筛子不是律师 |
scripts/extract_example_operauni.py |
抓取层样板:学生房源板详情页 HTML → 标准 listings.json |
依赖:仅 Python 标准库(3.9+)。
参考文件
| 文件 | 什么时候读 |
|---|---|
references/onboarding.md |
开工第一步;有代租/抄送需求时 |
references/verification_rules.md |
核验每条房源时;判定"假消息/旧消息/僵尸房源"时 |
references/commute.md |
算通勤距离与时间时;要给房源排序、解释"到学校多远"时 |
references/question_pack.md |
写问询前;准备看房、签约问答时 |
references/objection_handling.md |
房东提异议、要议价、催单时 |
references/negotiation_mandate.md |
准备谈判前;做授权表访谈时(A/B/C/D 四条线) |
references/document_prep.md |
准备证件/付款/填注册表时;付押金前 |
references/contract_risk.md |
拿到合同文本时;去看房/签约前(合同 13 项 + 红旗 + 面对面谈判风险与技巧) |
references/group_and_network.md |
加群、发帖、找学联与国际办公室时 |
references/country_packs.md |
换国家时;要该国身份号/担保/押金/合同/平台时 |
references/prompts.md |
想给用户示例提示词/触发词时;发布介绍时 |
assets/checklist.json |
前置门禁的全部条目与理由(prep_check.py 读它) |
assets/sources.json |
平台登记表(sources.py 读写;含别名映射与学习记录) |
assets/templates/messages.json |
问询 / 追问 / 确认看房 / 群申请 / 群发帖的多语言文案模板 |
隐私与数据落盘(用之前看清这一节)
本技能面向公开发布,以下两点必须明确。
一、本技能不回传任何用户数据
- 无遥测、无上报端点、无日志外发。 全部脚本仅用 Python 标准库,代码可逐行审。
- 唯一的对外网络行为有两处:
1.sources.py probe:向你自己登记的 URL 发一次只读 GET,判断"这个站点能不能访问"。
2.commute.py:把房源地址与工作/学习地地址发给你指定的公开地理服务
(Photon / Nominatim / OSRM / Overpass)以算距离。同样只读、不携带账号信息。 - 两者都只发地址文本(不是你的身份信息);
commute.py的结果缓存在本地geocache.json,不回传。 probe与commute.py都不放 POST、不上传任何文件、不携带任何账号信息;
probe是显式触发的(不加--id/--all什么都不做)。contract_check.py完全离线:只读本地合同文本,不联网。
二、"访问网站"必然携带源 IP —— 这是协议,不是本技能的缺陷
任何 HTTP 请求都会让目标网站看到访问者的出口 IP。你用浏览器打开同一个网站也一样。
所以"会不会暴露 IP"这个问题,正确的问法是"出口走的是哪个 IP"。
三、网络路由原则:直连优先,失败才降级走代理
这是本技能贯穿抓取层的默认策略,针对国内网络环境优化:
第 1 步 先直连(显式禁用代理)
└ 直连能通 → 就用直连。更快、不消耗代理流量、不受节点质量拖累
第 2 步 直连不通(超时/被墙/被重置)→ 自动降级走代理
└ 代理只作"兜底通道",绝不作默认通道
理由:国内直连多数站点本来就通,而代理常因节点质量反而更慢。
把代理设成默认,等于让所有请求都为少数被墙站点买单。
probe 会把实际路由打印出来:
python scripts/sources.py probe --all
# ✓ subito_it HTTP 200,可访问
# 路由:直连 ← 没走代理
# ✓ some_site HTTP 200,可访问|直连失败(timed out),已自动降级走代理
# 路由:降级→代理 http://127.0.0.1:7890 ← 直连不通才挂代理
| 参数 | 行为 |
|---|---|
| (不加) | 直连优先,失败自动降级走代理(推荐) |
--direct |
只用直连,直连失败也不降级 —— 想确认"这个站国内到底能不能直连" |
--proxy URL |
强制只走该代理,不做直连尝试 |
同一原则适用于全部抓取层:curl 抓静态页先直连;Edge CDP 默认直连;
只有确认被墙的平台才挂代理。sources.py probe 的探测结果会写回 access 字段
(direct / proxy / login / blocked),抓取时按它决定路由,不用每次试。
四、本技能会写哪些文件(可随时删除,删了不影响技能本体)
| 文件 | 内容 | 位置 |
|---|---|---|
housing.db |
台账(房源/问询/回复/门禁进度),SQLite | ~/.overseas-rental/housing.db(默认)或 --db 指定 |
sources.json |
平台登记表(含探测记录) | 技能目录 assets/ 下 |
mandate.json |
谈判授权表(含预算、红线、家庭租期) | 你 init --out 指定的位置 |
geocache.json |
地理编码缓存(地址 → 坐标),避免重复请求 | ~/.overseas-rental/geocache.json |
commute_map.html |
commute.py map 生成的地图(含房源坐标与链接),别提交到公开仓库 |
你的工作目录(--out 指定) |
listings.json / _raw_html/ |
抓取原文与解析结果 | 你的工作目录 |
rental/ |
旧版默认台账目录(若存在会优先使用) | 当前目录 |
台账不会随当前目录漂移:不论在哪个目录敲命令,默认都指向 ~/.overseas-rental/housing.db。
要换位置用 --db <路径> 或环境变量 RENTAL_DB。
敏感度提示:mandate.json 与台账含家庭信息(预算、租期、住址、孩子姓名)。不要提交到
代码仓库、不要丢进共享目录、不要随技能包分发。技能包本身不含任何用户数据。
红线
- 没实地看房,一分钱不付。 任何"先付押金""很多人抢"的催促,先停下核实。
- 不签"没有注册合同"的私下住。 会影响居留/签证材料,押金也无凭据。
- 不把未核实的信息写成"可以租"。 宁可清单短,不许写不确定的事实。
- 不自动向社交平台发消息 / 加群 / 发帖。 只草拟文案,由人发出(风控与封号)。
- 不代替本人做身份声明。 需要"本人系某校在读学生"这类声明的注册,只能本人做。
- 不重复问同一个问题。 问过什么,先查台账。
- 不越出谈判授权表。 超出
mandate.py brief范围的承诺,不许写进发给房东的消息。 - 押金只付合同主体。 转账前跑
iban_check.py,并在银行页面核对收款人姓名一致。 - 不在群里发证件。 证件与账号信息只在核实身份、进入合同环节后提供。
- 代租不冒名。 代租时署名必须是委托人,不得署租客本人;
cc_mode=confirmed_only时未核实的房源不得抄送第三人(需显式--confirmed)。 - 不自动付款、不自动签字、不自动提交证件。 这三件事只由人做;
邮件回复可以在授权表范围内自动进行,一旦产生新义务就先停下问人。 - 分类不靠猜。 回复分类必须有可解释的关键词依据;未命中的标
needs_review交给人,
不得用"感觉像"来判定房东意图。 - 通勤数字是估算,不是承诺。 步行/骑行时间按距离÷速度估算,公交/地铁乘车时长不接入 GTFS
就算不出来。呈现时必须标注"估算",不得写成"你坐车 18 分钟就到"。 - 合同扫描是筛子,不是法律意见。 它只按关键词定位条款、标出常见红旗,
不判断条款合法性。合法性存疑时引导用户找学校国际办或当地租客协会,不得替用户下"这合同没问题"的结论。