Análise de HTML e captura de tela com a biblioteca Simple HTML DOM

Se você precisar analisar HTML, as expressões regulares não são o caminho a seguir. Neste tutorial, você aprenderá como usar um analisador de software livre facilmente aprendido para ler, modificar e devolver HTML de fontes externas. Usando o Envato Tuts+ como exemplo, você aprenderá como obter uma lista de todos os artigos publicados no site e exibi-los.


1. Preparação

A primeira coisa a fazer é instalar o Composer, que permite instalar facilmente pacotes PHP.

Em seguida, crie uma nova pasta, abra um terminal dentro dessa pasta e execute composer init no terminal. Em seguida, percorra a caixa de diálogo com respostas como esta:

respostas de diálogorespostas de diálogorespostas de diálogo

As duas partes importantes são selecionar project como o tipo e adicione voku/simple_html_dom como uma dependência. Você pode configurar o resto como quiser.

Finalmente, crie um arquivo no diretório chamado simple_html_dom.php e coloque o seguinte no arquivo:

1

2
3
use vokuhelperHtmlDomParser;
4
5
require_once 'composer/autoload.php';
6

2. Parsing Basics

This library is very easy to use, but there are some basics you should review before putting it into action.

Loading HTML

1
// Load from a string

2
$dom = HtmlDomParser::str_get_html('

Hello World!

We're here

'
);
3
// Load from a file or URL

4
$dom = HtmlDomParser::file_get_html('https://bbc.com');

Você pode criar seu objeto inicial carregando HTML de uma string ou de um arquivo. O carregamento de um arquivo pode ser feito via URL ou por meio do sistema de arquivos local.

Uma nota de cautela: o load_file() método delega seu trabalho para PHP’s file_get_contents. Se allow_url_fopen não estiver definido como true em seu arquivo php.ini, talvez você não consiga abrir um arquivo remoto dessa maneira. Você sempre pode recorrer à biblioteca cURL para carregar páginas remotas nesse caso.

Acessando Informações

Depois de ter seu objeto DOM, você pode começar a trabalhar com ele usando find() e criação de coleções. Uma coleção é um grupo de objetos encontrados por meio de um seletor — a sintaxe é bastante semelhante à jQuery.

1

2

3
    

Hello World!

4
    

We're Here.

5

6

Neste exemplo de HTML, vamos dar uma olhada em como acessar as informações no segundo parágrafo, alterá-las e, em seguida, exibir os resultados.

1
// create & load the HTML

2
$dom = HtmlDomParser::str_get_html("

Hello World!

We're here

"
);
3
// get all paragraph elements

4
$element = $dom->find("p");
5
// modify the second

6
$element[1]->innerText .= " and we're here to stay.";
7
// print it

8
echo $dom->save();

Usar o método find() sempre retorna uma coleção (array) de tags, a menos que você especifique que deseja apenas o enésimo filho, como segundo parâmetro.

Primeiro carregamos o HTML de uma string, conforme explicado anteriormente.

o find chamada de método encontra tudo

tags no HTML e as retorna como uma matriz. O primeiro parágrafo terá um índice de 0 e os parágrafos subsequentes serão indexados de acordo.

Por fim, acessamos o segundo item de nossa coleção de parágrafos (índice 1) e fazemos uma adição ao seu innertext atributo. innertext representa o conteúdo entre as tags, enquanto outertext representa o conteúdo, incluindo a tag. Poderíamos substituir a tag inteiramente usando outertext.

Vamos adicionar mais uma linha e modificar a classe da nossa segunda tag de parágrafo.

1
$element[1]->class = "class_name";
2
echo $dom->save();

O HTML resultante do comando salvar seria:

1

2

3
    

Hello World!

4
     class="class_name">We're here and we're here to stay.

5

6

Outros seletores

Aqui estão alguns outros exemplos de seletores. Se você já usou jQuery, isso parecerá muito familiar.

1
// get the first occurrence of id="foo" 

2
$single = $dom->find('//foo', 0);
3
// get all elements with class="foo" 

4
$collection = $dom->find('.foo');
5
// get all the anchor tags on a page 

