KEGG数据库结构与API数据获取实战:从通路注释到工程化分析 KEGG 这个名词做生物信息的人几乎绕不开。我最早接触它是在一次代谢通路富集分析里当时对着一堆通路图发懵不知道那些方框、圆圈和连线到底在讲什么更不知道背后的数据是怎么组织的。后来项目做多了才慢慢摸清它的脾气——它不只是一个画通路图的网站本质上是一套把基因、蛋白、代谢物、疾病、药物串起来的关系数据库体系而且提供了相当完整的 API 接口能让我们把数据拉下来做批量处理。这篇东西我想聊的是 KEGG 数据库本身的结构以及怎么通过它的 API 把数据拿到手、用起来。适合两类人看一类是刚进组、被导师丢了一句你去查查 KEGG 通路的新手另一类是已经会用但总觉得数据来源不够透明、想搞清楚底层逻辑的老手。我会从数据库的组织方式讲起然后落到 API 的实际调用中间穿插我自己踩过的坑和几个容易被忽略的细节。核心关键词就三个KEGG 数据库、通路注释、API 数据获取。1. KEGG 到底由哪几块拼起来很多人以为 KEGG 就是一个通路图库这个理解不算错但太窄了。它其实是一个由多个子数据库组成的联合体每个子库负责一类实体彼此之间通过统一的标识符体系互相引用。搞清楚这个结构后面调 API 的时候你才知道该去哪个端点拿什么数据。1.1 三大类子库的分工KEGG 的子库大致可以分成三组。第一组是通路相关的核心是 KEGG PATHWAY也就是我们最常看的那张代谢或信号通路图配套的还有 KEGG MODULE功能单元比通路更细比如某个具体的降解模块和 KEGG BRITE层级分类体系把基因、蛋白按功能归类。第二组是基因组与分子实体相关的包括 KEGG GENES各物种的基因信息、KEGG GENOME物种基因组、KEGG ORTHOLOGY直系同源基因也就是 KO 编号的来源、KEGG COMPOUND化合物、KEGG REACTION生化反应、KEGG ENZYME酶。这一组是通路图的零件库通路图上的每个节点基本都能在这里找到对应的条目。第三组是表型与应用相关的比如 KEGG DISEASE疾病、KEGG DRUG药物、KEGG GLYCAN聚糖等。做药物靶点或者疾病机制研究的人会经常用到这一组。这三组之间的关系是通路图把分子实体组织成功能网络而表型类子库又把分子实体和临床、药物信息关联起来。理解这个零件—网络—应用的三层结构你在设计分析流程时就不会迷路。1.2 标识符体系为什么 KO 号这么重要KEGG 里最关键的标识符是KO 编号K 开头比如 K00001。KO 代表直系同源基因的功能分类一个 KO 号对应一组在不同物种中功能相同的基因。为什么它重要因为不同物种的基因编号比如人的基因、小鼠的基因各不相同但它们的 KO 号是统一的。做跨物种比较或者富集分析时我们通常先把各物种的基因映射到 KO再基于 KO 去做通路富集。除了 KO还有几个常见前缀需要记住map开头的是参考通路如 map00010 是糖酵解hsa开头的是人类专属通路hsa00010C开头是化合物R开头是反应ec开头是酶。这些前缀在调 API 时直接决定了你请求的 URL 路径记混了就会拿到空结果。提示同一个通路在不同物种下有不同的编号前缀但底层拓扑结构是共享的。做富集时用参考通路编号map 开头更通用做物种特异性分析时才用物种前缀。1.3 通路图的两种呈现参考图与物种图KEGG 通路图有两种形态。一种是参考通路图把所有物种里出现过的基因都画上去节点用 KO 号标注这是全集。另一种是物种特异性通路图只保留该物种实际拥有的基因节点用该物种的基因编号标注。这个区别在实际操作中很要命。如果你拿参考通路图去对照某个物种的基因列表会发现很多节点在该物种里根本不存在反过来如果你只用物种图又会丢失跨物种比较的视角。我的习惯是做单物种分析用物种图做多物种整合或者方法学展示用参考图两者配合着看。2. 调 API 之前必须想清楚的三件事KEGG 提供了 REST 风格的 API基础地址是https://rest.kegg.jp/操作类型通过路径段来区分比如get、list、find、link、conv等。看起来简单但直接上手就调往往会碰壁。我在实际项目里总结出三个必须先想清楚的问题想清楚了再写代码能省掉大量返工。2.1 你要的是列表还是条目详情这是最容易搞混的一点。list操作返回的是标识符列表比如list/pathway/hsa会返回人类所有通路的编号和名称但它不包含通路内部的基因信息。而get操作返回的是具体条目的详细内容比如get/hsa00010会返回糖酵解通路的完整记录包括基因、化合物、反应等。很多人一上来就想拿某个通路里有哪些基因结果用了list拿到的只是一堆通路名然后纳闷为什么没有基因。正确的做法是两步走先用list拿到通路编号再用link或者get去拿通路和基因的关联。link是专门用来查两个实体之间关联的操作比如link/hsa/hsa00010就是查人类基因和糖酵解通路的对应关系。2.2 请求频率与批量策略KEGG 的 API 对访问频率是有约束的虽然没有公开一个精确的阈值但短时间内高频请求会被限流甚至临时拒绝。我在早期项目里图省事写了个循环把几百个通路一个个get下来跑到一半就开始返回错误排查了半天才意识到是频率问题。后来我的做法是能批量就批量。KEGG 的 API 支持在路径里用拼接多个标识符比如get/hsa00010hsa00020一次拿两个通路。对于大批量任务我会在请求之间加一个短暂的间隔比如 0.3 到 0.5 秒并且做好失败重试。另外把结果缓存到本地是必须的——同一个数据反复请求既浪费配额又慢落盘之后后续分析直接读本地文件。2.3 返回格式的选择KEGG API 默认返回纯文本格式但很多操作支持通过参数指定返回 JSON。文本格式的好处是紧凑、易读适合快速查看JSON 的好处是结构化、好解析适合写进程序。我的经验是探索阶段用文本格式肉眼看正式流程里用 JSON 解析。不过要注意不是所有端点都支持 JSON具体要看操作类型。get和list一般可以通过在 URL 后加格式参数来切换但有些老端点只吐文本。写代码时最好对返回内容做个格式判断别硬编码解析逻辑。3. 几个高频 API 操作的实操拆解光讲概念没用这一节我把几个最常用的操作拆开讲每个都给请求示例、返回内容说明和使用场景。这些是我在真实项目里反复用到的不是照搬文档。3.1 用 list 摸清数据边界第一步永远是搞清楚有哪些东西。比如你想做人类代谢通路分析先得知道人类有多少条通路、编号长什么样。# 列出人类所有通路 curl https://rest.kegg.jp/list/pathway/hsa返回的是每行一个通路格式是通路编号\t通路名称。这里有个细节名称里可能包含分号分隔的多个别名解析时要注意按制表符切分别把名称里的分号也当分隔符。再比如列出所有物种curl https://rest.kegg.jp/list/organism这个返回里包含物种的三字母代码比如 hsa 是人类、mmu 是小鼠这个代码就是后面所有请求里要用的物种前缀。我见过有人拿物种全名去请求结果当然是空的——API 只认三字母代码。3.2 用 link 建立实体关联link是我用得最多的操作因为它直接回答A 和 B 有什么关系。常见用法有三种# 查某个通路包含哪些基因 curl https://rest.kegg.jp/link/hsa/hsa00010 # 查某个基因参与哪些通路 curl https://rest.kegg.jp/link/pathway/hsa:10327 # 查通路和 KO 的对应关系 curl https://rest.kegg.jp/link/ko/hsa00010返回格式是两列第一列是源实体第二列是目标实体用制表符分隔。这里要注意方向link/目标/源的顺序第一个路径段是你想查到的目标类型第二个是已知的源。写反了虽然不一定报错但结果会让你困惑。我踩过的一个坑是link/hsa/hsa00010返回的是基因编号比如 10327而不是基因符号比如 ALDH1A1。要拿基因符号还得再get一次基因条目或者用conv做编号转换。这个链路在设计流程时要提前规划好别到后面才发现拿到的是一堆数字。3.3 用 find 做关键词检索当你不知道确切编号、只有一个关键词时find就派上用场了。# 在人类基因里搜关键词 curl https://rest.kegg.jp/find/hsa/glucose # 在化合物里搜 curl https://rest.kegg.jp/find/compound/glucosefind是在名称和描述里做子串匹配返回匹配到的条目。它的局限是只做字面匹配不理解语义所以搜糖代谢这种中文概念是没用的得用英文关键词。另外搜索结果可能很多需要自己再做筛选。3.4 用 conv 做编号体系转换不同数据库用的编号体系不一样conv就是用来在 KEGG 编号和外部编号之间转换的。比如把 KEGG 基因编号转成 NCBI 的 Gene IDcurl https://rest.kegg.jp/conv/ncbi-geneid/hsa这个操作在做数据整合时特别有用。比如你手上有一批 NCBI Gene ID想映射到 KEGG 通路就得先conv成 KEGG 基因编号再link到通路。整个链路是外部编号 → conv → KEGG 基因 → link → 通路。少一步都接不上。4. 把 API 数据接进分析流程的完整链路单次调 API 只是拿数据真正有价值的是把数据组织成能支撑分析的形态。这一节我讲一个完整的、从原始请求到可用结果的链路以给定一批人类基因找出它们富集在哪些通路上为例。4.1 数据获取阶段的组织方式第一步是拿到基因和通路的全量关联。与其针对每个基因去查不如一次性把人类所有基因-通路关联拉下来存成本地表。请求是curl https://rest.kegg.jp/link/pathway/hsa -o hsa_gene_pathway.tsv这个请求返回人类所有基因和所有通路的关联对数据量不小但一次到位。落盘之后后续任何基因列表的富集都可以在这张表上做连接不用再联网。同样的思路把通路名称表、KO 关联表也各拉一份curl https://rest.kegg.jp/list/pathway/hsa -o hsa_pathway_names.tsv curl https://rest.kegg.jp/link/ko/hsa -o hsa_gene_ko.tsv这三张表构成了本地分析的基础。我一般会用一个脚本统一管理这些下载任务加上重试和校验比如检查文件行数是否合理避免某次网络抖动导致拿到残缺数据却不知道。4.2 编号对齐最容易被低估的环节拿到数据后第一个坎是编号对齐。你手上的基因列表可能是基因符号如 TP53而 KEGG 关联表里是基因编号如 7157。这中间的转换有两种办法一是用conv把 KEGG 编号转成 NCBI Gene ID 再和你的数据对齐二是用find或get拿到基因符号再做匹配。我倾向于第一种因为编号是稳定的符号可能因为别名、大小写、新旧命名而变化。实际操作中我会先建一张KEGG 基因编号 ↔ 基因符号 ↔ NCBI Gene ID的映射表后续所有对齐都走这张表。这张表本身也是通过 API 拉取基因条目后解析出来的。注意基因符号存在大量别名和历史命名直接按符号匹配的失败率比想象中高。能用编号就用编号符号只作为展示用。4.3 富集计算与结果解读有了基因-通路关联表和编号映射富集计算本身并不复杂对每个通路统计你的基因列表里落在该通路的基因数和背景期望做比较算一个显著性。这里不展开统计细节重点说数据层面的两个注意点。第一背景基因集的选择直接影响结果。如果你只把有 KEGG 注释的基因当背景和把全基因组当背景算出来的显著性差别很大。我的做法是明确记录背景集是怎么来的并在结果里注明避免别人复现时对不上。第二通路大小的影响。特别大的通路比如代谢通路往往包含上百个基因容易因为基因多而显得显著特别小的通路又容易因为偶然命中而假阳性。解读时要结合通路规模和实际生物学意义别只看 p 值。4.4 结果落盘与可复现性整个链路跑完后我会把中间产物全部保留原始 API 返回、解析后的关联表、映射表、富集结果。每一份都带上下载或生成的时间戳。这样做的好处是几个月后回头查某个结果能清楚知道当时用的是哪个版本的数据。KEGG 的数据是持续更新的通路会增删、基因注释会修订。如果不记录数据版本同一个分析在不同时间跑出来的结果可能不一致这在需要复现的项目里是致命的。我现在的习惯是每次正式分析前先拉一次全量数据存成带日期的快照分析只读快照。5. 那些文档里不会写的坑这一节是我这些年攒下来的经验都是实际踩过之后才明白的官方文档里基本不会提。5.1 物种代码不是想当然的物种三字母代码有历史遗留问题不是简单按学名缩写来的。比如人类是 hsa小鼠是 mmu大鼠是 rno这些还好记但有些物种的代码和直觉差很远靠猜基本猜不中。正确做法是先list/organism拿到全表再按名称检索。我见过有人硬编码了一个猜测的代码跑了一晚上返回全是空第二天才发现代码错了。5.2 通路编号的前缀陷阱前面提过 map 和物种前缀的区别这里再强调一个具体场景做富集时如果用物种前缀如 hsa00010拿到的是该物种特有的通路记录如果用 map 前缀map00010拿到的是参考通路。两者包含的基因集合可能不同。如果你的基因列表来自人类却用 map 前缀去关联可能会引入该物种没有的基因或者漏掉物种特有的基因。统一用哪个前缀要在流程设计时就定死别混用。5.3 返回内容的编码与分隔符KEGG API 返回的文本里字段分隔符是制表符但有些字段内部比如通路名称可能包含空格、分号、括号。解析时如果简单地按空白切分会把名称切碎。稳妥的做法是按制表符切分然后对每个字段单独处理。另外返回内容里偶尔会有非 ASCII 字符比如某些化合物名称里的特殊符号读取时要确保用 UTF-8 编码否则会出现乱码。5.4 批量请求的失败处理批量请求一定会遇到失败这不是会不会的问题是什么时候的问题。失败的原因可能是网络抖动、频率限制、服务端临时不可用。我的处理策略是三层第一层是请求间隔避免触发限流第二层是失败重试带指数退避第三层是结果校验检查返回内容是否符合预期格式不符合就当作失败重试。这里有个细节重试不能无脑重试同一个请求如果是因为频率限制失败立刻重试只会继续失败。指数退避的意思是第一次等 1 秒第二次等 2 秒第三次等 4 秒以此类推给服务端喘息的时间。5.5 数据更新带来的结果漂移KEGG 的数据不是静态的。通路会新增、基因注释会修订、某些条目会被合并或拆分。这意味着你半年前跑的分析今天用同样的代码跑结果可能不一样。这不是 bug是数据在演进。应对办法有两个一是固定数据快照分析只读快照保证可复现二是在报告里注明数据获取日期让读者知道结果对应的是哪个时间点的数据。我现在的项目里数据快照是标配没有快照的分析结果我不认。6. 从单次调用到工程化使用的思路如果你只是偶尔查一两个通路手动调 API 完全够用。但如果要把 KEGG 数据接进一个持续运行的分析流程就得考虑工程化的问题。这一节聊聊我在这方面的实践思路。6.1 封装一层客户端直接在每个脚本里写curl或者裸调 HTTP 库短期看省事长期看是灾难——请求逻辑、重试、缓存、解析散落各处改一处要动很多地方。我的做法是封装一个轻量的客户端把请求—重试—解析—缓存这套逻辑收进去上层只调方法。这个客户端至少要有几个能力统一的请求入口处理 URL 拼接和格式参数、自动重试带退避、本地缓存按请求 URL 做键命中就返回缓存、返回内容的结构化解析。封装好之后上层代码就变成我要某个通路的所有基因这样一句话底层细节全透明。6.2 缓存策略的设计缓存不是简单地把返回存下来就完事要考虑失效和更新。KEGG 数据会变缓存不能永久有效。我的策略是缓存带时间戳超过一定周期比如一周就视为过期下次请求时重新拉取。对于明确知道不会变的数据比如某个历史版本的通路记录可以标记为长期缓存。另外缓存要能区分不同的请求参数。同一个端点带不带格式参数、带哪些标识符返回是不同的缓存键必须把这些都编码进去否则会串数据。6.3 数据版本管理前面反复提到数据快照这里说具体怎么做。我的做法是每次全量拉取时把数据存到一个带日期的目录下同时在目录里放一个清单文件记录每个文件的来源 URL、行数、校验和。分析脚本读取数据时从清单里拿路径而不是硬编码。这样做的好处是任何时候都能回答这个结果用的是哪天的数据也能在数据出问题时快速定位是哪个文件坏了。清单文件本身很小但价值很大。6.4 与其他数据源的衔接KEGG 很少单独使用通常要和别的数据源配合。比如基因编号要和 NCBI 对齐化合物要和 PubChem 对齐通路要和 GO 或 Reactome 做交叉验证。这些衔接点都要在工程化设计时预留好接口。我的经验是把编号映射做成独立的一层所有跨数据源的转换都走这一层。这样当某个数据源的编号体系变化时只需要改映射层不用动上层分析逻辑。映射层的数据本身也来自 API 拉取同样要缓存和版本管理。7. 一些实际使用中的判断经验最后聊几个偏感觉的东西这些没法写成精确的规则但实际用起来很影响效率。关于什么时候用参考通路、什么时候用物种通路我的判断标准是看分析目的。如果目的是这个物种的代谢特征用物种通路如果目的是这个方法能不能推广到多物种用参考通路。两者不是对错问题是视角问题。关于富集结果的解读我越来越倾向于不只看显著性还要看通路之间的拓扑关系。KEGG 通路不是孤立的代谢通路之间有前体-产物的连接信号通路之间有上下游关系。一个基因同时落在几个相关通路上比落在几个毫不相干的通路上更有生物学意义。这个判断需要你对通路图本身有理解光看统计表是看不出来的。关于数据获取的粒度我的原则是宁可多拉不要少拉。全量拉一次存本地比反复按需请求要稳得多。存储成本很低反复请求的时间成本和失败风险反而更高。当然前提是做好缓存和版本管理别让本地数据变成一团乱麻。这套东西我用了几年从最开始的手动查网页到后来的脚本调用再到现在的客户端封装加数据快照每一步都是被实际问题逼出来的。KEGG 的 API 本身不复杂复杂的是围绕它的数据组织、编号对齐和可复现性。把这几件事想清楚剩下的就是写代码的体力活了。