Сбор данных из таблиц на нескольких веб-страницах в R (футболисты)

Я работаю над проектом для школы, где мне нужно собрать статистику карьеры для отдельных футболистов NCAA. Данные для каждого игрока находятся в этом формате.

http://www.sports-reference.com/cfb/players/ryan-aplin-1.html

Я не могу найти совокупность всех игроков, поэтому мне нужно переходить по странице за страницей и вытаскивать нижнюю строку каждого проходящего подсчета очков Спешка и получение и т. Д. Таблица html

Каждый игрок классифицируется по фамилии со ссылками на каждый алфавит, идущий здесь.

http://www.sports-reference.com/cfb/players/

Например, здесь находится каждый игрок с фамилией А.

http://www.sports-reference.com/cfb/players/a-index.html

Я впервые занимаюсь парсингом данных, поэтому я попытался найти похожие вопросы с ответами. Ближайший ответ, который я нашел, был этим вопросом

Я считаю, что мог бы использовать что-то очень похожее, где я меняю номер страницы на имя собранного игрока. Однако я не уверен, как изменить это, чтобы искать имя игрока вместо номера страницы.

Сэмюэл Л. Вентура также недавно рассказал об извлечении данных из данных НФЛ, которые можно найти здесь.

РЕДАКТИРОВАТЬ:

Бен был действительно полезен и предоставил отличный код. Первая часть работает очень хорошо, однако, когда я пытаюсь запустить вторую часть, я сталкиваюсь с этим.

> # unlist into a single character vector
> links <- unlist(links)
> # Go to each URL in the list and scrape all the data from the tables
> # this will take some time... don't interrupt it! 
> all_tables <- lapply(links, readHTMLTable, stringsAsFactors = FALSE)
Error in UseMethod("xmlNamespaceDefinitions") : 
 no applicable method for 'xmlNamespaceDefinitions' applied to an object of class "NULL"
> # Put player names in the list so we know who the data belong to
> # extract names from the URLs to their stats page...
> toMatch <- c("http://www.sports-reference.com/cfb/players/", "-1.html")
> player_names <- unique (gsub(paste(toMatch,collapse="|"), "", links))
Error: cannot allocate vector of size 512 Kb
> # assign player names to list of tables
> names(all_tables) <- player_names
Error: object 'player_names' not found
> fix(inx_page)
Error in edit(name, file, title, editor) : 
  unexpected '<' occurred on line 1
 use a command like
 x <- edit()
 to recover
In addition: Warning message:
In edit.default(name, file, title, editor = defaultEditor) :
  deparse may be incomplete

Это могло быть ошибкой из-за нехватки памяти (только 4 ГБ на компьютере, который я сейчас использую). Хотя я не понимаю ошибку

    > all_tables <- lapply(links, readHTMLTable, stringsAsFactors = FALSE)
Error in UseMethod("xmlNamespaceDefinitions") : 
 no applicable method for 'xmlNamespaceDefinitions' applied to an object of class "NULL"

Просматривая другие мои наборы данных, мои игроки действительно вернулись только к 2007 году. Если будет какой-то способ привлечь только людей с 2007 года, это может помочь уменьшить данные. Если бы у меня был список людей, чьи имена я хотел бы вытащить, мог бы я просто заменить линк в

 links[[i]] <- paste0("http://www.sports-reference.com", lnk)

только с теми игроками, которые мне нужны?


person Steve_Corrin    schedule 02.12.2013    source источник
comment
Возможно, вам будет лучше использовать специализированный инструмент для удаления веб-страниц. Я пытался сделать что-то подобное в R, но отказался и в итоге использовал Scrapy, чтобы выгрузить данные в CSV и затем проанализировать их в R. Scrapy написан на Python, поэтому его нельзя использовать. Существуют и другие подобные фреймворки на других языках: iRobot Visual Scraping, различные Ruby gems и т. д.   -  person Alex Popov    schedule 02.12.2013
comment
Вероятно, возникшая ошибка возникла из-за сбоя в подключении к Интернету или на сервере спортивного сайта. Я обновил свой ответ, чтобы обрабатывать ошибки, он будет пропускать URL-адреса, которые дают ошибку, и продолжаться. Я не довел его до конца, но последние несколько часов все идет хорошо. Если у вас возникнут дополнительные проблемы, вам следует принять полученный здесь ответ и опубликовать новый вопрос, чтобы взглянуть на него свежим взглядом. В опубликованном мной коде подустановка только данных 2007 года и более поздних версий возможна только после того, как у вас есть все таблицы для начала. Хотя могут быть и другие способы.   -  person Ben    schedule 03.12.2013
comment
Если у вас есть список игроков, это сэкономит много времени, поскольку мы можем подгруппировать список URL-адресов, прежде чем очищать их все. Это может быть шагом вперед в улучшении вашего метода. Или попробуйте python scrapy, как предложил @aseidlitz, здесь, в SO, тоже есть несколько экспертов. Я тоже использовал его с успехом, но в настоящее время я моноглот R.   -  person Ben    schedule 03.12.2013
comment
Я выполнил полный прогон этого кода (заняло около 5 часов, с 6 ГБ ОЗУ, никогда не использовалось более 40%), и, похоже, он работает нормально. Файл RData находится здесь: fileswap.com/dl/tNJYJ9yrN (9 МБ)   -  person Ben    schedule 04.12.2013