6
$collection = $dom->find('a');
7
// get all anchor tags that are inside H1 tags 

8
$collection = $dom->find('h1 a');
9
// get all img tags with a title of 'himom' 

10
$collection = $dom->find('img[title=himom]');

O primeiro exemplo não é totalmente intuitivo — todas as consultas por padrão retornam coleções, até mesmo uma consulta de ID, que deve retornar apenas um único resultado. No entanto, ao especificar o segundo parâmetro, estamos dizendo “retorne apenas o primeiro item desta coleção”.

Isso significa $single é um único elemento, em vez de uma matriz de elementos com um item.

O resto dos exemplos são auto-explicativos.

Documentação

A documentação completa sobre a biblioteca pode ser encontrada no GitHub oficial do projeto.

Exemplo de documentação da APIExemplo de documentação da APIExemplo de documentação da API

3. Um exemplo do mundo real

Para colocar esta biblioteca em ação, vamos escrever um script rápido para coletar o conteúdo do site Envato Tuts+ e produzir uma lista de artigos presentes no site por título e descrição… apenas como exemplo.

A raspagem pode causar problemas de negação de serviço e normalmente só deve ser realizada com permissão.

Tuts de raspagem de tela +Tuts de raspagem de tela +Tuts de raspagem de tela +

1
use vokuhelperHtmlDomParser;
2
require_once 'vendor/autoload.php';
3
4
$articles = array();
5
getArticles('https://code.tutsplus.com/tutorials');

Começamos incluindo a biblioteca e chamando o getArticles função com a página que gostaríamos de começar a analisar. Neste caso, estamos começando perto do fim e sendo gentis com o servidor do Tuts+.

Também estamos declarando uma matriz global para simplificar a coleta de todas as informações do artigo em um só lugar. Antes de começarmos a análise, vamos dar uma olhada em como o resumo de um artigo é descrito no Tuts+.

1
2
  
...
3
   class="posts__post-teaser">...
4
   class="posts__post-details">
5
     class="posts__post-teaser-overlay">
6
     class="posts__post-publication-meta">
7
      ...
8
       class="posts__post-details__info">
9
         class="posts__post-author">...
10
         class="posts__post-publication-date">...
11
      
12
    
13
     class="posts__post-primary-category">
14
       class="posts__post-primary-category-link topic-code" href="">...
15
    
16
  
17

Isso representa um formato básico de postagem no site, incluindo comentários do código-fonte. Por que os comentários são importantes? Eles contam como nós para o analisador.

4. Iniciando a função de análise

1
function getArticles($page) {
2
    global $articles;
3
    $html = HtmlDomParser::file_get_html($page);
4
    ...
5
}

Começamos obtendo a variável global $articles e carregar a página no Simple HTML DOM usando file_get_htmlcomo fizemos anteriormente. $page é a URL que passamos anteriormente


5. Encontrando as informações que queremos

1
$items = $html->find('article');
2
foreach($items as $post) {
3
    $articles[] = array(/* get title */ $post->findOne(".posts__post-title")->firstChild()->text(),
4
                        /* get description */ $post->findOne("posts__post-teaser")->text());
5
}

Esta é a carne do getArticles função. Vai dar uma olhada mais de perto para realmente entender o que está acontecendo.

Primeiro criamos um array de elementos—div’s com a classe de preview. Agora temos uma coleção de artigos armazenados em $items.

No bloco foreach, $post agora se refere a um único div da classe preview. Se olharmos para o HTML original, podemos ver que o título da postagem está contido no primeiro filho de posts__post-title. Portanto, para obter o título, pegamos o texto desse elemento.

A descrição está contida em posts_post-title. Tiramos o texto desse elemento e colocamos o segundo elemento no article item. Um único registro em artigos agora se parece com isto:

1
$articles[0][0] = "My Article Name Here";
2
$articles[0][1] = "This is my article description"

6. Paginação

A primeira coisa que fazemos é determinar como encontrar nossa próxima página. No Tuts+, as URLs são fáceis de descobrir, mas vamos fingir que não são e obter o próximo link por meio de análise.

