{"id":44368,"date":"2026-05-06T12:51:19","date_gmt":"2026-05-06T12:51:19","guid":{"rendered":"https:\/\/floppydata.com\/sem-categoria\/tutorial-de-raspagem-da-web-em-php-como-extrair-dados-com-php\/"},"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\/pt-br\/blog\/scraping\/php-web-scraping-tutorial\/","title":{"rendered":"Tutorial de raspagem da Web em PHP: Como extrair dados com PHP"},"content":{"rendered":"<h2>Introdu\u00e7\u00e3o<\/h2>\n<p>O PHP foi uma das minhas primeiras linguagens como desenvolvedor da Web, e ainda gosto de us\u00e1-lo para raspagem.<\/p>\n<p>Este tutorial aborda o que eu realmente uso na produ\u00e7\u00e3o. Vou orientar voc\u00ea em um fluxo de trabalho completo de raspagem da Web em PHP usando o <a href=\"https:\/\/floppydata.com\/web-unlocker\/\">Floppydata Web Unlocker<\/a> como camada de raspagem. <\/p>\n<p><strong>Por que a Floppydata?<\/strong><\/p>\n<p>Porque ele fornece uma API de raspagem que elimina a necessidade de gerenciar proxies, cabe\u00e7alhos ou l\u00f3gica anti-bot. Ao final deste artigo, voc\u00ea ter\u00e1 um conhecimento s\u00f3lido sobre como realizar raspagem da Web com PHP. <\/p>\n<h2>O que \u00e9 raspagem da Web em PHP?<\/h2>\n<p>O web scraping PHP \u00e9 o processo de usar o c\u00f3digo PHP para extrair dados de sites. Nem todos os sites fornecem uma API, como o Twitter, por exemplo, portanto, em muitos casos, a \u00fanica maneira de obter as informa\u00e7\u00f5es de que voc\u00ea precisa \u00e9 buscar a p\u00e1gina e analisar o HTML por conta pr\u00f3pria. <\/p>\n<p>O PHP faz muito sentido para isso se voc\u00ea j\u00e1 o usa todos os dias. Voc\u00ea pode inserir os dados extra\u00eddos diretamente em um backend existente, armazen\u00e1-los no MySQL ou executar o raspador em um trabalho cron sem introduzir outra linguagem na pilha. <\/p>\n<p>A quest\u00e3o principal n\u00e3o \u00e9 se o PHP pode fazer scraping. Com certeza pode. A verdadeira quest\u00e3o \u00e9 se o seu coletor de dados lida bem com solicita\u00e7\u00f5es bloqueadas, restri\u00e7\u00f5es baseadas em locais e CAPTCHAs agressivos.  <\/p>\n<p>\u00c9 exatamente por isso que eu uso PHP com o Floppydata Web Unlocker para ajudar voc\u00ea a preencher essa lacuna.<\/p>\n<h2>Vale a pena conhecer as bibliotecas PHP de raspagem da Web (2026)<\/h2>\n<p>O PHP tem muitas bibliotecas de raspagem, mas, sinceramente, escolhi apenas algumas que realmente uso. Aqui est\u00e1 uma r\u00e1pida olhada nelas. <\/p>\n<ul>\n<li><strong>Guzzle:<\/strong> Um cliente HTTP s\u00f3lido que lida com solicita\u00e7\u00f5es POST, cargas \u00fateis JSON, redirecionamentos e cabe\u00e7alhos de forma limpa. Vamos us\u00e1-lo ao longo deste tutorial para conversar com a API do Web Unlocker. <\/li>\n<li><strong>Symfony DomCrawler:<\/strong> Permite que voc\u00ea navegue em HTML e XML usando seletores CSS ou XPath. Quando combinado com o componente symfony\/css-selector, ele oferece a voc\u00ea uma filtragem no estilo jQuery que funciona de forma confi\u00e1vel em HTML confuso. Ele \u00e9 aut\u00f4nomo, portanto voc\u00ea n\u00e3o precisa do restante do Symfony.  <\/li>\n<li><strong>Symfony HttpBrowser:<\/strong> Esse \u00e9 o substituto moderno da biblioteca Goutte, agora obsoleta. Ela foi criada com base no BrowserKit e no DomCrawler e permite que voc\u00ea simule cliques, envios de formul\u00e1rios e cadeias de redirecionamento. \u00c9 excelente quando sua l\u00f3gica de raspagem abrange v\u00e1rias p\u00e1ginas.  <\/li>\n<li><strong>DiDOM:<\/strong> O DiDOM \u00e9 um analisador r\u00e1pido e de depend\u00eancia zero com uma API semelhante \u00e0 do jQuery. Perfeito para scripts menores em que voc\u00ea deseja evitar o uso de componentes do Symfony. <\/li>\n<li><strong>Symfony Panther:<\/strong> Conduz um navegador Chrome ou Chromium real por meio do WebDriver. Voc\u00ea pode usar isso quando um site renderiza tudo em JavaScript (React, Vue, SPAs pesados) e uma solicita\u00e7\u00e3o HTTP simples retorna um shell vazio. Como \u00e9 mais pesado, eu o uso somente quando nada mais funciona.  <\/li>\n<\/ul>\n<p>O Goutte costumava ser uma recomenda\u00e7\u00e3o comum, mas agora est\u00e1 obsoleto, portanto, eu n\u00e3o recomendaria que voc\u00ea criasse um novo projeto com base nele.<\/p>\n<p>Para este guia, o Guzzle e o Symfony DomCrawler s\u00e3o suficientes. Como o Web Unlocker j\u00e1 executa o JavaScript e retorna o HTML final renderizado, n\u00e3o precisamos executar um navegador sem cabe\u00e7a no nosso lado. <\/p>\n<h2>Pr\u00e9-requisitos<\/h2>\n<p>Antes de escrever qualquer c\u00f3digo, certifique-se de que voc\u00ea tenha os quatro itens a seguir no lugar. Se voc\u00ea nunca configurou um projeto PHP do zero, n\u00e3o se preocupe, eu o orientarei em todas as etapas. <\/p>\n<h3>1. PHP 8.2 ou mais recente<\/h3>\n<p>O PHP vem pr\u00e9-instalado em muitos sistemas Mac e Linux, mas nunca \u00e9 demais verificar. Abra o terminal e verifique a vers\u00e3o do PHP: <\/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>Se o PHP j\u00e1 estiver instalado, voc\u00ea ver\u00e1 um n\u00famero de vers\u00e3o. Para este tutorial, use o PHP 8.2 ou mais recente. Esse \u00e9 o ponto de partida mais seguro com as vers\u00f5es de depend\u00eancia que vamos instalar.  <\/p>\n<p>Se o PHP estiver ausente, siga as instru\u00e7\u00f5es para instal\u00e1-lo:<\/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>Ap\u00f3s a instala\u00e7\u00e3o, execute novamente o endere\u00e7o <code>php -v<\/code> para confirmar a vers\u00e3o. No Homebrew, voc\u00ea n\u00e3o precisa de pacotes php-curl ou php-xml separados para este tutorial. Essas extens\u00f5es j\u00e1 est\u00e3o inclu\u00eddas na instala\u00e7\u00e3o principal do PHP.  <\/p>\n<h3>2. Compositor<\/h3>\n<p>O Composer \u00e9 o gerenciador de pacotes padr\u00e3o para PHP. Ele \u00e9 basicamente o equivalente ao npm ou pip para projetos PHP. Vamos us\u00e1-lo para instalar o Guzzle e os pacotes do analisador Symfony.  <\/p>\n<p>Primeiro, verifique se ele j\u00e1 est\u00e1 dispon\u00edvel:<\/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>Se o Composer ainda n\u00e3o estiver instalado, use:<\/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>Quando isso for feito, o site <code>composer --version<\/code> dever\u00e1 imprimir um n\u00famero de vers\u00e3o e voc\u00ea estar\u00e1 pronto para criar o projeto.<\/p>\n<h3>3. Uma conta Floppydata<\/h3>\n<p>Crie uma <a href=\"http:\/\/app.floppydata.com\">conta na Floppydata<\/a> e copie sua chave de API do painel. Cada nova conta recebe 5 scrapes gratuitos para o 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=\"  Conta Floppydata  \" 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>Depois de fazer login em seu painel, v\u00e1 para <strong>Manage API Keys (Gerenciar chaves de API)<\/strong> no Web Unlocker e gere uma chave de API. Copie-a imediatamente e guarde-a em um local seguro. <\/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=\"Gerenciar chaves de API\" 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>Voc\u00ea usar\u00e1 essa chave no cabe\u00e7alho X-Api-Key de cada solicita\u00e7\u00e3o do Web Unlocker. Vamos adicion\u00e1-la ao nosso c\u00f3digo em alguns minutos. <\/p>\n<h3>4. Diret\u00f3rio do projeto e depend\u00eancias<\/h3>\n<p>Agora, vamos criar a pasta em que nosso coletor de dados ficar\u00e1 e instalar as bibliotecas PHP de que precisamos. Em seu 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>Inicialize um novo projeto do Composer:<\/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>Agora, instale os pacotes necess\u00e1rios:<\/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>O Composer far\u00e1 o download das tr\u00eas bibliotecas e de suas depend\u00eancias em uma pasta vendor\/ e criar\u00e1 um arquivo composer.json que rastreia exatamente quais vers\u00f5es voc\u00ea est\u00e1 usando.<\/p>\n<p>A partir de agora, cada arquivo PHP no projeto pode carregar as depend\u00eancias com:<\/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>Nesse ponto, a configura\u00e7\u00e3o est\u00e1 conclu\u00edda e podemos passar para o raspador propriamente dito.<\/p>\n<h2>Como extrair dados com PHP usando o Floppydata Web Unlocker<\/h2>\n<h3>Etapa 1: Teste seu alvo<\/h3>\n<p>Nunca gosto de escrever c\u00f3digo \u00e0s cegas para saber exatamente quais seletores e estrutura de dados voc\u00ea deve esperar. Para este exemplo, vou usar <a href=\"https:\/\/www.scrapethissite.com\/pages\/simple\/\" rel=\"nofollow noopener\" target=\"_blank\">o scrapethissite<\/a>, um site de demonstra\u00e7\u00e3o para raspagem de dados. Ele cont\u00e9m uma lista de todos os 250 pa\u00edses com sua capital, popula\u00e7\u00e3o e \u00e1rea.  <\/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=\"raspa-etissita\" 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>Para acompanhar voc\u00ea, visite o <a href=\"http:\/\/app.floppydata.com\/tools\/scrape\">Floppydata Web Unlocker Playground<\/a>. Essa ferramenta sem c\u00f3digo est\u00e1 dispon\u00edvel diretamente no seu painel e permite que voc\u00ea veja o HTML exato que a API retornar\u00e1 sem configurar um projeto. <\/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=\"dados de 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>Agora, digite o URL e clique em Scrape. Em segundos, voc\u00ea ver\u00e1 o HTML completo na visualiza\u00e7\u00e3o de sa\u00edda. Esse \u00e9 exatamente o mesmo HTML que seu script PHP receber\u00e1 daqui a algumas etapas.  <\/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=\"Script PHP\" 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>Se os dados parecerem corretos, voc\u00ea poder\u00e1 copiar o HTML ou fazer download da resposta. Mas, no nosso caso, deixaremos que o script PHP fa\u00e7a isso automaticamente. <\/p>\n<h3>Etapa 2: Enviando sua primeira solicita\u00e7\u00e3o do Web Unlocker com o Guzzle<\/h3>\n<p>O n\u00facleo de todo o fluxo de trabalho \u00e9 uma solicita\u00e7\u00e3o POST para o endpoint da 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>Para fazer isso, primeiro criamos um cliente Guzzle e preparamos a configura\u00e7\u00e3o da solicita\u00e7\u00e3o. Em seguida, enviamos a solicita\u00e7\u00e3o e tratamos a resposta. <\/p>\n<p>Crie um arquivo chamado <strong>scrape.php<\/strong> e comece com o esqueleto b\u00e1sico:<\/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>Substitua YOUR_API_KEY por sua chave real. Agora, criamos a chamada POST real. Enviamos JSON para o endpoint da API, inclu\u00edmos a chave da API nos cabe\u00e7alhos e passamos o URL de destino mais alguns argumentos no corpo:  <\/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>Os campos <strong>pa\u00eds<\/strong> e <strong>cidade<\/strong> informam ao Web Unlocker por qual localiza\u00e7\u00e3o geogr\u00e1fica voc\u00ea deve encaminhar a solicita\u00e7\u00e3o. O campo <strong>difficulty<\/strong> controla a agressividade com que o desbloqueador lida com as prote\u00e7\u00f5es antibot. Estou usando <strong>low<\/strong> aqui porque nosso alvo de sandbox n\u00e3o tem nenhuma prote\u00e7\u00e3o.  <\/p>\n<p>Para alvos protegidos por Cloudflare ou DataDome, defina isso como <strong>m\u00e9dio<\/strong> para que o desbloqueador aplique uma l\u00f3gica mais forte de impress\u00e3o digital e de solu\u00e7\u00e3o de CAPTCHA.<\/p>\n<p>Agora, observe que o Web Unlocker retorna o HTML bruto dentro de um objeto JSON, o que significa que voc\u00ea precisa decodificar o JSON e extrair a marca\u00e7\u00e3o real da p\u00e1gina do campo <strong>html<\/strong>.<\/p>\n<p>Se voc\u00ea se esquecer disso e tratar todo o corpo da resposta como HTML, seu analisador ser\u00e1 interrompido. Com isso, o lado da solicita\u00e7\u00e3o est\u00e1 conclu\u00eddo e podemos passar para a an\u00e1lise. <\/p>\n<h3>Etapa 3: Inspecione a estrutura da p\u00e1gina<\/h3>\n<p>Depois que a solicita\u00e7\u00e3o for bem-sucedida, a pr\u00f3xima tarefa \u00e9 inspecionar a estrutura da p\u00e1gina e direcionar os elementos repetidos que cont\u00eam os dados que desejamos. Cada pa\u00eds da p\u00e1gina segue exatamente esse padr\u00e3o HTML: <\/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>Essa estrutura repetida \u00e9 o que torna essa p\u00e1gina extremamente previs\u00edvel. Cada cart\u00e3o de pa\u00eds usa os mesmos nomes de classe: <strong>.country<\/strong> para o inv\u00f3lucro, <strong>.country-name<\/strong> para o t\u00edtulo e<strong>.country-capital<\/strong>, <strong>.country-population<\/strong> e <strong>.country-area<\/strong> para os campos de dados dentro de <strong>.country-info<\/strong>. <\/p>\n<h3>Etapa 4: Analisar dados com o Symfony DomCrawler<\/h3>\n<p>Como as classes s\u00e3o consistentes em todas as 250 entradas, podemos percorrer cada elemento <strong>.country<\/strong> e extrair os valores dos seletores filhos. Mas, primeiro, vamos adicionar uma pequena fun\u00e7\u00e3o auxiliar para nos ajudar a limpar o texto que extra\u00edmos: <\/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>Se voc\u00ea observar o HTML bruto, os nomes dos pa\u00edses t\u00eam espa\u00e7os em branco e novas linhas extras ao redor deles por causa das tags do \u00edcone da bandeira &lt;i&gt; dentro do &lt;h3&gt;.<\/p>\n<p>A fun\u00e7\u00e3o <em>normalizeText()<\/em> ajuda a remover os espa\u00e7os em branco \u00e0 esquerda e \u00e0 direita e, em seguida, usa um regex para recolher quaisquer espa\u00e7os ou novas linhas restantes, de modo que nomes como Andorra ou St. John&#8217;s retornem de forma limpa, em vez de carregar os espa\u00e7os em branco restantes do HTML.<\/p>\n<p>Com o helper pronto, criamos uma inst\u00e2ncia do Crawler e fazemos um loop em cada cart\u00e3o de pa\u00eds:<\/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>O DomCrawler nos oferece uma maneira limpa de percorrer o HTML usando seletores CSS. Come\u00e7amos envolvendo o HTML em um objeto Crawler e, em seguida, filtramos todos os blocos .country da p\u00e1gina. Dentro de cada bloco, pegamos o nome, a capital, a popula\u00e7\u00e3o e a \u00e1rea.<\/p>\n<p>Nesse ponto, se voc\u00ea executar o script, dever\u00e1 ver a mensagem &#8220;Parsed 250 countries&#8221; impressa no terminal.<\/p>\n<h3>Etapa 5: exportar os resultados para CSV e JSON<\/h3>\n<p>Quando o analisador fornece a voc\u00ea uma matriz <strong>$countries<\/strong>, a exporta\u00e7\u00e3o dos dados se torna muito simples.<\/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>A exporta\u00e7\u00e3o CSV \u00e9 \u00fatil porque fornece aos leitores um arquivo que pode ser aberto imediatamente no Excel, no Google Sheets ou em qualquer outra ferramenta de planilha eletr\u00f4nica. A exporta\u00e7\u00e3o JSON \u00e9 igualmente \u00fatil se voc\u00ea quiser alimentar os dados extra\u00eddos em outro script PHP ou em uma API posteriormente. <\/p>\n<p>Uma pequena atualiza\u00e7\u00e3o aqui \u00e9 o argumento de escape expl\u00edcito em <strong>fputcsv()<\/strong>. Nas vers\u00f5es mais recentes do PHP, isso evita avisos de deprecia\u00e7\u00e3o e mant\u00e9m o exemplo limpo quando os leitores o executam no terminal. <\/p>\n<h3>Etapa n\u00ba 6: Junte tudo em um \u00fanico script<\/h3>\n<p>Agora que cada parte funciona por si s\u00f3, aqui est\u00e1 o script completo:<\/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>Substitua &#8216;YOUR_API_KEY&#8217; e execute-o desta forma:<\/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>Quando tudo estiver configurado corretamente, o script buscar\u00e1 a p\u00e1gina por meio do Web Unlocker, analisar\u00e1 todos os 250 pa\u00edses e gravar\u00e1 countries.csv e countries.json na pasta do seu projeto.<\/p>\n<h3>Exibindo os resultados<\/h3>\n<p>Depois que o script for conclu\u00eddo, voc\u00ea poder\u00e1 abrir o arquivo countries.csv imediatamente. As primeiras linhas ter\u00e3o a seguinte apar\u00eancia: <\/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=\"pa\u00edses.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>Agora voc\u00ea pode importar o CSV para uma planilha ou enviar o JSON para outro aplicativo. Se quiser usar esse fluxo de trabalho para rastreamento de pre\u00e7os, voc\u00ea pode combin\u00e1-lo com os <a href=\"https:\/\/floppydata.com\/use-case\/price-monitoring-proxy\/\">proxies de monitoramento de pre\u00e7os<\/a> da Floppydata, portanto, \u00e9 bom dar uma olhada nisso. <\/p>\n<h2>Como lidar com medidas anti-scraping<\/h2>\n<p>Uma p\u00e1gina simples e est\u00e1tica n\u00e3o \u00e9 dif\u00edcil de analisar, como acabamos de ver. Mas sites protegidos podem ser uma dor de cabe\u00e7a. <\/p>\n<p>Voc\u00ea pode se deparar com bloqueios, dados ausentes, CAPTCHAs, renderiza\u00e7\u00e3o de JavaScript ou limites de taxa. \u00c9 nesse ponto que um scraper PHP normal come\u00e7a a ter dificuldades. <\/p>\n<p>Aqui est\u00e3o os problemas comuns que voc\u00ea pode enfrentar:<\/p>\n<ul>\n<li><strong>Bloqueio de IP:<\/strong> Os sites podem bloquear seu endere\u00e7o IP se detectarem v\u00e1rias solicita\u00e7\u00f5es provenientes do mesmo IP em um curto per\u00edodo.<\/li>\n<li><strong>CAPTCHAs:<\/strong> Os sistemas CAPTCHA s\u00e3o usados para diferenciar entre bots e humanos, apresentando desafios que s\u00e3o dif\u00edceis de serem resolvidos pelos bots.<\/li>\n<li><strong>Limita\u00e7\u00e3o de taxa:<\/strong> Os sites geralmente limitam o n\u00famero de solicita\u00e7\u00f5es que voc\u00ea pode fazer em um determinado per\u00edodo de tempo para evitar o excesso de raspagem.<\/li>\n<li><strong>Detec\u00e7\u00e3o de agente de usu\u00e1rio:<\/strong> Os agentes de usu\u00e1rio que n\u00e3o s\u00e3o do navegador s\u00e3o bloqueados porque n\u00e3o se parecem com visitantes reais.<\/li>\n<li><strong>Desafios do JavaScript:<\/strong> O conte\u00fado s\u00f3 \u00e9 carregado ap\u00f3s a execu\u00e7\u00e3o do JavaScript, o que uma solicita\u00e7\u00e3o HTTP simples pode n\u00e3o ocorrer.<\/li>\n<\/ul>\n<p>Voc\u00ea pode tentar resolver esses problemas manualmente, mas isso n\u00e3o \u00e9 conveniente nem dimension\u00e1vel.<\/p>\n<p>\u00c9 a\u00ed que entra o Floppydata Web Unlocker. Em vez de resolver cada desafio sozinho, voc\u00ea pode descarregar toda a camada anti-bot e se concentrar na extra\u00e7\u00e3o e no armazenamento dos dados. <\/p>\n<p><strong>Floppydata Web Unlocker manipula:<\/strong><\/p>\n<ul>\n<li>Rota\u00e7\u00e3o de IP com um grande conjunto de proxies residenciais e de data center<\/li>\n<li>Impress\u00e3o digital do navegador e navegadores sem cabe\u00e7a<\/li>\n<li>Renderiza\u00e7\u00e3o de JavaScript para p\u00e1ginas din\u00e2micas<\/li>\n<li>Tentativas autom\u00e1ticas e resolu\u00e7\u00e3o de CAPTCHA<\/li>\n<li>Segmenta\u00e7\u00e3o geogr\u00e1fica at\u00e9 o n\u00edvel da cidade<\/li>\n<\/ul>\n<p>Se voc\u00ea precisar de mais controle, a Floppydata tamb\u00e9m oferece <a href=\"https:\/\/floppydata.com\/proxy-type\/isp-proxy\/\">proxies residenciais est\u00e1ticos<\/a> para raspagem de sess\u00f5es longas e <a href=\"https:\/\/floppydata.com\/proxy-type\/datacenter-proxy\/\">proxies de datacenter<\/a> para trabalho em volume de alta velocidade.<\/p>\n<p>Mas, para a maioria das p\u00e1ginas protegidas, o Web Unlocker \u00e9 a maneira mais r\u00e1pida de passar de uma solicita\u00e7\u00e3o bloqueada para um HTML analis\u00e1vel.<\/p>\n<h2>Considera\u00e7\u00f5es finais<\/h2>\n<p>O PHP \u00e9 uma linguagem muito capaz de fazer raspagem na Web e, a esta altura, voc\u00ea j\u00e1 deve ter uma base s\u00f3lida sobre raspagem na Web com PHP.<\/p>\n<p>Vou interromper este tutorial neste ponto, pois ele \u00e9 uma introdu\u00e7\u00e3o \u00e0 coleta de dados da Web com PHP. Em tutoriais futuros, expandiremos nosso coletor de dados para que ele possa seguir links, lidar com pagina\u00e7\u00e3o e coletar dados de alvos mais complexos. <\/p>\n<p>Se, enquanto isso, voc\u00ea quiser saber mais sobre raspagem da Web, confira estes recursos:<\/p>\n<ul>\n<li><a href=\"https:\/\/floppydata.com\/blog\/what-is-web-unblocker\/\">O que \u00e9 um desbloqueador da Web?<\/a><\/li>\n<li><a href=\"https:\/\/floppydata.com\/blog\/best-proxies-for-web-scrapers\/\">Melhores proxies para raspadores da Web<\/a><\/li>\n<\/ul>\n<p>Voc\u00ea est\u00e1 pronto para experimentar o Web Unlocker? <a href=\"https:\/\/app.floppydata.com\/\">Comece hoje mesmo<\/a> com 5 raspagens gratuitas e raspe tudo sem dor de cabe\u00e7a.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introdu\u00e7\u00e3o O PHP foi uma das minhas primeiras linguagens como desenvolvedor da Web, e ainda gosto de us\u00e1-lo para raspagem. Este tutorial aborda o que eu realmente uso na produ\u00e7\u00e3o. Vou orientar voc\u00ea em um fluxo de trabalho completo de raspagem da Web em PHP usando o Floppydata Web Unlocker como camada de raspagem. Por [&hellip;]<\/p>\n","protected":false},"author":15,"featured_media":44218,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[564,441,491],"tags":[],"class_list":["post-44368","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scraping","category-blog","category-how-to"],"acf":[],"_links":{"self":[{"href":"https:\/\/floppydata.com\/pt-br\/wp-json\/wp\/v2\/posts\/44368","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/floppydata.com\/pt-br\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/floppydata.com\/pt-br\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/floppydata.com\/pt-br\/wp-json\/wp\/v2\/users\/15"}],"replies":[{"embeddable":true,"href":"https:\/\/floppydata.com\/pt-br\/wp-json\/wp\/v2\/comments?post=44368"}],"version-history":[{"count":0,"href":"https:\/\/floppydata.com\/pt-br\/wp-json\/wp\/v2\/posts\/44368\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/floppydata.com\/pt-br\/wp-json\/wp\/v2\/media\/44218"}],"wp:attachment":[{"href":"https:\/\/floppydata.com\/pt-br\/wp-json\/wp\/v2\/media?parent=44368"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/floppydata.com\/pt-br\/wp-json\/wp\/v2\/categories?post=44368"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/floppydata.com\/pt-br\/wp-json\/wp\/v2\/tags?post=44368"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}