Extraindo informações de um site com PHP

Valdeir Psr
Feb 23, 2018 · 5 min read

Recentemente estava no StackOverflow e um usuário publicou uma dúvida referente a extração de dados de um determinado site. Então resolvi responde-lo com duas formas: Funções nativas e biblioteca. Segue minha resposta.

Capturando HTML do site.

Para capturar informações de sites nós podemos utilizar a função file_get_contents. Ela fará uma requisição e nos retornará o HTML da página.

Com essa função nós precisamos passar apenas a URL, mas você também pode passar o argumento flag:

  • FILE_TEXT: Ler o HTML no padrão UTF-8
  • FILE_BINARY: Ler o HTML como binário.

E também o argumento context. Nele nós podemos passar um objeto do tipo Stream Context. Assim poderemos passar alguns valores como Cookies, User-Agent etc.

$content = file_get_contents("https://www.infomoney.com.br/mercados/cambio");

Uma alternativa (e uma solução mais robusta) para o file_get_contents é usar a biblioteca cURL (no exemplo abaixo utilizo uma requisição do tipo POST como exemplo).

/* Inicializa a biblioteca cURL */
$ch = curl_init();
/* Define as configurações da requisição */
curl_setopt_array($ch, [
/* Informa a URL */
CURLOPT_URL => $url,

/* Informa que deseja capturar o retorno */
CURLOPT_RETURNTRANSFER => true,

/* Permite o redirecionamento */
CURLOPT_FOLLOWLOCATION => true,

/* Informa que o tipo da requisição é POST */
CURLOPT_POST => true,

/* Converte os dados para application/x-www-form-urlencoded */
CURLOPT_POSTFIELDS => http_build_query($fields),

/**
* Habilita a escrita de Cookies
*(É obrigatório para alguns sites)
*/
CURLOPT_COOKIEJAR => 'cookies.txt',

/* Desabilita a verificação do SSL,
* caso você possua, pode deixar habilitado
*/
CURLOPT_SSL_VERIFYPEER => false,
]);
/* Executa a requisição e captura o retorno */
$response = curl_exec($ch);
/* Captura eventuais erros */
$error = curl_error($ch);
/* Captura a informação da requisição */
$info = curl_getinfo($ch);
/* Fecha a conexão */
curl_close($ch);

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -

Carregando os dados recebidos

Para tratar esses dados, precisamos utilizar a classe DOMDocument. Com ela nós poderemos carregar o HTML da página, capturar elementos através de uma tag ou ID; Podemos criar e ler atributos etc.

Para isso basta instanciar o objeto e utilizar o método loadHTML.

$dom = new domDocument();
@$dom->loadHTML($content);

O “@” serve para ignorar os erros do HTML, como por exemplo, o não fechamento de uma tag.

Com isso já é possível listar os elementos que contém os dados da moda, como por exemplo nome e preço de compra e venda das moedas. Caso seja uma estrutura HTML básica, bastaríamos:

  1. Utilizar o método getElementsByTagName
  2. Percorrer todos os elementos necessários através de um foreach
  3. Filtra-los e exibir na tela.

Mas queremos algo mais simples (já que a estrutura do site não é tão simples para nosso código), por isso iremos utilizar a classe DOMXPath.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -

Filtrando os elementos necessários

Para filtrar os dados iremos utilizar a classe DOMXPath, com ela nós conseguiremos criar queries para tornar as buscas pelos elementos, mais simples.

Essa classe, infelizmente, não funciona com seletores do jQuery. Ela trabalha com expressões XPath e através dessas expressões, nós conseguiremos buscar os elementos conforme sua tag e atributos. Ela funciona da seguinte forma:

nodename: Seleciona todos os elementos com o nome nodename

/ (Barra): Selecione o elemento root

// (Duas barras): Seleciona o elemento no documento a partir do nó atual que corresponde à seleção, independentemente de onde eles estão

. (ponto): Seleciona o elemento atual

.. (dois ponto): Seleciona o elemento “pai”

@ (Arroba): Seleciona a partir dos atributos

Para saber mais, segue o link da documentação

Agora podemos avançar e começar a selecionar os elementos, mas primeiro iremos instanciar o objeto DOMXPath e depois utilizaremos o método query.

