R:2019 年更改后的网页抓取 yahoo.finance
R: web scraping yahoo.finance after 2019 change
很长一段时间以来,我一直很高兴地使用主要从其他 Whosebug 答案中借用的代码来抓取 yahoo.finance 页面,并且效果很好,但是在过去的几周中,雅虎更改了他们的 table s 为 collapsible/expandable tables。这已经破坏了代码,尽管我几天来尽了最大的努力,但我还是无法修复这个错误。
这里是其他人多年来使用的代码示例(然后由不同的人以不同的方式解析和处理)。
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 个观察值是该页面上的主要 table,从
开始
市值(盘中)5 [此处的值]
企业价值3【这里的价值】
等等...
这可能看起来有点绕房子,但我想避免页面上很多我怀疑是动态的内容(例如,许多 classNames)并提供可能稍长的内容 shelf-life.
您的代码失败,部分原因是没有 table
元素容纳该数据。相反,您可以使用 more stable looking fi-row
class 属性收集所需输出 table 的 "rows"。然后,在每一行中,您可以通过根据 parent 行节点匹配具有 title
属性或 data-test='fin-col'
的元素来收集列。
我使用正则表达式来匹配日期(因为这些会随着时间的推移而变化)并将它们与静态的两个 headers 结合起来以提供最终的数据帧 headers 用于输出。我将正则表达式限制为单个节点的文本,我知道该文本应该包含仅那些必需日期的模式匹配。
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)
样本:
Py:
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)
如上面的评论所述,这里有一个替代方案试图处理已发布的不同 table 尺寸。我已经研究过这个并且得到了朋友的帮助。
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)
很长一段时间以来,我一直很高兴地使用主要从其他 Whosebug 答案中借用的代码来抓取 yahoo.finance 页面,并且效果很好,但是在过去的几周中,雅虎更改了他们的 table s 为 collapsible/expandable tables。这已经破坏了代码,尽管我几天来尽了最大的努力,但我还是无法修复这个错误。
这里是其他人多年来使用的代码示例(然后由不同的人以不同的方式解析和处理)。
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 个观察值是该页面上的主要 table,从
开始市值(盘中)5 [此处的值] 企业价值3【这里的价值】 等等...
这可能看起来有点绕房子,但我想避免页面上很多我怀疑是动态的内容(例如,许多 classNames)并提供可能稍长的内容 shelf-life.
您的代码失败,部分原因是没有 table
元素容纳该数据。相反,您可以使用 more stable looking fi-row
class 属性收集所需输出 table 的 "rows"。然后,在每一行中,您可以通过根据 parent 行节点匹配具有 title
属性或 data-test='fin-col'
的元素来收集列。
我使用正则表达式来匹配日期(因为这些会随着时间的推移而变化)并将它们与静态的两个 headers 结合起来以提供最终的数据帧 headers 用于输出。我将正则表达式限制为单个节点的文本,我知道该文本应该包含仅那些必需日期的模式匹配。
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)
样本:
Py:
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)
如上面的评论所述,这里有一个替代方案试图处理已发布的不同 table 尺寸。我已经研究过这个并且得到了朋友的帮助。
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)