Obtenez les données de tous les joueurs de Premier League en grattant avec Ruby (nokogiri)

Ravi de vous rencontrer. Mon nom est Tatsu. Actuellement étudiant à l'université, je suis un jeune étudiant qui étudie pour devenir ingénieur. Je suis nerveux à propos du premier message Qiita de ma vie. Je vous remercie.

Le thème du premier article mémorable est le grattage à l'aide de la bibliothèque Ruby nokogiri. Entrons immédiatement dans le sujet principal.

Aperçu

Le site ciblé pour le scraping cette fois-ci est un site appelé Worldfootball.net qui couvre des informations sur le football dans le monde.

Un de mes amis qui effectue une analyse de données dans une étude universitaire m'a demandé de m'aider à collecter des données détaillées sur tous les joueurs de Premier League dans Excel, alors j'ai dit: "Si vous écrivez ce code, cela se terminera en un instant, non?" Je l'ai accepté. C'est le premier travail de ma vie qui utilise la programmation. (Bien que ce soit un volontaire complet ...)

Ce site se compose d'une page de liste (avec des nations de page) où les données de base sont dans un format de tableau et une page de détail où des données plus détaillées de chaque joueur sont écrites. La procédure générale consiste à obtenir d'abord les données de base de tous les joueurs et les liens vers les pages de détails de chaque joueur à partir de la page de liste, puis d'obtenir les données de chaque page de détails. Les données de base et les données détaillées se composent des éléments suivants.

↓ Page de la liste des joueurs https://www.worldfootball.net/players_list/eng-premier-league-2019-2020/nach-name/1/ ↓ Page de détail (Exemple: page de détail du lecteur Minamino) https://www.worldfootball.net/player_summary/takumi-minamino/

0. Préparation

Tapez la commande suivante pour installer nokogiri. gem install nokogiri

Ensuite, chargez la bibliothèque requise cette fois.

get_all.rb


require 'nokogiri' 
require 'open-uri' #Une bibliothèque pour accéder aux URL.
require 'csv' #Une bibliothèque pour lire et écrire des fichiers csv.

1. Acquisition de données de base

La première cible est les données de base. En d'autres termes, ce sont les données qui peuvent être obtenues à partir de la page de liste. La page de liste est paginée et vous obtiendrez plusieurs URL. Par conséquent, chaque URL est stockée dans un tableau comme indiqué ci-dessous.

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

Maintenant que nous avons une page de liste de 14 pages, nous sommes prêts à obtenir les données de base pour tous les joueurs en question! !!

À partir de là, je ne sais pas si la méthode que j'ai utilisée est correcte, mais je suis heureux de la présenter car j'ai pu atteindre mon objectif. Je serais très heureux si vous pouviez signaler quelque chose.

La prochaine chose que j'ai faite a été d'obtenir les données de base de chaque joueur dans un tableau pour chaque donnée. Avec nokogiri, vous pouvez utiliser le sélecteur CSS pour obtenir les données souhaitées sur votre site comme suit.

sampe


doc = Nokogiri::HTML(URI.open(url)) #Récupérer les données de l'URL cible

target = doc.css(".container > div > a") #Obtenez l'élément a directement sous le div directement sous la classe conteneur.
link = target[:href] #Obtenez l'attribut href dans l'élément a.
text = target.inner_html #Récupérez le texte dans l'élément a.

Sur cette base, nous allons acquérir les données de base de chaque joueur à partir de la page de liste. Maintenant que nous avons un tableau d'URL, nous allons itérer dessus et obtenir les données de chaque page.

get_all.rb


players_pages = [] #Stocker l'URL détaillée de chaque joueur
names = [] #Nom de chaque joueur 〃
teams = []#Équipe à laquelle appartient chaque joueur 〃
birthdays = [] #Anniversaire de chaque joueur 〃
height_data = [] #Hauteur de chaque joueur 〃

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

Avec cela, nous avons pu acquérir les données de base de tous les joueurs. A ce stade, si vous écrivez p names et l'exécutez, les noms de tous les joueurs seront crachés! !!

La bibliothèque csv vous permet d'écrire et de lire des fichiers csv. Tout d'abord, je voudrais exporter le fichier csv de la liste de liens de la page de détails du lecteur. Ce fichier sera chargé et utilisé à l'étape suivante (obtention de données détaillées).

get_all.rb


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

Enfin, je souhaite exporter les données de base récupérées sous la forme d'un fichier csv. C'est très simple, ajoutez simplement les 5 lignes suivantes. Vous n'êtes pas obligé d'avoir un en-tête, mais je pense que c'est mieux si vous en avez un, car vous pouvez le sortir dans Excel ou dans un tableur tel quel.

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

Ceci complète un fichier contenant les données de base de tous les joueurs. Le fichier csv est automatiquement créé dans le répertoire dans lequel vous travaillez. Pour l'instant, l'acquisition des données de base est désormais terminée! !!

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. Obtenez des données détaillées

J'ai eu du mal à partir d'ici. La raison en est que les méthodes d'acquisition des données étaient différentes. Cela signifie que la page de détail contient également des informations sur toutes les saisons des joueurs de Hazono, ainsi que des informations sur les équipes nationales et d'autres ligues, et accédez aux données souhaitées d'un joueur et parcourez-les. Je ne pouvais pas faire ça. Par exemple, si vous auriez dû accéder à la première ligne du tableau pour accéder aux données cibles (données de la saison 19/20 de Premier League) sur la page de détail du joueur A, mais également sur la page du joueur B. La première ligne peut avoir été dans une autre saison, ce qui rend impossible la collecte des données prévues. J'ai eu du mal comme ça, mais j'ai réussi à l'implémenter en utilisant le branchement conditionnel, alors j'aimerais l'introduire.