$xpath = new DOMXPath($dom);
$tables = $xpath->query("//table[@class=\"table-general\"]");
$values = $xpath->query(".//tbody/tr", $tables->item(0));

Dessa forma iremos acessar todos os elementos table que possui a classe table-general, logo depois acessaremos todos os elementos tbody tr da primeira tabela encontrada.

Agora só precisamos executar um foreach na variável $values para obtermos os elementos td(onde estão os valores).

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -

Filtrando os valores necessários

No foreach supracitado, iremos utilizar mais uma vez o método query. Isso nos retornará um objeto do tipo DOMNodeList.

Esse objeto retornado nos possibilita ter acesso ao método item, que por sua vez retorna um DOMNode. Com esse DOMNode, nós poderemos retornar o conteúdo do elemento ou um atributo dele.

$currencies = [];foreach($values as $value) {    
$currency = $xpath->query(".//td", $value);
/* Acessa o conteúdo em texto do primeiro elemento TD */
$name = trim($currency->item(0)->textContent);
/**
* Acessa o conteúdo o segundo elemento da tag TD,
* após isso capturamos o "irmão" (próximo elemento na mesma raiz), no caso o IMG
* e então captura o atributo SRC
*/
$img = trim( $currency->item(1)->firstChild->nextSibling->getAttribute("src") );
/* Acessa o conteúdo em texto do terceiro elemento TD */
$purchasePrice = trim($currency->item(2)->textContent);
/* Acessa o conteúdo em texto do quarto elemento TD */
$salePrice = trim($currency->item(3)->textContent);
/* Armazenamos em um array para posteriormente exibir aos usuários. */
$currencies[] = [
"img" => $img,
"name" => $name,
"purchasePrice" => $purchasePrice,
"salePrice" => $salePrice,
];
}

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -

Exibindo os dados para o usuário

Esse passo é opcional, iremos apenas demonstrar (para quem tiver dúvida), como exibir esses valores na tela.

Irei utilizar o Pure-CSS por questão estética, porém ele também é opcional.

<link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/pure-min.css" integrity="sha384-nn4HPE8lTHyVtfCBi5yW9d20FjT8BJwUXyWZT9InLYax14RDjBj46LmSztkmNP9w" crossorigin="anonymous"><table class="pure-table">
<thead>
<tr>
<th></th>
<th>Moeda</th>
<th>Compra</th>
<th>Venda</th>
</tr>
</thead>
<tbody> <!-- Percorre todo o array criado -->
<?php foreach($currencies as $currency): ?>
<tr>
<!-- Concate o endereço do site com o caminho da imagem -->
<td><img src="<?php echo "http://www.infomoney.com.br{$currency['img']}" ?>" /></td>
<!-- Exibe nome da moeda -->
<td><?php echo $currency['name'] ?></td>
<!-- Exibe valor da compra -->
<td><?php echo $currency['purchasePrice'] ?></td>
<!-- Exibe valor da venda -->
<td><?php echo $currency['salePrice'] ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>

Código Completo

Sei que o código é um tanto quanto longo e até desmotiva quem está começando, mas para utilizarmos bibliotecas, antes, você precisa conhecer o código base.

Como foi citado no início do texto, irei adicionar uma opção mais compacta. Aqui nós utilizaremos a biblioteca QueryPath.

Utilizando a biblioteca QueryPath

Serei breve aqui, apenas postarei o código comentado. Para explicações mais detalhadas basta acessar a documentação.

<?php/* Carrega as bibliotecas via composer */
require_once "vendor/autoload.php";
/* Acessa e baixa o HTML do site */
$qp = html5qp("https://www.infomoney.com.br/mercados/cambio");
/* Captura os atributos "tr" da tag "tbody" da primeira tabela coma classe "table-general" */
$values = $qp->find("table.table-general:first tbody tr");
/* Percorre os valores capturados */
foreach($values as $value) {
/* Armazenamos em um array para posteriormente exibir aos usuários. */
$currencies[] = [
"img" => trim($value->find('td:eq(2) img')->attr("src")),
"name" => trim($value->find('td:eq(1)')->text()),
"purchasePrice" => trim($value->find('td:eq(3)')->text()),
"salePrice" => trim($value->find('td:eq(0)')->text()),
];
}

Código Completo

Resultado Final

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade