2017-12-15 6 views
1

Dies ist mein erster Versuch im Web Scraping. Ich versuche, eine Liste von Tabellen (Spaltenname: Öl & Gas Tables) von dieser Webseite zu extrahieren. Oil and Gas Data. Es war einfach, die Daten für einen Zustand zu extrahieren, indem der Link für diesen Zustand Alabama Data verwendet wurde. Ich möchte jedoch ein Programm, das Daten für alle Zustände extrahieren kann und sie im Jahr wie in den HTML-Daten gezeigt behält. Ich habe Pakete geladen RCurl, XML, Rlist und Purrr basierend auf früheren ähnlichen Posts stieß ich.Extrahieren mehrerer Tabellen von der Webseite mit Hyperlinks mit R

How can I use R (Rcurl/XML packages ?!) to scrape this webpage? Diese Lösung sieht vollständig, jedoch hat sich das Problem wahrscheinlich die Homepage geändert, seit es eingeschickt wurde (ich nacheifern wollte, aber nicht konnte)

R: XPath expression returns links outside of selected element. Wie kann ich meine Xpath benötigten Tabellen zu extrahieren, da sie alle „stateinitials_table.html“, wie er in für Alabama enthalten „al_table.html“ view source

theurl <- getURL("https://www.eia.gov/naturalgas/archive/petrosystem/al_table.html",.opts = list(ssl.verifypeer = FALSE)) 
tables <- readHTMLTable(theurl) 
tables <- list.clean(tables, fun = is.null, recursive = FALSE) 
berilium<-tables[seq(3,length(tables),2)] 

Dies ist die Ausgabe für „al_table.html“ eine Liste von 15 Datenrahmen für 15 Jahre. 26 rows817 columns, 15 data frames 1 for each year

Also muß ich,

eine Funktion (Xpath vs readHTMLTable, bevorzugt Xpath) machen alle Tabellen aus der Hauptnetzverbindung zu extrahieren. Und ich brauche es "markiert" nach Zustand und Jahr wie auf den Webseiten gezeigt. (Derzeit nicht besorgt über die Bereinigung von nutzlosen Spalten und Zeilen)

+0

Gibt es einen Grund, warum Sie nicht die Excel-Datei oder die APIs verwenden? – hrbrmstr

+0

@hrbrmstr Ich möchte etwas Web Scraping lernen, ich bin mit Öldaten und R. vertraut. – Poppinyoshi

Antwort

2

Dies ist eher ein Blog-Post oder Tutorial als eine SO-Antwort, aber ich kann auch den Wunsch zu lernen schätzen und arbeite auch an einem Buch für dieses Thema und das scheint wie ein gd-beispiel.

library(rvest) 
library(tidyverse) 

Wir werden mit der Top-Level-Seite starten:

pg <- read_html("https://www.eia.gov/naturalgas/archive/petrosystem/petrosysog.html") 

Jetzt werden wir einen XPath verwenden, die uns nur Tabellenzeilen bekommt, die Zustandsdaten in ihnen haben. Vergleichen Sie die XPath-Ausdrücke mit den Tags im HTML-Code. Dies sollte sinnvoll sein. All <tr> s ohne colspan Attribute und nur wählen verbleibenden <tr> s, die sowohl die richtige Klasse und einen Link zu einem Staat haben:

states <- html_nodes(pg, xpath=".//tr[td[not(@colspan) and 
        contains(@class, 'links_normal') and a[@name]]]") 

data_frame(
    state = html_text(html_nodes(states, xpath=".//td[1]")), 
    link = html_attr(html_nodes(states, xpath=".//td[2]/a"), "href") 
) -> state_tab 

Es in einem Datenrahmen ist, damit es ordentlich und handlich.

Sie müssen das nächste Bit unter die Funktion stellen, die danach kommt, aber ich muss die Iteration erklären, bevor ich die Funktion zeige.

