R语言rinat包循环调用iNaturalist API偶发编码错误中断循环
问题根源定位
你遇到的这个报错,既不是编码参数传错导致的,也和你填的物种ID、地点ID没有关系,问题来自三个层面:
- rinat包的内部容错缺陷:报错触发点是包内部对响应头的判断逻辑——它默认API返回的响应一定带
content-type头,且值固定为CSV格式,但没有做非空校验。当你拿到的响应对象因为网络问题、服务端限流出现响应头缺失、或者响应内容为空时,直接取x$headers$content-type做判断,就会抛出“参数长度为零”的错误。你看到的No encoding supplied: defaulting to UTF-8只是个无关的提示信息,不是报错根因,你之前尝试手动传入编码参数的操作自然没有效果。 - 原脚本的笔误:代码中
total_count <- append(total, length(monthly$scientific_name))这行存在未定义对象:你本意是往total_count里追加值,却写了不存在的total对象,哪怕API请求全部正常成功,运行到这行也会触发报错,只是之前你先碰到了响应空值的问题,没有跑到这个逻辑分支。 - 服务端的偶发限流:iNaturalist公共API对无认证的高频请求没有太宽松的额度,你在双层循环里连续发请求、没有间隔的情况下,很容易触发限流,服务端会直接返回不带正常业务响应头的空页面/限流提示,刚好触发包内部的判断bug。
解决方案
按照下面的步骤调整,就能完全避免循环中断、漏拉数据的问题:
- 给API请求套上带重试的安全封装,不再让单次请求的报错中断整个循环。示例封装逻辑如下:
# 带自动重试的安全观测拉取函数 safe_pull_obs <- function(place_id, taxon_id, year, month, max_retry = 5) { for (round in 1:max_retry) { pull_result <- tryCatch( get_inat_obs( place_id = place_id, taxon_id = taxon_id, year = year, month = month ), error = function(err) NULL ) # 拉取成功且数据非空时直接返回 if (!is.null(pull_result) && nrow(pull_result) > 0) { return(pull_result) } # 失败后随重试次数加长等待时间,避免触发更严格的限流 Sys.sleep(2 * round) } # 重试耗尽时返回空数据框,不打断循环流程 return(data.frame()) } - 修正脚本里的变量笔误:把原来追加计数的
append(total, ...)改成append(total_count, length(pull_result$scientific_name)),避免未定义对象报错。 - 在循环逻辑里加固定请求间隔:每完成一个月份的数据拉取,执行一次
Sys.sleep(1),降低请求频率,从源头减少服务端限流触发的概率。 - 如果调整后还是频繁碰到响应头缺失的问题,可以绕过rinat包内部的响应判断逻辑:包的核心逻辑只是固定拼iNaturalist的公开API请求地址,拉取CSV格式的观测数据,你可以用通用的HTTP请求工具直接拉取对应数据,自行做响应有效性判断,完全不受包内部无意义判断逻辑的限制。
内容的提问来源于stack exchange,提问作者Adam Pingatore
相关产品推荐
相关产品推荐

