Erhalten Sie Daten aller Premier League-Spieler, indem Sie mit Ruby (nokogiri) kratzen.

Freut mich, dich kennenzulernen. Ich heiße Tatsu. Derzeit bin ich ein Universitätsstudent und ein junger Student, der studiert, um Ingenieur zu werden. Ich bin nervös wegen des ersten Qiita-Beitrags in meinem Leben. Vielen Dank.

Das Thema des denkwürdigen ersten Beitrags ist das Scraping mit der Ruby-Bibliothek nokogiri. Kommen wir sofort zum Hauptthema.

Überblick

Die Website, auf die dieses Mal abgekratzt werden soll, heißt Worldfootball.net und enthält Informationen zum Fußball auf der ganzen Welt.

Ein Freund von mir, der in einer Universitätsstudie Datenanalysen durchführt, bat mich, bei der Erfassung detaillierter Daten aller Premier League-Spieler in Excel zu helfen. Ich sagte: "Wenn Sie diesen Code schreiben, endet er sofort, oder?" Ich akzeptierte es. Dies ist die erste Arbeit in meinem Leben, die Programmierung verwendet. (Obwohl es ein kompletter Freiwilliger ist ...)

Diese Seite besteht aus einer Listenseite (mit Paginierung), auf der die Basisdaten im Tabellenformat vorliegen, und einer Detailseite mit detaillierteren Daten für jeden Spieler. Das allgemeine Verfahren besteht darin, zuerst die Basisdaten aller Spieler und die Links zu den Detailseiten jedes Spielers von der Listenseite abzurufen und dann die Daten von jeder Detailseite abzurufen. Die Basisdaten und Detaildaten bestehen aus folgenden Elementen.

↓ Seite mit der Spielerliste https://www.worldfootball.net/players_list/eng-premier-league-2019-2020/nach-name/1/ ↓ Detailseite (Beispiel: Detailseite des Minamino-Players) https://www.worldfootball.net/player_summary/takumi-minamino/

0. Vorbereitung

Geben Sie den folgenden Befehl ein, um nokogiri zu installieren. gem install nokogiri

Laden Sie anschließend die erforderliche Bibliothek.

get_all.rb


require 'nokogiri' 
require 'open-uri' #Eine Bibliothek für den Zugriff auf URLs.
require 'csv' #Eine Bibliothek zum Lesen und Schreiben von CSV-Dateien.

1. Erfassung von Grunddaten

Das erste Ziel sind Basisdaten. Mit anderen Worten, es sind die Daten, die von der Listenseite abgerufen werden können. Die Listenseite ist paginiert und Sie erhalten mehrere URLs. Daher wird jede URL wie unten gezeigt in einem Array gespeichert.

get_all.rb


urls = []

(1..14).each do |num|
  urls.push("https://www.worldfootball.net/players_list/eng-premier-league-2019-2020/nach-mannschaft/#{num}/")
end

Jetzt, da wir eine 14-seitige Listenseite haben, sind wir bereit, die Basisdaten für alle fraglichen Spieler zu erhalten! !!

Von hier aus bin ich mir nicht sicher, ob die Methode, die ich angewendet habe, korrekt ist, aber ich freue mich, sie vorstellen zu können, weil ich meinen Zweck erreichen konnte. Ich würde mich sehr freuen, wenn Sie auf etwas hinweisen könnten.

Als nächstes habe ich die Basisdaten jedes Spielers in einem Array für jede Daten abgerufen. Mit nokogiri können Sie den CSS-Selektor verwenden, um die gewünschten Daten auf Ihrer Site wie folgt abzurufen.

sampe


doc = Nokogiri::HTML(URI.open(url)) #Daten von der Ziel-URL abrufen

target = doc.css(".container > div > a") #Holen Sie sich das a-Element direkt unter dem div direkt unter der Containerklasse.
link = target[:href] #Ruft das href-Attribut im a-Element ab.
text = target.inner_html #Holen Sie sich den Text in das a-Element.

Auf dieser Grundlage erhalten wir die Basisdaten jedes Spielers von der Listenseite. Nachdem wir nun ein Array von URLs haben, werden wir darüber iterieren und die Daten von jeder Seite abrufen.

get_all.rb


players_pages = [] #Speichern Sie die detaillierte URL jedes Spielers
names = [] #Name jedes Spielers 〃
teams = []#Team, zu dem jeder Spieler gehört 〃
birthdays = [] #Geburtstag jedes Spielers 〃
height_data = [] #Größe jedes Spielers 〃

