You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何高效抓取美国输液中心定位网站的全部结果?

问题描述

我想从https://locator.infusioncenter.org/获取美国所有输液中心的完整列表(包含中心名称、地址)。该网站需要输入“城市, 州”或邮编,并设置5-100英里的搜索半径,美国共有超过3500家输液中心,我需要高效的抓取方法。

我发现搜索后URL不会更新,但还是写了一个R函数,传入批量邮编来尝试抓取,用了对应的CSS选择器:

get_infusion_centers <- function(zip) {
  url <- paste0("https://locator.infusioncenter.org/?zip=", zip)
  page <- read_html(url)

 locations <- page %>%
    html_nodes("#root > div > div > div.locator-container.flex.flex1 > div.locator-form.flex.column > div.flex1.scrollY > div.locator-results-container > div > div:nth-child(1) > a > div > div") %>%
    html_text()
  
  return(data.frame(zip = zip, locations = locations))
}

运行后出现错误:

Error in data.frame(zip = zip, locations = locations) :  arguments imply differing number of rows: 1, 0

我猜测是位置信息抓取失败了,作为网页抓取新手,希望得到帮助。


解决方案

1. 代码失败原因

这个网站是单页应用(SPA),搜索结果是通过AJAX动态加载的,直接用read_html请求URL只能拿到静态页面框架,获取不到实际的搜索数据,所以locations会是空向量,和传入的zip行数(1)不匹配,才抛出了行数不一致的错误。另外你的CSS选择器只定位了第一个结果,就算能拿到数据也只能获取单条记录。

2. 两种可行抓取方案

方法一:直接调用API接口(高效推荐)

打开浏览器开发者工具(F12)切换到Network标签,输入邮编搜索后,能看到网站会向一个API接口发送POST请求获取数据,请求体包含邮编、半径等参数。用httr和jsonlite包直接调用接口,能拿到结构化的JSON数据:

library(httr)
library(jsonlite)
library(dplyr)

get_infusion_api <- function(zip, radius = 50) {
  # 构造请求参数
  body <- list(
    zip = zip,
    radius = radius,
    page = 1,
    pageSize = 100  # 单次请求获取最大数量结果
  )
  
  # 发送POST请求
  response <- POST(
    url = "https://locator.infusioncenter.org/api/locations/search",
    body = body,
    encode = "json",
    add_headers(
      "Content-Type" = "application/json",
      "Accept" = "application/json"
    )
  )
  
  # 解析JSON结果并提取核心字段
  data <- fromJSON(content(response, "text"))
  results <- data$results %>%
    select(name, address, city, state, zip)
  
  return(results)
}

方法二:用RSelenium模拟浏览器操作(应对反爬)

如果API接口有反爬限制,就用RSelenium模拟真实浏览器的搜索操作:

library(RSelenium)
library(rvest)
library(dplyr)

# 启动Chrome浏览器驱动(需提前安装对应版本的chromedriver)
driver <- rsDriver(browser = "chrome", port = 4567L)
remDr <- driver[["client"]]

get_infusion_selenium <- function(zip, radius = 50) {
  # 打开目标页面
  remDr$navigate("https://locator.infusioncenter.org/")
  
  # 输入邮编
  zip_input <- remDr$findElement(using = "css", value = "input[placeholder='Enter City, State or ZIP']")
  zip_input$sendKeysToElement(list(zip))
  
  # 设置搜索半径
  radius_select <- remDr$findElement(using = "css", value = "select[name='radius']")
  radius_select$sendKeysToElement(list(as.character(radius)))
  
  # 点击搜索按钮
  search_btn <- remDr$findElement(using = "css", value = "button[type='submit']")
  search_btn$clickElement()
  
  # 等待结果加载完成
  Sys.sleep(3)
  
  # 解析页面结果
  page_source <- remDr$getPageSource()[[1]]
  page <- read_html(page_source)
  
  locations <- page %>%
    html_nodes(".locator-result-item") %>%
    map_df(function(node) {
      name <- node %>% html_node(".result-name") %>% html_text(trim = TRUE)
      address <- node %>% html_node(".result-address") %>% html_text(trim = TRUE)
      tibble(name = name, address = address)
    })
  
  return(locations)
}

# 使用完毕后关闭浏览器
# remDr$close()
# driver$server$stop()

3. 批量获取与去重

遍历美国邮编列表调用上述函数后,需要对结果去重(不同邮编搜索会出现重复的输液中心):

# 假设zips是你的美国邮编向量
all_results <- purrr::map_dfr(zips, get_infusion_api)
unique_centers <- all_results %>% distinct(name, address, .keep_all = TRUE)

注意事项

  • 不要高频请求,建议添加Sys.sleep(1)之类的延迟,避免触发反爬机制
  • 提前查看网站的robots.txt,确保抓取行为符合网站使用条款

内容的提问来源于stack exchange,提问作者Russell Morgan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 11:08:20