Scraping von HTML-Tabellen in R-Datenrahmen mithilfe des XML-Pakets

153

Wie kratz ich HTML-Tabellen mit dem XML-Paket?

Nehmen Sie zum Beispiel diese Wikipedia-Seite über die brasilianische Fußballmannschaft . Ich möchte es in R lesen und die Tabelle "Liste aller Spiele, die Brasilien gegen von der FIFA anerkannte Mannschaften gespielt hat" als data.frame erhalten. Wie kann ich das machen?

Eduardo Leoni
quelle
11
Um die xpath-Selektoren zu ermitteln, besuchen Sie selectorgadget.com/ - es ist fantastisch
hadley

Antworten:

144

… Oder ein kürzerer Versuch:

library(XML)
library(RCurl)
library(rlist)
theurl <- getURL("https://en.wikipedia.org/wiki/Brazil_national_football_team",.opts = list(ssl.verifypeer = FALSE) )
tables <- readHTMLTable(theurl)
tables <- list.clean(tables, fun = is.null, recursive = FALSE)
n.rows <- unlist(lapply(tables, function(t) dim(t)[1]))

Die ausgewählte Tabelle ist die längste auf der Seite

tables[[which.max(n.rows)]]
Jim G.
quelle
Die readHTMLTable-Hilfe bietet auch ein Beispiel für das Lesen einer Nur-Text-Tabelle aus einem HTML-PRE-Element mit htmlParse (), getNodeSet (), textConnection () und read.table ()
Dave X
48
library(RCurl)
library(XML)

# Download page using RCurl
# You may need to set proxy details, etc.,  in the call to getURL
theurl <- "http://en.wikipedia.org/wiki/Brazil_national_football_team"
webpage <- getURL(theurl)
# Process escape characters
webpage <- readLines(tc <- textConnection(webpage)); close(tc)

# Parse the html tree, ignoring errors on the page
pagetree <- htmlTreeParse(webpage, error=function(...){})

# Navigate your way through the tree. It may be possible to do this more efficiently using getNodeSet
body <- pagetree$children$html$children$body 
divbodyContent <- body$children$div$children[[1]]$children$div$children[[4]]
tables <- divbodyContent$children[names(divbodyContent)=="table"]

#In this case, the required table is the only one with class "wikitable sortable"  
tableclasses <- sapply(tables, function(x) x$attributes["class"])
thetable  <- tables[which(tableclasses=="wikitable sortable")]$table

#Get columns headers
headers <- thetable$children[[1]]$children
columnnames <- unname(sapply(headers, function(x) x$children$text$value))

# Get rows from table
content <- c()
for(i in 2:length(thetable$children))
{
   tablerow <- thetable$children[[i]]$children
   opponent <- tablerow[[1]]$children[[2]]$children$text$value
   others <- unname(sapply(tablerow[-1], function(x) x$children$text$value)) 
   content <- rbind(content, c(opponent, others))
}

# Convert to data frame
colnames(content) <- columnnames
as.data.frame(content)

Bearbeitet, um hinzuzufügen:

Beispielausgabe

                     Opponent Played Won Drawn Lost Goals for Goals against  % Won
    1               Argentina     94  36    24   34       148           150  38.3%
    2                Paraguay     72  44    17   11       160            61  61.1%
    3                 Uruguay     72  33    19   20       127            93  45.8%
    ...
Richie Cotton
quelle
7
Für alle anderen, die das Glück haben, diesen Beitrag zu finden, wird dieses Skript wahrscheinlich nur ausgeführt, wenn der Benutzer seine "User-Agent" -Informationen hinzufügt, wie in diesem anderen hilfreichen Beitrag beschrieben: stackoverflow.com/questions/9056705/…
Rguy
26

Eine weitere Option mit Xpath.

library(RCurl)
library(XML)

theurl <- "http://en.wikipedia.org/wiki/Brazil_national_football_team"
webpage <- getURL(theurl)
webpage <- readLines(tc <- textConnection(webpage)); close(tc)

pagetree <- htmlTreeParse(webpage, error=function(...){}, useInternalNodes = TRUE)

# Extract table header and contents
tablehead <- xpathSApply(pagetree, "//*/table[@class='wikitable sortable']/tr/th", xmlValue)
results <- xpathSApply(pagetree, "//*/table[@class='wikitable sortable']/tr/td", xmlValue)

# Convert character vector to dataframe
content <- as.data.frame(matrix(results, ncol = 8, byrow = TRUE))

# Clean up the results
content[,1] <- gsub(" ", "", content[,1])
tablehead <- gsub(" ", "", tablehead)
names(content) <- tablehead

Erzeugt dieses Ergebnis

> head(content)
   Opponent Played Won Drawn Lost Goals for Goals against % Won
1 Argentina     94  36    24   34       148           150 38.3%
2  Paraguay     72  44    17   11       160            61 61.1%
3   Uruguay     72  33    19   20       127            93 45.8%
4     Chile     64  45    12    7       147            53 70.3%
5      Peru     39  27     9    3        83            27 69.2%
6    Mexico     36  21     6    9        69            34 58.3%
learnr
quelle
Hervorragender Aufruf zur Verwendung von xpath. Kleiner Punkt: Sie können das Pfadargument leicht vereinfachen, indem Sie // * / in // ändern, z. B. "// table [@ class = 'wikitable sortable'] / tr / th"
Richie Cotton
Ich erhalte die Fehlermeldung "Skripte sollten eine informative User-Agent-Zeichenfolge mit Kontaktinformationen verwenden, da sie sonst ohne vorherige Ankündigung IP-blockiert werden können." [2] "Gibt es einen Weg, um diese Methode zu implementieren?
pssguy
2
Optionen (RCurlOptions = list (useragent = "zzzz")). Weitere Alternativen und Diskussionen finden Sie im Abschnitt "Laufzeit" unter omegahat.org/RCurl/FAQ.html .
Learnr
25

Das rvestzusammen mit xml2ist ein weiteres beliebtes Paket zum Parsen von HTML-Webseiten.

library(rvest)
theurl <- "http://en.wikipedia.org/wiki/Brazil_national_football_team"
file<-read_html(theurl)
tables<-html_nodes(file, "table")
table1 <- html_table(tables[4], fill = TRUE)

Die Syntax ist einfacher zu verwenden als das xmlPaket und für die meisten Webseiten bietet das Paket alle Optionen, die benötigt werden.

Dave2e
quelle
Die read_html gibt mir den Fehler "'file: ///Users/grieb/Auswertungen/tetyana-snp-2016/data/snp-nexus/15/SNP%20Annotation%20Tool.html' existiert nicht im aktuellen Arbeitsverzeichnis (' / Users / grieb / Auswertungen / tetyana-snp-2016 / code '). "
scs