簡體   English   中英

使用R(rvest)使用沒有清晰模式的分隔符將html文本刮到表格中

[英]Scraping html text into table with delimiters that do not have a clear pattern using R (rvest)

我只是在學習如何使用 R 從網頁中抓取數據,我遇到了幾個問題。

作為參考,我正在練習的網站在這里: http : //www.rsssf.com/tables/34q.html

據我所知,我從中抓取數據的網站不是表格,因此我無法直接將信息抓取到表格中,因此這是我編寫的代碼,僅包含所有文本:

wcq_1934_html <- read_html("http://www.rsssf.com/tables/34q.html")
wcq_1934_node <- html_nodes(wcq_1934_html, "pre")
wcq_1934_text <- html_text(wcq_1934_node, trim = TRUE)

這會產生一個非常長的文本文件,其中包含我需要的所有信息,只是沒有以理想的方式格式化。

所以我接下來嘗試對這個文本進行子字符串化,以獲得看起來像這樣的輸出。

國家 A - 國家 A 得分 - 國家 B - 國家 B 得分

它不必完全像這樣,我基本上只需要該國家/地區的每場比賽以及他們進了多少球,理想情況下應該與同一場比賽的另一個國家/地區進行比較,這樣我就可以知道誰贏了誰輸了! 我不需要任何其他信息,例如玩游戲的地點等。

所以我嘗試了三種不同的方法來獲得這個:

第一個測試:用破折號分割文本:

test <- strsplit(wcq_1934_text, "-")
df_test <- data.frame(test)

這為我提供了表格中所需的信息,但行與我需要的確切分數不匹配(即立陶宛 0 和瑞典 2 在不同的行中)

第二個測試:按空格分割文本:

test2 <- strsplit(wcq_1934_text, " ")
df_test2 <- data.frame(test2)

這很有用,因為它給了我一行的分數(第一場比賽是 0-2),但是國家在行中的分布不均勻。

第三個測試:按“標簽”拆分文本

test3 <- strsplit(wcq_1934_text, "    ")
df_test3 <- data.frame(test3)

這與第一次測試有類似的問題。

任何建議將不勝感激。 這是我有史以來的第一篇 Stack Overflow 帖子,盡管我已經潛伏了一段時間,而且這個網站對我很有幫助。 先感謝您!

這是一個為您提供大部分所需的解決方案,盡管正如 MrFlick 評論的那樣,它對這個頁面來說有點脆弱。 我會繼續使用rvest ,盡管正如 biomiha 所建議的那樣,它在這里並沒有真正給你帶來很多好處(盡管它確實打破了<pre>塊)。

從您的wcq_1934_text開始,它是一個長字符串,讓我們用換行符(在本例中為 CRLF)將其分解:

wcq_1934_text <- strsplit(wcq_1934_text, "[\r\n]+")[[1]]
str(wcq_1934_text)
#  chr [1:51] "Hosts: Italy (not automatically qualified)" "Holders: Uruguay (did not enter)" "Group 1 [Sweden]" ...

我會使用magrittr包,因為它有助於使用%>%非管道分解過程的每個步驟; 你可以轉換它非magrittr通過改變(例如) func1() %>% func2() %>% func3()func3(func2(func1()))呸)或中間分配的返回值, ret1 <- func1(); ret2 <- func2(ret1); ... ret1 <- func1(); ret2 <- func2(ret1); ... ret1 <- func1(); ret2 <- func2(ret1); ...

library(magrittr)
dat <- Filter(function(a) grepl("^[0-9][0-9]", a), wcq_1934_text) %>%
  paste(., collapse = "\n") %>%
  textConnection() %>%
  read.fwf(file = ., widths = c(10, 16, 17, 4, 99), stringsAsFactors = FALSE) %>%
  lapply(trimws) %>%
  as.data.frame(stringsAsFactors = FALSE)

寬度是脆弱的,並且是該頁面獨有的。 如果其他報告頁面的列布局略有不同,則您需要使用不同的功能,也許是可以自動確定中斷的功能。

head(dat)
#        V1        V2        V3  V4       V5
# 1 11.06.33 Stockholm    Sweden 6-2  Estonia
# 2 29.06.33    Kaunas Lithuania 0-2   Sweden
# 3 11.03.34    Madrid     Spain 9-0 Portugal
# 4 18.03.34    Lisboa  Portugal 1-2    Spain
# 5 25.03.34    Milano     Italy 4-0   Greece
# 6 25.03.34     Sofia  Bulgaria 1-4  Hungary

從這里開始,由您決定要使用哪些列。

例如,處理日期,您可能需要:

dat$V1 <- as.POSIXct(gsub("([0-9]+)$", "19\\1", dat$V1), format = "%d.%m.%Y")
dat$V1
#  [1] "1933-06-11 PST" "1933-06-29 PST" "1934-03-11 PST" "1934-03-18 PST" "1934-03-25 PST" "1934-03-25 PST" "1934-04-25 PST" "1934-04-29 PST"
#  [9] "1933-10-15 PST" "1934-03-15 PST" "1933-09-24 PST" "1933-10-29 PST" "1934-04-29 PST" "1934-02-25 PST" "1934-04-08 PST" "1934-04-29 PST"
# [17] "1934-03-11 PST" "1934-04-15 PST" "1934-01-28 PST" "1934-02-01 PST" "1934-02-04 PST" "1934-03-04 PST" "1934-03-11 PST" "1934-03-18 PST"
# [25] "1934-05-24 PST" "1934-03-16 PST" "1934-04-06 PST"

gsub東西是因為as.POSIXct假設小於 69 的兩位數年份是在 20 世紀,19 是 69-99。

對分數使用strsplit很容易,但您也可以這樣做:

