R脚本调用API出现Could not resolve host错误的原因及运行方案咨询
问题描述
我的R脚本在运行时偶尔会出现以下错误:
[1] "2023-08-12 16:47:37.463" Error in curl::curl_fetch_memory(url, handle = handle): Could not resolve host: api.abc.com Request failed [ERROR]. Retrying in 1.3 seconds... Error in curl::curl_fetch_memory(url, handle = handle): Could not resolve host: api.abc.com Request failed [ERROR]. Retrying in 1 seconds... Error in curl::curl_fetch_memory(url, handle = handle): Could not resolve host: api.abc.com
注:api.abc.com是示例域名,实际使用商用API,API方确认该时段服务器未宕机,宕机时会返回503状态码。
我有两个问题:
- 此类错误的可能原因是什么?
- 如何让脚本在出现此类错误时继续运行?目前脚本会报错中断,我原本以为用
RETRY结合GET能避免这种情况。
脚本通过tclTaskSchedule每10秒调用一次,示例使用免费API universities.hipolabs.com,同时通过Plumber将数据流式传输到本地服务器。
一、错误原因分析
Could not resolve host错误本质是域名解析失败,和API服务器是否在线无关,可能原因包括:
- 本地DNS解析异常:本地DNS缓存过期/损坏,或者网络提供商的DNS服务器无法解析目标API域名
- 本地网络波动:临时断网、路由器/网关故障,导致无法连接DNS服务器完成解析
- 防火墙/代理拦截:本地防火墙、企业网关或代理服务器阻止了域名解析请求
- API域名的DNS服务临时故障:虽然API服务器正常,但域名的DNS记录(如A记录、CNAME记录)出现短暂异常
二、让脚本持续运行的修改方案
httr::RETRY默认只针对HTTP状态码错误(如4xx、5xx)进行重试,而域名解析失败属于curl底层错误,会直接抛出中断脚本。需要用tryCatch捕获这类错误,同时完善异常处理逻辑:
修改后的核心函数代码
library(httr) library(jsonlite) library(dplyr) library(readr) library(purrr) library(tidyr) library(stringr) library(tibble) library(tcltk2) library(lubridate) run_api_once <- function() { mydatalist <- list() my_next_page_with_number <- "http://universities.hipolabs.com/search?country=United+States" # 用tryCatch捕获所有请求相关错误 tryCatch({ # 指定重试次数,默认3次,可根据需求调整 mydata1 <- RETRY("GET", my_next_page_with_number, times = 3) if(mydata1$status_code != 200){ print(mydata1$status_code) # 初始化全局变量,避免首次报错时变量不存在 if(!exists("http_responses")) http_responses <- c() http_responses <<- append(http_responses, paste(mydata1$status_code, Sys.time())) } else { rawdata <- rawToChar(mydata1$content) mydata2 <- fromJSON(rawdata, flatten = FALSE, simplifyVector = FALSE) mydatalist <- c(mydatalist, mydata2) } }, error = function(e) { # 捕获到错误时的处理:打印错误信息、记录日志 error_msg <- paste("请求失败:", e$message, "时间:", Sys.time()) print(error_msg) if(!exists("http_responses")) http_responses <- c() http_responses <<- append(http_responses, error_msg) }) # 打印当前时间 print(format(Sys.time(), "%Y-%m-%d %H:%M:%S")) # 处理空数据情况,避免unnest_wider报错,同时保证Plumber能正常返回 if(length(mydatalist) == 0){ myvar <<- tibble() } else { users <- tibble(user = mydatalist) myvar <<- users %>% unnest_wider(user) } return(myvar) } # 调度任务:注意传函数名而非函数调用(原代码带括号会立即执行一次,调度逻辑失效) tclTaskSchedule(10000, run_api_once, id = "run_api_once", redo = TRUE) # 结束任务用:tclTaskDelete("run_api_once")
关键修改点说明
- 用
tryCatch捕获底层错误:包裹整个请求逻辑,捕获curl解析失败等非HTTP状态码错误,错误发生时执行自定义日志记录,不会中断脚本 - 初始化全局变量:避免首次报错时
http_responses变量不存在导致的二次错误 - 处理空数据场景:当请求失败时,设置
myvar为空数据框,保证Plumber接口能正常返回,不会因为数据缺失报错 - 修正调度任务参数:原代码中
tclTaskSchedule的第二个参数是run_api_once()(带括号),会立即执行函数并将返回值作为调度对象,改为run_api_once(函数名)才会每10秒调用一次函数
内容的提问来源于stack exchange,提问作者user2165379
相关产品推荐
相关产品推荐

