使用R的httr包POST爬取数据时偶发失败的问题排查
解决R中httr POST请求oip.nypdonline.org的不稳定问题
问题背景
使用R的httr包向https://oip.nypdonline.org/api/reports/1/datasource/list发送POST请求抓取公开警务统计数据时,代码运行不稳定,偶发HTTP 500错误或**"Username字段必填"**错误。简化测试代码如下:
url <- "https://oip.nypdonline.org/api/reports/1/datasource/list" payload <- '{filters: [{key: "@TAXID", label: "TAXID", values: ["940123"]}]}' response <- httr::POST(url, content_type("application/json"), body = payload) http_error(response)
发现先在浏览器打开对应页面(如https://oip.nypdonline.org/view/1/@TAXID=940123)后,代码常能正常运行,但仍不规律。尝试过使用handle、VPN、重启R,也尝试添加BNI_persistence、user会话cookie,但SetCookies()无效。verbose输出的"WARNING: failed to open cookie file"在成功和失败时均出现,排除其为直接原因。无法确定是cookie问题还是R/httr的隐藏存储机制导致偶发失败,需要找到模拟浏览器请求的解决方法。
模拟浏览器请求的解决方法
1. 先通过GET请求获取有效会话cookie
直接复制cookie容易过期,不如先模拟浏览器访问目标页面,让服务器生成有效会话cookie,再复用该会话发送POST请求:
library(httr) # 初始化会话handle,用于保持cookie session_handle <- handle("https://oip.nypdonline.org") # 先GET访问页面,触发服务器设置会话cookie GET("https://oip.nypdonline.org/view/1/@TAXID=940123", handle = session_handle) # 发送POST请求,handle会自动携带已获取的cookie url <- "https://oip.nypdonline.org/api/reports/1/datasource/list" payload <- '{filters: [{key: "@TAXID", label: "TAXID", values: ["940123"]}]}' response <- POST(url, content_type("application/json"), body = payload, handle = session_handle) # 检查请求状态 http_error(response) # 解析返回内容 content(response, "parsed")
2. 严格遵循标准JSON格式
原payload使用的是非标准JSON(键名未加双引号),部分服务器可能对格式敏感导致偶发错误,建议用jsonlite生成标准JSON:
library(httr) library(jsonlite) # 用R列表构造参数,再转换为标准JSON payload_list <- list( filters = list( list(key = "@TAXID", label = "TAXID", values = list("940123")) ) ) payload_json <- toJSON(payload_list, auto_unbox = TRUE) # 复用之前的会话handle发送请求 response <- POST(url, content_type("application/json"), body = payload_json, handle = session_handle)
3. 完整模拟浏览器请求头
服务器可能会验证请求头的完整性,从浏览器开发者工具(F12-网络面板)复制成功请求的所有请求头,添加到httr请求中:
library(httr) # 从浏览器复制的请求头,替换为实际值 browser_headers <- add_headers( `User-Agent` = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", `Referer` = "https://oip.nypdonline.org/view/1/@TAXID=940123", `Accept` = "application/json, text/plain, */*", `Accept-Language` = "zh-CN,zh;q=0.9", `Origin` = "https://oip.nypdonline.org", `Sec-Fetch-Dest` = "empty", `Sec-Fetch-Mode` = "cors", `Sec-Fetch-Site` = "same-origin" ) # 结合会话handle和请求头发送请求 response <- POST(url, browser_headers, content_type("application/json"), body = payload_json, handle = session_handle)
4. 添加重试机制处理偶发错误
针对偶发的HTTP 500错误,用purrr实现自动重试逻辑:
library(httr) library(purrr) # 定义带重试的安全请求函数 safe_post <- safely(POST) retry_post <- insistently( safe_post, rate_backoff(pause_base = 1, pause_cap = 5, max_times = 3) ) # 执行带重试的请求 result <- retry_post(url, browser_headers, content_type("application/json"), body = payload_json, handle = session_handle) # 处理结果 if (!is.null(result$result) && !http_error(result$result)) { data <- content(result$result, "parsed") } else { message("请求失败:", result$error$message) }
内容的提问来源于stack exchange,提问作者ThoureaulyRekt
相关产品推荐
相关产品推荐

