Raspagem da Web com Goutte em PHP: Tutorial 2025

Domine a raspagem da Web do Goutte com este guia passo a passo. Aprenda a configuração, as alternativas e como contornar as limitações do scraping para obter uma melhor extração de dados.
3 min de leitura
Web Scraping With Goutte blog image

Neste guia de raspagem da Web da Goutte, você aprenderá:

  • O que é a biblioteca PHP Goutte
  • Como usá-lo para raspagem da Web em um tutorial passo a passo
  • Alternativas ao Goutte para raspagem da Web
  • As limitações dessa abordagem e as possíveis soluções

Vamos mergulhar de cabeça!

O que é Goutte?

Goutte é uma biblioteca PHP para raspagem de tela e rastreamento da Web, oferecendo uma API intuitiva para navegar em sites e extrair dados de respostas HTML/XML. Ela inclui um cliente HTTP integrado e recursos de análise de HTML, permitindo que você recupere páginas da Web por meio de solicitações HTTP e as processe para raspagem de dados.

Observação: a partir de 1º de abril de 2023, o Goutte não é mais mantido e agora é considerado obsoleto. No entanto, até o momento em que este texto foi escrito, ele ainda funciona de forma confiável.

Como realizar a raspagem da Web com o Goutte: guia passo a passo

Siga esta seção do tutorial passo a passo e veja como usar o Goutte para extrair dados do site “Hockey Teams“:

A página de destino "Equipes de hóquei"

O objetivo é extrair os dados da tabela acima e exportá-los para um arquivo CSV.

Chegou a hora de aprender a realizar a coleta de dados da Web com o Goutte!

Etapa 1: Configuração do projeto

Antes de começar, certifique-se de que seu sistema atenda aos requisitos do Goutte – PHP7.1 ou superior. Para verificar sua versão atual do PHP, execute o seguinte comando:

php -v

O resultado deve ser semelhante a este:

PHP 8.4.3 (cli) (built: Jan 19 2025 14:20:58) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.4.3, Copyright (c) Zend Technologies
    with Zend OPcache v8.4.3, Copyright (c), by Zend Technologies

Se a sua versão do PHP for inferior à 7.1, você precisará atualizá-la antes de continuar.

Em seguida, lembre-se de que o Goutte será instalado por meio do Composer, um gerenciador de dependências para PHP. Se o Composer não estiver instalado em seu sistema, faça o download no site oficial e siga as instruções de instalação.

Agora, crie um novo diretório para seu projeto Goutte e navegue até ele no terminal:

mkdir goutte-parser
cd goutte-parser

Em seguida, use o comando composer init para inicializar um projeto do Composer dentro da pasta:

composer init

O Composer solicitará que você insira detalhes do projeto, como nome e descrição do pacote. As respostas padrão funcionarão, mas fique à vontade para personalizá-las de acordo com seus objetivos.

Agora, abra a pasta do projeto em seu IDE PHP favorito. O Visual Studio Code com a extensão PHP ou o IntelliJ WebStorm são boas opções.

Crie um arquivo index.php vazio na pasta do projeto, que deve conter:

php-html-parser/
  ├── vendor/
  ├── composer.json
  └── index.php

Abra o index.php e adicione a seguinte linha de código para importar as bibliotecas do Composer:

<?php

require_once __DIR__ . "/vendor/autoload.php";

// scraping logic...

Em breve, esse arquivo conterá a lógica de raspagem da Goutte.

Agora você pode executar seu script usando esse comando:

php index.php

Muito bom! Você está pronto para começar a extrair dados com o Goutte em PHP.

Etapa 2: Instalar e configurar o Goutte

Instale o Goutte com o comando Compose abaixo:

composer require fabpot/goutte

Isso adicionará a dependência do fabpot/goutte ao seu arquivo composer.json, que agora incluirá:

"require": {
    "fabpot/goutte": "^4.0"
}

Em index.php, importe o Goutte adicionando a seguinte linha de código:

