[PYTHON] Tischkratzen mit schöner Suppe

Einführung

HTML-Tabellen können mit pandas 'pd.read_html () in wenigen Zeilen gekratzt werden, aber dieses Mal möchte ich Ihnen zeigen, wie man ohne read_html () kratzt.

Vorbereitung

Installieren Sie schöne Suppe. (Dieses Mal verwenden wir auch Pandas, um einen Datenrahmen zu erstellen. Installieren Sie ihn daher entsprechend.)

$ pip install beautifulsoup4 # or conda install

Politik

Als Beispiel erhalten wir dieses Mal die folgende Liste von CPUs von dieser Wikipedia-Seite. Screen Shot 2020-02-27 at 20.34.11.png

Referenz

Als Referenz möchte ich hier die Methode zeigen, wenn die supereinfache Methode pd.read_html () verwendet wird.

import pandas as pd

url = 'https://en.wikipedia.org/wiki/Transistor_count' #URL der Zielwebseite
dfs = pd.read_html(url) #Wenn die Webseite mehrere Tabellen enthält, werden diese im Listenformat in dfs gespeichert

Dieses Mal scheint die Zieltabelle im ersten Index von dfs gespeichert zu sein. Geben wir also dfs [1] aus (dfs [0] speichert eine Tabelle einer anderen Klasse).

dfs[1] 

Das Ausgabeergebnis sieht wie im Bild unten aus, und Sie können es sicher kratzen. Screen Shot 2020-02-27 at 20.41.53.png

Überblick

Bevor wir eine Tabelle mit BeautifulSoup kratzen, werfen wir einen Blick auf die Webseite, auf die sie geschabt wird. Lassen Sie uns über den zuvor gezeigten Link zur Wikipedia-Seite springen und die Entwicklertools öffnen (im Fall von Chrome können Sie sie anzeigen, indem Sie mit der rechten Maustaste auf die Tabelle klicken ⇒ inspizieren. Sie können auch Option + Befehl + I auswählen). Wenn Sie sich die HTML-Quelle der Seite mit den Entwicklertools ansehen, befindet sich die Zieltabelle unter dem Tag \

\ (Tabellenkörper) ⇒ \ (Tabellenspaltenkomponente) ⇒ \ auf derselben Ebene wie das \ -Tag. , Entspricht dem Spaltennamen (Prozessor ~ MOS-Prozess) der Tabelle).

Screen Shot 2020-02-27 at 21.37.26.png

Code

Schreiben wir den Code unter Berücksichtigung der obigen Übersicht.

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

url = "https://en.wikipedia.org/wiki/Transistor_count"
#Holen Sie sich Webseitendaten
page = requests.get(url)
#HTML analysieren
soup = BeautifulSoup(page.text, 'html.parser')

Werfen wir einen Blick auf die analysierten Daten.

print(soup.prettify())

Wie unten gezeigt, können Sie die hierarchische Struktur in den Entwicklertools endlos sehen.

output


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

・ ・ ・

Extrahieren wir den Teil, der der Tabelle entspricht. Verwenden Sie die find () -Methode, um das

