【问题标题】:Web-scraping dynamic Javascript page with RSelenium and rvest使用 RSelenium 和 rvest 抓取动态 Javascript 页面
【发布时间】:2019-07-13 19:34:12
【问题描述】:

我正在尝试从this site 创建一个包含颜色 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 次等。

【问题讨论】:

  • 是否必须与硒一起使用? (我知道这是一个供你学习的项目)
  • 绝对不必是硒。如果有纯 R 或 tidyverse 解决方案,我会全力以赴。

标签: javascript r rvest rselenium


【解决方案1】:

我会回来更新此内容以采纳我的建议。导航到该页面,然后在浏览器中打开开发工具,比如 Chrome,使用 F12 并转到网络选项卡。然后,选择月份和日期并点击立即查看。您将看到流量出现在网络选项卡中。该页面发出 POST xhr 请求以获取您在单击视图图标后看到的内容。

POST 请求本身非常简单,并且有一个包含您选择的月份和日期的正文(表单):

因此,您可以模仿该 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)

现在,这就是我稍后将重新讨论的内容,上面的内容可以转换为返回列表或 df 的函数,该函数可以组合成最终的数据帧。您可以提前生成每个主体并作为参数传递给函数。我会考虑使用 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 也是一种不错的方法。我仍在尝试了解如何格式化月/日组合和实际循环。会继续努力。
  • 我还没写完。只是我需要自学如何在 R 中做下一点。可以用其他语言做。认为我有主要部分。
  • 日月组合对于在循环中执行 POST 已经是正确的。我只需要完成top bit到可以和Session一起工作的函数的转换。
【解决方案2】:

找到了合理的解决方案!它并不完美,但它让我比以前更接近了。我最终按照@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)
}
}



【讨论】:

    猜你喜欢
    • 2018-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-24
    • 2019-04-24
    • 2018-10-27
    • 1970-01-01
    • 2018-03-20
    相关资源
    最近更新 更多