【问题标题】:Nothing returned when I try to scrape mlb.com transactions using rvest当我尝试使用 rvest 抓取 mlb.com 交易时没有返回任何内容
【发布时间】:2019-05-24 15:58:15
【问题描述】:

我一直在尝试抓取 mlb 交易页面 (http://mlb.mlb.com/mlb/transactions/index.jsp#month=5&year=2019) 以获取每个给定交易的相应日期和文本,但没有成功。使用 rvest 和选择器小工具,我编写了一个简短的函数,它应该给我显示从第一个可用 n 2001 到 2019 年 3 月的表格。

我只是收到这一系列错误,但什么也没有发生。

这是我从给定网站上抓取数据的代码。

library(tidyverse)
library(rvest)


# breaking the URL into the start and end for easy pasting to fit timespan
url_start = "http://mlb.mlb.com/mlb/transactions/index.jsp#month="
url_end = "&year="

# function which scrapes data

mlb_transactions = function(month, year){

  url = paste0(url_start, month, url_end, year)

  payload = read_html(url) %>%
              html_nodes("td") %>%
                html_table() %>%
                  as.data.frame()

  payload

}

# function run on appropriate dates

mlb_transactions(month = 1:12, year = 2001:2019)

这是我遇到的错误

 Show Traceback

 Rerun with Debug
 Error in doc_parse_file(con, encoding = encoding, as_html = as_html, options = options) : 
  Expecting a single string value: [type=character; extent=19]. 

这里是追溯

12.
stop(structure(list(message = "Expecting a single string value: [type=character; extent=19].", 
    call = doc_parse_file(con, encoding = encoding, as_html = as_html, 
        options = options), cppstack = NULL), class = c("Rcpp::not_compatible", 
"C++Error", "error", "condition"))) 
11.
doc_parse_file(con, encoding = encoding, as_html = as_html, options = options) 
10.
read_xml.character(x, encoding = encoding, ..., as_html = TRUE, 
    options = options) 
9.
read_xml(x, encoding = encoding, ..., as_html = TRUE, options = options) 
8.
withCallingHandlers(expr, warning = function(w) invokeRestart("muffleWarning")) 
7.
suppressWarnings(read_xml(x, encoding = encoding, ..., as_html = TRUE, 
    options = options)) 
6.
read_html.default(url) 
5.
read_html(url) 
4.
eval(lhs, parent, parent) 
3.
eval(lhs, parent, parent) 
2.
read_html(url) %>% html_nodes("td") %>% html_table() %>% as.data.frame() 
1.
mlb_transactions(month = 1:12, year = 2001:2019) 

最后一点是,我的计划是,虽然我还不知道该怎么做,因为在交易表上,并不是每笔交易都有一个直接离开的日期,但有一个隐含的日期跨度,我可以这样做吗加载后,每个空的日期列都会在其上方填充列的信息(如果已填充),这会运行一种循环,还是有更好的方法从一开始就加载日期?

【问题讨论】:

  • 这里有几个问题。用两个向量调用函数不是嵌套循环。在函数中添加 'print(url)' 以查看结果。 read_html() 也被设计为接受单个 url 而不是 URL 的向量。
  • @Dave2e 我是否必须将其修改为 for 循环?喜欢(2001 年:2019 年)..等?
  • 是的,这是直截了当的方式。我还建议阅读网站上的服务条款,以确保您没有违反。另外,我会在调用 read_html 之间添加 1 秒的暂停,这样就不会像拒绝服务攻击一样。

标签: r function dataframe web-scraping rvest


【解决方案1】:

伪代码(与语言无关):

还有一个替代的 url 构造,它通过查询字符串返回 json。查询字符串有开始和结束日期。

http://lookup-service-prod.mlb.com/json/named.transaction_all.bam?start_date=20010101&end_date=20031231&sport_code=%27mlb%27

通过使用 Python 进行测试(因此 R 里程可能会有所不同 - 我希望稍后会添加一个 R 示例)您可以一次发出 *2 年的请求并获得包含数据行的 json 响应。*这是更可靠的时间框架。

您可以在 2001 年到 2018 年的循环中构建它,步长为 2,即

间隔

['2001-2003', '2004-2006', '2007-2009' ,'2010-2012', '2013-2015', '2016-2018]

然后为感兴趣的数据解析 json 响应。示例 json 响应 here.

json 中的示例行:

{"trans_date_cd":"D","from_team_id":"","orig_asset":"Player","final_asset_type":"","player":"Rafael Roque","resolution_cd":"FIN","final_asset":"","name_display_first_last":"Rafael Roque","type_cd":"REL","name_sort":"ROQUE, RAFAEL","resolution_date":"2001-03-14T00:00:00","conditional_sw":"","team":"Milwaukee Brewers","type":"Released","name_display_last_first":"Roque, Rafael","transaction_id":"94126","trans_date":"2001-03-14T00:00:00","effective_date":"2001-03-14T00:00:00","player_id":"136305","orig_asset_type":"PL","from_team":"","team_id":"158","note":"Milwaukee Brewers released LHP Rafael Roque."}

注意:

允许非批量使用材料,但批量使用需要事先征得同意。


Python 示例:

import requests

for year in range(2001, 2018, 2):       
    r = requests.get('http://lookup-service-prod.mlb.com/json/named.transaction_all.bam?start_date={0}0101&end_date={1}1231&sport_code=%27mlb%27'.format(year,year + 1)).json()
    print(len(r['transaction_all']['queryResults']['row'])) # just to demonstrate response content

这个

len(r['transaction_all']['queryResults']['row'])

给出每个请求的数据行数/事务数(2 年期间)

这会产生以下交易计数:

[163, 153, 277, 306, 16362, 19986, 20960, 23352, 24732]

【讨论】:

    【解决方案2】:

    这是一个R 替代方案 - 类似于@QHarr 的解决方案。以下函数get_datayear 作为参数,并获取year;year+1 的数据作为开始和结束日期

    get_data <- function (year) {
      root_url <- 'http://lookup-service-prod.mlb.com'
      params_dates <- sprintf('start_date=%s0101&end_date=%s1231', year, year+1)
      params <- paste('/json/named.transaction_all.bam?&sport_code=%27mlb%27', params_dates, sep = '&')
      js <- jsonlite::fromJSON(paste0(root_url, params))
      return (js)
    }
    get_processed_data <- function (year) get_data(year=year)$transaction_all$queryResults$row
    

    输出js 属于list 类,数据存储在$transaction_all$queryResults$row 中。

    最后,与其他解决方案相同的循环打印输出的行数

    for (year in seq(2001, 2018, 2)) print(nrow(get_data(year)$transaction_all$queryResults$row))
    # [1] 163
    # [1] 153
    # [1] 277
    # [1] 306
    # [1] 16362
    # [1] 19986
    # [1] 20960
    # [1] 23352
    # [1] 24732
    

    【讨论】:

    • @niko 非常感谢。看起来它收集了适当的数据,但是我如何将该特定部分转换为尊重列的数据框,例如我的以下代码给了我一个单列数据框,它看起来很奇怪并且有几个空白等:df &lt;- data.frame(matrix(unlist(test)))
    • @8bit 查看编辑(添加功能get_processed_data)。你可以试试df &lt;- get_processed_data(2009)。关于您的测试:您取消列出一个列表 (test),其中已经包含数据框 (test$transaction_all$queryResults$row),它变成了一个向量。然后你把它变成一个矩阵,但由于你没有指定ncol,它假设只有一列。无论如何,使用unlist 是一个坏主意,因为数据已经正确存储,unlist 将剥夺它的任何结构。
    猜你喜欢
    • 2020-09-09
    • 2020-11-19
    • 1970-01-01
    • 2017-09-11
    • 1970-01-01
    • 1970-01-01
    • 2020-04-24
    • 1970-01-01
    • 2017-11-21
    相关资源
    最近更新 更多