使用RSelenium和rvest爬取动态Javascript页面

3
我正在尝试从这个网站创建一个颜色ID、描述和日期的数据框,该网站通过下拉菜单输入日期和月份,并返回一个动态JS生成的页面。我是新手,认为这将是一个有趣的玩具项目。我想使用RSelenium自动化下拉选择,并使用rvest来抓取生成的内容。我希望得到的数据框结构如下:
description, date, meta
"paragraph about birthday", Jun 01, "DAFFODIL PANTONE 17-1512 POWERFUL KNOWING EXPRESSIVE"

我正在尝试首先使用for循环来遍历一年中的每个月的某一天,然后逐步获取每个月的每一天。但我卡在了仅仅让循环遍历每个月,并获取内容上。对于这部分任务,我需要一些概念性帮助,感谢任何洞见!
library(RSelenium)
library(rvest)
library(tidyverse)
library(xml2)

## first run: docker run -d -p 4445:4444 selenium/standalone-chrome
## open a new connection to Chrome
remDr <- RSelenium::remoteDriver(remoteServerAddr = "localhost",
                                 port = 4445L,
                                 browserName = "chrome")

remDr$open()
remDr$navigate("https://www.pantone.com/pages/iphone/iphone_colorstrology.html#___1__") #Entering our URL gets the browser to navigate to the page
remDr$screenshot(display = TRUE) 

#### create list of month/days
 month_day<- read_html(remDr$getPageSource()[[1]])
 page_i <- month_day %>%
   html_nodes(".list") %>%
   html_children() %>% 
   html_text()

months <- page_i[1:12]
months <- (paste("'", months,"'", sep=''))
days <- page_i[13:43]
days <- as.numeric(days)


## create an object for month xpath elements
for (m in months){
  elements <- paste0("//option[contains(text(),",months,")]")
}

## attempt at loop

total <- data.frame()

for (e in elements){
remDr$navigate("https://www.pantone.com/pages/iphone/iphone_colorstrology.html#___1__") 
      print(e)
      month <- remDr$findElement(using = 'xpath', e)
      month$clickElement()
      day <- remDr$findElement(using = 'xpath', "//select[@id='lstDay']//option[5]") ## arbitrarily picking the 5th of each month
      day$clickElement()
      submit <- remDr$findElement(using = 'xpath', "/html[1]/body[1]/form[1]/div[1]/a[1]")
      submit$clickElement()
      html <- read_html(remDr$getPageSource()[[1]])
      description <- html %>%  html_nodes(xpath = "//tr//tr[2]//td[1]") %>% html_text() %>% gsub("^\\s+|\\s+$", "", .)
      meta <- html %>% html_nodes(xpath = "//td[@id='tdBg']") %>%  html_text() %>% gsub("^\\s+|\\s+$", "", .) 
      date <- html %>% html_nodes(xpath = "//td[@id='bgHeaderDate']//div") %>%  html_text() %>% gsub("^\\s+|\\s+$", "", .)
      df <- data.frame(cbind(description,meta,date))
      total <- rbind(total, df)
}

没有出现任何错误,但每次结果都出乎意料。有时候会重复单个月/日的组合,比如Jan05*12次或jan05 * 3次,Apr 05 *3次等。


必须使用Selenium吗?(我知道这是你学习的项目) - QHarr
绝对不必使用Selenium。如果有纯R或tidyverse解决方案,我全听着。 - pocketprotector
2个回答

2
我会回来更新并采纳我的建议。进入该页面并在浏览器中打开开发工具,例如Chrome,按下F12并转到网络选项卡。然后,选择一个月份和日期,点击立即查看。你将在网络选项卡中看到流量。页面通过POST xhr请求获取您在单击查看图标后看到的内容。

enter image description here

这个POST请求本身非常简单,它有一个包含你选择的月份和日期的表单(body)。

enter image description here

所以,您可以模仿那个POST请求,然后解析响应。您提到的日期的一个例子可能是:
library(rvest)

body <- list('month' = 6,'day' = 1)
url <- 'https://www.pantone.com/pages/iphone/iphone_colorstrology_results.aspx'
page <- html_session(url) %>%
  rvest:::request_POST(url, body = body, encode = "form") %>%
  read_html()

date <- page %>% html_node('table table td') %>% html_text() %>% 
  gsub('^\\s+|\\s+$|[\r\n\t]', '', .)
description <- page %>% html_node('tr:nth-of-type(2) div') %>% html_text() %>% 
  gsub('^\\s+|\\s+$|[\r\n\t]', '', .)
meta <- page %>% html_nodes('#tdBg span') %>% html_text()

df <- data.frame(date, description, meta)

现在,这是我稍后将要重新审视的,上述内容可以转换为一个函数,该函数返回一个可组合成最终数据框的列表或数据框。您可以预先生成每个正文并将其作为参数传递给函数。我建议使用Session对象、http Session来提高重复使用当前连接的效率。在循环/嵌套循环中,可以更新表单正文中的月份和日期——具体取决于它们如何被生成。我是R的新手,知道它没有字典,但也许它有命名列表或类似的东西,可以从原始页面中抓取月份:可能值的关联,以便在循环中使用。我很乐意向更有经验的R人学习如何实现上述功能——我的R知识还有一些空白需要填补。也许有人会发布类似的答案,这将非常有帮助。
生成POST请求体:
看一下下拉菜单,它是针对标准年份的,因此您可以在一个嵌套的for循环中生成所需的POST请求体。我使用1,12表示月份,并使用lubridate返回基于标准年份的每个月的天数。
library(lubridate)

for(i in seq(1,12)){
  date <- as.Date(gsub('placeholder',i, "2019-placeholder-01"), "%Y-%m-%d")
  days <- days_in_month(date)[[1]]
  for(j in seq(1,days)){
    body = list('month' = i,'day' = j)
    # pass body to function or add to an iterable for later looping
  }
}

感谢您的建议。我喜欢简洁的rvest模式和推荐。避免使用xpath也是一个不错的方法。我仍在努力理解如何格式化月/日组合和实际循环。会继续尝试的。 - pocketprotector
我还没有完成它。只是我需要自学如何在R中完成下一步。可以用其他语言完成。我想我已经有了主要的部分。 - QHarr
日期和月份的组合已经正确,可以在循环中进行POST。我只需要完成将顶部位转换为一个能够与Session一起使用的函数。 - QHarr

1

我找到了一个合理的解决方案!虽然不是完美的,但比之前更接近了。最终我按照@QHarr的建议编写了一个函数,并使用了他们的rvest模式:

library(rvest)

colorstrology <- function(i,j){

  body <- list('month' = i,'day' = j)
  url <- 'https://www.pantone.com/pages/iphone/iphone_colorstrology_results.aspx'
  page <- html_session(url) %>%
    rvest:::request_POST(url, body = body, encode = "form") %>%
    read_html()

  date <- page %>% html_node('table table td') %>% html_text() %>% 
    gsub('^\\s+|\\s+$|[\r\n\t]', '', .)
  description <- page %>% html_node('tr:nth-of-type(2) div') %>% html_text() %>% 
    gsub('^\\s+|\\s+$|[\r\n\t]', '', .)
  meta <- page %>% html_nodes('#tdBg span') %>% html_text()

  df <- data.frame(date, description, meta)
}



months <- c(1:12)
days <- c(1:31)

df <- data.frame(date, description, meta)
for (m in months){
  for (d in days){
    temp <- colorstrology(m,d)
    df <- rbind(temp, df)
}
}




网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接