Webhose.io 和 R - 来自 API 的多个查询

Webhose.io and R - multiple queries from API

使用 Webhose API 提取数据但是每次调用只带回 100 条记录,之后 Webhose 以列的形式提供下一个 URL 以调用下 100 条记录,直到你拥有所有你的数据。以下是我目前所拥有的

**在我的例子中,我有 200 条记录,这意味着我必须 运行 两次才能获得我正在寻找的所有数据

#Pull the data from Webhose as JSON
as_json<- f"http://webhose.io/filterWebContent?token=XXXb&format=json&ts=1213456&sort=crawled&q=(China%20AND%20United%20)%20language%3Aenglish%20site_type%3Anews%20site%3Abloomberg.com")

#Convert the JSON into a DataFrame
 df_1 <- as.data.frame(as_json)

#Subset the new URL which appears as column from the first pull 
 next_url <- df_1$next.[1]

#Pull data from Webhose as JSON using the new URL - to retrieve the next 100
 as_json2 <- fromJSON(next_url)

#Convert the JSON into a DataFrame - 2nd Time
 df_2 <- as.data.frame(as_json2)

我的问题是需要反复执行此操作,直到不再需要调用为止。数据框中有一列称为 moreResultsAvailable。当它达到零时,可以假设没有剩余的拉力。我假设我们将使用此专栏来帮助关闭 loop.I 也不知道这可能需要多少调用。

然后我想将所有 dfs 连接到一个名为 combo 的数据框中

有人知道我如何才能有效地做到这一点吗?

您需要费点功夫并连接必要的样板代码以迭代所需的响应数量。

我们需要一些包:

library(httr)
library(jsonlite)
library(tidyverse)

创建 cpl 函数会让生活更轻松。

首先,一个用于请求内容:

filter_web_content <- function(query, sort = "relevancy",
                               ts = (Sys.time() - (3 * 24 * 60 * 60)),
                               order = "asc", size = 100, from = 0,
                               token = Sys.getenv("WEBHOSE_TOKEN")) {

  params <- list(
    token = token,
    format = "json",
    q = query,
    sort = sort,
    order = order,
    size = size,
    ts = ts
  )

  httr::GET(
    url = "https://webhose.io/filterWebContent",
    query = params
  ) -> res

  httr::stop_for_status(res)

  res <- httr::content(res, as = "text", encoding = "UTF-8")
  res <- jsonlite::fromJSON(res, flatten = TRUE)

  res

}

他们针对此特定端点的 REST API 可以采用更多参数。如果我有一些空闲周期,我会为整个 REST 制作一个 pkg 包装器 API(我会回到这里 if/when 我这样做)。

现在,一个从可能令人厌恶的列名中创建漂亮的列名的方法:

mcga <- function(tbl) {

  x <- colnames(tbl)
  x <- tolower(x)
  x <- gsub("[[:punct:][:space:]]+", "_", x)
  x <- gsub("_+", "_", x)
  x <- gsub("(^_|_$)", "", x)
  x <- make.unique(x, sep = "_")

  colnames(tbl) <- x

  tbl

}

这是设置位:

  • 预先分配一个列表以避免增长和复制。我不知道“30”是否适合这个 API
  • 创建一个循环,递增列表索引变量和API获取起始位置
  • 将结果推入列表中的下一个空位

之后我们会用数据做一些事情。

PRE_ALLOC_MAX <- 30
results <- vector(mode = "list", length = PRE_ALLOC_MAX)

i <- 1
from <- 0
repeat {
  res <- filter_web_content("(China AND United ) language:english site_type:news site:bloomberg.com", 
                            ts = 1213456, from = from)
  results[[i]] <- res
  if (res[["moreResultsAvailable"]] > 0) {
    message("Fetching next 100 records...")
    i <- i + 1
    from <-  from + 100  
  } else {
    break
  }
}
## Fetching next 100 records...
## Fetching next 100 records...

现在:

  • 删除所有 NULL(未填充的)条目
  • 用我们需要的数据提取API结果的一部分
  • 将其包装在数据框中
  • 让列名再次变得很棒

你不需要这样做,但我认为它在长运行:

中更具可读性
discard(results, is.null) %>% 
  map_df(~{ .x$posts}) %>% 
  tbl_df() %>% 
  mcga()
## # A tibble: 227 x 42
##                                        uuid
##                                       <chr>
##  1 ea6f6084be16a50b0d4791ffa268956ca691c16d
##  2 bd0ac60981ac73e2a7e71378881272eb5b6147d7
##  3 3f2c2c13aa2b3c6d5fc8300f3a9876d9c86c08d1
##  4 659d73d3ddba3c0a0505da8fc15862bc33ac9519
##  5 371293cf38efe9c9a4708403c816c8b33eeb1298
##  6 38a3522fe1d268519aa0e2c3c865bbee19f9ee65
##  7 a4b1f0e4a8d94354ae41c80bebe56237b5a39ca8
##  8 323660c1c21662a1e5b147455f7a4c70f60e12b8
##  9 3233102dbbed6bd90c19ddb2cf7df9111de6ffcf
## 10 c4f126943968be899a6c5fdd806274f0ca848714
## # ... with 217 more rows, and 41 more variables: url <chr>, ord_in_thread <int>,
## #   author <chr>, published <chr>, title <chr>, text <chr>, highlighttext <chr>,
## #   highlighttitle <chr>, language <chr>, external_links <list>, rating <lgl>,
## #   crawled <chr>, thread_uuid <chr>, thread_url <chr>, thread_site_full <chr>,
## #   thread_site <chr>, thread_site_section <chr>, thread_site_categories <list>,
## #   thread_section_title <chr>, thread_title <chr>, thread_title_full <chr>,
## #   thread_published <chr>, thread_replies_count <int>,
## #   thread_participants_count <int>, thread_site_type <chr>, thread_country <chr>,
## #   thread_spam_score <dbl>, thread_main_image <chr>,
## #   thread_performance_score <int>, thread_domain_rank <int>,
## #   thread_social_facebook_likes <int>, thread_social_facebook_comments <int>,
## #   thread_social_facebook_shares <int>, thread_social_gplus_shares <int>,
## #   thread_social_pinterest_shares <int>, thread_social_linkedin_shares <int>,
## #   thread_social_stumbledupon_shares <int>, thread_social_vk_shares <int>,
## #   entities_persons <list>, entities_organizations <list>,
## #   entities_locations <list>

考虑在 rOpenSci github 源中搜索 API 包。他们中的一些人有类似的习语来做这种API迭代的事情。

更新

您现在可以为此使用 https://github.com/hrbrmstr/webhose 中的 2 个函数。您需要执行此操作才能安装它,直到它在 CRAN 上:

devtools::install_github("hrbrmstr/webhose")

如果您想自己处理分页或只查询一组结果,那么

res <- webhose::filter_web_content("(China AND United) language:english site_type:news site:bloomberg.com", ts = 1213456)

如果你想要自动-API分页那么:

res <- webhose::fetchall_web_content("(China AND United) language:english site_type:news site:bloomberg.com", ts = 1213456)

我会过一段时间再讲 API 的其余部分。