Wir müssen über jede Verbindung iterieren. In jeder Iteration wir:

  • Pause, da Ihre Bedürfnisse sind nicht wichtiger als Serverbelastung der EIA
  • finden alle „Zweig“ <div> s, da sie zwei Informationen halten wir brauchen (den Zustand + Jahr und die Datentabelle für diesen Zustand + Jahr).
  • es wickeln alle in einem netten Datenrahmen

Anstatt Unordnung auf die anonyme Funktion bis wir diese Funktionalität in einer anderen Funktion gesetzt werden (wieder, die vor diesem Iterator definiert werden muss, funktioniert):

pb <- progress_estimated(nrow(state_tab)) 
map_df(state_tab$link, ~{ 

    pb$tick()$print() 

    pg <- read_html(sprintf("https://www.eia.gov/naturalgas/archive/petrosystem/%s", .x)) 

    Sys.sleep(5) # scrape responsibly 

    html_nodes(pg, xpath=".//div[@class='branch']") %>% 
    map_df(extract_table) 

}) -> og_df 

Dies ist der harte Arbeiter des Bündels. Wir müssen alle State + Year-Labels auf der Seite finden (jeweils in einer <table>), dann müssen wir die Tabellen mit Daten in ihnen finden. Ich nehme die Freiheit der Erklärungs Klappentext am Boden jeder Entfernung und auch jeweils in eine tibble drehen (aber das ist nur meine Klasse Vorliebe):

extract_table <- function(pg) { 

    t1 <- html_nodes(pg, xpath=".//../tr[td[contains(@class, 'SystemTitle')]][1]") 
    t2 <- html_nodes(pg, xpath=".//table[contains(@summary, 'Report')]") 

    state_year <- (html_text(t1, trim=TRUE) %>% strsplit(" "))[[1]] 

    xml_find_first(t2, "td[@colspan]") %>% xml_remove() 

    html_table(t2, header=FALSE)[[1]] %>% 
    mutate(state=state_year[1], year=state_year[2]) %>% 
    tbl_df() 

} 

Re-Einfügen der aforeposted Code nur um sicherzustellen, dass Sie grok es hat nach der Funktion kommen:

pb <- progress_estimated(nrow(state_tab)) 
map_df(state_tab$link, ~{ 

    pb$tick()$print() 

    pg <- read_html(sprintf("https://www.eia.gov/naturalgas/archive/petrosystem/%s", .x)) 

    Sys.sleep(5) # scrape responsibly 

    html_nodes(pg, xpath=".//div[@class='branch']") %>% 
    map_df(extract_table) 

}) -> og_df 

Und es funktioniert (Sie sagten, Sie die endgültige Bereinigung separat tun würde):