urls.each do |url|
  doc = Nokogiri::HTML(URI.open(url))

  doc.css(".standard_tabelle td:nth-child(1) > a").each do |name|
    players_pages.push(name[:href])
    names.push(name.inner_html)
  end

  doc.css(".standard_tabelle td:nth-child(3) > a").each do |team|
    teams.push(team.inner_html)
  end

  doc.css(".standard_tabelle td:nth-child(4)").each do |born|
    birthdays.push(born.inner_html)
  end

  doc.css(".standard_tabelle td:nth-child(5)").each do |height|
    height_data.push(height.inner_html)
  end

end

Damit konnten wir die Basisdaten aller Spieler erfassen. Wenn Sie zu diesem Zeitpunkt "p names" schreiben und ausführen, werden die Namen aller Spieler ausgespuckt! !!

Mit der CSV-Bibliothek können Sie CSV-Dateien schreiben und lesen. Zunächst möchte ich die CSV-Datei der Linkliste der Player-Detailseite exportieren. Diese Datei wird geladen und im nächsten Schritt verwendet (detaillierte Daten erhalten).

get_all.rb


CSV.open('players_pages.csv', 'w') do |csv|
  csv << players_pages
end

Abschließend möchte ich die abgerufenen Basisdaten in Form einer CSV-Datei exportieren. Es ist sehr einfach, fügen Sie einfach die folgenden 5 Zeilen hinzu. Sie müssen keinen Header haben, aber ich denke, es ist besser, wenn Sie einen haben, weil Sie ihn so ausgeben können, wie er ist.

get_all.rb


CSV.open('data_1.csv', 'w') do |csv|
  headers = %w(name team born height)
  csv << headers
  names.zip(teams, birthdays, height_data).each { |data| csv << data }
end

Dies vervollständigt eine Datei, die die Basisdaten aller Spieler enthält. Die CSV-Datei wird automatisch in dem Verzeichnis erstellt, in dem Sie arbeiten. Die Erfassung der Basisdaten ist vorerst abgeschlossen! !!

get_all.rb


require 'nokogiri'
require 'open-uri'
require 'csv'

urls = []

(1..14).each do |num|
  urls.push("https://www.worldfootball.net/players_list/eng-premier-league-2019-2020/nach-mannschaft/#{num}/")
end

players_pages = []
names = []
teams = []
birthdays = []
height_data = []

urls.each do |url|
  doc = Nokogiri::HTML(URI.open(url))
  
  doc.css(".standard_tabelle td:nth-child(1) > a").each do |name|
    players_pages.push(name[:href])
    names.push(name.inner_html)
  end

  doc.css(".standard_tabelle td:nth-child(3) > a").each do |team|
    teams.push(team.inner_html)
  end

  doc.css(".standard_tabelle td:nth-child(4)").each do |born|
    birthdays.push(born.inner_html)
  end

  doc.css(".standard_tabelle td:nth-child(5)").each do |height|
    height_data.push(height.inner_html)
  end

end

CSV.open('players_pages.csv', 'w') do |csv|
  csv << players_pages
end

CSV.open('data_1.csv', 'w') do |csv|
  headers = %w(name team born height)
  csv << headers
  names.zip(names, teams, birthdays, height_data).each { |data| csv << data }
end

2. Holen Sie sich detaillierte Daten

Von hier aus hatte ich es schwer. Der Grund ist, dass die Datenerfassungsmethoden unterschiedlich waren. Dies bedeutet, dass die Detailseite auch Informationen zu allen Spielzeiten von Hazono-Spielern sowie Informationen zu Nationalmannschaften und anderen Ligen enthält und auf die gewünschten Daten eines Spielers zugreift und diese wiederholt. Das konnte ich nicht machen. Wenn Sie beispielsweise auf die erste Zeile der Tabelle zugreifen mussten, um auf die Zieldaten (Premier League-Daten der Saison 19/20) auf der Detailseite von Spieler A, aber auch auf der Seite von Spieler B zuzugreifen. Die erste Reihe war möglicherweise in einer anderen Saison, was es unmöglich macht, die beabsichtigten Daten zu sammeln. Ich hatte solche Probleme, aber ich habe es geschafft, es mithilfe der bedingten Verzweigung zu implementieren, daher möchte ich es einführen.

Ich habe diesmal eine andere Datei erstellt, aber dieselbe Datei ist in Ordnung. Laden Sie zunächst die erforderlichen Bibliotheken wie bei den Basisdaten und die URL der Detailseite jedes Players aus der zuletzt erstellten CSV-Datei.

