【问题标题】:R: web scraping yahoo.finance after 2019 changeR:2019 年更改后的网络抓取 yahoo.finance
【发布时间】:2020-02-07 10:45:05
【问题描述】:

很长一段时间以来,我一直很高兴地使用主要从其他 stackoverflow 答案中借来的代码来抓取 yahoo.finance 页面,并且效果很好,但是在过去的几周里,雅虎已经将他们的表格更改为可折叠/可展开的表格。这已经破坏了代码,尽管我尽了最大的努力,但仍然无法修复错误。

这是其他人多年来使用的代码示例(然后由不同的人以不同的方式解析和处理)。

library(rvest)
library(tidyverse)

# Create a URL string
myURL <- "https://finance.yahoo.com/quote/AAPL/financials?p=AAPL"

# Create a dataframe called df to hold this income statement called df
df <- myURL %>% 
  read_html() %>% 
  html_table(header = TRUE) %>% 
  map_df(bind_cols) %>% 
  as_tibble()

谁能帮忙?


编辑以获得更清晰:

如果你运行上面的然后查看你得到的 df

# A tibble: 0 x 0

对于预期结果的示例,我们可以尝试 yahoo 未更改的另一个页面,如下所示:

 # Create a URL string
myURL2 <-  "https://finance.yahoo.com/quote/AAPL/key-statistics?p=AAPL"

df2 <- myURL2 %>% 
  read_html() %>% 
  html_table(header = FALSE) %>% 
  map_df(bind_cols) %>% 
  as_tibble()

如果您查看 df2,您会得到一个包含 59 个观察值的小标题,其中两个变量是该页面上的主表,以

开头

市值(盘中)5 [此处的值] 企业价值3【这里的价值】 等等……

【问题讨论】:

  • 您能否更清楚地说明预期输出与实际发生的情况?
  • 当然。我将更详细地编辑上面的问题。 :-)
  • 你将如何使用你的脚本来构建一个循环来运行多个代码的抓取,然后将它们绑定在一起?

标签: r web-scraping rvest yahoo-finance


【解决方案1】:

这可能看起来有点绕房子,但我想避免页面上的大部分我怀疑是动态的(例如许多类名),并提供一些可能具有稍长保质期的东西。

您的代码失败,部分原因是没有 table 元素容纳该数据。相反,您可以使用看起来更稳定的fi-row 类属性来收集所需输出表的“行”。然后,您可以在每一行中根据父行节点通过匹配具有title 属性或data-test='fin-col' 属性的元素来收集列。

我使用正则表达式匹配日期(随着时间的推移而变化),并将它们与静态的两个标头结合起来,以提供最终的数据帧标头以供输出。我将正则表达式限制为单个节点的文本,我知道该文本应包含仅是那些必需日期的模式匹配。


R:

library(rvest)
library(stringr)
library(magrittr)

page <- read_html('https://finance.yahoo.com/quote/AAPL/financials?p=AAPL')
nodes <- page %>%html_nodes(".fi-row")
df = NULL

for(i in nodes){
  r <- list(i %>%html_nodes("[title],[data-test='fin-col']")%>%html_text())
  df <- rbind(df,as.data.frame(matrix(r[[1]], ncol = length(r[[1]]), byrow = TRUE), stringsAsFactors = FALSE))
}

matches <- str_match_all(page%>%html_node('#Col1-3-Financials-Proxy')%>%html_text(),'\\d{1,2}/\\d{1,2}/\\d{4}')  
headers <- c('Breakdown','TTM', matches[[1]][,1]) 
names(df) <- headers
View(df)

示例:


派:

import requests, re
import pandas as pd
from bs4 import BeautifulSoup as bs

r = requests.get('https://finance.yahoo.com/quote/AAPL/financials?p=AAPL')
soup = bs(r.content, 'lxml')
results = []

for row in soup.select('.fi-row'):
    results.append([i.text for i in row.select('[title],[data-test="fin-col"]')])

p = re.compile(r'\d{1,2}/\d{1,2}/\d{4}')
headers = ['Breakdown','TTM']
headers.extend(p.findall(soup.select_one('#Col1-3-Financials-Proxy').text))
df = pd.DataFrame(results, columns = headers)
print(df)

【讨论】:

  • 干得好!非常感谢。我真的很喜欢你的方法。我也一直在研究这个(花了不健康的时间),并且使用了与您类似的方法,逐行浏览它,我也添加了它以按列浏览它。我将在下面的答案中发布我的代码。
  • 你在这里没有太多选择,但仍然能够生产出可能会持续一段时间的东西。
  • 您编写的 Python 代码也很棒 - 简短而甜美。你会在这里看到我的 R 版本——它不像你的那么简洁。做得好,非常感谢。
  • 不客气。请记住,您可以在两天内接受自己的答案。这样做有助于向人们展示什么是有效的。
  • str_match_all(page%>%html_node('#Col1-1-Financials-Proxy')%>%html_text(),'\\d{1,2}/\\d{1, 2}/\\d{4}')
【解决方案2】:

正如上面评论中提到的,这里有一个替代方案,它试图处理已发布的不同表大小。我已经解决了这个问题,并得到了朋友的帮助。

library(rvest)
library(tidyverse)

url <- https://finance.yahoo.com/quote/AAPL/financials?p=AAPL

# Download the data
raw_table <- read_html(url) %>% html_nodes("div.D\\(tbr\\)")

number_of_columns <- raw_table[1] %>% html_nodes("span") %>% length()

if(number_of_columns > 1){
  # Create empty data frame with the required dimentions
  df <- data.frame(matrix(ncol = number_of_columns, nrow = length(raw_table)),
                      stringsAsFactors = F)

  # Fill the table looping through rows
  for (i in 1:length(raw_table)) {
    # Find the row name and set it.
    df[i, 1] <- raw_table[i] %>% html_nodes("div.Ta\\(start\\)") %>% html_text()
    # Now grab the values
    row_values <- raw_table[i] %>% html_nodes("div.Ta\\(end\\)")
    for (j in 1:(number_of_columns - 1)) {
      df[i, j+1] <- row_values[j] %>% html_text()
    }
  }
view(df)

【讨论】:

    猜你喜欢
    • 2018-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-15
    • 2015-10-11
    • 1970-01-01
    相关资源
    最近更新 更多