[PYTHON] Grattage de table avec belle soupe

introduction

Les tableaux HTML peuvent être récupérés en quelques lignes à l'aide de pd.read_html () de pandas, mais cette fois, j'aimerais vous montrer comment gratter sans utiliser read_html ().

Préparation

Installez Beautiful Soup. (Cette fois, nous utiliserons également des pandas pour créer une trame de données, alors installez-la comme il convient.)

$ pip install beautifulsoup4 # or conda install

politique

Cette fois, à titre d'exemple, obtenons la liste suivante des processeurs à partir de cette page wikipedia. Screen Shot 2020-02-27 at 20.34.11.png

référence

Ici, pour référence, je voudrais montrer la méthode lors de l'utilisation de la méthode super-facile pd.read_html ().

import pandas as pd

url = 'https://en.wikipedia.org/wiki/Transistor_count' #URL de la page Web cible
dfs = pd.read_html(url) #Si la page Web comporte plusieurs tables, elles seront stockées au format dfs au format liste

Cette fois, il semble que la table cible soit stockée dans le premier index de dfs, donc sortons dfs [1](dfs [0] stocke une table d'une autre classe).

dfs[1] 

Le résultat de sortie ressemble à l'image ci-dessous et vous pouvez certainement le gratter. Screen Shot 2020-02-27 at 20.41.53.png

Aperçu

Avant de gratter une table avec BeautifulSoup, jetons un coup d'œil à la page Web sur laquelle elle est grattée. Passons à la page wikipedia à partir du lien ci-dessus et ouvrons les outils de développement (dans le cas de chrome, vous pouvez l'afficher en faisant un clic droit sur le tableau ⇒ inspecter. Vous pouvez également sélectionner option + commande + I). En regardant la source html de la page avec les outils de développement, la table cible se trouve sous la balise \

, \ (corps du tableau) ⇒ \ (composant colonne du tableau) ⇒ \ au même niveau que la balise \ . , Correspond au nom de la colonne (Processor ~ MOS process) de la table).

Screen Shot 2020-02-27 at 21.37.26.png

code

Écrivons le code en gardant à l'esprit la vue d'ensemble ci-dessus.

import requests
from bs4 import BeautifulSoup
import csv
import pandas as pd

url = "https://en.wikipedia.org/wiki/Transistor_count"
#Obtenir des données de page Web
page = requests.get(url)
#Analyser le HTML
soup = BeautifulSoup(page.text, 'html.parser')

Jetons un coup d'œil aux données analysées.

print(soup.prettify())

Comme indiqué ci-dessous, la structure hiérarchique telle qu'elle apparaît dans les outils de développement est visible à l'infini.

output


<!DOCTYPE html>
<html class="client-nojs" dir="ltr" lang="en">
 <head>
  <meta charset="utf-8"/>
  <title>
   Transistor count - Wikipedia
  </title>
  <script>

・ ・ ・

Extrayons la partie qui correspond à la table. Utilisez la méthode find () pour spécifier la balise

(données de la cellule du tableau) Vous pouvez voir qu'il a une structure hiérarchique (non visible dans l'image ci-dessous, mais il y a une balise \ dans la hiérarchie ci-dessous \
et la classe wikitable à partir des balises pour extraire les parties pertinentes.

table = soup.find('table', {'class':'wikitable'}).tbody

Vous pouvez penser que vous n'avez pas nécessairement besoin de spécifier la classe de la table, mais vous devez la spécifier s'il existe des tables d'autres classes. Cette fois, comme le montre l'image ci-dessous, il existe avec un autre nom de classe appelé box-More, donc la classe wikitable est explicitement spécifiée.

Screen Shot 2020-02-27 at 22.48.42.png

Ensuite, dans le corps de la table extrait, récupérez la partie balise

(composant de ligne de la table). Le find_all ('tr') suivant stocke chaque composant de ligne au format liste.


rows = table.find_all('tr')

Regardons le 0ème élément du composant de ligne récupéré.


print(rows[0])

Comme indiqué ci-dessous, il existe une hiérarchie de balises \

, et vous pouvez voir que celles-ci correspondent à la partie en-tête du tableau.

output



<tr>
<th><a href="/wiki/Microprocessor" title="Microprocessor">Processor</a>
</th>
<th data-sort-type="number"><a class="mw-redirect" href="/wiki/MOS_transistor" title="MOS transistor">MOS transistor</a> count
</th>
<th>Date of<br/>introduction
</th>
<th>Designer
</th>
<th data-sort-type="number"><a href="/wiki/MOSFET" title="MOSFET">MOS</a><br/><a href="/wiki/Semiconductor_device_fabrication" title="Semiconductor device fabrication">process</a>
</th>
<th data-sort-type="number">Area
</th></tr>

D'un autre côté, jetons un coup d'œil au 0ème élément suivant des composants de ligne acquis.


print(rows[1])