get_detail.rb


require 'nokogiri'
require 'open-uri'
require 'csv'

urls = CSV.read('players_pages.csv') #Lesen Sie die CSV-Datei und speichern Sie sie in einem Array.

Sie haben jetzt ein Array (URLs) erstellt, das die URLs aller Player-Detailseiten enthält. Die Arbeit zum Erfassen von Daten für all diese wird unter Verwendung jeder Anweisung ausgeführt, das Problem ist jedoch die Verarbeitung, die in jeder Anweisung ausgeführt wird. Dieses Mal habe ich beschlossen, die Daten jedes Spielers als Hash für jeden Gegenstand abzurufen und eine Reihe von Hashes zu erstellen, die sie zusammenfassen. Definieren Sie zunächst ein Array, das alle Daten enthält.

get_detail.rb


details = []

In der von nun an durchzuführenden Verarbeitung wird eine bedingte Verzweigung durchgeführt. Die Bedingungen für die Verzweigung sind zunächst, ob Premier-League-Daten vorliegen oder nicht, und dann, ob Premier-League-Daten für die Saison 19/20 vorliegen oder nicht.

Wenn Sie mit Fußball nicht vertraut sind, wissen Sie vielleicht nicht, was es ist, aber zu den Spielern, auf die diesmal abgezielt wird, gehören Spieler, die an nur einem Spiel teilgenommen haben. Es ist möglich, dass solche Spieler keine Premier League-Daten haben. Außerdem haben einige Spieler möglicherweise nicht die Zieltabelle auf ihren Seiten, obwohl sie registriert sind, aber keine Daten für die Saison 19/20 haben. Daher ist eine solche Verzweigung notwendig.

Zuerst werde ich einen Prozess schreiben, der abhängig davon verzweigt, ob Premier League-Daten vorhanden sind oder nicht.

get_detail.rb


urls.each do |u|

  url =  u[0] #urls[0][0]Es ist ein Bild. Wenn nur du["htpps://~~~~.com"]Wird in Form von ausgegeben.

  doc = Nokogiri::HTML(URI.open(url))

  prLeagues = []

  doc.css("td > a").each do |a| #Verarbeitet alle Elemente unter td.
    if a.text == "Pr. League"
      prLeagues.push(a)
    end
  end

  tds = []

  if prLeagues.any?
    prLeagues.each { |league| tds.push(league.parent) } #Ruft das td-Element ab, das das übergeordnete Element des a-Elements (Premier League) ist.
  else #Wenn es keine Premier League gibt. Erstellen Sie einen Hash mit allen Daten als 0.
    data = { appearances: 0, scores: 0, yellow: 0, red_with_2yellow: 0, red: 0 }
    details.push(data)
    next #Beenden Sie und fahren Sie mit dem nächsten Prozess fort.
  end

# appearances=Anzahl der gespielten Spiele, scores=Anzahl der Punkte

Als nächstes werden wir eine bedingte Verzweigung vornehmen, je nachdem, ob Daten für die Premier League für die Saison 19/20 vorliegen oder nicht. Das tds-Array enthält die td-Elemente, die die Premier League-Zeichenfolge ("pr. League") enthalten. Unmittelbar nach dem Zeitpunkt, an dem die Liga angezeigt wird, wird die Saison angezeigt. Greifen Sie also darauf zu und extrahieren Sie nur gültige Tabellen. In diesem Fall kann nur eine Zeile gültig sein. Daher habe ich viel + verwendet, einen CSS-Selektor, der "unmittelbar danach" darstellt, um Daten zu erfassen, die sich horizontal ausbreiten.

get_detail.rb


  valid_table = nil #Initialisieren

  tds.each do |td|
    valid_table = td if td.css(" + td > a").text == "2019/2020" #19/Wenn Sie Daten für 20 Jahreszeiten haben
  end

  if valid_table
    scores = valid_table.css("+ td + td + td + td").text
    yellow_cards = valid_table.css("+ td + td + td + td + td + td + td + td").text
    red_cards_with_2yellow = valid_table.css("+ td + td + td + td + td + td + td + td + td").text
    red_cards = valid_table.css("+ td + td + td + td + td + td + td + td + td + td").text
    
    data = { appearances: appearances, scores: scores, yellow: yellow_cards, red_with_2yellow: red_cards_with_2yellow, red: red_cards  }
    details.push(data)
  else #19/Wenn Sie für 20 Jahreszeiten keinen Tisch haben(Für null). Auch in diesem Fall werden die Daten mit allem auf 0 gesetzt.
    data = { appearances: 0, scores: 0, yellow: 0, red_with_2yellow: 0, red: 0 }
    details.push(data)
  end