(Tabellenzellendaten). Sie können sehen, dass es eine hierarchische Struktur hat (im Bild unten nicht sichtbar, aber es gibt ein \ -Tag in der Hierarchie unter \
-Tag und die wikitable-Klasse aus dem -Tag anzugeben und den relevanten Teil zu extrahieren.

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

Möglicherweise müssen Sie die Klasse der Tabelle nicht unbedingt angeben, aber Sie müssen sie angeben, wenn Tabellen anderer Klassen vorhanden sind. Dieses Mal existiert es, wie in der Abbildung unten gezeigt, mit einem anderen Klassennamen namens box-More, sodass die wikitable-Klasse explizit angegeben wird.

Screen Shot 2020-02-27 at 22.48.42.png

Rufen Sie dann im extrahierten Tabellenkörper den

-Tag-Teil (Zeilenkomponente der Tabelle) ab. Das folgende find_all ('tr') speichert jede Linienkomponente im Listenformat.


rows = table.find_all('tr')

Schauen wir uns das 0. Element der abgerufenen Zeilenkomponente an.


print(rows[0])

Wie unten gezeigt, gibt es innerhalb des \

-Tags eine Hierarchie von \ -Tags, und Sie können sehen, dass diese den Datenkomponenten jeder Zelle in der ersten Zeile der Tabelle entsprechen.

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>

Erstellen eines Datenrahmens

Erstellen Sie als Nächstes einen Datenrahmen aus den extrahierten Daten. Beginnen wir mit dem Spaltennamen des Datenrahmens. Ruft alle \

-Tags, und Sie können sehen, dass diese dem Header-Teil der Tabelle entsprechen.

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>

Schauen wir uns andererseits das 0. nächste Element der erfassten Zeilenkomponenten an.


print(rows[1])

Wie unten gezeigt, gibt es eine Hierarchie von \

-Tags innerhalb des \
-Tags ab, die die Header-Komponenten aus der 0. Zeile der Tabelle sind, und extrahiert nur die Textkomponente (v.text).


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

Das Ergebnis ist wie folgt, aber \ n zeigt an, dass ein Zeilenumbruch ein Hindernis darstellt.

output


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

Ändern wir also den obigen Code wie folgt:


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

Das Ergebnis ist wie folgt. Nur der Spaltenname konnte sauber extrahiert werden.

output


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

Bereiten wir nun einen leeren Datenrahmen vor, indem wir den obigen Spaltennamen angeben.


df = pd.DataFrame(columns=columns)
df

Das Ergebnis ist wie folgt. Im Kopfteil wird nur der Spaltenname angezeigt, und Sie können den leeren Datenrahmen sehen.

Screen Shot 2020-02-27 at 23.21.59.png

Nachdem die Spalten extrahiert wurden, extrahieren wir jede Datenkomponente der Tabelle.

#Über eine bestimmte Zeilenkomponente aller Zeilen
for i in range(len(rows)):
    #Alle<td>Holen Sie sich Tags (Zellendaten), speichern Sie sie in tds und listen Sie sie auf
    tds = rows[i].find_all('td')
    #Schließen Sie Fälle aus, in denen die Anzahl der tds-Daten nicht mit der Anzahl der Spalten (leer) usw. übereinstimmt.
    if len(tds) == len(columns):
        #Speichern und listen Sie alle Zellendaten (einer bestimmten Zeilenkomponente) als Textkomponenten in Werten auf
        values = [ td.text.replace('\n', '').replace('\xa0', ' ') for td in tds ]
        #Werte pd.In Seriendaten konvertieren, in Datenrahmen kombinieren
        df = df.append(pd.Series(values, index=columns), ignore_index= True)

Lassen Sie uns den erstellten Datenrahmen ausgeben.

df

Das Ergebnis sollte wie im Bild unten aussehen. Ich konnte den Tisch mit Beautiful Soup sauber abkratzen.

Screen Shot 2020-02-28 at 00.10.16.png

Übrigens, wenn der obige td.text.replace ('\ n', ''). Replace ('\ xa0', '') einfach als td.text ausgeführt wird, lauten die Werte wie folgt. (Eine Komponente von Werten wird als Beispiel gezeigt).

output


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

Wie bei der Kopfzeile sind der Zeilenvorschubcode \ n und der Leerzeichencode \ xa0 enthalten. Daher ist es notwendig, jedes durch die replace () -Methode zu ersetzen.

Speichern Sie den erstellten Datenrahmen entsprechend im CSV-Format.

#Geben Sie ohne Kopfzeile die Registerkarte für das Trennzeichen an
df.to_csv('processor.csv', index=False, sep='\t' )

Codeübersicht

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