Comme indiqué ci-dessous, il existe une hiérarchie de balises \

, et vous pouvez voir que celles-ci correspondent aux composants de données de chaque cellule de la première ligne du tableau.

output



<tr>
<td><a class="mw-redirect" href="/wiki/MP944" title="MP944">MP944</a> (20-bit, <i>6-chip</i>)
</td>
<td><i><b>?</b></i>
</td>
<td>1970<sup class="reference" id="cite_ref-F-14_20-1"><a href="#cite_note-F-14-20">[20]</a></sup><sup class="reference" id="cite_ref-22"><a href="#cite_note-22">[a]</a></sup>
</td>
<td><a href="/wiki/Garrett_AiResearch" title="Garrett AiResearch">Garrett AiResearch</a>
</td>
<td><i><b>?</b></i>
</td>
<td><i><b>?</b></i>
</td></tr>

Créer un bloc de données

Ensuite, créez une trame de données à partir des données extraites. Commençons par le nom de la colonne du bloc de données. Obtient toutes les balises \

à l'intérieur de la balise \
à l'intérieur de la balise \
qui sont les composants d'en-tête de la 0ème ligne du tableau et extrait uniquement le composant de texte (v.text).


columns = [v.text for v in rows[0].find_all('th')]
print(columns)

Le résultat est le suivant, mais \ n indiquant qu'un saut de ligne est un obstacle.

output


['Processor\n', 'MOS transistor count\n', 'Date ofintroduction\n', 'Designer\n', 'MOSprocess\n', 'Area\n']

Modifions donc le code ci-dessus comme suit:


columns = [v.text.replace('\n', '') for v in rows[0].find_all('th')]
print(columns)

Le résultat est le suivant. Seul le nom de la colonne pouvait être extrait proprement.

output


['Processor', 'MOS transistor count', 'Date ofintroduction', 'Designer', 'MOSprocess', 'Area']

Maintenant, préparons un bloc de données vide en spécifiant le nom de colonne ci-dessus.


df = pd.DataFrame(columns=columns)
df

Le résultat est le suivant. Seul le nom de la colonne est affiché dans la partie d'en-tête et vous pouvez voir le bloc de données vide.

Screen Shot 2020-02-27 at 23.21.59.png

Maintenant que les colonnes ont été extraites, extrayons chaque composant de données de la table.

#À propos d'un certain composant de ligne de toutes les lignes
for i in range(len(rows)):
    #Tous<td>Obtenez des balises (données de cellule), stockez-les dans tds et répertoriez-les
    tds = rows[i].find_all('td')
    #Excluez les cas où le nombre de données tds ne correspond pas au nombre de colonnes (vide), etc.
    if len(tds) == len(columns):
        #Stocker et répertorier toutes les données de cellule (d'un certain composant de ligne) en tant que composants de texte dans les valeurs
        values = [ td.text.replace('\n', '').replace('\xa0', ' ') for td in tds ]
        #valeurs pd.Convertir en données de série, combiner en bloc de données
        df = df.append(pd.Series(values, index=columns), ignore_index= True)

Sortons la trame de données créée.

df

Le résultat devrait ressembler à l'image ci-dessous. J'ai pu racler proprement la table avec Beautiful Soup.

Screen Shot 2020-02-28 at 00.10.16.png

À propos, si le td.text.replace ('\ n', ''). Replace ('\ xa0', '') ci-dessus est simplement exécuté en tant que td.text, les valeurs seront les suivantes. (Un composant de valeurs est présenté à titre d'exemple).

output


['Intel 4004 (4-bit, 16-pin)\n', '2,250\n', '1971\n', 'Intel\n', '10,000\xa0nm\n', '12\xa0mm²\n']

Comme pour l'en-tête, le code de saut de ligne \ n et le code d'espace \ xa0 sont inclus. Par conséquent, il est nécessaire de remplacer chacun par la méthode replace ().

Enregistrez la trame de données créée au format csv le cas échéant.

#Sans en-tête, spécifiez l'onglet pour le délimiteur
df.to_csv('processor.csv', index=False, sep='\t' )

Résumé du code

import requests
from bs4 import BeautifulSoup
import csv
import pandas as pd

url = 'https://en.wikipedia.org/wiki/Transistor_count'
page = requests.get(url)

soup = BeautifulSoup(page.text, 'html.parser')
table = soup.find('table', {'class':'wikitable'}).tbody

rows = table.find_all('tr')
columns = [v.text.replace('\n', '') for v in rows[0].find_all('th')]

df = pd.DataFrame(columns=columns)

for i in range(len(rows)):
    tds = rows[i].find_all('td')

    if len(tds) == len(columns):
        values = [ td.text.replace('\n', '').replace('\xa0', ' ') for td in tds ]
        df = df.append(pd.Series(values, index=columns), ignore_index= True)

df.to_csv('processor.csv', index=False, sep='\t' )

Recommended Posts