做网络药理学的人,应该都经历过这个阶段:文献里到处都是TCMSP、OB、DL、靶点预测这些词,真到自己动手的时候,第一步取数据就被卡住了。TCMSP确实能查,但是你要把几十个成分、上百个靶点一个一个从网页上复制到Excel,再手动去重、匹配基因名,一晚上就这么过去了。我第一次做中药-靶点网络图的时候就是这么干的,后来痛定思痛,把所有能自动化的环节全部用R语言脚本化,才有了这套从网页爬虫到最终网络图输出的完整流程。
这篇文章我不讲虚的,直接把整个流程拆开给你看:怎么分析TCMSP的请求结构,怎么写爬虫脚本,怎么用R语言清洗数据,怎么构建中药-靶点网络图,以及我踩过哪些坑。全文用一套真实的代码串下来,你只要能跑通R,跟着抄就行。
1. 项目概述:为什么要把TCMSP、R和爬虫放在一起
1.1 这个项目到底在解决什么问题
中药-靶点网络分析的常规操作是:选定一味中药,在TCMSP数据库里查到它的活性成分,再查每个成分对应的靶点蛋白,最后把这些关系画成一张网络图。听起来很简单,但实际执行的时候你会发现三个痛点。
第一个痛点是数据量大。一味中药通常有几十个甚至上百个化学成分,每个成分又对应多个靶点,一个项目下来动辄上千条关系记录。手动复制粘贴不仅慢,而且容易出错——我见过有人把两行数据贴反,导致后面整个靶点富集分析都错了。
第二个痛点是数据格式不统一。TCMSP返回的表格里有成分ID、分子名称、OB值、DL值、靶点名称等十几列,有些列名在不同页面里还不一样。直接下载下来用Excel打开,中文可能乱码,列名可能有空格,目标基因名称时而是标准Symbol、时而是UniProt ID,这些都需要清洗。
第三个痛点是重复工作。今天分析黄芪,明天分析丹参,后天分析一个复方,如果每次都手动操作,同样的清洗逻辑要写三遍。但只要你把爬虫和清洗脚本封装成函数,换一味药只是改一个参数的事。
所以我做这个项目的目标很明确:输入一位中药的英文名或拼音,输出一张可以直接放进论文里的中药-靶点网络图,中间所有环节都自动化。
1.2 为什么选TCMSP而不是其他数据库
搞药物靶点预测的数据库其实不少,比如STITCH、SwissTargetPrediction、Batman-TCM,但我个人最常用的还是TCMSP。原因有几点。
TCMSP的全称是Traditional Chinese Medicine Systems Pharmacology Database and Analysis Platform,由西北农林科技大学团队开发,2014年发表在Journal of Cheminformatics上,算是中药网络药理学领域引用率最高的数据库之一。它的数据覆盖面广,包含了近500味中药材、3万多个化学成分、3千多个靶点以及相关的疾病信息,尤其适合做中药全成分分析。
另外它的数据字段是现成的,OB(口服生物利用度)和DL(类药性)这两个筛选指标直接列在表格里,不需要自己再去其他数据库计算。做网络药理学的时候,这两个指标几乎绕不开,TCMSP直接给出来就能省很多事。
TCMSP的劣势是网页技术比较老旧,批量操作极其不友好。这个数据库没有提供公开的API接口,想要自动拿数据就必须走网页请求的路子。这就引出了下面的话题:所谓的爬虫,本质上就是把浏览器里手动点击的过程,变成用代码发HTTP请求的过程。
1.3 为什么流程搭建选择R语言而不是Python
说到爬虫,大多数人第一反应是Python,这没有错。但我的场景比较特殊,因为整个分析链条的后半段——数据清洗、网络构建、可视化——我是打算在R里完成的。如果爬虫用Python,那我就要在两套语言之间反复切换,数据还得通过CSV文件中转,流程上多出不少麻烦。
R语言在生物信息学领域有天然优势,尤其是Bioconductor体系的包,比如org.Hs.eg.db做基因ID转换、clusterProfiler做富集分析,这些都是Python生态里没有的对等替代。rvest包做HTML表格解析也足够顺手,处理TCMSP这种老式网页完全够用。
如果你本身熟悉Python,也可以用requests+BeautifulSoup完成爬虫部分,最后把结果存成CSV再给R用。这两种方案我都试过,但最终保留了R单语言方案,收益是流程更好维护,一个脚本从头跑到尾,不容易在语言切换的过程中丢信息。
2. 环境准备与包管理:先把地基打好
2.1 R和RStudio的基础安装
如果你电脑上还没有R环境,先去CRAN的国内镜像下载R安装包,安装的时候保持默认选项即可。装完之后我建议再装一个RStudio,它不是必需的,但确实好用,代码高亮、变量查看、绘图预览都方便很多。R语言本身是一个解释型语言,安装完成后打开RStudio,在Console窗口输入version,能看到版本信息就说明环境没问题。
TCMSP相关的分析对R版本没有特殊要求,3.6以上就能跑,但如果你要用最新版的dplyr或ggplot2,还是建议把R升级到当前的最新稳定版。老版本的R在安装某些依赖包的时候会报错,比如tidyverse里的某些包要求R版本不低于4.0,这一点装包的时候注意看提示就行。
2.2 需要安装的R包清单与作用
这个项目会用到这么几类包,我按功能列一下,方便你对照安装。
- rvest:核心爬虫包,用于发送HTTP请求和解析HTML表格,本项目的爬虫部分全靠它。
- httr:底层请求工具,处理User-Agent、Referer、Cookie等细节,很多时候需要和rvest搭配使用。
- dplyr和tidyr:数据清洗利器,负责筛选OB/DL值、去重、列名重命名等操作。
- stringr:字符串处理,比如从靶点名称里去掉多余空格、统一大小写。
- igraph:网络分析核心包,负责构建图和计算网络属性。
- ggraph:基于ggplot2的网络可视化包,画出来的图比igraph自带绘图函数美观得多。
安装命令很简单,在RStudio里一行搞定。
install.packages(c("rvest", "httr", "dplyr", "tidyr", "stringr", "igraph", "ggraph"))如果你安装ggraph时遇到依赖问题,通常是因为它的底层依赖tidygraph和graphlayouts还没装好。可以先把这两个包装上,再回头装ggraph,这样报错概率低很多。
2.3 项目目录结构的设计思路
动手写代码之前,建议先规划好项目目录。我的习惯是建一个项目文件夹,里面划分成scripts、data、output三个子目录。scripts放R脚本,data放原始爬取的数据,output放清洗后的表格和网络图文件。
这个分法看起来很基础,但实际项目中能省下不少事。TCMSP爬虫脚本需要跑一段时间,中间可能会断,跑出来的数据先存成RDS格式存放在data目录,即使后续脚本改坏了,也不用从头再爬一遍。R语言里保存中间数据用的是saveRDS()和readRDS(),比反复读写CSV要快,而且能保留数据类型信息。
3. 拆解TCMSP的请求结构:爬虫前必须先学会看网页
3.1 用浏览器开发者工具查真实请求
写爬虫之前最重要的一步,是搞清楚TCMSP网页提交请求时到底发了一个什么样的HTTP请求。很多人一上来就写代码,结果怎么都抓不到数据,问题往往出在这里。
以我目前使用的情况来看,TCMSP的入口地址是一个PHP文件,你在首页搜索框里输入草药名称,点击搜索之后,浏览器会向服务器发送一个带参数的GET请求。参数里有两个关键字段,一个表示搜索关键字,另一个表示搜索类型,比如搜索草药时类型就是herb_all,搜索某个具体成分时类型就是mol_all。
验证方法很直接:在浏览器里打开TCMSP首页,按F12进入开发者工具,切到Network面板,然后在搜索框里输入黄芪的英文名Astragalus,点击搜索。观察Network面板里新出现的那个请求,点开看它的Query String Parameters,就能看到实际发送的参数名和值。这个操作每次写爬虫前都要做一遍,因为网站改版后参数名可能会变,直接抄旧代码很可能失效。
3.2 用httr包模拟请求拿到HTML
确认好请求地址和参数之后,用httr包模拟请求就很简单了。核心思路是把参数放进query列表,同时设置User-Agent伪装成浏览器,避免被服务器拒绝。
library(httr) library(rvest) base_url <- "http://tcmsp-e.com/tcmsp.php" resp <- GET( base_url, query = list( qr = "Astragalus", qsr = "herb_all" ), add_headers( `User-Agent` = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36", `Referer` = "http://tcmsp-e.com/" ), timeout(30) ) if (resp$status_code == 200) { page <- content(resp, as = "parsed", encoding = "UTF-8") tables <- html_table(page, fill = TRUE) message("成功获取页面,共解析到 ", length(tables), " 张表格") } else { message("请求失败,HTTP状态码:", resp$status_code) }这一步是整个项目的起点。status_code返回200表示请求成功,如果返回403或503,多半是User-Agent没设置好,或者请求频率太高被暂时限制了。遇到这种情况,先检查请求头,再考虑降低访问频率。
3.3 解析HTML表格时怎么定位目标表
html_table()返回的是一个列表,列表里每一个元素就是网页中的一张表格。问题在于TCMSP的结果页面包含多张表,有筛选栏、有成分列表、有页脚信息,你不能假设第1张表就是要的数据。
我的做法是先循环检查所有表格的列名,打印出来看一眼,再根据列名判断哪张是成分表。
for (i in seq_along(tables)) { cat("表格", i, "的列名为:", paste(colnames(tables[[i]]), collapse = " | "), "\n") }通常成分表的列名里会包含MOL ID、Molecule Name、OB(%)这些字段。定位到目标表之后,再把它单独提取出来。TCMSP返回的表格列名可能有空格或特殊符号,比如OB(%),这时候在dplyr里操作要用反引号包裹列名,否则会报错。
4. 数据抓取完整实操:从一味药到完整的成分-靶点关系表
4.1 抓取中药成分列表并记录原始数据
拿到HTML并定位到成分表之后,下一步是提取成分列表,并且立刻把原始数据保存一份。这一步不要急着清洗,先存档再说。
herb_raw <- tables[[target_index]] saveRDS(herb_raw, "data/herb_raw.rds")保存原始数据的好处是,哪怕后面清洗逻辑写错了,也可以随时回到起点重新来。你也不需要每次调试都重新访问TCMSP,既省时间又避免对公共数据库造成压力。
有一点要提醒:TCMSP的成分列表并不直接等于中药的活性成分,它只是这个药材里检测到的化学成分集合。究竟哪些算活性成分,需要后面的OB和DL指标来筛选。所以这一步你不用动手砍数据,把全量成分都保留下来,后面再做判断。
4.2 根据成分ID逐个抓取靶点
成分列表里每一行都有一个MOL ID,它是TCMSP里每个化学分子的唯一编号。接下来我们需要拿着这个编号去请求成分详情页,从详情页里解析出靶点表格。
这里涉及一个循环请求的过程,是整个项目里耗时最长的环节。以黄芪为例,TCMSP里收录了大约80多个化学成分,抓取每个成分的靶点页面需要几秒钟,整个循环跑下来大概三四分钟。如果换成复方,几百个成分循环一遍就要比较久了。
循环里一定要控制请求频率,每次请求之间至少间隔2秒,我习惯用Sys.sleep(3),宁可慢一点也不要触发服务器的限流机制。代码结构大致如下。
mol_ids <- herb_raw$`MOL ID` target_list <- list() for (mol_id in mol_ids) { resp <- GET( base_url, query = list(qr = mol_id, qsr = "mol_all"), add_headers( `User-Agent` = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0 Safari/537.36" ), timeout(30) ) page <- content(resp, as = "parsed", encoding = "UTF-8") tab <- html_table(page, fill = TRUE) # 找到包含靶点信息的目标表,这里以列名里出现Gene name或Target为判断依据 target_tab <- NULL for (t in tab) { if (any(grepl("Gene|Target", colnames(t)))) { target_tab <- t break } } if (!is.null(target_tab)) { target_tab$MOL_ID <- mol_id target_list[[mol_id]] <- target_tab } Sys.sleep(3) } all_targets_raw <- do.call(rbind, target_list) saveRDS(all_targets_raw, "data/all_targets_raw.rds")这里有一个细节值得注意:循环里对每个成分都做了tryCatch式的判断,即使某个成分页面解析失败,也不会中断整个流程,最多就是丢掉这一个成分的数据。实际运行的时候,我碰到过个别MOL ID请求反复超时的情况,这种通常是网站服务器临时不稳定,稍后重跑一次脚本就能补上。
4.3 批量请求时的网络异常处理
采集环节最大的敌人是网络不稳定,而不是你代码写错。我在实际跑脚本时遇到过几次情况:连续请求几十个页面之后,服务器开始返回空页面,status_code还是200,但解析出来没有任何表格。这种问题非常隐蔽,因为程序没有报错,表面上看一切正常,实际数据已经丢了。
解决办法是加一层校验,在解析表格之后检查结果是否为空,如果为空就重试。重试次数可以设置成3次,每次重试前等待更长的时间,比如10秒。这种过载保护机制在正式运行脚本时很有必要,尤其是当你需要一口气抓几百个成分的时候。
retry_count <- 0 while (retry_count < 3 && is.null(target_tab)) { Sys.sleep(10) # 重新请求和解析 retry_count <- retry_count + 1 }4.4 把成分和靶点合并成一张关系表
靶点抓完之后,我们手上有了两份数据:一份是成分信息表(herb_raw),一份是成分-靶点对应表(all_targets_raw)。下一步是把它们合并成分析用的关系表。
合并的目标很明确,就是构造一个包含三列的数据框:成分名称、成分ID、靶点基因名。这里的关键是搞清楚哪一列是靶点基因名。TCMSP靶点表里不同版本给出的列名不完全一样,有时候是Gene name,有时候是Target,还有时候直接给UniProt ID。你要根据实际抓取到的列名做调整。
library(dplyr) relations <- all_targets_raw %>% left_join(herb_raw %>% select(`MOL ID`, `Molecule Name`, `OB(%)`, DL), by = "MOL ID") %>% rename( compound = `Molecule Name`, target = `Gene name` ) %>% select(compound, target, MOL_ID = `MOL ID`, OB = `OB(%)`, DL) %>% filter(!is.na(target), target != "") %>% distinct()合并完之后一定要做distinct去重,因为同一个成分对同一个靶点可能多次出现,网络图里重复的边没有任何意义。
5. 数据清洗与核心阈值筛选:别让垃圾数据毁了你的网络图
5.1 OB和DL阈值怎么定才有说服力
TCMSP里面存储的是全量化学成分数据,这并不意味着每个成分都要进入后续分析。口服生物利用度OB表示药物经口服后被机体吸收进入循环系统的速度和程度,类药性DL用来衡量一个化合物与已知药物在结构上的相似程度。这两个值越高的成分,理论上越有可能成为发挥药效的活性成分。
目前学术界使用最广泛的标准是OB大于等于30,DL大于等于0.18,这个标准最早来自TCMSP数据库自身的推荐,后续大量文献沿用,已经形成了一个约定俗成的参考线。我自己的习惯是筛选的时候用这两个指标,但在论文里会明确写出筛选标准,并且把筛选前后的成分数量对比列出来,这样审稿人看了会更信服。
active_compounds <- relations %>% filter(OB >= 30, DL >= 0.18) %>% distinct(compound, MOL_ID, OB, DL)有一个容易忽略的地方:OB和DL筛选应该作用在成分层面,而不是关系层面。你先通过成分表筛出符合条件的活性成分,再拿这些成分去关联靶点,而不是先把所有关系表建好再按OB和DL过滤关系行。两者结果可能有细微差别,逻辑上前者更清晰。
5.2 靶点名称的统一与ID转换
TCMSP返回的靶点基因名通常用的是官方Symbol,比如PTGS1、ESR1、NOS3这种格式,直接拿来做网络图没有问题。但如果你后续要做通路富集分析,或者要跟疾病靶点取交集,最好把基因Symbol统一转换为Entrez ID或Ensembl ID,这样不同数据源之间才能精确匹配。
R里面做ID转换用的最多的是Bioconductor的org.Hs.eg.db包。这个包装起来比普通CRAN包稍微麻烦一点,需要用BiocManager安装。
if (!requireNamespace("BiocManager", quietly = TRUE)) install.packages("BiocManager") BiocManager::install("org.Hs.eg.db")转换逻辑很简单,把基因Symbol作为key,映射到Entrez ID即可。
library(org.Hs.eg.db) symbols <- unique(relations$target) entrez_map <- AnnotationDbi::mapIds( org.Hs.eg.db, keys = symbols, keytype = "SYMBOL", column = "ENTREZID" )需要说明的是,mapIds在某些基因映射不到时会返回NA,这些缺失值需要人工检查一下,我遇到过TCMSP的靶点列表里含有非人类基因的情况,这在做人体靶点分析时需要剔除。
5.3 关系表里隐含的常见脏数据
清洗关系表时最烦的不是格式问题,而是语义层面的杂质。我举几个实际遇到过的例子。
有些靶点名称里带有括号注释,比如ESR1 (estrogen receptor 1),实际上gene symbol只是ESR1。直接用这个带注释的字符串去做网络图,你会看到两个节点,一个是ESR1,一个是ESR1 (estrogen receptor 1),数据被错误拆分成两个靶点。处理方式是用正则表达式把括号里的注释去掉,只保留前面的标准化名称。
还有些成分名称本身会重复,同一个化学成分在不同列表里出现了多次,合并的时候如果不做distinct,网络图的边就会重复计算,节点之间的连线条数虚高,影响后续度中心性的计算。
另外,TCMSP靶点表里我见过空行和占位符,比如某些行只有MOL ID,靶点列是空的或者一个横杠。这些行在构建网络图时必须剔除,不然igraph会把它当成一个名称是NA的节点,导致画图的时候报错。
5.4 多成分对应同一靶点的去重逻辑
网络分析中还有一个细节,就是多成分对同一靶点的关系如何处理。假设黄芪里有10个成分都作用在PTGS1上,那么在边表里会有10条记录都是compound到PTGS1。igraph会自动把它们变成10条平行边,画出来的网络图上两个节点之间会有10根线叠在一起,非常难看。
处理方式看你分析的目的。如果只是想展示成分与靶点之间的关联关系,建议保留一条边;如果你想在节点或边的属性里记录成分数量,那可以在去重之前先统计次数,把次数作为边的权重属性存下来。
network_edges <- active_relations %>% group_by(compound, target) %>% summarise(weight = n(), .groups = "drop")这样处理之后,边的数据里多了一列weight,后续在网络图里可以把weight映射为边的粗细或透明度,既美观又保留了原始信息。
6. 中药-靶点网络图构建与可视化:让你的数据变成一张能发表的好图
6.1 用igraph构建网络对象
igraph是R语言里最经典、最稳定的网络分析包。构建网络的第一步是加载igraph库,然后通过graph_from_data_frame函数,把边表转换成图对象。
library(igraph) g <- graph_from_data_frame(network_edges[, c("compound", "target")], directed = FALSE)这里directed=FALSE表示构建无向网络,因为成分和靶点之间的作用关系本身不分方向。如果你后续想分析调控方向,比如激活或抑制,那就需要用有向图,但TCMSP本身的数据不包含这类信息,所以通常用无向图就够了。
构建完图对象之后,可以顺手给节点加一个类型属性。节点分为成分和靶点两类,加这个属性是为了画图时用颜色区分不同类型,也方便后续做网络拓扑分析。
V(g)$type <- ifelse(V(g)$name %in% network_edges$compound, "compound", "target")网络构建完成后,用summary(g)看一下节点数和边数。如果节点数跟你的预期差距很大,比如莫名多出几十个节点,赶紧回去查数据清洗那一步,大概率是靶点名称里混入了杂质。
6.2 用ggraph美化网络图的实操代码
igraph自带的plot函数能快速出图,检查数据结构够用,但要放进论文或汇报材料里就不太够看了。我推荐用ggraph包做美化,它基于ggplot2的语法体系,出图风格统一,而且可定制性很强。
library(ggraph) library(tidygraph) tg <- as_tbl_graph(g) p <- ggraph(tg, layout = "kk") + geom_edge_link(aes(width = weight), alpha = 0.3, color = "grey60") + geom_node_point(aes(color = type), size = 3) + scale_color_manual(values = c(compound = "#E76F51", target = "#2A9D8F")) + scale_edge_width(range = c(0.5, 2)) + theme_void() + theme(legend.position = "bottom") ggsave("output/network_ggraph.png", p, width = 8, height = 6, dpi = 300)layout = "kk"表示使用Kamada-Kawai布局算法,这种布局在节点数量不算太多的时候表现很好,节点之间的相对位置能比较直观地反映拓扑距离。如果你发现画的图太挤,可以换成layout = "fr",是Fruchterman-Reingold算法,对中等规模网络的可读性更好。两个都试试,看哪个顺眼用哪个。
6.3 节点太多时怎么让图更清晰
中药-靶点网络最常遇到的问题就是节点太多,画出来一团黑。活性成分加对应靶点,动不动就是上百个节点,全部画在一张图里必然杂乱。
我常用的做法是按节点度数做过滤。度数指的是一个节点连接了多少条边,在网络里度越高的节点通常越重要。如果一张图上节点太多,可以先只保留度排名前50的节点,其余藏在布局里的小节点暂时不放出来。
deg <- degree(g) top_nodes <- names(sort(deg, decreasing = TRUE))[1:50] g_pruned <- induced_subgraph(g, top_nodes)这个方法特别适合做PPT汇报,先展示核心子网络,再展开说明全网络。网络上还有一个通用技巧,就是把节点标签藏起来。节点多到一定程度,标签只会互相遮挡,根本看不清,不如导出图片后在后期工具里单独标注重点节点。
6.4 导出为Cytoscape可用的GraphML格式
igraph绘制的图适合做快速浏览和汇报,但如果你要深入研究网络拓扑属性,或者把网络拿去跟疾病靶点做交集分析,我建议导出到Cytoscape里操作。Cytoscape是专门做生物网络可视化的软件,很多网络药理学论文里的精美网络图都是在Cytoscape里完成的。
igraph导出Cytoscape格式非常简单,GraphML是一种通用的XML格式,Cytoscape可以直接打开。
write.graph(g, "output/tcmsp_network.graphml", format = "graphml")打开Cytoscape之后,用File > Import > Network from File选择这个graphml文件即可。导入后节点类型和边的权重属性都会保留,你可以用Cytoscape的样式编辑器给成分和靶点设置不同的形状和颜色,也能用它的Network Analyzer插件直接计算度中心性、介数中心性等指标。
7. 常见问题排查实录:这些坑我替你已经踩过了
7.1 网页表格解析出来是空行或0行
这个问题我在TCMSP改版期间遇到过,原因多半是html_table()解析时,表格里有些行是合并单元格或者嵌套表格。一般情况下可以先试一下html_table的fill参数,正常情况下填TRUE就不会报错。如果还是解析不出来,改用更底层的办法,直接抽取所有td单元格内容。
cells <- page %>% html_nodes("table td") %>% html_text()拿到的cells是一个文本向量,再根据你已知的列数手动切分成数据框。这属于暴力解法,但确实能解决一些表格嵌套造成的解析问题。
7.2 中文药名搜索时返回异常结果
TCMSP虽然支持中文或拼音检索,但爬虫请求时涉及URL编码问题。我建议直接在请求参数里使用药材的标准拉丁名或英文名,比如黄芪用Astragalus,丹参用Salvia miltiorrhiza,这样能避免编码不一致带来的麻烦。
如果你确实需要按中文名搜索,记得先用URLencode()对中文进行编码再放进query里。R中可以用以下方式处理。
keyword <- URLencode("黄芪", reserved = TRUE)另外要确认TCMSP的qsr参数是herb_all还是herb_name。不同搜索模式下参数值不同,直接看浏览器Network面板最靠谱。
7.3 网络图节点数比预期多很多
节点数异常增加的根源,基本都在数据清洗环节。最常见的原因是靶点列里混入了一列非靶点信息,比如把靶点表的序号列或蛋白描述列误当成基因名。排查方法很简单,检查relations表里target列的唯一值数量,然后随机抽几个值去TCMSP页面人工比对。
还有一种情况是成分列里有多余的空白字符串,导致igraph创建了一个名字为空的节点。清洗时用filter(!is.na(compound), compound != "")把空值去掉,问题就能解决。
7.4 请求过于频繁被服务器拒绝
TCMSP是公共学术数据库,没有强反爬机制,但如果你在短时间内发起大量请求,服务器还是会返回异常响应。我在跑复方成分的时候有过一次惨痛教训:脚本里忘记加Sys.sleep,连续请求了三百多次,跑到一半开始出现大量空响应,页面解析结果全部缺失,最后被迫从头再来。
我的建议是每次请求之间至少等待2到3秒,批量抓取上百个成分时,把间隔拉长到5秒。这种看似低效的做法,实际运行下来反而更高效,因为你不用反复重试,也不会触发限流。另外,尽量在工作时间非高峰时段运行脚本,比如上午或者深夜,对数据库服务器也更友好。
7.5 保存的CSV在Excel里打开乱码
如果你把清洗好的数据保存成CSV然后用Excel打开,很可能会看到中文乱码。原因是R写CSV默认用UTF-8编码,而Windows版的Excel默认按本地编码解析。解决方案有两个:一是用write.csv的时候加上fileEncoding = "UTF-8-BOM",这样Excel能识别;二是干脆存成RDS或xlsx格式,避开编码问题。
write.csv(clean_data, "output/clean_data.csv", row.names = FALSE, fileEncoding = "UTF-8-BOM")如果是你自己后续在R里继续分析,我其实最推荐saveRDS(),RDS格式读取速度快、数据类型不会丢失,是R语言内部交换数据的最佳选择。
8. 这套流程还能怎么延伸:从单味药到复方和疾病靶点
这套自动化流程的价值不在于跑通一味药,而在于把方法复用到更复杂的场景里。我做完单味药分析之后,立刻把代码封装成了函数,默认参数是药材名称和保存路径,这样下次分析其他药材,一行代码就能启动。
往复方方向扩展时,只需要把多味药材的成分表合并后再去重。比如你分析一个含有5味药材的方子,每味药材抓一次成分列表,合并之后做OB和DL筛选,再去抓靶点,网络图就自动变成复方-靶点网络。这个网络里还能进一步区分某个成分来自哪几味药材,算是一张更复杂的二分网络,扩展价值很高。
往疾病方向扩展时,可以下载DisGeNET或GeneCards的疾病靶点数据,跟TCMSP预测靶点取交集,就能筛选出中药作用于疾病的关键靶点。再配合clusterProfiler做GO和KEGG富集分析,就是一篇标准网络药理学论文里从预测到机制的核心数据链条。
我在实际做课题的时候,通常是把R语言爬虫出数据、igraph网络分析、clusterProfiler富集分析三个环节串成一个完整的R脚本,跑完自动输出所有图表。前期搭建脚本比较费时间,但一个流程跑顺之后,后续每换一味药、一个方子,基本就是改一个参数的事。个人最深的体会是,网络药理学分析里那些看起来高深的网络图,真正决定成败的反而是最基础的取数环节,数据取干净了,后面所有的分析都是锦上添花;数据一旦是脏的,后面跑得再漂亮的图也只是自我安慰。希望这套流程能帮你少走一些我当时走过的弯路。