【问题标题】:Scrape with a loop and avoid 404 error用循环刮擦,避免 404 错误
【发布时间】:2017-01-30 16:40:06
【问题描述】:

我正在尝试为我的项目抓取 wiki 以获取某些与天文学相关的定义。该代码运行良好,但我无法避免 404。我试过tryCatch。我想我在这里遗漏了一些东西。

我正在寻找一种在运行循环时克服 404 的方法。这是我的代码:

library(rvest)
library(httr)
library(XML)
library(tm)


topic<-c("Neutron star", "Black hole", "sagittarius A")

for(i in topic){

  site<- paste("https://en.wikipedia.org/wiki/", i)
  site <- read_html(site)

  stats<- xmlValue(getNodeSet(htmlParse(site),"//p")[[1]]) #only the first paragraph
  #error = function(e){NA}

  stats[["topic"]] <- i

  stats<- gsub('\\[.*?\\]', '', stats)
  #stats<-stats[!duplicated(stats),]
  #out.file <- data.frame(rbind(stats,F[i]))

  output<-rbind(stats,i)

}

【问题讨论】:

  • 我想你的意思是记下错误然后跳到循环的下一个迭代?
  • 相关/可能重复的帖子stackoverflow.com/questions/8093914
  • 使用httr::GET()检索目标URL的内容。将content(res, as="text", encoding="UTF-8")(假设您将httr::GET 调用的结果存储在res 中)传递给read_html()。您可以测试res 的状态码。此外,当您将 read_html 已解析的内容和 xml2 对象传递给 XML 包函数时,您确实对 read_html 没有多大好处。在完成这个项目之前,我会花一些时间真正了解您尝试使用的各种功能和包。
  • 我在下面回答了......但我不明白为什么你有 tm 包来源......或者为什么第一段是;默认;您需要的唯一数据。但无论如何...

标签: r xml-parsing web-scraping rvest


【解决方案1】:
  1. 使用sprintf 在循环中构建变量url。
  2. 从段落节点中提取所有正文文本。
  3. 删除所有返回长度 (0) 的向量
  4. 我添加了一个步骤,将所有正文文本都包含在前面的 [paragraph - n] 注释中以供参考。因为好吧...朋友不要让朋友浪费数据或发出多个 http 请求。
  5. 为您的主题列表中的每个迭代构建一个数据框,格式如下:
  6. 将列表中的所有data.frames绑定到一个...

  7. wiki_url:应该很明显

  8. 主题:来自主题列表
  9. info_summary:第一段(您在帖子中提到)
  10. all_info:如果您需要更多信息,请知道。

  11. 请注意,我使用的是旧版本的 rvest 源代码

  12. 为了便于理解,我只是将名称 html 分配给您的 read_html。

       library(rvest)
       library(jsonlite)
    
       html <- rvest::read_html
    
       wiki_base <- "https://en.wikipedia.org/wiki/%s"
    
       my_table <- lapply(sprintf(wiki_base, topic), function(i){
    
            raw_1 <- html_text(html_nodes(html(i),"p"))
    
            raw_valid <- raw_1[nchar(raw_1)>0]
    
            all_info <- lapply(1:length(raw_valid), function(i){
                sprintf(' [paragraph - %d] %s ', i, raw_valid[[i]])
            }) %>% paste0(collapse = "")
    
            data.frame(wiki_url = i, 
                       topic = basename(i),
                       info_summary = raw_valid[[1]],
                       trimws(all_info),
                       stringsAsFactors = FALSE)
    
        }) %>% rbind.pages
    
       > str(my_table)
       'data.frame':    3 obs. of  4 variables:
        $ wiki_url    : chr  "https://en.wikipedia.org/wiki/Neutron star"     "https://en.wikipedia.org/wiki/Black hole" "https://en.wikipedia.org/wiki/sagittarius A"
        $ topic       : chr  "Neutron star" "Black hole" "sagittarius A"
        $ info_summary: chr  "A neutron star is the collapsed core of a large star (10–29 solar masses). Neutron stars are the smallest and densest stars kno"| __truncated__ "A black hole is a region of spacetime exhibiting such strong gravitational effects that nothing—not even particles and electrom"| __truncated__ "Sagittarius A or Sgr A is a complex radio source at the center of the Milky Way. It is located in the constellation Sagittarius"| __truncated__
        $ all_info    : chr  " [paragraph - 1] A neutron star is the collapsed core of a large star (10–29 solar masses). Neutron stars are the smallest and "| __truncated__ " [paragraph - 1] A black hole is a region of spacetime exhibiting such strong gravitational effects that nothing—not even parti"| __truncated__ " [paragraph - 1] Sagittarius A or Sgr A is a complex radio source at the center of the Milky Way. It is located in the constell"| __truncated__
    

编辑

错误处理函数....返回一个逻辑。所以这成为我们的第一步。

url_works <- function(url){
tryCatch(
    identical(status_code(HEAD(url)),200L), 
    error = function(e){
        FALSE
    })
}

根据您对“exoplanet”的使用,以下是来自 wiki 页面的所有适用数据:

 exo_data <- (html_nodes(html('https://en.wikipedia.org/wiki/List_of_exoplanets'),'.wikitable')%>%html_table)[[2]]

