一个「爬两个字段」的工单,怎么变成三次推翻前提、九个数据源、最后把其中一半任务取消掉。
工单只有一句:给「V3鱼总表」补 偏好水温上下限两个字段,数据从 FishBase 爬。
预期路径很短:取鱼种清单 → 爬 FishBase → 写回表格。
而这件事从头到尾,没有一次「取回一个事实」。 每个值最后都是「用一条明确规则把位置填上,并标注它是什么」—— 找源失败不是执行问题,是这些量本来就不在源里。
这是最花时间的一段——找源、发现源不对、换源。九个源逐个说清。
Preferred temperature (Ref. 123201)采用16.9 - 31.1, mean 26.7。
它是 AquaMaps 模型推算的「海洋生态位」偏好水温——海洋/溯河鱼有值,纯淡水鱼系统性缺失。取数必须走 country/CountrySpeciesSummary.php,summary/<属>-<种>.html 那个页面不渲染这一行。
用于 海洋模型 51 行 + 淡水期缺 13 行。
Ref. 12741 等)排除Environment: 行里的 Subtropical; 10°C - 28°C。逐字能对上表内既有的 资料水温下限/上限。
它是按气候带查表,不是物种属性:267 行只有 141 个不同取值,17–27 一个值被 26 种互不相关的鱼共用;还含 −1.3℃ 这种水在 0℃ 就结冰的负值。ref 也不唯一(鲻鱼用的是 4944)。
10.5066/f7wd3zh8)排除 · 最险的坑MINTEMP/MAXTEMP(℃、每种一对上下限)……
但语义完全不对:MINTEMP = 分布区质心的「30 年 1 月最低温」,MAXTEMP = 「7 月最高温」,由 range map + PRISM 400m 栅格读出 ⇒ 陆地气温气候位,不是鱼的适温。MINTEMP 低到 −22.5℃——水 0℃ 就结冰。-1 不是「−1℃」而是「美国本土无分布图」。
sciencebase.gov/data.usgs.gov 被 Cloudflare 403,挂代理重试仍 403。
绕路核法:改走 DataCite REST API(不受 CF 影响),按 USGS 的 DOI 前缀 10.5066 全库枚举 + 十余组关键词 ——
temperature preference marine fish → 0;thermal tolerance marine fishes → 0;preferendum → 全库 0 命中。
⇒ USGS 的水温数据几乎全是站点/课题级观测,不是跨物种汇编。
TempAffinity无边界字段temperature_fav(最喜欢的温度 ×10,单点)+ temp_affected_ratio(半宽)+ temp_threshold(门控)。
⇒ 模型里没有任何「下限/上限」字段——「可接受水温」在生产模型里根本不存在这个概念。
BasicFishHabitus.temperature_range | 只有 2 行真实数据,且两行值完全相同 |
FishWiki.water_temp | 49 行,有值的 0 行 |
PondList.hypolimnion_t | 13 个钓场全是 100(同一常量) |
首轮抓完 77/267(28%)。按常规该「再爬一遍」。查下去发现不是抓取问题——看按科的命中分布:
sources/scrape_out.csv⇒ 引出两条补法:淡水源(Hasnain)与模板法。
偏好列写进去后,发现 77 条里 55 条「偏好区」跑到「可接受区」外面——比如褐鳟偏好 14.5–17.6,而「可接受」写 18–24,完全不重叠。
按常规该去找一个更准的可接受源。但先查了一件事:「资料水温」到底怎么来的?
逐字核对 FishBase:白斑狗鱼 10–28、褐鳟 18–24、虹鳟 10–24 —— 对应的是 Subtropical; 10°C - 28°C 这种 气候带温度范围。
⇒ 那个「倒置」被读懂为「两列本就不同源」——倒置是证据,不是待修的病。
顺着「可接受区要不要重做」往下,做了一件更省事的动作——去翻生产配置表,看这个字段到底被谁读。
生产水温模型=「单点 + 半宽 + 门限」,没有区间
temperature_fav(最喜欢的温度 ×10,单点)+ temp_affected_ratio(半宽)+ temp_threshold(门控)
⇒ 资料水温下限/上限 不进入生产模型,它是「资料」参考列。
⇒ 整个「找可接受水温源」的任务取消了——那个方向本来会白费一轮。
「FishBase 里没有」这句话,其实含 三种完全不同的情况——把三者混为一谈,就会在最难的那一类上反复白找源。
水温数据口径 = 海洋模型·淡水期缺 即可取待办清单一句判据:「FishBase 没有」→ 先问是「真没有」、还是「给了错阶段」。
前者要去别的源找;后者只要换一个更贴近游戏场景的源。
两者混在一起找,会在第三类上白耗。
值来自多个源,所以加了一列 水温数据口径 逐行标出来源——把「推导值」与「观测值」分开。
模板法有一个我自己埋的坑(后来才发现):
广温带当时特意用「全跨度」而不是中位数,理由是「避免宽度塌缩」。结果 广温模板宽 31.4℃,是其它带的 3–4 倍——当时没觉得有问题,直到它把接受区算成负数才暴露。
而「广温」的语义是「耐受广」,「广」应该体现在可接受上,不是偏好上。
22 条洄游种当时用的都是 AquaMaps 海洋值,而早先裁过「洄游种一律取淡水期值」——这批全部不合规。
Noob_Sea「新手海」存在但 is_show=否(禁用)⇒ 没有独立海鱼池;
真正的海洋种集中在港口村;22 条里只有 7 条真被投放,其余 15 条没进任何水域关键判据不是 FishBase 的 diadromous 标记,而是「这条鱼在游戏里主要在哪个水体被钓到」——
公牛鲨、鲻鱼、沙鮻主要是海洋鱼,FishBase 挂标记就给它们填淡水值是荒唐的。
判据是嵌套(偏好区必须落在接受区里):成立率只有 30%(80/267),187 行越界,其中 57 行偏好区比它自己还宽。
用 Hasnain 的 UILT(上临界致死温度)卡——可接受上限必须低于致死线,否则必错。
| F | 越致死线的种数 | 说明 |
|---|---|---|
| 1.0 | 1 | 外扩几乎看不见 |
| 2.0 | 1 | 降段可见、且仍在致死线内 |
| 2.5 | 16 | 接近一半的种越过致死线 |
一处自我更正:中途说过「k ≤ 1.25 是硬上界」——表述不准。
精确上界是 k < 1.372,且由单个种(蓝鳃太阳鱼)决定、只在 15% 的种上可测、用的是致死线 ⇒
必要条件,不是充分条件。F 的实际取值由「要留多宽的降段」这个设计意图定,边界只给上限。
temp_threshold 同向但不同层:一个是形状外沿,一个是消费判据蓝鳃太阳鱼:偏好 26.9–31.2 → 接受 24.9–33.2,而致死线 32.0 ⇒ 越过。
但这两个数同出 Hasnain,偏好上限离致死线只剩 0.8℃ —— 真实蓝鳃太阳鱼耐受远高于 32℃。 ⇒ 判为那个致死线读数偏低,不是 F 取大了。保留并标注,不为它把全表的 F 压到 0.8。
| 落点 | 内容 |
|---|---|
| 飞书 V3鱼总表 rev 5047 | 新增 5 列:偏好 2 + 口径 1 + 接受 2;267 行全有值、0 空值 |
| 交付目录 | final/(权威,飞书回读生成)/sources/(源与脚本)/backup/(6 份写入前快照) |
| Notion 鱼种库 | 加 5 个属性 + 267 条记录更新(含源表版本 4987→5047) |
| 线上速览 | 本页 |
这些都不是推演出来的,是踩过之后才留下形状的。
| 原则 | 来自哪个坑 |
|---|---|
| 先查用法,再找源 | 三次转折都靠它,最后一次直接把任务取消 |
| 列名 ≠ 语义 | scientific_name 装俗名、Temperature Tolerances 装陆地气温… |
| 看填充率,不只看字段在不在 | FishWiki.water_temp 单看名字以为有数据,49 行全空 |
| 断言「不存在」要给系统级证据 | 「我没找到」是关于我的;「字段建了但全空」是关于系统的 |
| 过程成立 ≠ 产物可用 | 「模板不必重推」推不出「模板值可写进生产」 |
| 交付物要回读生成 | 中间产物混进交付目录,会被当成生效数据引用 |
| 自我更正也要留痕 | 「k ≤ 1.25 是硬上界」被自己证伪,更正一并记 |
共同点:每一次正确的判断,都来自「先去看它实际是什么」,而不是「先假设它应该是什么」。
全程用到的来源(含采纳、校验、以及调研过但排除的)。完整清单含可达性实测见 SOURCES.md。
| 用途 | 来源 | 链接 |
|---|---|---|
| 采纳|海洋模型 51+淡水期缺 13 | FishBase Preferred temperature(Ref. 123201) |
fishbase.se/country/CountrySpeciesSummary.php?GenusName=…&SpeciesName=… |
| 采纳|野外实测 41 | Hasnain, Minns & Shuter 2010, Key Ecological Temperature Metrics for Canadian Freshwater Fishes(CCRR-17) | collections.ola.org/mon/25007/299113.pdf |
| 校验|接受上限天花板 | 同上的 UILT(上临界致死温度,39 种可测) |
同 CCRR-17 |
| 排除|气候带查表值 | FishBase Environment: 行(常见 Ref. 12741,鲻鱼为 4944) |
fishbase.se/References/FBRefSummary.php?ID=12741 |
| 排除|语义错(是陆地气温) | USGS FishTraits Database(10.5066/f7wd3zh8) |
api.datacite.org/dois/10.5066/f7wd3zh8 |
| 排除|USGS 无逐种温度数据 | DataCite API 按 DOI 前缀 10.5066 全库枚举 |
api.datacite.org |
| 备查未采用|覆盖更大的海洋候选 | Morley et al. 2019(Dryad 10.5061/dryad.1m2vn52)Dahlke et al. 2020 Science(PANGAEA 10.1594/PANGAEA.917796) |
datadryad.org / doi.pangaea.de |
| 本地|权威目标表 | 飞书多维表格「V3鱼总表」(267 行) | base_token P9rTbUTARaE1GasZp3rctaoungb |
| 本地|生产配置表 | preCompute/configs/xlsx/(TempAffinity、fish_pond 等) |
— |
可达性实测(2026-09-27):主用源与关键 PDF 全部 200 ✓。
sciencebase.gov / hydrosource.ornl.gov / zenodo.org 为 403(Cloudflare)——
与采集当时实测一致,正因如此当时才改走 DataCite API 绕路。
完整过程与决策记录:PROCESS-RECORD.md · 原始来源清单:SOURCES.md
交付目录:fishbase-pref-temp-2026-09-18/ · 目标表:飞书「钓鱼元素周期表 → V3鱼总表」rev 5047