library(tidyr)
dat %>%
  separate(V4, c("score1", "score2"), sep="-") %>%
  head()
# Warning: Too few values at 1 locations: 10
#           V1        V2        V3 score1 score2       V5
# 1 1933-06-11 Stockholm    Sweden      6      2  Estonia
# 2 1933-06-29    Kaunas Lithuania      0      2   Sweden
# 3 1934-03-11    Madrid     Spain      9      0 Portugal
# 4 1934-03-18    Lisboa  Portugal      1      2    Spain
# 5 1934-03-25    Milano     Italy      4      0   Greece
# 6 1934-03-25     Sofia  Bulgaria      1      4  Hungary

(警告是意料之中的,因為沒有玩過一場比賽,所以得分為"n/p" 。您可能希望在嘗試拆分之前處理V4中的V4分值,也許用NA替換任何不是 numeric-dash-numeric 的值.)

同樣特定於這個特定站點,但可能更容易概括:

library(rvest)
library(purrr)
library(dplyr)
library(stringi)

pg <- read_html("http://www.rsssf.com/tables/34q.html")

定位<pre>並去掉一些不屬於“表格”的東西:

html_nodes(pg, "pre") %>% 
  html_text() %>% 
  stri_split_lines() %>% 
  flatten_chr() %>% 
  discard(stri_detect_regex, "^(NB|  )") -> lines

現在,我們得到每個“組”的開始和結束行索引:

starts <- which(grepl("^Group", lines))
ends <- c(starts[-1], length(lines))

我們迭代這些開始和結束,然后:

  • 提取組信息
  • 收拾桌子
  • 丟棄任何“空”表
  • 將表格數據轉換為數據框,並在此過程中進行一些修改

如果需要,我可以對以下內容進行更多注釋:

map2_df(starts, ends, ~{

  grp_info <- stri_match_all_regex(lines[.x], "Group ([[:digit:]]+) \\[(.*)]")[[1]][,2:3]

  lines[(.x+1):.y] %>%
    discard(stri_detect_regex, "(^[^[:digit:]]| round)") %>% 
    discard(`==`, "") -> grp

  if (length(grp) == 0) return(NULL)

  stri_split_regex(grp, "\ \ +") %>% 
    map_df(~{
      .x[1:4] %>% 
        as.list() %>% 
        set_names(c("date", "team_a", "team_b", "score_team")) %>% 
        flatten_df() %>% 
        separate(score_team, c("score", "team_c"), sep=" ") %>% 
        mutate(group_num = grp_info[1], group_info = grp_info[2]) %>% 
        separate(date, c("d", "m", "y")) %>% 
        mutate(date = as.Date(sprintf("19%s-%s-%s", y, m, d))) %>% 
        select(-d, -m, -y)
    })

})
## # A tibble: 27 x 7
##            team_a         team_b score         team_c group_num           group_info       date
##             <chr>          <chr> <chr>          <chr>     <chr>                <chr>     <date>
##  1      Stockholm         Sweden   6-2        Estonia         1               Sweden 1933-06-11
##  2         Kaunas      Lithuania   0-2         Sweden         1               Sweden 1933-06-29
##  3         Madrid          Spain   9-0       Portugal         2                Spain 1934-03-11
##  4         Lisboa       Portugal   1-2          Spain         2                Spain 1934-03-18
##  5         Milano          Italy   4-0         Greece         3                Italy 1934-03-25
##  6          Sofia       Bulgaria   1-4        Hungary         4     Hungary, Austria 1934-03-25
##  7           Wien        Austria   6-1       Bulgaria         4     Hungary, Austria 1934-04-25
##  8       Budapest        Hungary   4-1       Bulgaria         4     Hungary, Austria 1934-04-29
##  9       Warszawa         Poland   1-2 Czechoslovakia         5       Czechoslovakia 1933-10-15
## 10          Praha Czechoslovakia   n/p         Poland         5       Czechoslovakia 1934-03-15
## 11        Beograd     Yugoslavia   2-2    Switzerland         6 Romania, Switzerland 1933-09-24
## 12           Bern    Switzerland   2-2        Romania         6 Romania, Switzerland 1933-10-29
## 13      Bucuresti        Romania   2-1     Yugoslavia         6 Romania, Switzerland 1934-04-29
## 14         Dublin        Ireland   4-4        Belgium         7 Netherlands, Belgium 1934-02-25
## 15      Amsterdam    Netherlands   5-2        Ireland         7 Netherlands, Belgium 1934-04-08
## 16      Antwerpen        Belgium   2-4    Netherlands         7 Netherlands, Belgium 1934-04-29
## 17     Luxembourg     Luxembourg   1-9        Germany         8      Germany, France 1934-03-11
## 18     Luxembourg     Luxembourg   1-6         France         8      Germany, France 1934-04-15
## 19 Port-au-Prince          Haiti   1-3           Cuba        11                  USA 1934-01-28
## 20 Port-au-Prince          Haiti   1-1           Cuba        11                  USA 1934-02-01
## 21 Port-au-Prince          Haiti   0-6           Cuba        11                  USA 1934-02-04
## 22  Cd. de Mexico         Mexico   3-2           Cuba        11                  USA 1934-03-04
## 23  Cd. de Mexico         Mexico   5-0           Cuba        11                  USA 1934-03-11
## 24  Cd. de Mexico         Mexico   4-1           Cuba        11                  USA 1934-03-18
## 25           Roma            USA   4-2         Mexico        11                  USA 1934-05-24
## 26          Cairo          Egypt   7-1      Palestina        12                Egypt 1934-03-16
## 27       Tel Aviv      Palestina   1-4          Egypt        12                Egypt 1934-04-06

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM