之前用rvest帮人写了一个定期抓取amazon价格库存,并与之前价格比较的小程序,算是近期写过的第一个完整的程序了。里面涉及了一些报错的处理。
这里主要参考了stackoverflow上的以下问答:
- How to skip an error in a loop
- skip to next value of loop upon error in R
tryCatch部分,后续查找资料,发现以下博文: 1. R语言使用tryCatch进行简单的错误处理
以下是代码示例:
1)使用tryCatch函数跳过错误信息。(示例以download.file为样式)
看以下代码。这里需要批量下载一堆amazon产品信息。如果产品ID号不对,或者IP被限制,网页会打不开,而download.file会报错。我这里用tryCatch来获取网页打不开时的错误信息。并且要求执行下一步循环“”。
for (n in 1:length(productlink)){
tryCatch({
download.file(productlink[n],paste0(getwd(),"/html/",productid[n,],".html"),cacheOK = TRUE)
},error=function(e){cat("ERROR :",conditionMessage(e),"\n")})
Sys.sleep(0.5) #增加了Sys.sleep(seconds)函数,让每一步循环都暂停一段时间。这个虽然会降低程序速度,但对于有访问限制的网站,不失为一个好的办法。
}
上述示例由两个重要函数构成,即tryCatch和cat
查阅函数,tryCatch属于base包,condition system。在R语言使用tryCatch进行简单的错误处理这篇博文里有tryCatch的简单示范如下:
result = tryCatch(
{expr},
warning = function(w) {warning-handler-code},
error = function(e) { error-handler-code},
finally = {cleanup-code}
)
即如果warning时,对warning怎么处理,如果error时对error怎么处理。如果没有任何条件吻合,则最后会输出expr里的内容。如果有final项的话,则会同时输出finally项以及expr项
tryCatch({a<-"c"
b<-"c"
b==a},
error=function(e){cat("hahaha",conditionMessage(e),"\n\n")},
finally={print("ccc")})
[1] "ccc"
[1] TRUE
tryCatch({a<-"c"
cc==a}, #cc不存在
error=function(e){cat("hahaha",conditionMessage(e),"\n\n")},
finally={print("ccc")})
hahaha object 'cc' not found
对于代码示例,即为,download成功则返回download内容,不成功则返回error=function(e){cat("ERROR :",conditionMessage(e),"\n")}
然后是cat函数。这个cat是一个输入输出值。这里等于,要求系统输出“ERROR :”+conditionMessage(e)的内容。然后用“”分行。
另外,在stackoverflow上的这篇问答,由mmann1123回答的问题里,我们看到了更为有趣的一个应用。
这里收缩起来,展开亦可阅读。
tryCatch示范
2)利用if语句以及stop语句。
即,如果某条件不成立,则停止程序,并输出stop里的内容。我这里主要用于检查原始product id是否输入正确。
if (!sum(check)==length(productlink)) { productlink<-NULL
productid<-NULL
stop("invalid productid please double check if any space or else in, and resave the file or the script will not run")
}
3)处理使用data.frame批量读取数据时,元素因为不存在导致的data.frame报错。
譬如说以下示例,因为a不存在,而导致data.frame报错。
a<-NULLb<-c("cc","dd")
data.frame(a,d)
> Error in data.frame(a, d) : 参数值意味着不同的行数: 0, 2
因此,对于在循环里,需要先单独合成data.frame,再使用rbind把各个data.frame合成在一起时,可以考虑增加异常值的赋值。如下面两段,如果我拉的网页里不存在product name,则length(productname)==1为FALSE,直接输出“product not download or not existing”,那么这个字段就不是空值或者2-3个行,而是1行,之后合并为data.frame时就不会报错了。
data<-function(n){ ####隐掉获得productname/price/category的代码
if(!length(productname)==1) {productname="Product not download or not existing"}
if (!length(price)==1) {
price=NA
category<-"Product not download or not existing"
}
data.frame(productname,price,category)
#这里合成data.frame,如果这三个行数不等(多为空值NULL,或者某个字段有2-3行所导致。
#使用上面的IF判断赋值的好处是,最后出来的productname,price,category保证是1行,可以用data.frame合并。并且对异常值也有输出。
由于处理第2/3类错误时我还不了解tryCatch函数。目前看下来,貌似tryCatch函数能做的事情更多?
写下来供以后写代码时参考。
另外,tryCatch在java,C里均有类似功效。看来R归根到底,还是脱离不了底层语言啊。