end #Ende jeder Aussage

Damit ist die Erfassung aller Daten abgeschlossen! !! Ich hatte es schwer und hatte den Eindruck, dass es ziemlich chaotisch war, aber ich habe es geschafft, die Daten zu bekommen, die ich anstrebte. Ich bin der Meinung, dass das n-te Kind nach dem Erwerb des Elternelements verwendet werden kann, um "unmittelbar nach ..." zu sagen, indem + viel verwendet wird. Es scheint einen besseren Weg zu geben, sich zu verzweigen. Ich hoffe, Sie erhalten es als Kampf für die Unerfahrenen.

Schließlich werden wir die CSV-Datei erneut exportieren. Der Wert des Hash kann wie folgt abgerufen werden.

get_detail.rb


CSV.open('data_2.csv', 'w') do |csv|
  headers = %w(appearances score yellow red(2yellow) red(only) )
  csv << headers
  details.each { |detail| csv << detail.values }
end

Damit ist der Vorgang abgeschlossen! !!

get_detail.rb


require 'nokogiri'
require 'open-uri'
require 'csv'

urls = CSV.read('players_pages.csv')

details = []

urls.each do |u|
  p details.count
  
  url =  u[0]

  doc = Nokogiri::HTML(URI.open(url))

  prLeagues = []

  doc.css("td > a").each do |a|
    if a.text == "Pr. League"
      prLeagues.push(a)
    end
  end

  tds = []

  if prLeagues.any?
    prLeagues.each { |league| tds.push(league.parent) }
  else
    data = { appearances: 0, scores: 0, yellow: 0, red_with_2yellow: 0, red: 0 }
    details.push(data)
    next
  end

  valid_table = nil

  tds.each do |td|
    valid_table = td if td.css(" + td > a").text == "2019/2020"
  end

  if valid_table
    appearances = valid_table.css("+ td + td + td > a").text
    scores = valid_table.css("+ td + td + td + td").text
    yellow_cards = valid_table.css("+ td + td + td + td + td + td + td + td").text
    red_cards_with_2yellow = valid_table.css("+ td + td + td + td + td + td + td + td + td").text
    red_cards = valid_table.css("+ td + td + td + td + td + td + td + td + td + td").text
    
    data = { appearances: appearances, scores: scores, yellow: yellow_cards, red_with_2yellow: red_cards_with_2yellow, red: red_cards  }
    details.push(data)
  else
    data = { appearances: 0, scores: 0, yellow: 0, red_with_2yellow: 0, red: 0 }
    details.push(data)
  end

end

CSV.open('data_2.csv', 'w') do |csv|
  headers = %w(appearances score yellow red(2yellow) red(only) )
  csv << headers
  details.each { |detail| csv << detail.values }
end

Sie können eine Tabelle wie ein Bild erstellen, indem Sie die beiden fertigen CSV-Dateien mit Excel oder einer Tabelle laden. スクリーンショット 2020-10-14 15.13.55.png

Schließlich

Diesmal habe ich zum ersten Mal versucht zu kratzen, und es hat viel Spaß gemacht, obwohl ich es schwer hatte. Ich denke, es hat lange gedauert, aber es war viel schneller als manuell zu arbeiten. Nur wenige Menschen möchten vielleicht genau das Gleiche tun, aber wir hoffen, dass es jedem hilft, der kratzen möchte. (Bitte achten Sie beim Schaben nicht auf die Regeln!)

Ich bin noch ein junger und unreifer Mensch, aber ich möchte meine Fähigkeiten verbessern und weitere nützliche Informationen senden! !! Vielen Dank für das Lesen des Artikels. Wir freuen uns darauf, in Zukunft mit Ihnen zusammenzuarbeiten! !! !!

Recommended Posts

Erhalten Sie Daten aller Premier League-Spieler, indem Sie mit Ruby (nokogiri) kratzen.
Holen Sie sich Daten mit der API, die mit dem Befehl curl erstellt wurde
Yahoo-Nachrichten mit [Ruby + Nokogiri] kratzen → CSV speichern
Ruby, Nokogiri: Ruft den Elementnamen des ausgewählten Knotens ab