HTML-Tabellen sind die besten Datenquellen im Web. Sie sind leicht zu verstehen und können eine immense Menge an Daten in einem einfach zu lesenden und verständlichen Format enthalten. Die Fähigkeit, HTML-Tabellen zu scrapen, ist eine wichtige Fähigkeit für jeden Entwickler, der sich für Data Science oder Datenanalyse im Allgemeinen interessiert.
In diesem Tutorial werden wir uns näher mit HTML-Tabellen befassen und ein einfaches, aber leistungsstarkes Skript erstellen, um Tabellendaten zu extrahieren und in eine CSV-Datei zu exportieren.
Inhaltsübersicht
Was ist eine HTML-Webtabelle?
Eine HTML-Tabelle ist eine Reihe von Zeilen und Spalten, die dazu dienen, Informationen in einem Rasterformat direkt auf einer Webseite anzuzeigen. Sie werden häufig verwendet, um tabellarische Daten anzuzeigen, z.B. in Tabellenkalkulationen oder Datenbanken, und sind eine hervorragende Datenquelle für unsere Projekte.
Von Sport- und Wetterdaten bis hin zu Büchern und Autorendaten sind die meisten großen Datensätze im Web über HTML-Tabellen zugänglich, da sie sich hervorragend eignen, um Informationen in einem strukturierten und leicht zu navigierenden Format anzuzeigen.
Die gute Nachricht für uns ist, dass sich die Daten der HTML-Tabelle im Gegensatz zu dynamisch generierten Inhalten direkt innerhalb des Tabellenelements in der HTML-Datei befinden, was bedeutet, dass wir alle benötigten Informationen genau wie bei anderen Elementen im Web abrufen können - solange wir ihre Struktur verstehen.
Die Struktur von HTML-Tabellen verstehen
Obwohl Sie im Frontend nur die Spalten und Zeilen sehen können, werden diese Tabellen in Wirklichkeit mit einigen verschiedenen HTML-Tags erstellt:
<table>
: Es markiert den Anfang einer HTML-Tabelle<tr>
: Zeigt eine Zeile in der Tabelle an<td>
: Definiert eine Zelle in der Tabelle
Der Inhalt wird innerhalb der <td>
Tag und <tr>
wird verwendet, um eine Zeile zu erstellen. Mit anderen Worten: Tabelle > Zeile > Zelle || Tabelle > tr > td Hierarchie folgt, um eine HTML-Tabelle zu erstellen.
Eine spezielle Zelle kann mit dem Befehl <th>
Tag, was bedeutet Tabellenkopf. Grundsätzlich können die ersten Zellen der ersten Zeile mit der Methode <th>
Tag, um anzuzeigen, dass die Zeile die Überschrift der Tabelle ist.
Hier ist ein Beispiel für die Erstellung einer einfachen zweizeiligen und zweispaltigen HTML-Tabelle:
<table>
<tr>
<th>Haustier 1</th>
<th>Haustier 2</th>
</tr>
<tr>
<td>Hund</td>
<td>Katze</td>
</tr>
</table>
Beim Scrapen von HTML-Tabellen gibt es jedoch einen großen Unterschied. Im Gegensatz zu anderen Elementen auf einer Webseite zielen CSS-Selektoren auf die gesamten Zellen und Zeilen - oder sogar auf die gesamte Tabelle - ab, denn alle diese Elemente sind eigentlich Bestandteile der <table>
Element.
Anstatt für jeden Datenpunkt, den wir auslesen möchten, einen CSS-Selektor zu verwenden, müssen wir eine Liste mit allen Zeilen der Tabelle erstellen und sie in einer Schleife durchgehen, um die Daten aus den Zellen zu holen.
Wenn wir diese Logik verstehen, ist die Erstellung unseres Skripts eigentlich ziemlich einfach.
Scraping von HTML-Tabellen in CSV mit Node.JS
Wenn Sie Node.JS zum ersten Mal für Web Scraping verwenden, ist es vielleicht hilfreich, einige unserer früheren Tutorials durchzuarbeiten:
Wir werden diese Anleitung jedoch so anfängerfreundlich wie möglich halten, damit Sie sie auch als Ausgangspunkt verwenden können.
Anmerkung: Eine Anleitung zur Installation von Node.JS finden Sie im ersten Artikel der Liste.
Für unser heutiges Projekt werden wir einen Web Scraper mit Axios und Cheerio erstellen, um die auf https://datatables.net/examples/styling/display.html angezeigten Mitarbeiterdaten zu scrapen.
Wir extrahieren den Namen, die Position, das Büro, das Alter, das Anfangsdatum und das Gehalt für jeden Mitarbeiter und senden die Daten dann mit dem Paket ObjectsToCsv an eine CSV-Datei.
1. Bereitstellen unserer Dateien
Um unser Projekt zu starten, erstellen wir ein neues Verzeichnis namens html-table-scraperöffnen Sie den neuen Ordner in VScode (oder dem von Ihnen bevorzugten Code-Editor) und öffnen Sie ein neues Terminal.
Im Terminal führen wir Folgendes aus npm init -y
um ein neues Node.JS-Projekt zu starten. Sie werden nun eine neue JSON-Datei in Ihrem Ordner haben.
Als nächstes installieren wir unsere Abhängigkeiten mit den folgenden Befehlen:
- Axios:
npm install axios
- Auf Wiedersehen:
npm install cheerio
- ObjectsToCsv:
npm install objects-to-csv
Unser für eine Ein-Befehl-Installation: npm i axios cheerio objects-to-csv
.
Jetzt können wir eine neue Datei namens tablescraper.js und importieren Sie unsere Abhängigkeiten am Anfang.
const axios = require("axios");
const cheerio = require("cheerio");
const ObjectsToCsv = require("objects-to-csv");
Außerdem sollte Ihr Projekt folgendermaßen aussehen:
2. Testen der Zielsite mit DevTools
Bevor wir den Code schreiben, müssen wir verstehen, wie die Website strukturiert ist. Ja, alle Tabellen verwenden die Grundstruktur, aber das bedeutet nicht, dass alle gleich aufgebaut sind.
Als erstes müssen wir feststellen, ob es sich tatsächlich um eine HTML-Tabelle handelt oder nicht. Es ist sehr üblich, dass Websites JavaScript verwenden, um Daten in ihre Tabellen einzufügen, insbesondere wenn es sich um Echtzeitdaten handelt. In solchen Fällen müssen wir einen völlig anderen Ansatz wählen, z.B. einen Browser ohne Kopfzeile verwenden.
Um zu testen, ob sich die Daten in der HTML-Datei befinden, müssen wir nur einige Datenpunkte - sagen wir den Namen - kopieren und im Quellcode der Seite suchen.
Wir haben das Gleiche für andere Namen und Datenpunkte getan, nur um sicherzugehen, und ja, alle Daten stehen uns zur Verfügung. Eine weitere interessante Überraschung ist, dass sich alle Zeilen der Tabelle im rohen HTML befinden, obwohl es anscheinend eine Art Paginierung im Frontend gibt.
Außerdem wissen wir jetzt, dass es insgesamt 57 Zeilen gibt, die wir abrufen können. Das ist wichtig, denn so wissen wir, ob wir tatsächlich alle verfügbaren Daten abrufen können oder nicht.
Die zweite Sache, die wir direkt im Browser testen wollen, sind unsere Selektoren. Anstatt einen Haufen unnötiger Anfragen zu senden, können wir die Konsole des Browsers verwenden, um Elemente mit dem document.querySelectorAll()
method
.
Wenn wir in die Konsole gehen und Folgendes eingeben document.querySelectorAll('table')
gibt es vier verschiedene Tabellen zurück.
Wenn wir mit der Maus über die Tabellen fahren, stellen wir schnell fest, dass die erste Tabelle (Nummer 0) die richtige ist. Also machen wir es noch einmal, aber mit der Angabe der Klasse - die in der Liste durch die Punkte (.) dargestellt wird.
Großartig, wir sind unseren Daten einen Schritt näher gekommen!
Bei näherer Betrachtung sind die Daten der Tabelle um ein <tbody>
Tag, also fügen wir ihn zu unserem Selektor hinzu, um sicherzustellen, dass wir nur die Zeilen mit den gewünschten Daten abrufen.
Schließlich wollen wir alle Zeilen erfassen und überprüfen, ob unser Selektor die gesamten 57 Zeilen erfasst.
Anmerkung: Da wir die Konsole verwenden, um Elemente in der gerenderten HTML-Datei auszuwählen, müssen wir die Gesamtzahl der angezeigten Elemente auf 100 setzen. Andernfalls würde unser Selektor in der Konsole nur 10 Knotenelemente anzeigen.
Mit all diesen Informationen können wir nun beginnen, unseren Code zu schreiben!
3. Senden unserer HTTP-Anfrage und Parsen des Roh-HTML
Axios macht es super einfach, HTTP-Anfragen innerhalb einer Async Function
. Alles, was wir tun müssen, ist, eine asynchrone Funktion zu erstellen und die URL in einer Konstante namens Axios zu übergeben response
. Wir protokollieren auch den Statuscode der Antwort (der bei einer erfolgreichen Anfrage 200 sein sollte).
(async function html_scraper() {
const response = await axios('https://datatables.net/examples/styling/display.html');
console.log(response.status)
})();
Anmerkung: Sie können diese Variablen benennen, wie Sie möchten, aber halten Sie sie so beschreibend wie möglich.
Als Nächstes speichern wir die Daten aus der Antwort (rohes HTML) in einer neuen Konstante namens html, damit wir sie dann an Cheerio zum Parsen übergeben können. cheerio.load()
.
const html = await response.data;
const $ = cheerio.load(html);
4. Iteration durch die HTML-Tabellenzeilen
Lassen Sie uns mit dem Selektor, den wir zuvor getestet haben, alle Zeilen innerhalb der HTML-Tabelle auswählen.
const allRows = $('table.display > tbody > tr');
console.log(allRows.length)
Zu Testzwecken geben wir mit console.log() die Länge von allRows ein, um zu überprüfen, ob wir tatsächlich alle Zielzeilen ausgewählt haben.
57 ist genau das, was wir anstrebten!
Um durch die Liste der Zeilen zu gehen, verwenden wir natürlich die Funktion .each()
Methode, aber es gibt noch eine Sache, die wir herausfinden müssen: die Reihenfolge der Zellen.
Im Gegensatz zu gewöhnlichen HTML-Elementen ist Zellen keine eindeutige Klasse zugewiesen. Der Versuch, jeden Datenpunkt mit einer CSS-Klasse zu scrapen, könnte also zu einem Chaos führen. Stattdessen werden wir das Ziel der <td>
s Position innerhalb seiner Reihe.
Mit anderen Worten, wir weisen unser Skript an, jede Zeile aufzurufen, alle Zellen innerhalb der Zeile auszuwählen und dann jeden Datenpunkt in einer Variablen zu speichern, die auf seiner Position innerhalb der Zeile basiert.
Anmerkung: In Node.JS beginnen alle Listen bei 0. Die erste Position wäre also [0], und die zweite Zelle wäre [1].
Aber woher wissen wir, welche Position welche ist? Wir gehen zurück zur Konsole unseres Browsers und testen es:
Da wir nun wissen, wo jedes Element im Verhältnis zum Rest steht, sehen Sie hier den fertigen Parser:
allRows.each((index, element) => {
const tds = $(element).find('td');
const name = $(tds[0]).text();
const position = $(tds[1]).text();
const office = $(tds[2]).text();
const age = $(tds[3]).text();
const startDate = $(tds[4]).text();
const salary = $(tds[5]).text();
5. Schieben Sie die gescrapten Daten in ein leeres Array
Wenn wir console.log()
Wenn wir uns die ausgewerteten Daten ansehen, sehen wir, dass wir den Text aus jeder Zelle auslesen, allerdings mit sehr ungeordneten Ergebnissen - was wiederum die Erstellung unserer CSV-Datei erschwert.
Bevor wir also unsere Daten exportieren, sollten wir sie ordnen, indem wir die Daten in ein leeres Array verschieben, um eine einfache Knotenliste zu erstellen.
Erstellen Sie zunächst ein leeres Array außerhalb der Hauptfunktion - wenn Sie das leere Array innerhalb der Funktion erstellen, wird es bei jeder Iteration überschrieben, was wir nicht wollen.
Als Teil unseres Parsers verwenden wir dann die Methode .push(), um unsere Daten in der leeren Liste zu speichern, die wir erstellt haben.
employeeData.push({
'Name': name,
'Position': position,
'Office': office,
'Age': age,
'Start Date': startDate,
'Salary': salary,
})
Wie immer, lassen Sie uns console.log()
die Länge von employeeData, um sicherzustellen, dass wir jetzt 57 Elemente darin haben.
Für den visuellen Kontext können wir auch das Array protokollieren, um zu sehen, was darin gespeichert ist.
Wie wir sehen können, werden alle Daten nun in Knotenelementen gespeichert, die alle Daten in einem strukturierten Format enthalten.
6. Senden von gescrapten Daten in eine CSV-Datei
Nachdem wir unsere Daten organisiert haben, können wir unsere Liste an ObjectsToCsv
und es erstellt die Datei für uns ohne zusätzliche Arbeit:
const csv = new ObjectsToCsv(employeeData);
await csv.toDisk('./employeeData.csv')
Alles, was wir tun müssen, ist, ein neues csv-Objekt zu erstellen und die Liste an ObjectsToCsv
und weisen Sie es dann an, es auf unserem Rechner zu speichern, indem Sie den Pfad angeben.
7. HTML Table Scraper [Vollständiger Code]
Herzlichen Glückwunsch, Sie haben offiziell Ihren ersten HTML-Table Scraper erstellt! Vergleichen Sie Ihren Code mit der fertigen Codebasis dieses Tutorials, um sicherzustellen, dass Sie nichts übersehen haben:
const axios = require("axios");
const cheerio = require("cheerio");
const ObjectsToCsv = require("objects-to-csv");
employeeData = [];
(async function html_scraper() {
const response = await axios('https://datatables.net/examples/styling/display.html')
const html = await response.data;
const $ = cheerio.load(html);
//Selecting all rows inside our target table
const allRows = $('table.display > tbody > tr');
console.log('Going through rows')
//Looping through the rows
allRows.each((index, element) => {
//Selecting all cells within the row
const tds = $(element).find('td');
//Extracting the text out of each cell
const name = $(tds[0]).text();
const position = $(tds[1]).text();
const office = $(tds[2]).text();
const age = $(tds[3]).text();
const startDate = $(tds[4]).text();
const salary = $(tds[5]).text();
//Pushing scraped data to our empty array
employeeData.push({
'Name': name,
'Position': position,
'Office': office,
'Age': age,
'Start Date': startDate,
'Salary': salary,
})
})
//Exporting scraped data to a CSV file
console.log('Saving data to CSV');
const csv = new ObjectsToCsv(employeeData);
await csv.toDisk('./employeeData.csv')
console.log('Saved to CSV')
})();
Nachdem wir unser Skript ausgeführt haben, wird eine neue CSV-Datei im Ordner unseres Projekts erstellt:
Jetzt können Sie diese Daten für weitere Analysen verwenden, z. B. für Gehaltsvergleiche auf der Grundlage der Stellenbezeichnung oder des Eintrittsdatums, oder um nach Trends in größeren Jobdatenbanken zu suchen.
Natürlich kann dieses Skript an fast jede HTML-Tabelle angepasst werden, die Sie finden, also halten Sie Ihren Geist offen für neue Möglichkeiten und
Vermeiden Sie es, blockiert zu werden: Integration der ScraperAPI in einer einzigen Code-Zeile
Bevor Sie gehen, müssen wir noch eine Sache tun, um unseren Scraper widerstandsfähiger zu machen, und das ist der Umgang mit Anti-Scraping-Techniken und -Systemen. Viele Websites lassen sich nicht gerne scrapen, denn leider sind viele Scraper schlecht optimiert und schaden ihren Websites.
Aus diesem Grund müssen Sie einige Best Practices für das Web Scraping befolgen, um sicherzustellen, dass Sie Ihre Projekte korrekt durchführen, ohne die Ziel-Website zu sehr unter Druck zu setzen und ohne Ihr Skript und Ihre IP-Adresse dem Risiko auszusetzen, gesperrt oder auf eine schwarze Liste gesetzt zu werden - was es unmöglich macht, von Ihrem Rechner aus erneut auf die benötigten Daten zuzugreifen.
Um die IP-Rotation, das JavaScript-Rendering, das Finden und Implementieren von HTTP-Headern, CAPTCHAs und mehr zu handhaben, müssen wir nur unsere erste Anfrage über den Server von ScraperAPI senden. Diese API nutzt jahrelange statistische Analysen und maschinelles Lernen, um die beste Kombination von Headern und Proxy zu ermitteln, erfolglose Anfragen zu bearbeiten und unsere Anfrage so zu timen, dass sie unseren Zielserver nicht überlastet.
Wenn Sie es zu unserem Skript hinzufügen, müssen Sie nur diese Zeichenkette zu der an Axios übergebenen URL hinzufügen:
const response = await axios('http://api.scraperapi.com?api_key={Your_API_Key}&url=https://datatables.net/examples/styling/display.html')
Denken Sie daran, zu ersetzen {Your_API_Key}
mit Ihrem eigenen API-Schlüssel - den Sie generieren können, indem Sie ein kostenloses ScraperAPI-Konto anlegen.
Ihre erste Anfrage wird etwas länger dauern, während ScraperAPI alle komplexen Vorgänge für Sie erledigt und nur für erfolgreiche Anfragen API-Credits verbraucht.
Jetzt sind Sie dran. Beim Web Scraping geht es vor allem um Übung. Jede Website ist ein anderes Puzzle, es gibt also keine Einheitslösung. Konzentrieren Sie sich stattdessen darauf, die Grundlagen zu nutzen, um sich komplexeren Herausforderungen zu stellen.
Wenn Sie weiter üben möchten, empfehlen wir Ihnen einige Websites:
Bis zum nächsten Mal, viel Spaß beim Schaben!