str(exo_data)

    'data.frame':   2048 obs. of  16 variables:
 $ Name                          : chr  "Proxima Centauri b" "KOI-1843.03" "KOI-1843.01" "KOI-1843.02" ...
 $ bf                            : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Mass (Jupiter mass)           : num  0.004 0.0014 NA NA 0.1419 ...
 $ Radius (Jupiter radii)        : num  NA 0.054 0.114 0.071 1.012 ...
 $ Period (days)                 : num  11.186 0.177 4.195 6.356 19.224 ...
 $ Semi-major axis (AU)          : num  0.05 0.0048 0.039 0.052 0.143 0.229 0.0271 0.053 1.33 2.1 ...
 $ Ecc.                          : num  0.35 1.012 NA NA 0.0626 ...
 $ Inc. (deg)                    : num  NA 72 89.4 88.2 87.1 ...
 $ Temp. (K)                     : num  234 NA NA NA 707 ...
 $ Discovery method              : chr  "radial vel." "transit" "transit" "transit" ...
 $ Disc. Year                    : int  2016 2012 2012 2012 2010 2010 2010 2014 2009 2005 ...
 $ Distance (pc)                 : num  1.29 NA NA NA 650 ...
 $ Host star mass (solar masses) : num  0.123 0.46 0.46 0.46 1.05 1.05 1.05 0.69 1.25 0.22 ...
 $ Host star radius (solar radii): num  0.141 0.45 0.45 0.45 1.23 1.23 1.23 NA NA NA ...
 $ Host star temp. (K)           : num  3024 3584 3584 3584 5722 ...
 $ Remarks                       : chr  "Closest exoplanet to our Solar System. Within host star’s habitable zone; possibl
 y Earth-like." "controversial" "controversial" "controversial" ...

在表格的随机样本上测试我们的 url_works 函数

tests <- dplyr::sample_frac(exo_data, 0.02) %>% .$Name

现在让我们构建一个 ref 表,其中包含名称、要检查的 url 以及 url 是否有效的逻辑,并在一步中创建一个包含两个数据帧的列表,一个包含不存在的 url... .和其他的。签出的那些我们可以毫无问题地运行上述功能。这样,错误处理就在我们真正开始尝试循环解析之前完成。避免令人头疼的问题,并为需要进一步研究的项目提供参考确认。

b <- ldply(sprintf('https://en.wikipedia.org/wiki/%s',tests), function(i){
data.frame(name = basename(i), url_checked = i,url_valid = url_works(i))
}) %>%split(.$url_valid)

> str(b)
List of 2
 $ FALSE:'data.frame':  24 obs. of  3 variables:
  ..$ name       : chr [1:24] "Kepler-539c" "HD 142 A c" "WASP-44 b" "Kepler-280 b" ...
  ..$ url_checked: chr [1:24] "https://en.wikipedia.org/wiki/Kepler-539c" "https://en.wikipedia.org/wiki/HD 142 A c" "https://en.wikipedia.org/wiki/WASP-44 b" "https://en.wikipedia.org/wiki/Kepler-280 b" ...
  ..$ url_valid  : logi [1:24] FALSE FALSE FALSE FALSE FALSE FALSE ...
 $ TRUE :'data.frame':  17 obs. of  3 variables:
  ..$ name       : chr [1:17] "HD 179079 b" "HD 47186 c" "HD 93083 b" "HD 200964 b" ...
  ..$ url_checked: chr [1:17] "https://en.wikipedia.org/wiki/HD 179079 b" "https://en.wikipedia.org/wiki/HD 47186 c" "https://en.wikipedia.org/wiki/HD 93083 b" "https://en.wikipedia.org/wiki/HD 200964 b" ...
  ..$ url_valid  : logi [1:17] TRUE TRUE TRUE TRUE TRUE TRUE ...

显然列表的第二项包含具有有效 url 的数据框,因此将先前的函数应用于该列表中的 url 列。请注意,出于解释的目的,我对所有行星的表进行了抽样...有 2400 个奇怪的名称,因此在您的情况下,检查将需要一两分钟的时间。希望能帮到你。

【讨论】:

  • 我试图用正则表达式清理文本,所以我尝试了 tm 包。我只需要第一段来测试我的代码。我知道这不会花费很多时间,但我有一长串主题。你给的工作很好,但我没有看到错误处理的步骤。
  • 塞巴斯蒂安,你是对的!我正在寻找一种方法来跳过错误 URL 或记下变量中的错误,然后继续下一项。
  • 错误的根源在哪里......这还不清楚。是列表中的项目可能没有页面的错误吗?你的例子没有抛出错误......所以我认为这是基础。你需要弄清楚错误来自哪里……就正则表达式而言……这很模糊,但没关系。
  • 我有一份与天文学相关的项目清单,可能准确也可能不准确。我知道他们中的大多数是准确的,但有些可能不是。例如,有一颗名为 HAT-P-32b 的系外行星。我在列表中有这个,作为 HAT_P_​​32b。我知道这是一个错误,所以我手动更正了它。我可能无法遍历整个列表来更正它们中的每一个,因此如果请求引发错误,那么我将更正它们并再次运行这些条目。请分享你的想法!顺便说一句,我喜欢您在解决方案上方解释您的步骤的方式。谢谢!
  • 这很好用!第一次没有运行。我加载了 httr 库,然后它工作了!高超!我感谢您为目录和段落拆分共享的额外代码。我比我原来的想法更喜欢它。再次感谢你!干杯!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-09
  • 2015-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
相关资源
最近更新 更多