splashr批量爬取URL时cron任务触发curl连接重置错误如何解决?
问题排查与解决方案
1. 内存/垃圾回收操作必要性
你确实需要在两次爬取间隔执行资源清理操作,但不局限于R层面的垃圾回收:
- 当前代码每次循环都会新建Splash连接,但没有显式释放会话资源,Splash服务端的内存、网络句柄会持续累积,到达阈值后就会主动断开连接,也就是你遇到的
Recv failure: Connection reset by peer错误。 - 你观察到的
QNetworkReplyImplPrivate::error报错是Splash服务端资源耗尽的前置预警,本质是内部未释放的网络请求对象冲突导致的。 - 清理操作需要覆盖两个层面:每次爬取结束后显式关闭Splash会话,同时调用R的
gc()释放闲置的curl句柄和内存。
2. 直接运行成功、cron任务失败的原因
两者的运行环境存在核心差异:
- 资源限制不同:cron的非交互式运行环境默认的ulimit(最大打开文件数、网络超时时间、内存配额)都比RStudio Server的交互式会话严格,资源耗尽的阈值更低,所以会更早触发连接断开。
- 自动回收逻辑不同:RStudio的交互式会话会在后台自动触发闲置资源回收,而非交互式的R脚本不会主动执行垃圾回收,内存和句柄泄漏速度更快。
- 服务状态校验缺失:你在RStudio中运行前会手动确认Splash服务正常运行,但cron任务启动时如果Splash服务异常重启、端口被占用,没有对应的容错逻辑,会直接触发报错。
3. 报错修复方案
3.1 修改爬取代码
增加会话释放、错误重试、显式垃圾回收逻辑:
library("tidyverse") library("splashr") library("rvest") # 启动Splash时增加资源限制参数,避免内存溢出 system2("docker", args = c("run -d -p 5023:5023 -p 8050:8050 -p 8051:8051 --memory=2g -e SPLASH_MAX_RSS=2048 -e SPLASH_TIMEOUT=180 scrapinghub/splash:latest"), wait = FALSE) # 等待服务启动 Sys.sleep(10) pause_after_html_read <- 5 pause_after_html_text <- 3 # 单URL最大重试次数 max_retry <- 3 for(idx in 1:28){ retry_cnt <- 0 pg <- NULL while(retry_cnt < max_retry && is.null(pg)){ tryCatch({ spl <- splash(host = "localhost", port = 8050L) pg <- spl |> splash_response_body(FALSE) %>% splash_go(url = url_df$web_page[idx]) %>% splash_wait(pause_after_html_read) %>% splash_html() |> html_text() # 显式关闭Splash会话 splash_close(spl) }, error = function(e){ message(sprintf("第%d个URL第%d次爬取失败: %s", idx, retry_cnt+1, e$message)) if(exists("spl")) splash_close(spl) # 重试到最后一次仍失败则重启Splash服务应对严重异常 if(retry_cnt == max_retry-1){ system2("docker", args = c("stop $(docker ps -q --filter ancestor=scrapinghub/splash:latest)")) Sys.sleep(5) system2("docker", args = c("run -d -p 5023:5023 -p 8050:8050 -p 8051:8051 --memory=2g -e SPLASH_MAX_RSS=2048 -e SPLASH_TIMEOUT=180 scrapinghub/splash:latest"), wait = FALSE) Sys.sleep(10) } Sys.sleep(5) }) retry_cnt <- retry_cnt + 1 } Sys.sleep(pause_after_html_text) # 显式执行垃圾回收 gc(verbose = FALSE) } # 爬取结束后关闭Splash容器 system2("docker", args = c("stop $(docker ps -q --filter ancestor=scrapinghub/splash:latest)"))
3.2 调整cron任务配置
- 执行任务前增加ulimit调整,在cron执行的R脚本开头添加
ulimit -n 65535提升最大打开文件数限制 - 确保cron任务的执行用户和你运行RStudio的用户一致,避免docker访问权限问题
- 不要丢弃cron任务的输出,重定向到日志文件方便后续排查异常:
* * * * * /usr/bin/Rscript /path/to/your/script.R >> /path/to/logfile.log 2>&1
内容的提问来源于stack exchange,提问作者ixodid
相关产品推荐
相关产品推荐