use Goutte\Client;

Isso expõe o cliente HTTP da Goutte que você pode usar para se conectar a uma página de destino, analisar seu HTML e extrair dados dela. Veja como fazer isso na próxima etapa!

Etapa 3: obtenha o HTML da página de destino

Primeiro, crie um novo cliente Goutte HTTP:

$client = new Client();

Nos bastidores, a classe Client do Goutte é simplesmente um invólucro do componente BrowserKit\HttpBrowser do Symfony. Veja-o em ação em nosso guia sobre coleta de dados da Web com o Laravel.

Em seguida, armazene o URL da página da Web de destino em uma variável e use o método request() para buscar seu conteúdo:

$url = "https://www.scrapethissite.com/pages/forms/";
$crawler = $client->request("GET", $url);

Isso envia uma solicitação GET para a página da Web, recupera o documento HTML e o analisa para você. Especificamente, o objeto $crawler fornece acesso a todos os métodos do componente DomCrawler do Symfony. $crawler é o objeto que você usará para navegar e extrair dados da página.

Incrível! Agora você tem tudo o que precisa para a coleta de dados da Web do Goutte.

Etapa 4: Prepare-se para extrair os dados de interesse

Antes de extrair dados, você deve se familiarizar com a estrutura HTML da página de destino.

Primeiro, lembre-se de que os dados de interesse são apresentados em linhas dentro de uma tabela. Como essa tabela contém várias linhas, uma matriz é uma ótima estrutura de dados para armazenar os dados extraídos:

$teams = [];

Agora, concentre-se na estrutura HTML da tabela. Visite a página de destino em seu navegador, clique com o botão direito do mouse na tabela que contém os dados de interesse e selecione a opção “Inspect” (Inspecionar):

A estrutura HTML do elemento de tabela

No DevTools, você verá que a tabela tem uma classe de tabela e está contida em um elemento

com o id=``"``hockey``". Isso significa que você pode direcionar a tabela usando o seguinte seletor CSS:

#hockey .table

Aplique o seletor CSS para selecionar o nó da tabela usando o método $crawler->filter():

$table = $crawler->filter("#hockey .table");

Em seguida, observe que cada linha é representada por um elemento

com a classe team. Selecione todas as linhas e itere sobre elas, preparando-se para extrair dados delas:

    $table->filter("tr.team")->each(function ($tr) use (&$teams) {
      // data extraction logic...
    });

Maravilhoso! Agora você tem um esqueleto pronto para a coleta de dados do Goutte.

Etapa nº 5: implementar a lógica de extração de dados

Assim como antes, desta vez inspecione as linhas dentro da tabela:

A estrutura HTML dos elementos de linha

O que você pode notar é que cada linha contém as seguintes informações em colunas dedicadas:

  • Nome da equipe → dentro do elemento .name
  • Ano da estação → dentro do elemento .year
  • Número de vitórias → dentro do elemento .wins
  • Número de perdas → dentro do elemento .losses
  • Perdas com horas extras → dentro do elemento .ot-losses
  • Porcentagem de vitórias → dentro do elemento .pct
  • Gols marcados (Goals For – GF) → dentro do elemento .gf
  • Gols sofridos (Gols contra – GA) → dentro do elemento .ga
  • Diferença de gols → dentro do elemento .diff

Para recuperar uma única informação, você precisa aplicar essas duas etapas:

  1. Selecione o elemento HTML usando filter()
  2. Extraia seu conteúdo de texto usando o método text() e remova todos os espaços extras com trim()

Por exemplo, você pode extrair o nome da equipe com:

$teamElement = $tr->filter(".name");
$team = trim($teamElement->text());

Da mesma forma, estenda essa lógica a todas as outras colunas:

$yearElement = $tr->filter(".year");
$year = trim($yearElement->text());

$winsElement = $tr->filter(".wins");
$wins = trim($winsElement->text());

$lossesElement = $tr->filter(".losses");
$losses = trim($lossesElement->text());

