{"id":44365,"date":"2026-05-06T12:51:19","date_gmt":"2026-05-06T12:51:19","guid":{"rendered":"https:\/\/floppydata.com\/nicht-kategorisiert\/php-web-scraping-tutorial-wie-man-daten-mit-php-scrapt\/"},"modified":"2026-05-06T12:51:19","modified_gmt":"2026-05-06T12:51:19","slug":"php-web-scraping-tutorial","status":"publish","type":"post","link":"https:\/\/floppydata.com\/de\/blog\/scraping\/php-web-scraping-tutorial\/","title":{"rendered":"PHP Web Scraping Tutorial: Wie man Daten mit PHP scrapt"},"content":{"rendered":"<h2>Einf\u00fchrung<\/h2>\n<p>PHP war damals eine meiner ersten Sprachen als Webentwickler, und ich verwende es immer noch gerne f\u00fcr Scraping.<\/p>\n<p>Dieses Tutorial behandelt das, was ich tats\u00e4chlich in der Produktion verwende. Ich f\u00fchre Sie durch einen kompletten PHP-Web-Scraping-Workflow mit <a href=\"https:\/\/floppydata.com\/web-unlocker\/\">Floppydata Web Unlocker<\/a> als Scraping-Ebene. <\/p>\n<p><strong>Warum Floppydata?<\/strong><\/p>\n<p>Weil es eine Scraping-API bietet, die die Verwaltung von Proxys, Headern oder Anti-Bot-Logik \u00fcberfl\u00fcssig macht. Am Ende dieses Artikels werden Sie wissen, wie Sie Web Scraping mit PHP durchf\u00fchren k\u00f6nnen. <\/p>\n<h2>Was ist PHP Web Scraping?<\/h2>\n<p>PHP Web Scraping ist der Prozess, bei dem Sie mit PHP-Code Daten aus Websites extrahieren. Nicht jede Website stellt Ihnen eine API zur Verf\u00fcgung, wie z.B. Twitter, so dass in vielen F\u00e4llen die einzige M\u00f6glichkeit, die ben\u00f6tigten Informationen zu erhalten, darin besteht, die Seite abzurufen und den HTML-Code selbst zu parsen. <\/p>\n<p>PHP ist f\u00fcr diesen Zweck sehr sinnvoll, wenn Sie es bereits t\u00e4glich verwenden. Sie k\u00f6nnen die gescrapten Daten direkt in ein bestehendes Backend einf\u00fcgen, sie in MySQL speichern oder den Scraper \u00fcber einen Cron-Job laufen lassen, ohne eine weitere Sprache in den Stack einzuf\u00fchren. <\/p>\n<p>Das Hauptproblem ist nicht, ob PHP scrapen kann. Das kann es auf jeden Fall. Die eigentliche Frage ist, wie gut Ihr Scraper mit blockierten Anfragen, standortbezogenen Einschr\u00e4nkungen und aggressiven CAPTCHAs umgehen kann.  <\/p>\n<p>Das ist genau der Grund, warum ich PHP mit Floppydata Web Unlocker kombiniere, um die L\u00fccke zu schlie\u00dfen.<\/p>\n<h2>Wissenswerte PHP Web Scraping Bibliotheken (2026)<\/h2>\n<p>PHP verf\u00fcgt \u00fcber eine Vielzahl von Scraping-Bibliotheken, aber ehrlich gesagt habe ich mich auf einige wenige beschr\u00e4nkt, die ich tats\u00e4chlich benutze. Hier ist ein kurzer Blick auf sie. <\/p>\n<ul>\n<li><strong>Guzzle:<\/strong> Ein solider HTTP-Client, der POST-Anfragen, JSON-Payloads, Weiterleitungen und Header sauber verarbeitet. Wir werden ihn in diesem Tutorial verwenden, um mit der Web Unlocker API zu kommunizieren. <\/li>\n<li><strong>Symfony DomCrawler:<\/strong> Damit k\u00f6nnen Sie mit CSS-Selektoren oder XPath durch HTML und XML navigieren. In Kombination mit der Komponente symfony\/css-selector erhalten Sie eine jQuery-\u00e4hnliche Filterung, die zuverl\u00e4ssig mit unordentlichem HTML funktioniert. Die Komponente ist eigenst\u00e4ndig, so dass Sie den Rest von Symfony nicht ben\u00f6tigen.  <\/li>\n<li><strong>Symfony HttpBrowser:<\/strong> Dies ist der moderne Ersatz f\u00fcr die inzwischen veraltete Goutte-Bibliothek. Sie baut auf BrowserKit und DomCrawler auf und erm\u00f6glicht es Ihnen, Klicks, Formulareingaben und Weiterleitungsketten zu simulieren. Ideal, wenn sich Ihre Scraping-Logik \u00fcber mehrere Seiten erstreckt.  <\/li>\n<li><strong>DiDOM:<\/strong> DiDOM ist ein schneller, abh\u00e4ngigkeitsfreier Parser mit einer jQuery-\u00e4hnlichen API. Perfekt f\u00fcr kleinere Skripte, bei denen Sie die Einbindung von Symfony-Komponenten vermeiden m\u00f6chten. <\/li>\n<li><strong>Symfony Panther:<\/strong> Steuert einen echten Chrome- oder Chromium-Browser \u00fcber WebDriver. Sie greifen zu diesem Tool, wenn eine Website alles in JavaScript rendert (React, Vue, umfangreiche SPAs) und eine einfache HTTP-Anfrage eine leere Shell zur\u00fcckgibt. Er ist schwerer, daher verwende ich ihn nur, wenn nichts anderes funktioniert.  <\/li>\n<\/ul>\n<p>Goutte war fr\u00fcher eine g\u00e4ngige Empfehlung, ist aber inzwischen veraltet, so dass ich nicht empfehlen w\u00fcrde, ein neues Projekt darauf aufzubauen.<\/p>\n<p>F\u00fcr diese Anleitung ist Guzzle plus Symfony DomCrawler ausreichend. Da der Web Unlocker bereits JavaScript ausf\u00fchrt und das endg\u00fcltige gerenderte HTML zur\u00fcckgibt, brauchen wir keinen Headless-Browser auf unserer Seite laufen zu lassen. <\/p>\n<h2>Voraussetzungen<\/h2>\n<p>Bevor Sie irgendeinen Code schreiben, sollten Sie sicherstellen, dass Sie die folgenden vier Dinge beachtet haben. Wenn Sie noch nie ein PHP-Projekt von Grund auf eingerichtet haben, keine Sorge, ich werde Sie durch jeden Schritt begleiten. <\/p>\n<h3>1. PHP 8.2 oder neuere Version<\/h3>\n<p>Auf vielen Mac- und Linux-Systemen ist PHP bereits vorinstalliert, aber es schadet nie, dies zu \u00fcberpr\u00fcfen. \u00d6ffnen Sie Ihr Terminal und \u00fcberpr\u00fcfen Sie Ihre PHP-Version: <\/p>\n<div style=\"margin: 18px 0 26px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 16px 18px; margin: 0; font-size: 14px; line-height: 1.7;\"><code><span style=\"color: #9333ea;\">php<\/span> <span style=\"color: #16a34a;\">-v<\/span><\/code><\/pre>\n<\/div>\n<p>Wenn PHP bereits installiert ist, sollten Sie eine Versionsnummer sehen. Verwenden Sie f\u00fcr dieses Tutorial PHP 8.2 oder eine neuere Version. Das ist die sicherste Ausgangsbasis f\u00fcr die Versionen der Abh\u00e4ngigkeiten, die wir installieren werden.  <\/p>\n<p>Wenn PHP fehlt, folgen Sie den Anweisungen, um es zu installieren:<\/p>\n<div style=\"margin: 18px 0 26px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 16px 18px; margin: 0; font-size: 14px; line-height: 1.7;\"><code><span style=\"color: #64748b;\"># Windows (Chocolatey, run PowerShell as Administrator)<\/span>\n<span style=\"color: #9333ea;\">choco<\/span> <span style=\"color: #16a34a;\">install<\/span> php\n\n<span style=\"color: #64748b;\"># macOS (Homebrew)<\/span>\n<span style=\"color: #9333ea;\">brew<\/span> <span style=\"color: #16a34a;\">install<\/span> php<\/code><\/pre>\n<\/div>\n<p>F\u00fchren Sie nach der Installation erneut <code>php -v<\/code> aus, um die Version zu best\u00e4tigen. Auf Homebrew ben\u00f6tigen Sie f\u00fcr dieses Tutorial keine separaten php-curl oder php-xml Pakete. Diese Erweiterungen sind bereits in der Hauptinstallation von PHP enthalten.  <\/p>\n<h3>2. Komponist<\/h3>\n<p>Composer ist der Standard-Paketmanager f\u00fcr PHP. Er ist im Grunde das \u00c4quivalent zu npm oder pip f\u00fcr PHP-Projekte. Wir werden ihn verwenden, um Guzzle und die Symfony-Parser-Pakete zu installieren.  <\/p>\n<p>Pr\u00fcfen Sie zun\u00e4chst, ob es bereits verf\u00fcgbar ist:<\/p>\n<div style=\"margin: 18px 0 26px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 16px 18px; margin: 0; font-size: 14px; line-height: 1.7;\"><code><span style=\"color: #9333ea;\">composer<\/span> <span style=\"color: #16a34a;\">--version<\/span><\/code><\/pre>\n<\/div>\n<p>Wenn Composer noch nicht installiert ist, verwenden Sie:<\/p>\n<div style=\"margin: 18px 0 26px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 16px 18px; margin: 0; font-size: 14px; line-height: 1.7;\"><code><span style=\"color: #64748b;\"># Windows (Chocolatey, as Administrator)<\/span>\n<span style=\"color: #9333ea;\">choco<\/span> <span style=\"color: #16a34a;\">install<\/span> composer\n\n<span style=\"color: #64748b;\"># macOS (Homebrew)<\/span>\n<span style=\"color: #9333ea;\">brew<\/span> <span style=\"color: #16a34a;\">install<\/span> composer<\/code><\/pre>\n<\/div>\n<p>Sobald dies geschehen ist, sollte <code>composer --version<\/code> eine Versionsnummer ausgeben, und Sie k\u00f6nnen das Projekt erstellen.<\/p>\n<h3>3. Ein Floppydata-Konto<\/h3>\n<p>Erstellen Sie ein <a href=\"http:\/\/app.floppydata.com\">Floppydata-Konto<\/a> und kopieren Sie Ihren API-Schl\u00fcssel aus dem Dashboard. Jedes neue Konto erh\u00e4lt 5 kostenlose Scrapes f\u00fcr den Web Unlocker. <\/p>\n<p><img fetchpriority=\"high\" decoding=\"async\" class=\"alignnone size-full wp-image-44186\" src=\"https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image5-2.png\" alt=\"  Floppydata-Konto  \" width=\"1999\" height=\"764\" srcset=\"https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image5-2.png 1999w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image5-2-300x115.png 300w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image5-2-1024x391.png 1024w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image5-2-768x294.png 768w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image5-2-1536x587.png 1536w\" sizes=\"(max-width: 1999px) 100vw, 1999px\" \/><\/p>\n<p>Nachdem Sie sich in Ihrem Dashboard angemeldet haben, gehen Sie im Web Unlocker zu <strong>API-Schl\u00fcssel verwalten<\/strong> und generieren Sie einen API-Schl\u00fcssel. Kopieren Sie ihn sofort und bewahren Sie ihn an einem sicheren Ort auf. <\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-44195\" src=\"https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image6-2.png\" alt=\"API-Schl\u00fcssel verwalten\" width=\"1999\" height=\"680\" srcset=\"https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image6-2.png 1999w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image6-2-300x102.png 300w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image6-2-1024x348.png 1024w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image6-2-768x261.png 768w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image6-2-1536x523.png 1536w\" sizes=\"(max-width: 1999px) 100vw, 1999px\" \/><\/p>\n<p>Sie werden diesen Schl\u00fcssel im X-Api-Key-Header jeder Web Unlocker-Anfrage verwenden. Wir werden ihn in ein paar Minuten in unseren Code einf\u00fcgen. <\/p>\n<h3>4. Projektverzeichnis und Abh\u00e4ngigkeiten<\/h3>\n<p>Lassen Sie uns nun den Ordner erstellen, in dem sich unser Scraper befinden wird, und die PHP-Bibliotheken installieren, die wir ben\u00f6tigen. In Ihrem Terminal: <\/p>\n<div style=\"margin: 18px 0 26px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 16px 18px; margin: 0; font-size: 14px; line-height: 1.7;\"><code><span style=\"color: #9333ea;\">mkdir<\/span> php-scrape-countries\n<span style=\"color: #9333ea;\">cd<\/span> php-scrape-countries<\/code><\/pre>\n<\/div>\n<p>Initialisieren Sie ein neues Composer-Projekt:<\/p>\n<div style=\"margin: 18px 0 26px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 16px 18px; margin: 0; font-size: 14px; line-height: 1.7;\"><code><span style=\"color: #9333ea;\">composer<\/span> <span style=\"color: #16a34a;\">init<\/span> <span style=\"color: #16a34a;\">--name=<\/span><span style=\"color: #dc2626;\">\"myname\/country-scraper\"<\/span> <span style=\"color: #16a34a;\">--require=<\/span><span style=\"color: #dc2626;\">\"php:^8.2\"<\/span> <span style=\"color: #16a34a;\">--no-interaction<\/span><\/code><\/pre>\n<\/div>\n<p>Installieren Sie nun die Pakete, die wir ben\u00f6tigen:<\/p>\n<div style=\"margin: 18px 0 26px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 16px 18px; margin: 0; font-size: 14px; line-height: 1.7;\"><code><span style=\"color: #9333ea;\">composer<\/span> <span style=\"color: #16a34a;\">require<\/span> guzzlehttp\/guzzle symfony\/dom-crawler:^7.4 symfony\/css-selector:^7.4<\/code><\/pre>\n<\/div>\n<p>Composer l\u00e4dt alle drei Bibliotheken und ihre Abh\u00e4ngigkeiten in einen vendor\/-Ordner herunter und erstellt eine composer.json-Datei, die genau festh\u00e4lt, welche Versionen Sie verwenden.<\/p>\n<p>Von nun an kann jede PHP-Datei im Projekt die Abh\u00e4ngigkeiten mit laden:<\/p>\n<div style=\"margin: 18px 0 26px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 16px 18px; margin: 0; font-size: 14px; line-height: 1.7;\"><code><span style=\"color: #ea580c;\">require_once<\/span> <span style=\"color: #6366f1;\">__DIR__<\/span> . <span style=\"color: #dc2626;\">'\/vendor\/autoload.php'<\/span>;<\/code><\/pre>\n<\/div>\n<p>Damit ist die Einrichtung abgeschlossen und wir k\u00f6nnen mit dem Scraper selbst weitermachen.<\/p>\n<h2>Wie man mit PHP und Floppydata Web Unlocker Daten ausliest<\/h2>\n<h3>Schritt #1: Testen Sie Ihr Ziel<\/h3>\n<p>Ich schreibe Code nie gerne blind, damit ich genau wei\u00df, welche Selektoren und Datenstrukturen mich erwarten. F\u00fcr dieses Beispiel verwende ich <a href=\"https:\/\/www.scrapethissite.com\/pages\/simple\/\" rel=\"nofollow noopener\" target=\"_blank\">scrapethissite<\/a>, eine Demo-Website f\u00fcr das Scraping von Daten. Sie enth\u00e4lt eine Liste aller 250 L\u00e4nder mit ihrer Hauptstadt, Bev\u00f6lkerung und Fl\u00e4che.  <\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-44177\" src=\"https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image4-2.png\" alt=\"Schrotthissit\" width=\"1999\" height=\"1184\" srcset=\"https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image4-2.png 1999w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image4-2-300x178.png 300w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image4-2-1024x607.png 1024w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image4-2-768x455.png 768w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image4-2-1536x910.png 1536w\" sizes=\"(max-width: 1999px) 100vw, 1999px\" \/><\/p>\n<p>Besuchen Sie den <a href=\"http:\/\/app.floppydata.com\/tools\/scrape\">Floppydata Web Unlocker Playground<\/a>, um mitzumachen. Dieses No-Code-Tool ist direkt von Ihrem Dashboard aus verf\u00fcgbar und erm\u00f6glicht es Ihnen, den genauen HTML-Code zu sehen, den die API zur\u00fcckgibt, ohne ein Projekt zu erstellen. <\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-44150\" src=\"https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image1-2.png\" alt=\"floppydata\" width=\"1057\" height=\"419\" srcset=\"https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image1-2.png 1057w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image1-2-300x119.png 300w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image1-2-1024x406.png 1024w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image1-2-768x304.png 768w\" sizes=\"(max-width: 1057px) 100vw, 1057px\" \/><\/p>\n<p>Geben Sie nun die URL ein und klicken Sie auf Scrape. Innerhalb von Sekunden sehen Sie den vollst\u00e4ndigen HTML-Code in der Ausgabevorschau. Das ist genau derselbe HTML-Code, den Ihr PHP-Skript in ein paar Schritten erhalten wird.  <\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-44168 size-full\" src=\"https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image3-2.png\" alt=\"PHP-Skript\" width=\"1010\" height=\"499\" srcset=\"https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image3-2.png 1010w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image3-2-300x148.png 300w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image3-2-768x379.png 768w\" sizes=\"(max-width: 1010px) 100vw, 1010px\" \/><\/p>\n<p>Wenn die Daten richtig aussehen, k\u00f6nnen Sie den HTML-Code kopieren oder die Antwort herunterladen. Aber in unserem Fall lassen wir das PHP-Skript dies automatisch tun. <\/p>\n<h3>Schritt #2: Senden Sie Ihre erste Web Unlocker Anfrage mit Guzzle<\/h3>\n<p>Der Kern des gesamten Arbeitsablaufs ist eine POST-Anfrage an den Endpunkt von Floppydata:<\/p>\n<div style=\"margin: 18px 0 26px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 16px 18px; margin: 0; font-size: 14px; line-height: 1.7;\"><code>https:\/\/client-api.floppy.host\/v1\/webUnlocker<\/code><\/pre>\n<\/div>\n<p>Zu diesem Zweck erstellen wir zun\u00e4chst einen Guzzle-Client und bereiten die Anfragekonfiguration vor. Dann senden wir die Anfrage und bearbeiten die Antwort. <\/p>\n<p>Erstellen Sie eine Datei namens <strong>scrape.php<\/strong> und beginnen Sie mit dem Grundger\u00fcst:<\/p>\n<div style=\"margin: 20px 0 28px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 18px; margin: 0; font-size: 13px; line-height: 1.6; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;\"><code><span style=\"color: #9333ea;\">&lt;?php<\/span>\n<span style=\"color: #64748b;\">\/\/ scrape.php<\/span>\n\n<span style=\"color: #ea580c;\">require_once<\/span> <span style=\"color: #6366f1;\">__DIR__<\/span> . <span style=\"color: #dc2626;\">'\/vendor\/autoload.php'<\/span>;\n\n<span style=\"color: #ea580c;\">use<\/span> GuzzleHttp\\Client;\n\n<span style=\"color: #0891b2;\">$apiKey<\/span> = <span style=\"color: #dc2626;\">'YOUR_API_KEY'<\/span>;   <span style=\"color: #64748b;\">\/\/ Replace with your real key<\/span>\n<span style=\"color: #0891b2;\">$targetUrl<\/span> = <span style=\"color: #dc2626;\">'https:\/\/www.scrapethissite.com\/pages\/simple\/'<\/span>;\n\n<span style=\"color: #0891b2;\">$client<\/span> = <span style=\"color: #ea580c;\">new<\/span> <span style=\"color: #9333ea;\">Client<\/span>([\n    <span style=\"color: #dc2626;\">'base_uri'<\/span> =&gt; <span style=\"color: #dc2626;\">'https:\/\/client-api.floppy.host'<\/span>,\n    <span style=\"color: #dc2626;\">'timeout'<\/span> =&gt; <span style=\"color: #16a34a;\">60<\/span>,\n]);<\/code><\/pre>\n<\/div>\n<p>Ersetzen Sie YOUR_API_KEY durch Ihren echten Schl\u00fcssel. Jetzt erstellen wir den eigentlichen POST-Aufruf. Wir senden JSON an den API-Endpunkt, f\u00fcgen den API-Schl\u00fcssel in die Kopfzeilen ein und \u00fcbergeben die Ziel-URL sowie ein paar Argumente im Textk\u00f6rper:  <\/p>\n<div style=\"margin: 20px 0 28px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 18px; margin: 0; font-size: 13px; line-height: 1.6; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;\"><code><span style=\"color: #0891b2;\">$response<\/span> = <span style=\"color: #0891b2;\">$client<\/span>-&gt;<span style=\"color: #9333ea;\">post<\/span>(<span style=\"color: #dc2626;\">'\/v1\/webUnlocker'<\/span>, [\n    <span style=\"color: #dc2626;\">'headers'<\/span> =&gt; [\n        <span style=\"color: #dc2626;\">'Content-Type'<\/span> =&gt; <span style=\"color: #dc2626;\">'application\/json'<\/span>,\n        <span style=\"color: #dc2626;\">'X-Api-Key'<\/span> =&gt; <span style=\"color: #0891b2;\">$apiKey<\/span>,\n    ],\n    <span style=\"color: #dc2626;\">'json'<\/span> =&gt; [\n        <span style=\"color: #dc2626;\">'url'<\/span> =&gt; <span style=\"color: #0891b2;\">$targetUrl<\/span>,\n        <span style=\"color: #dc2626;\">'country'<\/span> =&gt; <span style=\"color: #dc2626;\">'US'<\/span>,\n        <span style=\"color: #dc2626;\">'city'<\/span> =&gt; <span style=\"color: #dc2626;\">'New York'<\/span>,\n        <span style=\"color: #dc2626;\">'difficulty'<\/span> =&gt; <span style=\"color: #dc2626;\">'low'<\/span>,\n        <span style=\"color: #dc2626;\">'expiration'<\/span> =&gt; <span style=\"color: #16a34a;\">0<\/span>,\n    ],\n]);\n\n<span style=\"color: #0891b2;\">$payload<\/span> = <span style=\"color: #9333ea;\">json_decode<\/span>((<span style=\"color: #ea580c;\">string<\/span>) <span style=\"color: #0891b2;\">$response<\/span>-&gt;<span style=\"color: #9333ea;\">getBody<\/span>(), <span style=\"color: #6366f1;\">true<\/span>);\n<span style=\"color: #0891b2;\">$html<\/span> = <span style=\"color: #0891b2;\">$payload<\/span>[<span style=\"color: #dc2626;\">'html'<\/span>] ?? <span style=\"color: #dc2626;\">''<\/span>;\n<span style=\"color: #ea580c;\">echo<\/span> <span style=\"color: #dc2626;\">\"HTML received! Length: \"<\/span> . <span style=\"color: #9333ea;\">strlen<\/span>(<span style=\"color: #0891b2;\">$html<\/span>) . <span style=\"color: #dc2626;\">\" characters\\n\"<\/span>;<\/code><\/pre>\n<\/div>\n<p>Die Felder <strong>Land<\/strong> und <strong>Stadt<\/strong> teilen dem Web Unlocker mit, \u00fcber welchen geografischen Standort die Anfrage geleitet werden soll. Das Feld <strong>Schwierigkeit<\/strong> steuert, wie aggressiv der Unlocker mit Anti-Bot-Schutzma\u00dfnahmen umgeht. Ich verwende hier den Wert <strong>niedrig<\/strong>, weil unser Sandbox-Ziel keinen Schutz hat.  <\/p>\n<p>F\u00fcr gesch\u00fctzte Ziele hinter Cloudflare oder DataDome setzen Sie diesen Wert auf <strong>mittel<\/strong>, damit der Unlocker st\u00e4rkere Fingerabdr\u00fccke und CAPTCHA-L\u00f6sungslogik anwendet.<\/p>\n<p>Beachten Sie, dass der Web Unlocker das rohe HTML in einem JSON-Objekt zur\u00fcckgibt. Das bedeutet, dass Sie das JSON dekodieren und das eigentliche Seitenmarkup aus dem <strong>HTML-Feld<\/strong> ziehen m\u00fcssen.<\/p>\n<p>Wenn Sie dies vergessen und den gesamten Antwortk\u00f6rper als HTML behandeln, wird Ihr Parser nicht funktionieren. Damit ist die Anfrageseite fertig und wir k\u00f6nnen mit dem Parsen fortfahren. <\/p>\n<h3>Schritt #3: \u00dcberpr\u00fcfen Sie die Seitenstruktur<\/h3>\n<p>Nachdem die Anfrage erfolgreich war, besteht die n\u00e4chste Aufgabe darin, die Seitenstruktur zu untersuchen und die wiederholten Elemente zu finden, die die gew\u00fcnschten Daten enthalten. Jedes Land auf der Seite folgt genau diesem HTML-Muster: <\/p>\n<div style=\"margin: 20px 0 28px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 18px; margin: 0; font-size: 13px; line-height: 1.6; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;\"><code><span style=\"color: #9333ea;\">&lt;div<\/span> <span style=\"color: #16a34a;\">class=<\/span><span style=\"color: #dc2626;\">\"col-md-4 country\"<\/span><span style=\"color: #9333ea;\">&gt;<\/span>\n    <span style=\"color: #9333ea;\">&lt;h3<\/span> <span style=\"color: #16a34a;\">class=<\/span><span style=\"color: #dc2626;\">\"country-name\"<\/span><span style=\"color: #9333ea;\">&gt;<\/span>\n        <span style=\"color: #9333ea;\">&lt;i<\/span> <span style=\"color: #16a34a;\">class=<\/span><span style=\"color: #dc2626;\">\"flag-icon flag-icon-ad\"<\/span><span style=\"color: #9333ea;\">&gt;&lt;\/i&gt;<\/span>\n        Andorra\n    <span style=\"color: #9333ea;\">&lt;\/h3&gt;<\/span>\n    <span style=\"color: #9333ea;\">&lt;div<\/span> <span style=\"color: #16a34a;\">class=<\/span><span style=\"color: #dc2626;\">\"country-info\"<\/span><span style=\"color: #9333ea;\">&gt;<\/span>\n        <span style=\"color: #9333ea;\">&lt;strong&gt;<\/span>Capital:<span style=\"color: #9333ea;\">&lt;\/strong&gt;<\/span> <span style=\"color: #9333ea;\">&lt;span<\/span> <span style=\"color: #16a34a;\">class=<\/span><span style=\"color: #dc2626;\">\"country-capital\"<\/span><span style=\"color: #9333ea;\">&gt;<\/span>Andorra la Vella<span style=\"color: #9333ea;\">&lt;\/span&gt;&lt;br&gt;<\/span>\n        <span style=\"color: #9333ea;\">&lt;strong&gt;<\/span>Population:<span style=\"color: #9333ea;\">&lt;\/strong&gt;<\/span> <span style=\"color: #9333ea;\">&lt;span<\/span> <span style=\"color: #16a34a;\">class=<\/span><span style=\"color: #dc2626;\">\"country-population\"<\/span><span style=\"color: #9333ea;\">&gt;<\/span>84000<span style=\"color: #9333ea;\">&lt;\/span&gt;&lt;br&gt;<\/span>\n        <span style=\"color: #9333ea;\">&lt;strong&gt;<\/span>Area (km<span style=\"color: #9333ea;\">&lt;sup&gt;<\/span>2<span style=\"color: #9333ea;\">&lt;\/sup&gt;<\/span>):<span style=\"color: #9333ea;\">&lt;\/strong&gt;<\/span> <span style=\"color: #9333ea;\">&lt;span<\/span> <span style=\"color: #16a34a;\">class=<\/span><span style=\"color: #dc2626;\">\"country-area\"<\/span><span style=\"color: #9333ea;\">&gt;<\/span>468.0<span style=\"color: #9333ea;\">&lt;\/span&gt;&lt;br&gt;<\/span>\n    <span style=\"color: #9333ea;\">&lt;\/div&gt;<\/span>\n<span style=\"color: #9333ea;\">&lt;\/div&gt;<\/span><\/code><\/pre>\n<\/div>\n<p>Diese wiederholte Struktur macht diese Seite so sch\u00f6n vorhersehbar. Jede L\u00e4nderkarte verwendet die gleichen Klassennamen: <strong>.country<\/strong> f\u00fcr den Wrapper, <strong>.country-name<\/strong> f\u00fcr die \u00dcberschrift und<strong>.country-capital<\/strong>, <strong>.country-population<\/strong> und <strong>.country-area<\/strong> f\u00fcr die Datenfelder innerhalb von <strong>.country-info<\/strong>. <\/p>\n<h3>Schritt #4: Parsen von Daten mit Symfony DomCrawler<\/h3>\n<p>Da die Klassen in allen 250 Eintr\u00e4gen konsistent sind, k\u00f6nnen wir jedes <strong>.country-Element<\/strong> in einer Schleife durchlaufen und die Werte aus den untergeordneten Selektoren ziehen. Aber zuerst f\u00fcgen wir eine kleine Hilfsfunktion hinzu, die uns hilft, den extrahierten Text zu bereinigen: <\/p>\n<div style=\"margin: 20px 0 28px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 18px; margin: 0; font-size: 13px; line-height: 1.6; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;\"><code><span style=\"color: #ea580c;\">use<\/span> Symfony\\Component\\DomCrawler\\Crawler;\n\n<span style=\"color: #ea580c;\">function<\/span> <span style=\"color: #9333ea;\">normalizeText<\/span>(<span style=\"color: #ea580c;\">string<\/span> <span style=\"color: #0891b2;\">$text<\/span>): <span style=\"color: #ea580c;\">string<\/span>\n{\n    <span style=\"color: #ea580c;\">return<\/span> <span style=\"color: #9333ea;\">preg_replace<\/span>(<span style=\"color: #dc2626;\">'\/\\s+\/'<\/span>, <span style=\"color: #dc2626;\">' '<\/span>, <span style=\"color: #9333ea;\">trim<\/span>(<span style=\"color: #0891b2;\">$text<\/span>)) ?? <span style=\"color: #9333ea;\">trim<\/span>(<span style=\"color: #0891b2;\">$text<\/span>);\n}<\/code><\/pre>\n<\/div>\n<p>Wenn Sie sich das rohe HTML ansehen, haben die L\u00e4ndernamen zus\u00e4tzliche Leerzeichen und Zeilenumbr\u00fcche um sie herum, weil die &lt;i&gt; Flaggensymbol-Tags innerhalb der &lt;h3&gt; sitzen.<\/p>\n<p>Die Funktion <em>normalizeText()<\/em> entfernt f\u00fchrende und nachgestellte Leerzeichen und verwendet dann eine Regex, um alle verbleibenden Leerzeichen oder Zeilenumbr\u00fcche zu k\u00fcrzen, so dass Namen wie Andorra oder St. John&#8217;s sauber wiedergegeben werden, anstatt Leerzeichen aus dem HTML zu \u00fcbernehmen.<\/p>\n<p>Nachdem der Helper fertig ist, erstellen wir eine Crawler-Instanz und ziehen eine Schleife \u00fcber jede L\u00e4nderkarte:<\/p>\n<div style=\"margin: 20px 0 28px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 18px; margin: 0; font-size: 13px; line-height: 1.6; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;\"><code><span style=\"color: #0891b2;\">$crawler<\/span> = <span style=\"color: #ea580c;\">new<\/span> <span style=\"color: #9333ea;\">Crawler<\/span>(<span style=\"color: #0891b2;\">$html<\/span>);\n<span style=\"color: #0891b2;\">$countries<\/span> = [];\n\n<span style=\"color: #0891b2;\">$crawler<\/span>-&gt;<span style=\"color: #9333ea;\">filter<\/span>(<span style=\"color: #dc2626;\">'.country'<\/span>)-&gt;<span style=\"color: #9333ea;\">each<\/span>(<span style=\"color: #ea580c;\">function<\/span> (<span style=\"color: #9333ea;\">Crawler<\/span> <span style=\"color: #0891b2;\">$node<\/span>) <span style=\"color: #ea580c;\">use<\/span> (&<span style=\"color: #0891b2;\">$countries<\/span>): <span style=\"color: #ea580c;\">void<\/span> {\n    <span style=\"color: #0891b2;\">$countries<\/span>[] = [\n        <span style=\"color: #dc2626;\">'name'<\/span> =&gt; <span style=\"color: #9333ea;\">normalizeText<\/span>(<span style=\"color: #0891b2;\">$node<\/span>-&gt;<span style=\"color: #9333ea;\">filter<\/span>(<span style=\"color: #dc2626;\">'.country-name'<\/span>)-&gt;<span style=\"color: #9333ea;\">text<\/span>()),\n        <span style=\"color: #dc2626;\">'capital'<\/span> =&gt; <span style=\"color: #9333ea;\">normalizeText<\/span>(<span style=\"color: #0891b2;\">$node<\/span>-&gt;<span style=\"color: #9333ea;\">filter<\/span>(<span style=\"color: #dc2626;\">'.country-capital'<\/span>)-&gt;<span style=\"color: #9333ea;\">text<\/span>()),\n        <span style=\"color: #dc2626;\">'population'<\/span> =&gt; <span style=\"color: #9333ea;\">normalizeText<\/span>(<span style=\"color: #0891b2;\">$node<\/span>-&gt;<span style=\"color: #9333ea;\">filter<\/span>(<span style=\"color: #dc2626;\">'.country-population'<\/span>)-&gt;<span style=\"color: #9333ea;\">text<\/span>()),\n        <span style=\"color: #dc2626;\">'area'<\/span> =&gt; <span style=\"color: #9333ea;\">normalizeText<\/span>(<span style=\"color: #0891b2;\">$node<\/span>-&gt;<span style=\"color: #9333ea;\">filter<\/span>(<span style=\"color: #dc2626;\">'.country-area'<\/span>)-&gt;<span style=\"color: #9333ea;\">text<\/span>()),\n    ];\n});\n\n<span style=\"color: #ea580c;\">echo<\/span> <span style=\"color: #dc2626;\">'Parsed '<\/span> . <span style=\"color: #9333ea;\">count<\/span>(<span style=\"color: #0891b2;\">$countries<\/span>) . <span style=\"color: #dc2626;\">\" countries\\n\"<\/span>;<\/code><\/pre>\n<\/div>\n<p>DomCrawler gibt uns eine saubere M\u00f6glichkeit, uns mit CSS-Selektoren durch den HTML-Code zu bewegen. Wir beginnen damit, den HTML-Code in ein Crawler-Objekt zu verpacken und filtern dann jeden .country-Block auf der Seite heraus. Innerhalb jedes Blocks erfassen wir den Namen, die Hauptstadt, die Bev\u00f6lkerung und das Gebiet.<\/p>\n<p>Wenn Sie das Skript jetzt ausf\u00fchren, sollten Sie auf dem Terminal die Meldung &#8222;Parsed 250 countries&#8220; sehen.<\/p>\n<h3>Schritt #5: Exportieren Sie die Ergebnisse in CSV und JSON<\/h3>\n<p>Sobald der Parser Ihnen ein Array <strong>$countries<\/strong> liefert, wird der Export der Daten sehr einfach.<\/p>\n<div style=\"margin: 20px 0 28px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 18px; margin: 0; font-size: 13px; line-height: 1.6; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;\"><code><span style=\"color: #0891b2;\">$csvHandle<\/span> = <span style=\"color: #9333ea;\">fopen<\/span>(<span style=\"color: #dc2626;\">'countries.csv'<\/span>, <span style=\"color: #dc2626;\">'w'<\/span>);\n\n<span style=\"color: #9333ea;\">fputcsv<\/span>(<span style=\"color: #0891b2;\">$csvHandle<\/span>, [<span style=\"color: #dc2626;\">'Country'<\/span>, <span style=\"color: #dc2626;\">'Capital'<\/span>, <span style=\"color: #dc2626;\">'Population'<\/span>, <span style=\"color: #dc2626;\">'Area (km2)'<\/span>], <span style=\"color: #dc2626;\">','<\/span>, <span style=\"color: #dc2626;\">'\"'<\/span>, <span style=\"color: #dc2626;\">''<\/span>);\n\n<span style=\"color: #ea580c;\">foreach<\/span> (<span style=\"color: #0891b2;\">$countries<\/span> <span style=\"color: #ea580c;\">as<\/span> <span style=\"color: #0891b2;\">$country<\/span>) {\n    <span style=\"color: #9333ea;\">fputcsv<\/span>(<span style=\"color: #0891b2;\">$csvHandle<\/span>, [\n        <span style=\"color: #0891b2;\">$country<\/span>[<span style=\"color: #dc2626;\">'name'<\/span>],\n        <span style=\"color: #0891b2;\">$country<\/span>[<span style=\"color: #dc2626;\">'capital'<\/span>],\n        <span style=\"color: #0891b2;\">$country<\/span>[<span style=\"color: #dc2626;\">'population'<\/span>],\n        <span style=\"color: #0891b2;\">$country<\/span>[<span style=\"color: #dc2626;\">'area'<\/span>],\n    ], <span style=\"color: #dc2626;\">','<\/span>, <span style=\"color: #dc2626;\">'\"'<\/span>, <span style=\"color: #dc2626;\">''<\/span>);\n}\n\n<span style=\"color: #9333ea;\">fclose<\/span>(<span style=\"color: #0891b2;\">$csvHandle<\/span>);\n\n<span style=\"color: #9333ea;\">file_put_contents<\/span>(<span style=\"color: #dc2626;\">'countries.json'<\/span>, <span style=\"color: #9333ea;\">json_encode<\/span>(<span style=\"color: #0891b2;\">$countries<\/span>, <span style=\"color: #6366f1;\">JSON_PRETTY_PRINT<\/span> | <span style=\"color: #6366f1;\">JSON_UNESCAPED_SLASHES<\/span>));<\/code><\/pre>\n<\/div>\n<p>Der CSV-Export ist n\u00fctzlich, da er dem Leser eine Datei liefert, die er sofort in Excel, Google Sheets oder einem anderen Tabellenkalkulationsprogramm \u00f6ffnen kann. Der JSON-Export ist ebenso praktisch, wenn Sie die gescrapten Daten sp\u00e4ter in ein anderes PHP-Skript oder eine API einspeisen m\u00f6chten. <\/p>\n<p>Eine kleine Aktualisierung ist das explizite Escape-Argument in <strong>fputcsv()<\/strong>. Bei neueren PHP-Versionen werden dadurch Warnungen vor der Veralterung vermieden und das Beispiel bleibt sauber, wenn die Leser es vom Terminal aus ausf\u00fchren. <\/p>\n<h3>Schritt #6: Stellen Sie alles in einem Skript zusammen<\/h3>\n<p>Da nun jeder Teil f\u00fcr sich funktioniert, finden Sie hier das vollst\u00e4ndige Skript:<\/p>\n<div style=\"margin: 20px 0 28px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 18px; margin: 0; font-size: 13px; line-height: 1.6; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;\"><code><span style=\"color: #9333ea;\">&lt;?php<\/span>\n\n<span style=\"color: #9333ea;\">declare<\/span>(<span style=\"color: #6366f1;\">strict_types<\/span>=<span style=\"color: #16a34a;\">1<\/span>);\n\n<span style=\"color: #ea580c;\">require_once<\/span> <span style=\"color: #6366f1;\">__DIR__<\/span> . <span style=\"color: #dc2626;\">'\/vendor\/autoload.php'<\/span>;\n\n<span style=\"color: #ea580c;\">use<\/span> GuzzleHttp\\Client;\n<span style=\"color: #ea580c;\">use<\/span> Symfony\\Component\\DomCrawler\\Crawler;\n\n<span style=\"color: #ea580c;\">function<\/span> <span style=\"color: #9333ea;\">normalizeText<\/span>(<span style=\"color: #ea580c;\">string<\/span> <span style=\"color: #0891b2;\">$text<\/span>): <span style=\"color: #ea580c;\">string<\/span>\n{\n    <span style=\"color: #ea580c;\">return<\/span> <span style=\"color: #9333ea;\">preg_replace<\/span>(<span style=\"color: #dc2626;\">'\/\\s+\/'<\/span>, <span style=\"color: #dc2626;\">' '<\/span>, <span style=\"color: #9333ea;\">trim<\/span>(<span style=\"color: #0891b2;\">$text<\/span>)) ?? <span style=\"color: #9333ea;\">trim<\/span>(<span style=\"color: #0891b2;\">$text<\/span>);\n}\n\n<span style=\"color: #0891b2;\">$apiKey<\/span> = <span style=\"color: #dc2626;\">'YOUR_API_KEY'<\/span>;\n<span style=\"color: #0891b2;\">$targetUrl<\/span> = <span style=\"color: #dc2626;\">'https:\/\/www.scrapethissite.com\/pages\/simple\/'<\/span>;\n\n<span style=\"color: #ea580c;\">if<\/span> (<span style=\"color: #0891b2;\">$apiKey<\/span> === <span style=\"color: #dc2626;\">'YOUR_API_KEY'<\/span>) {\n    <span style=\"color: #9333ea;\">fwrite<\/span>(<span style=\"color: #6366f1;\">STDERR<\/span>, <span style=\"color: #dc2626;\">\"Replace YOUR_API_KEY before running the script.\\n\"<\/span>);\n    <span style=\"color: #ea580c;\">exit<\/span>(<span style=\"color: #16a34a;\">1<\/span>);\n}\n\n<span style=\"color: #0891b2;\">$client<\/span> = <span style=\"color: #ea580c;\">new<\/span> <span style=\"color: #9333ea;\">Client<\/span>([\n    <span style=\"color: #dc2626;\">'base_uri'<\/span> =&gt; <span style=\"color: #dc2626;\">'https:\/\/client-api.floppy.host'<\/span>,\n    <span style=\"color: #dc2626;\">'timeout'<\/span> =&gt; <span style=\"color: #16a34a;\">60<\/span>,\n]);\n\n<span style=\"color: #ea580c;\">try<\/span> {\n    <span style=\"color: #0891b2;\">$response<\/span> = <span style=\"color: #0891b2;\">$client<\/span>-&gt;<span style=\"color: #9333ea;\">post<\/span>(<span style=\"color: #dc2626;\">'\/v1\/webUnlocker'<\/span>, [\n        <span style=\"color: #dc2626;\">'headers'<\/span> =&gt; [\n            <span style=\"color: #dc2626;\">'Content-Type'<\/span> =&gt; <span style=\"color: #dc2626;\">'application\/json'<\/span>,\n            <span style=\"color: #dc2626;\">'X-Api-Key'<\/span> =&gt; <span style=\"color: #0891b2;\">$apiKey<\/span>,\n        ],\n        <span style=\"color: #dc2626;\">'json'<\/span> =&gt; [\n            <span style=\"color: #dc2626;\">'url'<\/span> =&gt; <span style=\"color: #0891b2;\">$targetUrl<\/span>,\n            <span style=\"color: #dc2626;\">'country'<\/span> =&gt; <span style=\"color: #dc2626;\">'US'<\/span>,\n            <span style=\"color: #dc2626;\">'city'<\/span> =&gt; <span style=\"color: #dc2626;\">'New York'<\/span>,\n            <span style=\"color: #dc2626;\">'difficulty'<\/span> =&gt; <span style=\"color: #dc2626;\">'low'<\/span>,\n            <span style=\"color: #dc2626;\">'expiration'<\/span> =&gt; <span style=\"color: #16a34a;\">0<\/span>,\n        ],\n    ]);\n} <span style=\"color: #ea580c;\">catch<\/span> (<span style=\"color: #9333ea;\">Throwable<\/span> <span style=\"color: #0891b2;\">$e<\/span>) {\n    <span style=\"color: #9333ea;\">fwrite<\/span>(<span style=\"color: #6366f1;\">STDERR<\/span>, <span style=\"color: #dc2626;\">\"Request failed: {<\/span><span style=\"color: #0891b2;\">$e<\/span>-><span style=\"color: #9333ea;\">getMessage<\/span>()<span style=\"color: #dc2626;\">}\\n\"<\/span>);\n    <span style=\"color: #ea580c;\">exit<\/span>(<span style=\"color: #16a34a;\">1<\/span>);\n}\n\n<span style=\"color: #0891b2;\">$payload<\/span> = <span style=\"color: #9333ea;\">json_decode<\/span>((<span style=\"color: #ea580c;\">string<\/span>) <span style=\"color: #0891b2;\">$response<\/span>-&gt;<span style=\"color: #9333ea;\">getBody<\/span>(), <span style=\"color: #6366f1;\">true<\/span>);\n\n<span style=\"color: #ea580c;\">if<\/span> (!<span style=\"color: #9333ea;\">is_array<\/span>(<span style=\"color: #0891b2;\">$payload<\/span>) || !<span style=\"color: #9333ea;\">isset<\/span>(<span style=\"color: #0891b2;\">$payload<\/span>[<span style=\"color: #dc2626;\">'html'<\/span>]) || !<span style=\"color: #9333ea;\">is_string<\/span>(<span style=\"color: #0891b2;\">$payload<\/span>[<span style=\"color: #dc2626;\">'html'<\/span>])) {\n    <span style=\"color: #9333ea;\">fwrite<\/span>(<span style=\"color: #6366f1;\">STDERR<\/span>, <span style=\"color: #dc2626;\">\"Unexpected API response. Expected JSON with an html field.\\n\"<\/span>);\n    <span style=\"color: #ea580c;\">exit<\/span>(<span style=\"color: #16a34a;\">1<\/span>);\n}\n\n<span style=\"color: #0891b2;\">$crawler<\/span> = <span style=\"color: #ea580c;\">new<\/span> <span style=\"color: #9333ea;\">Crawler<\/span>(<span style=\"color: #0891b2;\">$payload<\/span>[<span style=\"color: #dc2626;\">'html'<\/span>]);\n<span style=\"color: #0891b2;\">$countries<\/span> = [];\n\n<span style=\"color: #0891b2;\">$crawler<\/span>-&gt;<span style=\"color: #9333ea;\">filter<\/span>(<span style=\"color: #dc2626;\">'.country'<\/span>)-&gt;<span style=\"color: #9333ea;\">each<\/span>(<span style=\"color: #ea580c;\">function<\/span> (<span style=\"color: #9333ea;\">Crawler<\/span> <span style=\"color: #0891b2;\">$node<\/span>) <span style=\"color: #ea580c;\">use<\/span> (&<span style=\"color: #0891b2;\">$countries<\/span>): <span style=\"color: #ea580c;\">void<\/span> {\n    <span style=\"color: #0891b2;\">$countries<\/span>[] = [\n        <span style=\"color: #dc2626;\">'name'<\/span> =&gt; <span style=\"color: #9333ea;\">normalizeText<\/span>(<span style=\"color: #0891b2;\">$node<\/span>-&gt;<span style=\"color: #9333ea;\">filter<\/span>(<span style=\"color: #dc2626;\">'.country-name'<\/span>)-&gt;<span style=\"color: #9333ea;\">text<\/span>()),\n        <span style=\"color: #dc2626;\">'capital'<\/span> =&gt; <span style=\"color: #9333ea;\">normalizeText<\/span>(<span style=\"color: #0891b2;\">$node<\/span>-&gt;<span style=\"color: #9333ea;\">filter<\/span>(<span style=\"color: #dc2626;\">'.country-capital'<\/span>)-&gt;<span style=\"color: #9333ea;\">text<\/span>()),\n        <span style=\"color: #dc2626;\">'population'<\/span> =&gt; <span style=\"color: #9333ea;\">normalizeText<\/span>(<span style=\"color: #0891b2;\">$node<\/span>-&gt;<span style=\"color: #9333ea;\">filter<\/span>(<span style=\"color: #dc2626;\">'.country-population'<\/span>)-&gt;<span style=\"color: #9333ea;\">text<\/span>()),\n        <span style=\"color: #dc2626;\">'area'<\/span> =&gt; <span style=\"color: #9333ea;\">normalizeText<\/span>(<span style=\"color: #0891b2;\">$node<\/span>-&gt;<span style=\"color: #9333ea;\">filter<\/span>(<span style=\"color: #dc2626;\">'.country-area'<\/span>)-&gt;<span style=\"color: #9333ea;\">text<\/span>()),\n    ];\n});\n\n<span style=\"color: #ea580c;\">if<\/span> (<span style=\"color: #0891b2;\">$countries<\/span> === []) {\n    <span style=\"color: #9333ea;\">fwrite<\/span>(<span style=\"color: #6366f1;\">STDERR<\/span>, <span style=\"color: #dc2626;\">\"No countries were parsed.\\n\"<\/span>);\n    <span style=\"color: #ea580c;\">exit<\/span>(<span style=\"color: #16a34a;\">1<\/span>);\n}\n\n<span style=\"color: #0891b2;\">$csvHandle<\/span> = <span style=\"color: #9333ea;\">fopen<\/span>(<span style=\"color: #6366f1;\">__DIR__<\/span> . <span style=\"color: #dc2626;\">'\/countries.csv'<\/span>, <span style=\"color: #dc2626;\">'w'<\/span>);\n\n<span style=\"color: #ea580c;\">if<\/span> (<span style=\"color: #0891b2;\">$csvHandle<\/span> === <span style=\"color: #6366f1;\">false<\/span>) {\n    <span style=\"color: #9333ea;\">fwrite<\/span>(<span style=\"color: #6366f1;\">STDERR<\/span>, <span style=\"color: #dc2626;\">\"Could not create countries.csv.\\n\"<\/span>);\n    <span style=\"color: #ea580c;\">exit<\/span>(<span style=\"color: #16a34a;\">1<\/span>);\n}\n\n<span style=\"color: #9333ea;\">fputcsv<\/span>(<span style=\"color: #0891b2;\">$csvHandle<\/span>, [<span style=\"color: #dc2626;\">'Country'<\/span>, <span style=\"color: #dc2626;\">'Capital'<\/span>, <span style=\"color: #dc2626;\">'Population'<\/span>, <span style=\"color: #dc2626;\">'Area (km2)'<\/span>], <span style=\"color: #dc2626;\">','<\/span>, <span style=\"color: #dc2626;\">'\"'<\/span>, <span style=\"color: #dc2626;\">''<\/span>);\n\n<span style=\"color: #ea580c;\">foreach<\/span> (<span style=\"color: #0891b2;\">$countries<\/span> <span style=\"color: #ea580c;\">as<\/span> <span style=\"color: #0891b2;\">$country<\/span>) {\n    <span style=\"color: #9333ea;\">fputcsv<\/span>(<span style=\"color: #0891b2;\">$csvHandle<\/span>, [\n        <span style=\"color: #0891b2;\">$country<\/span>[<span style=\"color: #dc2626;\">'name'<\/span>],\n        <span style=\"color: #0891b2;\">$country<\/span>[<span style=\"color: #dc2626;\">'capital'<\/span>],\n        <span style=\"color: #0891b2;\">$country<\/span>[<span style=\"color: #dc2626;\">'population'<\/span>],\n        <span style=\"color: #0891b2;\">$country<\/span>[<span style=\"color: #dc2626;\">'area'<\/span>],\n    ], <span style=\"color: #dc2626;\">','<\/span>, <span style=\"color: #dc2626;\">'\"'<\/span>, <span style=\"color: #dc2626;\">''<\/span>);\n}\n\n<span style=\"color: #9333ea;\">fclose<\/span>(<span style=\"color: #0891b2;\">$csvHandle<\/span>);\n\n<span style=\"color: #9333ea;\">file_put_contents<\/span>(<span style=\"color: #6366f1;\">__DIR__<\/span> . <span style=\"color: #dc2626;\">'\/countries.json'<\/span>, <span style=\"color: #9333ea;\">json_encode<\/span>(<span style=\"color: #0891b2;\">$countries<\/span>, <span style=\"color: #6366f1;\">JSON_PRETTY_PRINT<\/span> | <span style=\"color: #6366f1;\">JSON_UNESCAPED_SLASHES<\/span>));\n\n<span style=\"color: #ea580c;\">echo<\/span> <span style=\"color: #dc2626;\">'Done! Parsed '<\/span> . <span style=\"color: #9333ea;\">count<\/span>(<span style=\"color: #0891b2;\">$countries<\/span>) . <span style=\"color: #dc2626;\">\" countries.\\n\"<\/span>;\n<span style=\"color: #ea580c;\">echo<\/span> <span style=\"color: #dc2626;\">\"Saved countries.csv and countries.json\\n\"<\/span>;<\/code><\/pre>\n<\/div>\n<p>Ersetzen Sie &#8218;IHR_API_KEY&#8216; und f\u00fchren Sie es wie folgt aus:<\/p>\n<div style=\"margin: 18px 0 26px 0;\">\n<pre style=\"background: #f8fafc; border: 1px solid #e5e7eb; border-radius: 10px; padding: 16px 18px; margin: 0; font-size: 14px; line-height: 1.7;\"><code><span style=\"color: #9333ea;\">php<\/span> scrape.php<\/code><\/pre>\n<\/div>\n<p>Wenn alles korrekt eingerichtet ist, ruft das Skript die Seite \u00fcber Web Unlocker ab, analysiert alle 250 L\u00e4nder und schreibt sowohl countries.csv als auch countries.json in Ihren Projektordner.<\/p>\n<h3>Anzeigen der Ergebnisse<\/h3>\n<p>Nachdem das Skript beendet ist, k\u00f6nnen Sie die Datei countries.csv sofort \u00f6ffnen. Die ersten Zeilen werden wie folgt aussehen: <\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-44159\" src=\"https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image2-2.png\" alt=\"L\u00e4nder.csv\" width=\"1152\" height=\"1148\" srcset=\"https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image2-2.png 1152w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image2-2-300x300.png 300w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image2-2-1024x1020.png 1024w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image2-2-150x150.png 150w, https:\/\/floppydata.com\/wp-content\/uploads\/2026\/05\/image2-2-768x765.png 768w\" sizes=\"(max-width: 1152px) 100vw, 1152px\" \/><\/p>\n<p>Sie k\u00f6nnen nun die CSV-Datei in eine Tabellenkalkulation importieren oder die JSON-Datei an eine andere Anwendung senden. Wenn Sie diesen Workflow f\u00fcr die Preisverfolgung nutzen m\u00f6chten, k\u00f6nnen Sie ihn mit den <a href=\"https:\/\/floppydata.com\/use-case\/price-monitoring-proxy\/\">Preis\u00fcberwachungs-Proxys<\/a> von Floppydata kombinieren. <\/p>\n<h2>Umgang mit Anti-Scraping-Ma\u00dfnahmen<\/h2>\n<p>Eine einfache, statische Seite ist nicht schwer zu analysieren, wie wir gerade gesehen haben. Aber gesch\u00fctzte Seiten k\u00f6nnen Kopfschmerzen bereiten. <\/p>\n<p>Sie k\u00f6nnten auf Bl\u00f6cke, fehlende Daten, CAPTCHAs, JavaScript-Rendering oder Ratenbeschr\u00e4nkungen sto\u00dfen. Das ist der Punkt, an dem ein normaler PHP-Scraper zu k\u00e4mpfen hat. <\/p>\n<p>Hier sind die h\u00e4ufigsten Probleme, mit denen Sie konfrontiert werden k\u00f6nnen:<\/p>\n<ul>\n<li><strong>IP-Blockierung:<\/strong> Websites k\u00f6nnen Ihre IP-Adresse blockieren, wenn sie mehrere Anfragen von derselben IP-Adresse innerhalb eines kurzen Zeitraums feststellen.<\/li>\n<li><strong>CAPTCHAs:<\/strong> CAPTCHA-Systeme werden verwendet, um zwischen Bots und Menschen zu unterscheiden, indem sie Aufgaben stellen, die f\u00fcr Bots schwer zu l\u00f6sen sind.<\/li>\n<li><strong>Ratenbegrenzung:<\/strong> Websites begrenzen oft die Anzahl der Anfragen, die Sie innerhalb eines bestimmten Zeitraums t\u00e4tigen k\u00f6nnen, um \u00fcberm\u00e4\u00dfiges Scraping zu verhindern.<\/li>\n<li><strong>Erkennung von Benutzer-Agenten:<\/strong> Nicht-Browser-Benutzeragenten werden blockiert, weil sie nicht wie echte Besucher aussehen.<\/li>\n<li><strong>JavaScript-Herausforderungen:<\/strong> Der Inhalt wird erst nach der Ausf\u00fchrung von JavaScript geladen, was bei einer einfachen HTTP-Anfrage m\u00f6glicherweise nicht der Fall ist.<\/li>\n<\/ul>\n<p>Sie k\u00f6nnen versuchen, diese Probleme manuell zu l\u00f6sen, aber das ist weder bequem noch skalierbar.<\/p>\n<p>Hier kommt der Floppydata Web Unlocker ins Spiel. Anstatt jede Herausforderung selbst zu l\u00f6sen, k\u00f6nnen Sie die gesamte Anti-Bot-Schicht auslagern und sich auf das Extrahieren und Speichern der Daten konzentrieren. <\/p>\n<p><strong>Floppydata Web Unlocker behandelt:<\/strong><\/p>\n<ul>\n<li>IP-Rotation mit einem gro\u00dfen Pool von Proxys f\u00fcr Privatanwender und Rechenzentren<\/li>\n<li>Browser-Fingerprinting und Headless-Browser<\/li>\n<li>JavaScript-Rendering f\u00fcr dynamische Seiten<\/li>\n<li>Automatische Wiederholungsversuche und CAPTCHA-Aufl\u00f6sung<\/li>\n<li>Geo-Targeting bis auf die Ebene der Stadt<\/li>\n<\/ul>\n<p>Wenn Sie mehr Kontrolle ben\u00f6tigen, bietet Floppydata auch <a href=\"https:\/\/floppydata.com\/proxy-type\/isp-proxy\/\">statische Proxys f\u00fcr Privatanwender<\/a> f\u00fcr das Scraping von langen Sitzungen und <a href=\"https:\/\/floppydata.com\/proxy-type\/datacenter-proxy\/\">Proxys f\u00fcr Rechenzentren<\/a> f\u00fcr die Arbeit mit hohen Geschwindigkeiten an.<\/p>\n<p>Aber f\u00fcr die meisten gesch\u00fctzten Seiten ist Web Unlocker der schnellste Weg, um von einer blockierten Anfrage zu analysierbarem HTML zu gelangen.<\/p>\n<h2>Abschlie\u00dfende Gedanken<\/h2>\n<p>PHP ist eine sehr f\u00e4hige Sprache f\u00fcr Web-Scraping, und inzwischen sollten Sie eine solide Grundlage f\u00fcr Web-Scraping mit PHP haben.<\/p>\n<p>Ich beende dieses Tutorial an dieser Stelle, da es sich um eine Einf\u00fchrung in das Web Scraping mit PHP handelt. In zuk\u00fcnftigen Tutorials werden wir unseren Scraper so erweitern, dass er Links folgen, mit Paginierung umgehen und komplexere Ziele scrapen kann. <\/p>\n<p>Wenn Sie in der Zwischenzeit mehr \u00fcber Web Scraping erfahren m\u00f6chten, sehen Sie sich diese Ressourcen an:<\/p>\n<ul>\n<li><a href=\"https:\/\/floppydata.com\/blog\/what-is-web-unblocker\/\">Was ist ein Web Unblocker?<\/a><\/li>\n<li><a href=\"https:\/\/floppydata.com\/blog\/best-proxies-for-web-scrapers\/\">Beste Proxys f\u00fcr Web Scraper<\/a><\/li>\n<\/ul>\n<p>Sind Sie bereit, den Web Unlocker auszuprobieren? <a href=\"https:\/\/app.floppydata.com\/\">Starten Sie noch heute<\/a> mit 5 kostenlosen Scrapes und scrapen Sie alles ohne Kopfschmerzen.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Einf\u00fchrung PHP war damals eine meiner ersten Sprachen als Webentwickler, und ich verwende es immer noch gerne f\u00fcr Scraping. Dieses Tutorial behandelt das, was ich tats\u00e4chlich in der Produktion verwende. Ich f\u00fchre Sie durch einen kompletten PHP-Web-Scraping-Workflow mit Floppydata Web Unlocker als Scraping-Ebene. Warum Floppydata? Weil es eine Scraping-API bietet, die die Verwaltung von Proxys, [&hellip;]<\/p>\n","protected":false},"author":15,"featured_media":44217,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[562,489,440],"tags":[],"class_list":["post-44365","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scraping","category-how-to","category-blog"],"acf":[],"_links":{"self":[{"href":"https:\/\/floppydata.com\/de\/wp-json\/wp\/v2\/posts\/44365","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/floppydata.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/floppydata.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/floppydata.com\/de\/wp-json\/wp\/v2\/users\/15"}],"replies":[{"embeddable":true,"href":"https:\/\/floppydata.com\/de\/wp-json\/wp\/v2\/comments?post=44365"}],"version-history":[{"count":0,"href":"https:\/\/floppydata.com\/de\/wp-json\/wp\/v2\/posts\/44365\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/floppydata.com\/de\/wp-json\/wp\/v2\/media\/44217"}],"wp:attachment":[{"href":"https:\/\/floppydata.com\/de\/wp-json\/wp\/v2\/media?parent=44365"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/floppydata.com\/de\/wp-json\/wp\/v2\/categories?post=44365"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/floppydata.com\/de\/wp-json\/wp\/v2\/tags?post=44365"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}