Ничего не вернулось, когда я пытаюсь очистить транзакции mlb.com с помощью rvest

Я пытался очистить страницу транзакций mlb (http://mlb.mlb.com/mlb/transactions/index.jsp#month=5&year=2019) для соответствующей даты и текста каждой транзакции безуспешно. Используя rvest и гаджет селектора, я написал краткую функцию, которая должна предоставить мне таблицу, отображаемую с начала 2001 года по март 2019 года.

Я просто получаю эту серию ошибок и вообще ничего не происходит.

Вот мой код для очистки данных с данного веб-сайта.

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]. 

а вот Traceback

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) 

И последнее замечание: мой план состоит в том, что, хотя я еще не знаю, как это сделать, потому что в таблицах транзакций не у каждой транзакции есть дата, которую она оставила, но есть подразумеваемый диапазон дат, могу ли я сделать это так, как только загружается каждый пустой столбец даты заполняется информацией столбца непосредственно над ним, если он заполнен, и это запускает своего рода цикл или есть лучший способ загрузить даты с самого начала?


person 8bit    schedule 24.05.2019    source источник
comment
У вас есть пара проблем. Вызов функции с двумя векторами не является вложенным циклом. Добавьте в функцию print (url), чтобы увидеть результат. Кроме того, read_html() предназначен для приема одного URL-адреса, а не вектора URL-адресов.   -  person Dave2e    schedule 24.05.2019
comment
@ Dave2e Придется ли мне преобразовать его в цикл for? как для (2001: 2019 год) .. и т. д.?   -  person 8bit    schedule 24.05.2019
comment
Да, это прямой путь. Я также предлагаю прочитать условия обслуживания на веб-сайте, чтобы убедиться, что вы не нарушаете их. Кроме того, я бы добавил паузу в 1 секунду между вызовами read_html, чтобы не действовать как атака отказа в обслуживании.   -  person Dave2e    schedule 24.05.2019


Ответы (2)


Псевдокод (не зависит от языка):

Существует альтернативная конструкция 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 со строками данных в. * This были более надежными временными рамками.

Вы можете построить это в цикле с 2001 по 2018 год с шагом 2, т.е.

интервалы

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

Затем проанализируйте ответ json на наличие интересующих данных. Пример ответа json здесь.

Пример строки в 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]
person QHarr    schedule 24.05.2019

Вот альтернатива R, аналогичная решению @QHarr. Следующая функция get_data принимает year в качестве аргумента и извлекает данные для 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
person niko    schedule 25.05.2019
comment
@niko Большое спасибо. Похоже, что он собирает соответствующие данные, но как мне преобразовать этот конкретный раздел в фрейм данных, уважающий столбцы, что-то вроде моего следующего кода дает мне фрейм данных с одним столбцом, который выглядит странно и имеет пару пробелов и т. Д .: df <- data.frame(matrix(unlist(test))) - person 8bit; 26.05.2019
comment
@ 8bit Смотрите правку (добавлена ​​функция get_processed_data). Вы можете попробовать df <- get_processed_data(2009). О вашем тесте: вы отменяете список (test), который уже содержит фрейм данных (test$transaction_all$queryResults$row), который становится вектором. Затем вы превращаете его в матрицу, но поскольку вы не указываете ncol, предполагается, что имеется только один столбец. Тем не менее, использование unlist - плохая идея, поскольку данные уже правильно сохранены и unlist лишит их какой-либо структуры. - person niko; 26.05.2019