【问题标题】:Web scraping with R and rvest when javascript-rendered content in the web page当网页中的 javascript 呈现内容时,使用 R 和 rvest 进行网页抓取
【发布时间】:2022-02-04 08:43:42
【问题描述】:

我正在尝试抓取网页https://www.filmweb.no/kinotoppen/ 以获取每部电影下的标题和其他信息。对于其他网页,我可以使用 SelectorGadget 运行几行 html_nodes() 和 html_text() 来选择 CSS 选择器来获得我想要的不同内容:

html <- read_html("https://www.filmweb.no/kinotoppen/")
title <- html %>% 
  html_nodes(".Kinotoppen_MovieTitle__2MFbT") %>% 
  html_text()

但是,在此网页上运行这些行时,我只会得到一个空字符向量。在进一步检查网页后,我发现它正在调用 javascripts。 我尝试将 html_nodes("script") 与 v8 库一起使用来运行 javascripts,但无济于事。我也不确定要运行哪些脚本,所以我尝试了所有这些:

ct <- v8()
ct$eval(scripts[3])

一般来说,有没有更简单的方法可以让网页变成我可以使用 rvest 的形式? 我对javascript一无所知。

【问题讨论】:

  • 您可能需要 RSelenium。我发布了另一个类似的答案here
  • 您想要前 10 条信息还是其他内容?
  • 所有前 60 名后按查看整个列表(“Se hele listen”)。在 Dave 的帮助下找到了下面的解决方案,但我们始终欢迎提供更多解决方案。

标签: javascript r web-scraping rvest


【解决方案1】:

这是使用 RSelenium 加载页面的样子。

library(rvest)
library(RSelenium)
remDr <- rsDriver(browser='chrome', port=4444L)
brow <- remDr[["client"]]
brow$open()
brow$navigate("https://www.filmweb.no/kinotoppen/")
h <- brow$getPageSource()
h <- read_html(h[[1]])
h %>% html_nodes(".Kinotoppen_MovieTitle__2MFbT") %>% 
  html_text()
# [1] "Spider-Man: No Way Home"              "Clifford: Den store røde hunden"      "Lise & Snøpels - Venner for alltid"  
# [4] "Familien Voff - alle trenger en venn" "Nightmare Alley"                      "Snødronningen"                       
# [7] "Scream"                               "Bergman Island"                       "Trøffeljegerne fra Piemonte"         
# [10] "Encanto"                             


【讨论】:

  • 谢谢!这有效,我会将其标记为已接受的答案。希望我能支持你一百次! :) 一个问题,这需要 PhantomJS 吗?运行 rsDriver 并查看您的其他帖子,我看到了。我没有安装它,但 RSelenium 似乎有。我还在我浏览的一个小插曲中看到了它。我读到该项目已暂停,那么这将在未来起作用吗?再次,非常感谢你:)
  • 我也一直在尝试按“Se hele listen”来获取整个列表: button
  • 我发现由于某种原因,应该与 findElement() 一起使用的东西并不总是如此,在这些情况下我有更好的运气,例如,brow()$executeScript() 使用适当的 javascript,例如:brow$executeScript("document.querySelector('.search-form &gt; div:nth-child(2) &gt; a:nth-child(1)').click()") 在 RSelenium 函数的等效操作不起作用时起作用。
  • 非常感谢您的回答。运行您的代码时,我奇怪地收到一个错误:Selenium 消息:javascript 错误:无法读取 null 的属性(读取“点击”)。但我也尝试检查按钮并复制 JS 路径,然后像上面那样插入 .click() :brow$executeScript('document.querySelector("#root &gt; main &gt; div.contentwrapper_contentWrapper__TndLw &gt; div.Kinotoppen_Kinotoppen__2crjn &gt; div &gt; div.Kinotoppen_MovieChartContainer__JFEcJ &gt; div.Kinotoppen_ShowAllMovies__6tT_i &gt; button").click()') 并且有效!谢谢!
【解决方案2】:

从 graphql 查询中动态检索数据。您可以复制该查询以获取包含所有所需数据的 JSON 响应。

在这种情况下,我选择使用httr2newish pipe operator (R 4.1.0)

关于如何管道标头向量,我查看了@MrFlick here 给出的解决方案。

library(httr2)

headers = c(
  'Accept' = 'application/json',
  'Referer' = 'https://www.filmweb.no/',
  'Content-Type' = 'application/json',
  'User-Agent' = 'Mozilla/5.0'
)

params = list(
  'query' = 'query($date:String,$chartType:String,$max:Int){movieQuery{getMovieChart(date:$date,chartType:$chartType,max:$max){chartType periodStart periodEnd movieChartItem{pos posPrev admissions admissionsPrev admissionsToDate weeksOnList movie{title mainVersionId premiere poster{name versions{width height url}}}}}}}',
  'variables' = '{"date":"2022-02-04","chartType":"weekend","max":1000}'
)

data <- request("https://skynet.filmweb.no/MovieInfoQs/graphql/") |> 
  (\(x) req_headers(x,  !!!headers))() |>  
 req_url_query(!!!params) |> 
  req_perform() |> 
  resp_body_json()

【讨论】:

  • 感谢您提供另一个解决方案。我很快尝试复制并运行它,现在更改为 magrittr 管道和旧的 anynomous 函数语法,因为我使用 4.0.4 并且它有效。我得到了一个包含大量信息的列表,并快速检查它,我可以看到一些电影标题和编号。非常好。一个可能很愚蠢但我对 Web 开发一无所知的问题;您可以通过检查检查是否从 graphql 查询中检索到数据?我认为情况并非总是如此,因为快速谷歌搜索表明 grapql 于 2015 年公开发布。
  • 你可以看出来,因为 graphql 在端点 /URI 中。您可以在浏览器的网络选项卡中过滤该字符串上的 XHR 网络流量。此外,查询字符串有点赠送。您应该在响应中看到所有 60 个结果,并且信息比页面上显示的更多。然后我可能会应用自定义函数从解析的 JSON 对象 data 中提取您想要的内容。
猜你喜欢
  • 2015-09-06
  • 1970-01-01
  • 2018-02-15
  • 2018-10-27
  • 2019-10-11
相关资源
最近更新 更多