在R中,循环如果出错,程序就会被kill掉。对于计算开销较大的循环来说,这样的错误是难以忍受的。
- 一种方法是逐步地debug,针对所有的情况都能提供处理方案,但是很费心费力;
- 一种方法是使用错误 异常控制函数,把错误跳过 或者说,提升对错误的容忍能力,即容错性。
针对第二种方法,有try tryCatch等函数。网上虽然教程较少,但是也能学个大概。最不济,看R 帮助文件也是可以的。本文,主要是记录我遇到的问题和解决的方法。
问题背景
使用爬虫爬取表格。该表格结构简单,不涉及到动态网页相关知识。问题就在于内容较大,每页有500行,有7000+页。
- 网页结构:静态网页
- url结构:有规律
- 网络状态:较差,难以次次连接成功
- 出现的问题:连接不上(报错),连接超时(不报错,但是爬虫卡死了)
由于代码涉及到项目,所以具体细节不能公开。这里只是说明类似的问题和提供解决的思路。
代码撰写
略,下文使用expr1
代表出现问题的代码块,其他expr\\d
表示用于处理该过程的代码块。
错误控制
- 针对
爬虫连接网页失败,报错并打断进程
的问题 - 使用的R基础包中的函数
tryCatch
tryCatch(
expr1,
error=function(e){
expr2
},finnally = {
expr3
}
)
- tryCatch的思路是
- run expr1
- if expr1 is error, doing expr2
- finnally, weather expr1 error or not, doing expr3
- 我的代码与下面类似:
while(i != m+1){
tryCatch(
{
expr1 # expr1是爬取网络的代码块 ,所以会遇到连接失败的error
i = i+1 # 如果expr1有error,则该语句不会运行,因而下一次循环使用的还是本次循环的i值
}, error = function(e){
cat("Sleeping for net connection agian at i = ", i, "\n")
Sys.sleep(10) # sleep 10s,而后重复发生了错误的那个i
}, finnaly = {
expr3 #是啥都行。我用来输出循环信息
}
)
}
异常控制
- 针对
连接超时,导致循环停滞,爬虫卡死
的问题 - 使用
R.utils
包的withTimeout
函数 - 我的代码类似于:
while(i != m+1){
tryCatch(
{
R.utils::withTimeout(
expr1,
timeout = 60
)
i = i+1
}, TimeoutException = function(ex){
message("Times out! Now refresh it") # 输出提示信息。但是事实上Timeout函数已经终止了expr1,并开始下一次循环(i值不变)
}, error = function(e){
cat("Sleeping for net connection agian at i = ", i, "\n")
Sys.sleep(10) # sleep 10s,而后重复发生了错误的那个i
}, finnaly = {
expr3 #是啥都行。我用来输出循环信息
}
)
}
感悟
爬取数据,虽然是爬,但是却是比手动复制粘贴优雅一千倍的贵族运动。电脑随便爬去吧,这边看会儿生信技能树、生信星球的推文或者去峡谷里维护一下正义,难道不香吗?