ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

DrugBank 5.1.7数据库解析与本地化实战:从XML到SQLite的完整数据处理流程

DrugBank 5.1.7数据库解析与本地化实战:从XML到SQLite的完整数据处理流程 简介DrugBank 5.17 是面向药物信息学、生物信息学及计算药理学研究者的权威结构化数据资源适用于开展靶点预测、药物重定位、ADMET建模、知识图谱构建等科研任务。资源为单文件ZIP压缩包140.19MB内含1个核心XML文件——full database.xml完整承载该版本全部约14,000药物条目的标准化描述涵盖通用名/商品名、SMILES与InChI结构式、ATC分类、作用靶点含蛋白类型与亲和力、药代动力学参数、临床适应症与禁忌、不良反应及PMID文献引用等多维度字段。XML格式具备强自描述性便于通过Pythonlxml/ElementTree、JavaJAXB或Rxml2等工具进行解析、抽取与二次分析。已有2160人学习下载读者可直接获取开箱即用的全量结构化药物知识源无需注册DrugBank官网或处理API限流显著提升数据预处理效率与研究可复现性。1. 项目缘起为什么是DrugBank 5.1.7在药物研发、生物信息学或者临床药理学领域无论你是刚入门的研究生还是经验丰富的科学家都绕不开一个名字DrugBank。它就像一个全球药物信息的“中央数据库”把药物的化学结构、靶点、药代动力学数据、副作用、临床试验信息等等全都整合在了一起。但很多时候我们拿到的是一个版本号比如“DrugBank 5.1.7”然后可能就懵了——这到底是个啥怎么用它和别的版本有啥区别今天我就以一个用了DrugBank快十年的“老用户”身份来给你彻底拆解一下这个版本告诉你它里面到底有什么“宝藏”以及怎么把它真正用起来而不是仅仅停留在“知道有这么个数据库”的层面。首先明确一点DrugBank 5.1.7不是一个软件也不是一个需要安装的程序。它是一个特定版本的数据集。DrugBank数据库本身是持续更新的每个版本号如5.0.0 5.1.0 5.1.7都对应着在某个时间点“冻结”并发布的一份完整数据快照。5.1.7这个版本从版本号看属于5.1.x系列的一个修订版通常修复了一些bug或进行了小规模数据更新是5.1系列中相对成熟和稳定的一个节点。对于大多数非前沿探索性的研究比如构建预测模型、进行大规模的药物-靶点网络分析、教学示例等使用一个稳定的、文档齐全的版本远比追新用可能还存在问题的“最新版”要靠谱得多。选择5.1.7往往意味着你参考的论文、教程或工具链是基于这个版本构建的保证了研究的可重复性。那么这个数据集里到底装了什么呢简单说它把药物相关的所有数据用一种结构化的XML格式也有其他格式但XML是最全的打包好了。你需要做的就是把这个“数据包”下载下来然后用代码Python、R等或者专门的工具去“拆包”提取你需要的信息。接下来我就带你一步步“解剖”DrugBank 5.1.7从获取数据到实际应用把每个环节的细节和坑都讲明白。2. 核心数据获取与“拆包”实操拿到DrugBank 5.1.7的数据是第一步也是最容易卡住的一步。因为它的完整数据库Full Database不是完全公开免费下载的。这里分两种情况对应两种获取策略。2.1 官方途径申请与下载如果你是学术机构的研究人员最正规的途径是通过DrugBank官网进行注册和申请。你需要用一个机构邮箱如.edu或.ac.cn结尾去注册账号然后填写一份使用协议说明你的研究用途。审批通常需要几个工作日。通过后你就能在下载页面看到各个历史版本的链接。对于DrugBank 5.1.7你通常会下载到一个名为drugbank_all_full_database.xml.zip的压缩包具体文件名可能略有不同但核心是那个巨大的XML文件。解压后你会得到一个几百MB甚至上GB的XML文件。这就是数据的“本体”。注意官方下载的数据是受许可协议约束的严禁用于商业用途并且要注意引用规范。在发表文章时务必按照DrugBank官网的要求进行引用。2.2 备用途径与数据快照很多时候我们只是想快速验证一个想法或者跑通一个教程里的示例代码等不及官方的审批流程。这时一些研究机构或开源项目可能会提供某个历史版本的数据快照Snapshot供教学和可重复研究使用。例如在一些GitHub上的生物信息学项目里你可能会找到指向drugbank_all_5.1.7.xml文件的链接。在使用这类资源时务必确认其许可状态并仅用于个人学习和非商业的研究验证。假设你现在已经拿到了这个drugbank_all_5.1.7.xml文件。直接用文本编辑器打开它会卡死因为它太大了。我们需要用程序化的方式来解析它。2.3 第一次“拆包”解析XML结构DrugBank的XML文件结构是高度嵌套的。最外层是drugbank标签里面包含了成千上万个drug标签每个drug标签就对应数据库中的一种药物包括已批准的、实验性的、甚至撤市的。每个drug下面又有几十个子标签比如name,description,targets,interactions等等。我们可以用一个简单的Python脚本配合xml.etree.ElementTree库Python标准库无需额外安装来先窥探一下结构。这里不追求一次性处理全部数据而是先看看“长什么样”。import xml.etree.ElementTree as ET # 注意直接解析大文件可能内存不足这里使用迭代解析 context ET.iterparse(drugbank_all_5.1.7.xml, events(start, end)) context iter(context) event, root next(context) # 获取根元素 drug_count 0 for event, elem in context: if event end and elem.tag drug: drug_count 1 # 只处理前5个药物作为示例避免输出过多 if drug_count 5: # 获取药物名称和DrugBank ID name elem.find(name).text if elem.find(name) is not None else N/A db_id elem.find(drugbank-id).text if elem.find(drugbank-id) is not None else N/A print(fDrug #{drug_count}: ID{db_id}, Name{name}) # 查看是否有靶点信息 targets elem.find(targets) if targets is not None: target_list targets.findall(target) print(f Number of targets: {len(target_list)}) for t in target_list[:2]: # 只打印前两个靶点 t_name t.find(name).text if t.find(name) is not None else N/A print(f - Target: {t_name}) # 关键清空已处理的元素释放内存 root.clear() if drug_count 10: # 我们只看10个 break print(f\nTotal drugs scanned (first 10): {drug_count})这段代码用了迭代解析 (iterparse)这对于处理大型XML文件是至关重要的技巧。直接ET.parse()会把整个文件加载到内存很可能导致内存溢出OOM。iterparse允许我们像流一样读取文件处理完一个drug就立刻清理掉内存占用很小。运行这个脚本你就能看到类似这样的输出Drug #1: IDDB00001, NameLepirudin Number of targets: 1 - Target: Prothrombin Drug #2: IDDB00002, NameCetuximab Number of targets: 1 - Target: Epidermal growth factor receptor ...这说明我们成功“摸到了”数据的门道。每个药物都有唯一的DrugBank ID如DB00001和名称并且关联着它的靶点。3. 构建本地可查询数据库从XML到SQLite直接每次分析都去解析那个巨大的XML文件效率太低了尤其是当你需要频繁查询“某个药物的所有副作用”或者“作用于某个靶点的所有药物”时。标准的做法是把这个XML文件“导入”到一个关系型数据库中比如SQLite。SQLite是一个文件型数据库无需安装服务器一个.db文件就是整个数据库非常适合个人研究和中小型项目。这个过程我们称之为“数据转换”或“ETL”抽取、转换、加载。我们需要设计数据库的表结构然后把XML中的数据解析出来填进对应的表里。这是整个数据处理流程中最核心、最需要耐心的一步。3.1 数据库表结构设计针对DrugBank数据的特点我们至少需要设计以下几张核心表它们之间通过外键关联drugs (药物表)存储药物的核心信息。drugbank_id(主键): 如 ‘DB00001’name: 通用名type: 类型 (如 ‘biotech’, ‘small molecule’)groups: 所属分组 (如 ‘approved’, ‘experimental’)可以用逗号分隔多个值description: 描述文本cas_number: CAS号unii: UNII码targets (靶点表)存储药物作用的靶点通常是蛋白质。id(主键): 自增ID或使用靶点唯一标识如UniProt IDuniprot_id: UniProt ID (如 ‘P00734’)name: 靶点名称organism: 物种 (如 ‘Humans’)drug_target (药物-靶点关联表)这是一个“多对多”关系表因为一个药物可以有多个靶点一个靶点也可以被多个药物作用。id(主键): 自增IDdrugbank_id(外键): 关联drugs.drugbank_idtarget_id(外键): 关联targets.idaction: 作用机制 (如 ‘inhibitor’, ‘agonist’, ‘antagonist’)interactions (药物相互作用表)存储药物-药物之间的相互作用。id(主键): 自增IDdrugbank_id_1(外键): 相互作用的药物Adrugbank_id_2(外键): 相互作用的药物Bdescription: 相互作用描述severity: 严重程度 (如 ‘high’, ‘moderate’)当然根据你的研究重点还可以设计pathways(通路)、salts(盐型)、products(商品)、external_identifiers(外部标识符如PubChem CID, ChEBI ID) 等表。这里我们先聚焦核心的“药物-靶点”关系。3.2 编写Python ETL脚本接下来我们写一个完整的Python脚本使用sqlite3库Python内置来创建数据库和表并解析XML文件填充数据。这个脚本会稍长但逻辑清晰。import sqlite3 import xml.etree.ElementTree as ET from pathlib import Path def create_tables(conn): 创建数据库表 cursor conn.cursor() # 删除旧表如果存在 cursor.execute(DROP TABLE IF EXISTS drug_target) cursor.execute(DROP TABLE IF EXISTS interactions) cursor.execute(DROP TABLE IF EXISTS targets) cursor.execute(DROP TABLE IF EXISTS drugs) # 创建药物表 cursor.execute( CREATE TABLE drugs ( drugbank_id TEXT PRIMARY KEY, name TEXT, type TEXT, groups TEXT, description TEXT, cas_number TEXT, unii TEXT ) ) # 创建靶点表 (这里简化假设用uniprot_id做主键实际中需要处理重复和空值) cursor.execute( CREATE TABLE targets ( uniprot_id TEXT PRIMARY KEY, name TEXT, organism TEXT ) ) # 创建药物-靶点关联表 cursor.execute( CREATE TABLE drug_target ( id INTEGER PRIMARY KEY AUTOINCREMENT, drugbank_id TEXT, target_id TEXT, action TEXT, FOREIGN KEY (drugbank_id) REFERENCES drugs (drugbank_id), FOREIGN KEY (target_id) REFERENCES targets (uniprot_id) ) ) # 创建药物相互作用表 cursor.execute( CREATE TABLE interactions ( id INTEGER PRIMARY KEY AUTOINCREMENT, drugbank_id_1 TEXT, drugbank_id_2 TEXT, description TEXT, severity TEXT, FOREIGN KEY (drugbank_id_1) REFERENCES drugs (drugbank_id), FOREIGN KEY (drugbank_id_2) REFERENCES drugs (drugbank_id) ) ) conn.commit() print(数据库表创建完成。) def parse_and_insert(xml_file_path, conn): 解析XML并插入数据到数据库 cursor conn.cursor() # 用于去重和临时存储靶点信息 targets_seen set() # 用于批量插入提高效率 drugs_to_insert [] targets_to_insert [] drug_targets_to_insert [] context ET.iterparse(xml_file_path, events(end,)) for event, elem in context: if elem.tag drug: # 提取药物基本信息 drugbank_id_elem elem.find(drugbank-id[primarytrue]) drugbank_id drugbank_id_elem.text if drugbank_id_elem is not None else None name elem.findtext(name) drug_type elem.findtext(type) # 注意属性访问方式 groups [g.text for g in elem.findall(groups/group)] groups_str , .join(groups) if groups else description elem.findtext(description) cas_number elem.findtext(cas-number) unii elem.findtext(unii) if drugbank_id and name: # 确保有基本标识 drugs_to_insert.append((drugbank_id, name, drug_type, groups_str, description, cas_number, unii)) # 提取靶点信息 targets elem.find(targets) if targets is not None: for target in targets.findall(target): target_id_elem target.find(polypeptide) # 靶点信息通常在polypeptide子标签下 if target_id_elem is not None: uniprot_id target_id_elem.get(id) # 获取uniprot-id属性 target_name target.findtext(name) organism target.findtext(organism) if uniprot_id and uniprot_id not in targets_seen: targets_seen.add(uniprot_id) targets_to_insert.append((uniprot_id, target_name, organism)) # 记录药物-靶点关系 action target.findtext(actions/action) if drugbank_id and uniprot_id: drug_targets_to_insert.append((drugbank_id, uniprot_id, action)) # 提取药物相互作用信息 (这里只处理drug-interactions下的) interactions elem.find(drug-interactions) if interactions is not None: for interaction in interactions.findall(drug-interaction): interact_drug_id interaction.findtext(drugbank-id) description interaction.findtext(description) severity interaction.findtext(severity) if drugbank_id and interact_drug_id: # 确保两个ID都存在并且按某种顺序存储以避免重复例如按字母顺序 id_pair tuple(sorted([drugbank_id, interact_drug_id])) # 在实际应用中这里需要更复杂的去重逻辑此处简化 cursor.execute( INSERT OR IGNORE INTO interactions (drugbank_id_1, drugbank_id_2, description, severity) VALUES (?, ?, ?, ?) , (id_pair[0], id_pair[1], description, severity)) # 清理内存 elem.clear() # 批量插入药物和靶点数据 print(f准备插入 {len(drugs_to_insert)} 条药物记录...) cursor.executemany(INSERT OR IGNORE INTO drugs VALUES (?,?,?,?,?,?,?), drugs_to_insert) print(f准备插入 {len(targets_to_insert)} 条靶点记录...) cursor.executemany(INSERT OR IGNORE INTO targets VALUES (?,?,?), targets_to_insert) print(f准备插入 {len(drug_targets_to_insert)} 条药物-靶点关系记录...) cursor.executemany(INSERT INTO drug_target (drugbank_id, target_id, action) VALUES (?,?,?), drug_targets_to_insert) conn.commit() print(数据插入完成。) def main(): xml_path Path(drugbank_all_5.1.7.xml) db_path Path(drugbank_5.1.7.db) if not xml_path.exists(): print(f错误未找到XML文件 {xml_path}) return # 连接或创建SQLite数据库 conn sqlite3.connect(db_path) print(f已连接到数据库: {db_path}) try: # 创建表 create_tables(conn) # 解析并导入数据 parse_and_insert(str(xml_path), conn) # 创建索引以加速查询 print(正在创建索引...) cursor conn.cursor() cursor.execute(CREATE INDEX IF NOT EXISTS idx_drug_target_did ON drug_target (drugbank_id)) cursor.execute(CREATE INDEX IF NOT EXISTS idx_drug_target_tid ON drug_target (target_id)) cursor.execute(CREATE INDEX IF NOT EXISTS idx_interactions_did1 ON interactions (drugbank_id_1)) cursor.execute(CREATE INDEX IF NOT EXISTS idx_interactions_did2 ON interactions (drugbank_id_2)) conn.commit() print(索引创建完成。) # 验证一下数据 cursor.execute(SELECT COUNT(*) FROM drugs) drug_count cursor.fetchone()[0] cursor.execute(SELECT COUNT(*) FROM targets) target_count cursor.fetchone()[0] cursor.execute(SELECT COUNT(*) FROM drug_target) relation_count cursor.fetchone()[0] print(f\n数据统计:) print(f 药物总数: {drug_count}) print(f 靶点总数: {target_count}) print(f 药物-靶点关系总数: {relation_count}) except Exception as e: print(f处理过程中发生错误: {e}) conn.rollback() finally: conn.close() print(f\n数据库已关闭。文件保存在: {db_path}) if __name__ __main__: main()这个脚本做了以下几件关键的事创建了一个结构化的SQLite数据库定义了四张核心表。使用迭代解析处理巨大的XML文件避免内存爆炸。提取了药物、靶点、药物-靶点关系、药物相互作用这几类核心信息。使用了批量插入 (executemany)和创建了索引这两步对于处理数万条记录的性能提升是巨大的。没有索引复杂的关联查询会慢得无法忍受。加入了简单的错误处理和去重逻辑INSERT OR IGNORE。运行这个脚本可能需要几分钟到十几分钟取决于你的电脑性能和XML文件大小。完成后你会得到一个drugbank_5.1.7.db文件。恭喜你你已经拥有了一个本地、可高速查询的DrugBank 5.1.7数据库4. 从数据到洞察典型查询与应用场景有了本地数据库我们就可以像使用任何关系型数据库一样用SQL进行灵活的查询。这才是发挥数据价值的关键。下面我举几个最常见的分析场景和对应的SQL查询。4.1 场景一查找特定药物的所有靶点假设我们想研究抗癌药“伊马替尼”Imatinib的作用机制。-- 首先找到伊马替尼的DrugBank ID SELECT drugbank_id, name, type FROM drugs WHERE name LIKE %imatinib%; -- 很可能返回 DB00619 -- 然后查询它的所有靶点 SELECT d.name as drug_name, t.name as target_name, t.uniprot_id, dt.action FROM drugs d JOIN drug_target dt ON d.drugbank_id dt.drugbank_id JOIN targets t ON dt.target_id t.uniprot_id WHERE d.drugbank_id DB00619;这条查询会返回伊马替尼的所有已知靶点比如著名的BCR-ABL、c-KIT、PDGFR等以及它对每个靶点是“抑制剂”inhibitor还是其他作用。4.2 场景二查找作用于某个特定靶点的所有药物这是药物重定位Drug Repurposing的经典思路。比如我们想知道有哪些药物作用于“血管紧张素转换酶”Angiotensin Converting Enzyme, ACE。-- 先找到靶点的UniProt ID SELECT uniprot_id, name FROM targets WHERE name LIKE %angiotensin converting enzyme%; -- 假设找到 P12821 (ACE) -- 查询作用于该靶点的所有药物 SELECT t.name as target_name, d.drugbank_id, d.name as drug_name, d.type, d.groups, dt.action FROM targets t JOIN drug_target dt ON t.uniprot_id dt.target_id JOIN drugs d ON dt.drugbank_id d.drugbank_id WHERE t.uniprot_id P12821 ORDER BY d.groups; -- 按药物分组如已批准、实验性排序结果里你会看到卡托普利Captopril、依那普利Enalapril等一堆“普利”类降压药它们都是ACE抑制剂。4.3 场景三分析药物的相互作用网络对于临床用药安全相互作用分析至关重要。我们可以查询与“华法林”Warfarin有相互作用的药物。-- 找到华法林的ID SELECT drugbank_id FROM drugs WHERE name LIKE %warfarin%; -- 假设是 DB00682 -- 查询其相互作用 SELECT d1.name as drug_a, d2.name as drug_b, i.description, i.severity FROM interactions i JOIN drugs d1 ON i.drugbank_id_1 d1.drugbank_id JOIN drugs d2 ON i.drugbank_id_2 d2.drugbank_id WHERE d1.drugbank_id DB00682 OR d2.drugbank_id DB00682 ORDER BY i.severity DESC; -- 按严重程度降序排列这个查询能列出所有与华法林存在相互作用的药物并标注严重程度对于理解其用药禁忌非常有帮助。4.4 场景四简单的统计与洞察我们还可以做一些宏观统计了解DrugBank 5.1.7这个版本的数据全貌。-- 统计已批准的小分子药物和生物技术药物各有多少 SELECT type, COUNT(*) as count FROM drugs WHERE groups LIKE %approved% GROUP BY type; -- 统计最“热门”的靶点被最多药物靶向 SELECT t.name as target_name, t.uniprot_id, COUNT(dt.drugbank_id) as drug_count FROM targets t JOIN drug_target dt ON t.uniprot_id dt.target_id GROUP BY t.uniprot_id, t.name ORDER BY drug_count DESC LIMIT 20;最后一个查询能告诉你像“多巴胺受体D2”、“血清素转运体”这样的靶点是药物研发的“兵家必争之地”。5. 进阶应用与避坑指南掌握了基础查询我们可以玩点更花的。但在这之前有几个我踩过的“坑”必须提醒你。5.1 数据清洗的“暗礁”DrugBank的数据质量很高但并非完美。直接使用原始数据可能会遇到问题靶点标识符混乱同一个蛋白质可能有多个UniProt ID不同亚型、不同物种。我们的脚本简单地将polypeptide标签的id属性当作UniProt ID但有时这个字段可能是空的或者包含其他数据库的ID如GenBank。更稳健的做法是优先使用polypeptide下的external-identifier子标签其中resource属性为“UniProt KB”的条目。药物分组Groups是列表我们把分组用逗号拼接成了一个字符串。这在查询时不够方便。更好的设计是单独建一张drug_groups表实现药物与分组的多对多关系这样查询“所有已批准药物”会更快更标准。相互作用去重我们的脚本中相互作用的存储逻辑是简化的。DrugBank的相互作用是双向的A与B相互作用但数据中每条记录只出现一次。我们的插入逻辑按字母排序存储可以避免重复但如果从不同药物的角度解析同一条相互作用可能会尝试重复插入。使用INSERT OR IGNORE并确保(drugbank_id_1, drugbank_id_2)组合唯一可以解决。5.2 结合其他数据源DrugBank的强大之处在于它可以作为“枢纽”连接其他数据库。例如连接PubChem通过drugbank_id关联PubChem CID可以获取更丰富的化学结构信息和活性数据。连接ChEMBL获取更详细的生物活性数据和IC50、Ki等参数。连接UniProt通过UniProt ID获取靶点的完整蛋白序列、功能域、GO注释等信息。这通常需要你下载这些外部数据库并建立基于共同标识符如DrugBank ID, UniProt ID, PubChem CID的连接表。工作量巨大但构建成功后你的分析能力将呈指数级提升。5.3 性能优化要点当你的分析变得复杂比如要做全网络的图分析时性能很重要。索引是关键如前所述在所有用于连接JOIN和筛选WHERE的列上创建索引。我们的脚本已经在drug_target和interactions表的相关列上创建了索引。考虑数据库分片如果数据量极大可以考虑按药物类型小分子/生物药或首字母将表水平拆分。使用更专业的工具对于复杂的图查询例如“找出与药物A有相同靶点但与药物B没有相互作用的药物”可以考虑将数据导入图数据库如Neo4j其查询语言Cypher比SQL更适合表达这种多跳的关系查询。5.4 版本差异的考量你可能会问为什么非要纠结于5.1.7我用最新版不行吗当然可以但需要注意数据模式Schema可能变化不同版本的XML标签结构可能有细微调整。为5.1.7写的解析脚本不一定能直接用于5.1.8或6.0.0。在解析前最好先用文本编辑器打开一小部分文件头部看看标签结构。可重复性在科学研究中指明你使用的数据版本DrugBank 5.1.7是保证他人能复现你结果的基本要求。直接说“使用了DrugBank数据库”是不严谨的。工具链兼容性很多开源的分析管道或软件包可能是在特定版本的DrugBank数据上开发和测试的。贸然升级版本可能导致不可预知的错误。处理完DrugBank 5.1.7你收获的不仅仅是一份数据更是一套处理复杂生物医学数据库的通用方法论从获取、解析、建模到查询分析。这套方法同样适用于UniProt、ChEMBL、KEGG等其他资源。记住数据本身是静态的但当你把它装进数据库并用清晰的逻辑去提问时它就变成了流动的洞察力。本文还有配套的精品资源点击获取
返回列表