$otLossesElement = $tr->filter(".ot-losses");
$otLosses = trim($otLossesElement->text());

$pctElement = $tr->filter(".pct");
$pct = trim($pctElement->text());

$gfElement = $tr->filter(".gf");
$gf = trim($gfElement->text());

$gaElement = $tr->filter(".ga");
$ga = trim($gaElement->text());

$diffElement = $tr->filter(".diff");
$diff = trim($diffElement->text());

Depois de extrair os dados de interesse da linha, armazene-os na matriz $teams:

$teams[] = [
  "team" => $team,
  "year" => $year,
  "wins" => $wins,
  "losses" => $losses,
  "ot_losses" => $otLosses,
  "win_perc" => $pct,
  "goals_for" => $gf,
  "goals_against" => $ga,
  "goal_diff" => $diff
];

Depois de percorrer todas as linhas, o array $teams conterá:

Array
(
    [0] => Array
        (
            [team] => Boston Bruins
            [year] => 1990
            [wins] => 44
            [losses] => 24
            [ot_losses] =>
            [win_perc] => 0.55
            [goals_for] => 299
            [goals_against] => 264
            [goal_diff] => 35
        )

    // ...

    [24] => Array
        (
            [team] => Chicago Blackhawks
            [year] => 1991
            [wins] => 36
            [losses] => 29
            [ot_losses] =>
            [win_perc] => 0.45
            [goals_for] => 257
            [goals_against] => 236
            [goal_diff] => 21
        )
)

Excelente! A raspagem de dados da Goutte foi realizada com sucesso.

Etapa nº 6: implementar a lógica de rastreamento

Agora, não se esqueça de que o site de destino apresenta dados em várias páginas, mostrando apenas uma parte de cada vez. Abaixo da tabela, há um elemento de paginação que fornece links para todas as páginas:

O elemento de paginação

Portanto, você pode gerenciar a paginação em seu script de raspagem com estas etapas simples:

  1. Selecione os elementos do link de paginação
  2. Extrair os URLs das páginas paginadas
  3. Visite cada página e aplique a lógica de raspagem desenvolvida anteriormente

Comece inspecionando os elementos do link de paginação:

A estrutura HTML dos elementos do link de paginação

Observe que você pode selecionar todos os links de paginação usando o seletor CSS a seguir:

.pagination li a

Para implementar a etapa 2 e coletar todos os URLs de paginação, use esta lógica:

$urls = [$url];

// select the pagination link elements
$crawler->filter(".pagination li a")->each(function ($a) use (&$urls) {
  // construct the absolute URL
  $url = "https://www.scrapethissite.com" . $a->attr("href");

  // add the pagination URL to the list only if it is not already present
  if (!in_array($url, $urls)) {
    $urls[] = $url;
  }
});

Isso inicializa uma lista de URLs que armazenará os links de paginação, começando com o URL da primeira página. Em seguida, seleciona todos os elementos de paginação e itera sobre eles, adicionando novos URLs à matriz $urls somente se eles ainda não estiverem presentes. Como os URLs na página são relativos, eles devem ser convertidos em URLs absolutos antes de serem adicionados à lista.

Como o tratamento da paginação só deve ser executado uma vez e não está diretamente ligado à extração de dados, é melhor envolvê-lo em uma função:

function getPaginationUrls($client, $url)
{
  // connect to the first page of the site
  $crawler = $client->request("GET", $url);

  // initialize the list of URLs to scrape with the current URL
  $urls = [$url];

  // select the pagination link elements
  $crawler->filter(".pagination li a")->each(function ($a) use (&$urls) {
    // construct the absolute URL
    $url = "https://www.scrapethissite.com" . $a->attr("href");

    // add the pagination URL to the list only if it is not already present
    if (!in_array($url, $urls)) {
      $urls[] = $url;
    }
  });

  return $urls;
}

Você pode chamar a função getPaginationUrls() da seguinte forma:

$urls = getPaginationUrls($client, "https://www.scrapethissite.com/pages/forms/?page_num=1");

