【问题标题】:Correct way to get response body of XHR requests generated by a page with RStudio Chromote使用 RStudio Chromote 获取页面生成的 XHR 请求的响应正文的正确方法
【发布时间】:2025-12-16 12:00:02
【问题描述】:

我想使用 Chromote 来收集网站发出的 XHR 调用的响应正文,但我发现掌握 API 有点复杂,尤其是异步管道。

我想我需要先启用网络功能,然后加载页面(可以这样做),但是我需要:

  • 列出所有 XHR 调用
  • 通过识别请求 URL 中的模式来过滤它们
  • 访问所选来源的请求正文

有人可以提供这方面的任何指导或教程材料吗?

更新: 好的,我切换到包crrri 并为此目的制作了一个通用功能。唯一缺少的部分是一些逻辑来决定何时关闭连接并返回结果:

get_website_resources <- function(url, url_filter = '*', type_filter = '*') {
  library(crrri)
  library(dplyr)
  library(stringr)
  library(jsonlite)
  library(magrittr)

  chrome <- Chrome$new()
  
  out <- new.env()
  
  out$l <- list()
  
  client <- chrome$connect(callback = ~ NULL)
  
  Fetch <- client$Fetch
  Page <- client$Page
  
  Fetch$enable(patterns = list(list(urlPattern="*", requestStage="Response"))) %...>% {
    Fetch$requestPaused(callback = function(params) {
      
      if (str_detect(params$request$url, url_filter) & str_detect(params$resourceType, type_filter)) {
        
        Fetch$getResponseBody(requestId = params$requestId) %...>% {
          resp <- .
          
          if (resp$body != '') {
            if (resp$base64Encoded) resp$body = base64_dec(resp$body) %>% rawToChar()
            
            body <- list(list(
              url = params$request$url,
              response = resp
            )) %>% set_names(params$requestId)
            
            str(body)
            
            out$l <- append(out$l, body)
          }
          
        }
      }
      
      Fetch$continueRequest(requestId = params$requestId)
    })
  } %...>% {
    Page$navigate(url)
  }
  
  
  out$l
}

【问题讨论】:

    标签: r web-scraping headless-browser chrome-devtools-protocol crrri


    【解决方案1】:

    破解它。这是最终的功能。它使用crrri::perform_with_chrome 来强制同步行为,并将进程的其余部分运行到promise 对象中,并在promise 本身之外定义一个resolve 回调,如果收集了许多资源或者如果收集到一定数量的资源,则会调用该回调时间已经过去了:

    get_website_resources <- function(url, url_filter = '*', type_filter = '*', wait_for = 20, n_of_resources = NULL, interactive = F) {
    
        library(crrri)
        library(promises)
    
        crrri::perform_with_chrome(function(client) {
            Fetch <- client$Fetch
            Page <- client$Page
    
            if (interactive) client$inspect()
    
            out <- new.env()
    
            out$results <- list()
            out$resolve_function <- NULL
    
            out$pr <- promises::promise(function(resolve, reject) {
                out$resolve_function <- resolve
    
                Fetch$enable(patterns = list(list(urlPattern="*", requestStage="Response"))) %...>% {
                    Fetch$requestPaused(callback = function(params) {
    
                        if (str_detect(params$request$url, url_filter) & str_detect(params$resourceType, type_filter)) {
    
                            Fetch$getResponseBody(requestId = params$requestId) %...>% {
                                resp <- .
    
                                if (resp$body != '') {
                                    if (resp$base64Encoded) resp$body = jsonlite::base64_dec(resp$body) %>% rawToChar()
    
                                    body <- list(list(
                                        url = params$request$url,
                                        response = resp
                                    )) %>% set_names(params$requestId)
    
                                    #str(body)
    
                                    out$results <- append(out$results, body)
    
                                    if (!is.null(n_of_resources) & length(out$results) >= n_of_resources) out$resolve_function(out$results)
                                }
    
                            }
                        }
    
                        Fetch$continueRequest(requestId = params$requestId)
                    })
                } %...>% {
                    Page$navigate(url)
                } %>% crrri::wait(wait_for) %>%
                    then(~ out$resolve_function(out$results))
    
            })
    
            out$pr$then(function(x) x)
        }, timeouts = max(wait_for + 3, 30), cleaning_timeout = max(wait_for + 3, 30))
    }
    

    【讨论】:

    • 非常有帮助,感谢您回答自己的问题。它帮助我清理了一些类似的代码。有什么理由选择绕过Filter$enable 的url 过滤功能并稍后在代码中重新实现它?
    最近更新 更多