J'ai créé un autre fichier cette fois, mais le même fichier convient. Tout d'abord, chargez les bibliothèques nécessaires comme dans le cas des données de base, et chargez l'URL de la page de détail de chaque lecteur à partir du fichier csv créé la dernière fois.

get_detail.rb


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

urls = CSV.read('players_pages.csv') #Lisez le fichier csv et stockez-le dans un tableau.

Vous avez maintenant créé un tableau (URL) contenant les URL de toutes les pages de détails du lecteur. Le travail d'acquisition de données pour tous ces éléments est effectué à l'aide de chaque instruction, mais le problème est le traitement effectué dans chaque instruction. Cette fois, j'ai décidé d'obtenir les données de chaque joueur sous forme de hachage pour chaque élément et de créer un tableau de hachages qui les résume. Tout d'abord, définissez un tableau qui contiendra toutes les données.

get_detail.rb


details = []

Le branchement conditionnel sera effectué dans le traitement à effectuer à partir de maintenant. Les conditions de branchement sont d'abord de savoir s'il existe ou non des données de Premier League, puis de savoir s'il existe ou non des données de Premier League pour la saison 19/20.

Si vous n'êtes pas familier avec le football, vous ne savez peut-être pas ce que c'est, mais les joueurs ciblés cette fois sont des joueurs qui ont participé à un seul match. Il est possible que ces joueurs ne disposent pas de données de Premier League. De plus, certains joueurs peuvent ne pas avoir la table cible sur leurs pages, même s'ils sont inscrits mais n'ont pas de données pour la saison cible 19/20. Par conséquent, une telle branche est nécessaire.

Tout d'abord, j'écrirai un processus qui se branche selon qu'il existe ou non des données de Premier League.

get_detail.rb


urls.each do |u|

  url =  u[0] #urls[0][0]C'est une image. Si juste toi["htpps://~~~~.com"]Sera produit sous la forme de.

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

  prLeagues = []

  doc.css("td > a").each do |a| #Traite tous les éléments sous td.
    if a.text == "Pr. League"
      prLeagues.push(a)
    end
  end

  tds = []

  if prLeagues.any?
    prLeagues.each { |league| tds.push(league.parent) } #Obtient l'élément td, qui est l'élément parent de l'élément a (premier league).
  else #S'il n'y a pas de Premier League. Créez un hachage avec toutes les données à 0.
    data = { appearances: 0, scores: 0, yellow: 0, red_with_2yellow: 0, red: 0 }
    details.push(data)
    next #Terminez et passez au processus suivant.
  end

# appearances=Nombre de parties jouées, scores=Nombre de points

Ensuite, nous ferons une branche conditionnelle selon qu'il existe ou non des données pour la Premier League pour la saison 19/20. Le tableau tds contient les éléments td qui contiennent la chaîne de Premier League ("pr. League"). Immédiatement après td où la ligue est affichée, la saison est affichée, alors accédez-y et extrayez uniquement les tables valides. Une seule ligne peut être valide dans ce cas. Par conséquent, j'ai utilisé beaucoup de +, qui est un sélecteur CSS qui représente "immédiatement après", afin d'acquérir des données qui se propagent horizontalement.

get_detail.rb


  valid_table = nil #Initialisation

  tds.each do |td|
    valid_table = td if td.css(" + td > a").text == "2019/2020" #19/Si vous avez des données pour 20 saisons
  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/Si vous n'avez pas de table depuis 20 saisons(Pour nul). Dans ce cas également, les données sont créées avec tout défini sur 0.
    data = { appearances: 0, scores: 0, yellow: 0, red_with_2yellow: 0, red: 0 }
    details.push(data)
  end

end #Fin de chaque déclaration

Ceci termine l'acquisition de toutes les données! !! J'ai eu du mal et j'ai eu l'impression que c'était assez compliqué, mais j'ai réussi à obtenir les données que je visais. Je pense que nth-child peut être utilisé après l'acquisition de l'élément parent pour dire "immédiatement après ..." en utilisant + beaucoup. Il semble y avoir une meilleure façon de créer des branches. J'espère que vous le recevrez comme un record de lutte pour les personnes inexpérimentées.

Enfin, nous exporterons à nouveau le fichier csv. La valeur du hachage peut être récupérée comme suit.

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

Ceci termine le processus! !!

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

Vous pouvez créer un tableau comme une image en chargeant les deux fichiers csv terminés avec Excel ou une feuille de calcul. スクリーンショット 2020-10-14 15.13.55.png

finalement

J'ai essayé de gratter pour la première fois cette fois, et c'était très amusant même si j'avais du mal. Je pense que cela a pris du temps, mais c'était beaucoup plus rapide que de travailler manuellement. Peu de gens voudront peut-être faire exactement la même chose, mais nous espérons que cela aidera tous ceux qui veulent gratter. (Veillez à ne pas enfreindre les règles lors du grattage!)

Je suis encore une personne jeune et immature, mais j'aimerais améliorer mes compétences et envoyer des informations plus utiles! !! Merci d'avoir lu l'article. Nous sommes impatients de travailler avec vous à l'avenir! !! !!

Recommended Posts

Obtenez les données de tous les joueurs de Premier League en grattant avec Ruby (nokogiri)
Obtenez des données avec une API créée par la commande curl
Scraping Yahoo News avec [Ruby + Nokogiri] → Enregistrer CSV
Ruby, Nokogiri: récupère le nom de l'élément du nœud sélectionné