Após a execução, $urls conterá todas as URLs paginadas:

Array
(
    [0] => https://www.scrapethissite.com/pages/forms/?page_num=1
    [1] => https://www.scrapethissite.com/pages/forms/?page_num=2
    [2] => https://www.scrapethissite.com/pages/forms/?page_num=3
    [3] => https://www.scrapethissite.com/pages/forms/?page_num=4
    [4] => https://www.scrapethissite.com/pages/forms/?page_num=5
    [5] => https://www.scrapethissite.com/pages/forms/?page_num=6
    [6] => https://www.scrapethissite.com/pages/forms/?page_num=7
    [7] => https://www.scrapethissite.com/pages/forms/?page_num=8
    [8] => https://www.scrapethissite.com/pages/forms/?page_num=9
    [9] => https://www.scrapethissite.com/pages/forms/?page_num=10
    [10] => https://www.scrapethissite.com/pages/forms/?page_num=11
    [11] => https://www.scrapethissite.com/pages/forms/?page_num=12
    [12] => https://www.scrapethissite.com/pages/forms/?page_num=13
    [13] => https://www.scrapethissite.com/pages/forms/?page_num=14
    [14] => https://www.scrapethissite.com/pages/forms/?page_num=15
    [15] => https://www.scrapethissite.com/pages/forms/?page_num=16
    [16] => https://www.scrapethissite.com/pages/forms/?page_num=17
    [17] => https://www.scrapethissite.com/pages/forms/?page_num=18
    [18] => https://www.scrapethissite.com/pages/forms/?page_num=19
    [19] => https://www.scrapethissite.com/pages/forms/?page_num=20
    [20] => https://www.scrapethissite.com/pages/forms/?page_num=21
    [21] => https://www.scrapethissite.com/pages/forms/?page_num=22
    [22] => https://www.scrapethissite.com/pages/forms/?page_num=23
    [23] => https://www.scrapethissite.com/pages/forms/?page_num=24
)

Perfeito! Você acabou de implementar o rastreamento da Web no Goutte.

Etapa nº 7: Extraia dados de todas as páginas

Agora que você tem todos os URLs de página armazenados em uma matriz, pode extraí-los um a um:

  1. Iteração sobre a lista
  2. Recuperação e análise do conteúdo HTML de cada URL
  3. Extração dos dados necessários
  4. Armazenamento das informações extraídas na matriz $teams.

Implemente a lógica acima da seguinte forma:

$teams = [];

// iterate over all pages and scrape them all
foreach ($urls as $_ => $url) {
  // logging which page the scraper is currently working on
  echo "Scraping webpage \"$url\"...\n";

  // retrieve the HTML of the current page and parse it
  $crawler = $client->request("GET", $url);

  // $table = $crawler-> ...
  // data extraction logic
}

Observe a instrução echo para registrar a página atual em que o scraper está operando. Essa informação é útil para entender o que o script está fazendo durante a execução.

Que lindo! Só falta exportar os dados extraídos para um formato legível por humanos, como CSV.

Etapa 8: Exportar os dados extraídos para CSV

No momento, os dados extraídos são armazenados na matriz $teams. Para torná-los acessíveis a outras equipes e mais fáceis de analisar, exporte-os para um arquivo CSV.

O PHP oferece suporte integrado para exportação de CSV por meio da função fputcsv(). Use-a para gravar os dados extraídos em um arquivo chamado teams.csv, conforme abaixo:

// open the output file for writing
$file = fopen("teams.csv", "w");

// write the header row
fputcsv($file, ["Team Name", "Year", "Wins", "Losses", "OT Losses", "Win %","Goals For (GF)",        "Goals Against (GA)", "+ / -"]);

// append each team as a new row
foreach ($teams as $team) {
  fputcsv($file, [
    $team["team"],
    $team["year"],
    $team["wins"],
    $team["losses"],
    $team["ot_losses"],
    $team["win_perc"],
    $team["goals_for"],
    $team["goals_against"],
    $team["goal_diff"]
  ]);
}