glimpse(og_df) 
## Observations: 14,028 
## Variables: 19 
## $ X1 <chr> "", "Prod.RateBracket(BOE/Day)", "0 - 1", "1 - 2", "2 - 4", "4 - 6", "... 
## $ X2 <chr> "", "||||", "|", "|", "|", "|", "|", "|", "|", "|", "|", "|", "|", "|"... 
## $ X3 <chr> "Oil Wells", "# ofOilWells", "26", "19", "61", "61", "47", "36", "250"... 
## $ X4 <chr> "Oil Wells", "% ofOilWells", "5.2", "3.8", "12.1", "12.1", "9.3", "7.1... 
## $ X5 <chr> "Oil Wells", "AnnualOilProd.(Mbbl)", "4.1", "7.8", "61.6", "104.9", "1... 
## $ X6 <chr> "Oil Wells", "% ofOilProd.", "0.1", "0.2", "1.2", "2.1", "2.2", "2.3",... 
## $ X7 <chr> "Oil Wells", "OilRateper Well(bbl/Day)", "0.5", "1.4", "3.0", "4.9", "... 
## $ X8 <chr> "Oil Wells", "AnnualGasProd.(MMcf)", "1.5", "3.5", "16.5", "19.9", "9.... 
## $ X9 <chr> "Oil Wells", "GasRateper Well(Mcf/Day)", "0.2", "0.6", "0.8", "0.9", "... 
## $ X10 <chr> "", "||||", "|", "|", "|", "|", "|", "|", "|", "|", "|", "|", "|", "|"... 
## $ X11 <chr> "Gas Wells", "# ofGasWells", "365", "331", "988", "948", "867", "674",... 
## $ X12 <chr> "Gas Wells", "% ofGasWells", "5.9", "5.4", "16.0", "15.4", "14.1", "10... 
## $ X13 <chr> "Gas Wells", "AnnualGasProd.(MMcf)", "257.6", "1,044.3", "6,360.6", "1... 
## $ X14 <chr> "Gas Wells", "% ofGasProd.", "0.1", "0.4", "2.6", "4.2", "5.3", "5.4",... 
## $ X15 <chr> "Gas Wells", "GasRateper Well(Mcf/Day)", "2.2", "9.2", "18.1", "30.0",... 
## $ X16 <chr> "Gas Wells", "AnnualOilProd.(Mbbl)", "0.2", "0.6", "1.6", "2.0", "2.4"... 
## $ X17 <chr> "Gas Wells", "OilRateper Well(bbl/Day)", "0.0", "0.0", "0.0", "0.0", "... 
## $ state <chr> "Alabama", "Alabama", "Alabama", "Alabama", "Alabama", "Alabama", "Ala... 
## $ year <chr> "2009", "2009", "2009", "2009", "2009", "2009", "2009", "2009", "2009"... 
+0

Das ist wunderbar. Ich werde zu dir zurückkommen, nachdem ich mehr gelernt habe. Viel Glück mit deinem Buch. – Poppinyoshi

1

hoffe, das hilft!

library(rvest) 
library(dplyr) 

main_page <- read_html("https://www.eia.gov/naturalgas/archive/petrosystem/petrosysog.html") 
state <- main_page %>% 
    html_nodes(xpath='//td[contains(@width, "110")]') %>% 
    html_children() %>% 
    html_text() 
state_link <- main_page %>% 
    html_nodes(xpath='//td[contains(@width, "160")]') %>% 
    html_children() %>% 
    html_attr('href') 

final_data <- list() 
for (i in 1:length(state)){ 
    child_page <- read_html(paste0("https://www.eia.gov/naturalgas/archive/petrosystem/",state_link[i])) 
    Sys.sleep(5) 

    child_page_stateAndYear <- child_page %>% 
    html_nodes(xpath = '//td[@class="c SystemTitle" and @style=" font-size: 14pt; color: #CC0000;"]') %>% 
    html_text 
    child_page_table <- lapply(
    (child_page %>% 
     html_nodes(xpath = '//table[contains(@class, "Table")]') %>% 
     html_table()), 
    function(x) x[-nrow(x),]) 
    final_data[[state[i]]] <- setNames(child_page_table, child_page_stateAndYear) 

    print(paste('Scrapped data for', state[i], '...')) 
    flush.console() 
} 
print('Congratulation! You have finished scrapping the required data!') 

names(final_data) 
names(final_data[[1]]) 

final_data weist die Elemente 34 (d.h. verfügbaren Zustände auf der Hauptwebseite) und jedes Element ist eine Liste von Tabellen (d.h.es Jahr wise tabellarische Daten eines Zustandes). Also, wenn Sie zu ‚Alabama 2009‘ gehörenden Daten haben wollen, dann geben Sie einfach

final_data[['Alabama']]['Alabama 2009'] 

(Anmerkung: Sie müssen möglicherweise wenig Daten Reinigung tun)

Edit:hinzugefügt Logik schlafen für einige Zeit vor dem Verschrotten einer anderen Webseite wie von @hrbrmstr vorgeschlagen

+0

Keine Crawl-Verzögerung? Ihre Zeit ist wichtiger als Server/Netzwerk-Besitzer $? – hrbrmstr

+0

@hrbrmstr Punkt gut bemerkt! – Prem

Verwandte Themen