Ответы (1)


Вот как вы можете легко получить все данные во всех таблицах на всех страницах плеера ...

Сначала составьте список URL-адресов для всех страниц игроков ...

require(RCurl); require(XML)
n <- length(letters) 
# pre-allocate list to fill
links <- vector("list", length = n)
for(i in 1:n){
  print(i) # keep track of what the function is up to
  # get all html on each page of the a-z index pages
  inx_page <- htmlParse(getURI(paste0("http://www.sports-reference.com/cfb/players/", letters[i], "-index.html")))
  # scrape URLs for each player from each index page
  lnk <- unname(xpathSApply(inx_page, "//a/@href"))
  # skip first 63 and last 10 links as they are constant on each page
  lnk <- lnk[-c(1:63, (length(lnk)-10):length(lnk))]
  # only keep links that go to players (exclude schools)
  lnk <- lnk[grep("players", lnk)]
  # now we have a list of all the URLs to all the players on that index page
  # but the URLs are incomplete, so let's complete them so we can use them from 
  # anywhere
  links[[i]] <- paste0("http://www.sports-reference.com", lnk)
}
# unlist into a single character vector
links <- unlist(links)

Теперь у нас есть вектор примерно из 67000 URL (кажется, много игроков, не так ли?), Так что:

Во-вторых, очистите все таблицы по каждому URL-адресу, чтобы получить их данные, например:

# Go to each URL in the list and scrape all the data from the tables
# this will take some time... don't interrupt it!
# start edit1 here - just so you can see what's changed
    # pre-allocate list
all_tables <- vector("list", length = (length(links)))
for(i in 1:length(links)){
  print(i)
  # error handling - skips to next URL if it gets an error
  result <- try(
    all_tables[[i]] <- readHTMLTable(links[i], stringsAsFactors = FALSE)
  ); if(class(result) == "try-error") next;
}
# end edit1 here
# Put player names in the list so we know who the data belong to
# extract names from the URLs to their stats page...
toMatch <- c("http://www.sports-reference.com/cfb/players/", "-1.html")
player_names <- unique (gsub(paste(toMatch,collapse="|"), "", links))
# assign player names to list of tables
names(all_tables) <- player_names

Результат выглядит так (это всего лишь фрагмент вывода):

all_tables
$`neli-aasa`
$`neli-aasa`$defense
   Year School Conf Class Pos Solo Ast Tot Loss  Sk Int Yds Avg TD PD FR Yds TD FF
1 *2007   Utah  MWC    FR  DL    2   1   3  0.0 0.0   0   0      0  0  0   0  0  0
2 *2010   Utah  MWC    SR  DL    4   4   8  2.5 1.5   0   0      0  1  0   0  0  0

$`neli-aasa`$kick_ret
   Year School Conf Class Pos Ret Yds  Avg TD Ret Yds Avg TD
1 *2007   Utah  MWC    FR  DL   0   0       0   0   0      0
2 *2010   Utah  MWC    SR  DL   2  24 12.0  0   0   0      0

$`neli-aasa`$receiving
   Year School Conf Class Pos Rec Yds  Avg TD Att Yds Avg TD Plays Yds  Avg TD
1 *2007   Utah  MWC    FR  DL   1  41 41.0  0   0   0      0     1  41 41.0  0
2 *2010   Utah  MWC    SR  DL   0   0       0   0   0      0     0   0       0

Наконец, допустим, мы просто хотим взглянуть на таблицы передачи ...

# just show passing tables
passing <- lapply(all_tables, function(i) i$passing)
# but lots of NULL in here, and not a convenient format, so...
passing <- do.call(rbind, passing)

И мы получаем фрейм данных, готовый для дальнейшего анализа (тоже просто фрагмент) ...

             Year             School Conf Class Pos Cmp Att  Pct  Yds Y/A AY/A TD Int  Rate
james-aaron  1978          Air Force  Ind        QB  28  56 50.0  316 5.6  3.6  1   3  92.6
jeff-aaron.1 2000 Alabama-Birmingham CUSA    JR  QB 100 182 54.9 1135 6.2  6.0  5   3 113.1
jeff-aaron.2 2001 Alabama-Birmingham CUSA    SR  QB  77 148 52.0  828 5.6  4.3  4   6  99.8
person Ben    schedule 02.12.2013
comment
Это было действительно полезно !! Однако во второй части я столкнулся с некоторыми проблемами и внес правку в свой исходный пост. - person Steve_Corrin; 03.12.2013
comment
@ user2269255 Я обновил код, чтобы обрабатывать ошибки без остановки очистки. - person Ben; 03.12.2013
comment
Этот код не будет работать на моем компьютере изначально из-за ограниченного объема памяти. Я испортил себя и обновился, и теперь могу сказать, что это работает как шарм !! - person Steve_Corrin; 03.01.2014