// close the file
fclose($file);

Missão concluída! O raspador Goutte está totalmente funcional.

Etapa nº 9: Juntar tudo

Seu script de raspagem da Web do Goutte deve conter agora:

<?php

require_once __DIR__ . "/vendor/autoload.php";

use Goutte\Client;

function getPaginationUrls($client, $url)
{
  // connect to the first page of the site
  $crawler = $client->request("GET", $url);

  // initialize the list of URLs to scrape with the current URL
  $urls = [$url];

  // select the pagination link elements
  $crawler->filter(".pagination li a")->each(function ($a) use (&$urls) {
    // construct the absolute URL
    $url = "https://www.scrapethissite.com" . $a->attr("href");

    // add the pagination URL to the list only if it is not already present
    if (!in_array($url, $urls)) {
      $urls[] = $url;
    }
  });

  return $urls;
}

// initialize a new Goutte HTTP client
$client = new Client();

// get the URLs of the pages to scrape
$urls = getPaginationUrls($client, "https://www.scrapethissite.com/pages/forms/?page_num=1");

// where to store the scraped data
$teams = [];

// iterate over all pages and scrape them all
foreach ($urls as $_ => $url) {
  // logging which page the scraper is currently working on
  echo "Scraping webpage \"$url\"...\n";

  // retrieve the HTML of the current page and parse it
  $crawler = $client->request("GET", $url);

  // select the table element with the data of interest
  $table = $crawler->filter("#hockey .table");

  // iterate over each row and extract data from them
  $table->filter("tr.team")->each(function ($tr) use (&$teams) {
    // data extraction logic

    $teamElement = $tr->filter(".name");
    $team = trim($teamElement->text());

    $yearElement = $tr->filter(".year");
    $year = trim($yearElement->text());

    $winsElement = $tr->filter(".wins");
    $wins = trim($winsElement->text());

    $lossesElement = $tr->filter(".losses");
    $losses = trim($lossesElement->text());

    $otLossesElement = $tr->filter(".ot-losses");
    $otLosses = trim($otLossesElement->text());

    $pctElement = $tr->filter(".pct");
    $pct = trim($pctElement->text());

    $gfElement = $tr->filter(".gf");
    $gf = trim($gfElement->text());

    $gaElement = $tr->filter(".ga");
    $ga = trim($gaElement->text());

    $diffElement = $tr->filter(".diff");
    $diff = trim($diffElement->text());

    // add the scraped data to the array
    $teams[] = [
      "team" => $team,
      "year" => $year,
      "wins" => $wins,
      "losses" => $losses,
      "ot_losses" => $otLosses,
      "win_perc" => $pct,
      "goals_for" => $gf,
      "goals_against" => $ga,
      "goal_diff" => $diff
    ];
  });
}

// open the output file for writing
$file = fopen("teams.csv", "w");

// write the header row
fputcsv($file, ["Team Name", "Year", "Wins", "Losses", "OT Losses", "Win %","Goals For (GF)",        "Goals Against (GA)", "+ / -"]);

// append each team as a new row
foreach ($teams as $team) {
  fputcsv($file, [
    $team["team"],
    $team["year"],
    $team["wins"],
    $team["losses"],
    $team["ot_losses"],
    $team["win_perc"],
    $team["goals_for"],
    $team["goals_against"],
    $team["goal_diff"]
  ]);
}

// close the file
fclose($file);

Inicie-o com este comando:

php index.php

O raspador registraria a seguinte saída:

Scraping webpage "https://www.scrapethissite.com/pages/forms/?page_num=1"...
// omitted for brevity..
Scraping webpage "https://www.scrapethissite.com/pages/forms/?page_num=24"...

No final da execução, um arquivo teams.csv contendo esses dados aparecerá na pasta do projeto:

O arquivo de saída CSV

E pronto! Os dados exatos do site de destino agora estão disponíveis em um formato estruturado.

Alternativas à biblioteca PHP Goutte para raspagem da Web

Conforme mencionado no início deste artigo, o Goutte está obsoleto e não é mais mantido. Isso significa que você deve considerar soluções alternativas.

o anúncio do GitHub sobre a descontinuação da biblioteca

Conforme explicado no GitHub, como o Goutte v4 se tornou essencialmente um proxy para a classe HttpBrowser do Symfony, você deve migrar para ele. Para isso, você só precisa instalar essas bibliotecas:

composer require symfony/browser-kit symfony/http-client

Em seguida, substitua:

use Goutte\Client;

com

use Symfony\Component\BrowserKit\HttpBrowser;

Por fim, remova o Goutte como uma dependência em seu projeto. A API subjacente permanece a mesma, portanto, você não deve precisar alterar muito em seu script.

Em vez do Goutte, você também pode combinar um cliente HTTP com um analisador de HTML. Algumas alternativas recomendadas:

  • Guzzle ou cURL para fazer solicitações HTTP.
  • Dom\HTMLDocument, Simple HTML DOM Parser ou DomCrawler para analisar HTML em PHP.

Todas essas alternativas oferecem mais flexibilidade e garantem que seu script de raspagem da Web permaneça sustentável a longo prazo.

Limitações dessa abordagem de raspagem da Web

O Goutte é uma ferramenta avançada, mas seu uso para raspagem da Web tem várias limitações:

  • A biblioteca está obsoleta
  • Sua API não é mais mantida
  • Ele está sujeito a limitadores de taxa e bloqueios antirrastreamento
  • Ele não pode lidar com páginas dinâmicas que dependem de JavaScript
  • Ele tem suporte limitado a proxy integrado, o que é essencial para evitar proibições de IP

Algumas dessas limitações podem ser atenuadas com o uso de bibliotecas alternativas ou abordagens diferentes, conforme abordado em nosso guia sobre raspagem da Web com PHP. Ainda assim, você sempre enfrentará medidas antirrastreamento que só podem ser contornadas usando uma API do Web Unlocker.

Uma API do Web Unlocker é um ponto de extremidade de raspagem especializado, projetado para contornar as proteções antibot e recuperar o HTML bruto de qualquer página da Web. Usá-la é tão simples quanto fazer uma chamada à API e analisar o conteúdo retornado. Essa abordagem se integra perfeitamente ao Goutte (ou aos componentes atualizados do Symfony), conforme demonstrado neste artigo.

Conclusão

Neste guia, você explorou o que é o Goutte e o que ele oferece para raspagem da Web por meio de um tutorial passo a passo. Como essa biblioteca está obsoleta, você também teve a oportunidade de explorar algumas de suas alternativas.

Independentemente da biblioteca de raspagem PHP que você escolher, o maior desafio é que a maioria dos sites protege seus dados usando tecnologias anti-bot e anti-raspagem. Esses mecanismos podem detectar e bloquear solicitações automatizadas, tornando ineficazes os métodos tradicionais de raspagem.

Felizmente, a Bright Data oferece um conjunto de soluções para evitar qualquer problema:

  • Web Unlocker: Uma API que contorna as proteções antirrastreamento e fornece HTML limpo de qualquer página da Web com o mínimo de esforço.
  • Navegador de raspagem: Um navegador controlável e baseado em nuvem com renderização de JavaScript. Ele lida automaticamente com CAPTCHAs, impressão digital do navegador, novas tentativas e muito mais para você. Integra-se perfeitamente ao Panther ou ao Selenium PHP.
  • APIs de raspagem da Web: Endpoints para acesso programático a dados estruturados da Web de dezenas de domínios populares.

Não quer lidar com raspagem da Web, mas ainda está interessado em “dados on-line da Web”? Explore nossos conjuntos de dados prontos para uso!

Inscreva-se agora na Bright Data e comece sua avaliação gratuita para testar nossas soluções de raspagem.

Não é necessário cartão de crédito