Se olharmos para o HTML, veremos o seguinte:

1
 rel="next" class="pagination__button pagination__next-button" aria-label="next" href="/tutorials?page=2"> class="fa fa-angle-right">

Se houver uma próxima página (e nem sempre haverá), encontraremos uma âncora com a classe de nextpostslink. Agora essa informação pode ser colocada em uso.

1
if($next = $html->find('a[class=pagination__next-button]', 0)) {
2
    $URL = $next->href;
3
    $html->clear();
4
    unset($html);
5
    getArticles($URL);
6
}

Na primeira linha, vemos se conseguimos encontrar uma âncora com a classe pagination__next-button. Preste atenção especial ao segundo parâmetro para find(). Isso especifica que queremos apenas o primeiro elemento (índice 0) da coleção encontrada retornado. $next conterá apenas um único elemento, em vez de um grupo de elementos.

Em seguida, atribuímos o href do link à variável $URL. Isso é importante porque estamos prestes a destruir o objeto HTML. Devido a um vazamento de memória de referências circulares do PHP, $html deve ser limpo e desativado antes que outro seja criado. Não fazer isso pode fazer com que você consuma toda a memória disponível.

Finalmente, chamamos getArticles com a URL da próxima página. Essa recursão termina quando não há mais páginas para analisar.

Você terminou a raspagem! É assim que o código final deve ficar:

1

2
use vokuhelperHtmlDomParser;
3
require_once 'vendor/autoload.php';
4
5
$articles = array();
6
getArticles('https://code.tutsplus.com/tutorials');
7
8
function getArticles($page) {
9
    global $articles;
10
    $html = HtmlDomParser::file_get_html($page);
11
    $items = $html->find('article');
12
    foreach($items as $post) {
13
        $articles[] = array(/* get title */ $post->findOne(".posts__post-title")->firstChild()->text(),
14
                            /* get description */ $post->findOne("posts__post-teaser")->text());
15
    }
16
    if($next = $html->find('a[class=pagination__next-button]', 0)) {
17
        $URL = $next->href;
18
        $html->clear();
19
        unset($html);
20
        getArticles($URL);
21
    }
22
}

7. Saída dos resultados

Primeiro, vamos configurar alguns estilos básicos. Isso é completamente arbitrário←você pode fazer com que sua saída tenha a aparência que desejar.

1
#main {
2
    margin:80px auto;
3
    width:500px;
4
}
5
h1 {
6
    font:bold 40px/38px helvetica, verdana, sans-serif;
7
    margin:0;
8
}
9
h1 a {
10
    color:#600;
11
    text-decoration:none;
12
}
13
p {
14
    background: #ECECEC;
15
    font:10px/14px verdana, sans-serif;
16
    margin:8px 0 15px;
17
    border: 1px #CCC solid;
18
    padding: 15px;
19
}
20
.item {
21
    padding:10px;
22
}

Em seguida, vamos colocar um pouco de PHP na página para exibir as informações armazenadas anteriormente.

1

2
    foreach($articles as $item) {
3
        echo "
";
4
        echo $item[0];
5
        echo $item[1];
6
        echo "";
7
    }
8
?>

O resultado final é uma única página HTML listando todos os artigos, começando na página indicada pelo primeiro getArticles() ligar.


Conclusão

Se você estiver analisando muitas páginas (digamos, o site inteiro), pode demorar mais do que o tempo máximo de execução permitido pelo seu servidor. Por exemplo, executar a partir da minha máquina local leva cerca de um segundo por página (incluindo o tempo de busca).

Em sites grandes como o Envato Tuts+, isso pode levar muito tempo.

Este tutorial deve ajudá-lo a começar a analisar HTML. Existem outros métodos para trabalhar com o DOM, incluindo o interno do PHP, que permite trabalhar com seletores xpath poderosos para localizar elementos. Mas, para facilidade de uso e início rápido, considero esta biblioteca uma das melhores.

Como nota final, lembre-se sempre de obter permissão antes de raspar um site: isso é importante. Obrigado por ler!

Deixe uma resposta