it-swarm.dev

Como você analisa e processa HTML/XML em PHP?

Como se pode analisar HTML/XML e extrair informações dele?

2021
RobertPitt

Tente Simple HTML DOM Parser

  • Um analisador de HTML DOM escrito em PHP 5+ que permite manipular HTML de uma maneira muito fácil!
  • Requerer PHP 5+.
  • Suporta HTML inválido.
  • Encontre tags em uma página HTML com seletores como o jQuery.
  • Extraia o conteúdo do HTML em uma única linha.
  • Download


Exemplos:

Como obter elementos HTML:

// Create DOM from URL or file
$html = file_get_html('http://www.example.com/');

// Find all images
foreach($html->find('img') as $element)
       echo $element->src . '<br>';

// Find all links
foreach($html->find('a') as $element)
       echo $element->href . '<br>';


Como modificar elementos HTML:

// Create DOM from string
$html = str_get_html('<div id="hello">Hello</div><div id="world">World</div>');

$html->find('div', 1)->class = 'bar';

$html->find('div[id=hello]', 0)->innertext = 'foo';

echo $html;


Extraia o conteúdo do HTML:

// Dump contents (without tags) from HTML
echo file_get_html('http://www.google.com/')->plaintext;


Raspar Slashdot:

// Create DOM from URL
$html = file_get_html('http://slashdot.org/');

// Find all article blocks
foreach($html->find('div.article') as $article) {
    $item['title']     = $article->find('div.title', 0)->plaintext;
    $item['intro']    = $article->find('div.intro', 0)->plaintext;
    $item['details'] = $article->find('div.details', 0)->plaintext;
    $articles[] = $item;
}

print_r($articles);
316
Naveed

Apenas use DOMDocument-> loadHTML () e termine com isto. O algoritmo de análise de HTML da libxml é bastante bom e rápido, e ao contrário da crença popular, não se afoga em HTML malformado.

230
Edward Z. Yang

Por que você não deveria e quando deveria usar expressões regulares?

Primeiro, um equívoco comum: Regexps não são para " parsing " HTML. Regexes podem no entanto " extract " data. Extração é o que eles são feitos para. A principal desvantagem da extração de expressões regulares de HTML sobre kits de ferramentas SGML ou analisadores de XML de linha de base é seu esforço sintático e confiabilidade variável.

Considere que fazer uma regex de extração de HTML um pouco confiável:

<a\s+class="?playbutton\d?[^>]+id="(\d+)".+?    <a\s+class="[\w\s]*title
[\w\s]*"[^>]+href="(http://[^">]+)"[^>]*>([^<>]+)</a>.+?

é muito menos legível que um simples equivalente phpQuery ou QueryPath:

$div->find(".stationcool a")->attr("title");

No entanto, existem casos de uso específicos em que eles podem ajudar.

  • Muitos frontends de travessia de DOM não revelam comentários HTML <!--, que, no entanto, às vezes são as âncoras mais úteis para extração. Em particular, variações pseudo-HTML <$var> ou resíduos SGML são fáceis de domar com regexps.
  • Muitas vezes, expressões regulares podem economizar pós-processamento. No entanto, as entidades HTML geralmente exigem manutenção manual.
  • E por último, para e tarefas extremamente simples como extrair <img src = urls, elas são de fato uma ferramenta provável. A vantagem de velocidade em relação aos analisadores SGML/XML, na maioria das vezes, apenas vem para esses procedimentos de extração muito básicos.

Às vezes, é até aconselhável extrair previamente um fragmento de HTML usando expressões regulares /<!--CONTENT-->(.+?)<!--END-->/ e processar o restante usando os frontends do analisador de HTML mais simples.

Nota: Eu realmente tenho este app , onde eu emprego XML análise e expressões regulares em alternativa. Na semana passada, a análise do PyQuery quebrou e o regex ainda funcionou. Sim, estranho, e eu não posso explicar isso sozinho. Mas assim aconteceu.
Então, por favor, não baixe as considerações do mundo real, apenas porque ele não combina com o meme regex = mal. Mas também não vamos votar muito nisso. É apenas uma sidenote para este tópico.

143
mario

phpQuery e QueryPath são extremamente semelhantes na replicação da API jQuery fluente. É também por isso que eles são duas das abordagens mais fáceis para corretamente analisar HTML em PHP.

Exemplos para QueryPath

Basicamente, você primeiro cria uma árvore DOM que pode ser consultada a partir de uma string HTML:

 $qp = qp("<html><body><h1>title</h1>..."); // or give filename or URL

O objeto resultante contém uma representação completa da árvore do documento HTML. Pode ser percorrido usando métodos DOM. Mas a abordagem comum é usar seletores CSS como no jQuery:

 $qp->find("div.classname")->children()->...;

 foreach ($qp->find("p img") as $img) {
     print qp($img)->attr("src");
 }

Principalmente, você quer usar simples seletores de tags #id e .class ou DIV para ->find(). Mas você também pode usar instruções XPath , que às vezes são mais rápidas. Também os métodos típicos de jQuery, como ->children() e ->text() e particularmente ->attr(), simplificam a extração dos snippets HTML corretos. (E já tem suas entidades SGML decodificadas.)

 $qp->xpath("//div/p[1]");  // get first paragraph in a div

O QueryPath também permite injetar novas tags no fluxo (->append) e, posteriormente, imprimir e aperfeiçoar um documento atualizado (->writeHTML). Ele não só pode analisar HTML malformado, mas também vários dialetos XML (com namespaces) e até extrair dados de microformatos HTML (XFN, vCard).

 $qp->find("a[target=_blank]")->toggleClass("usability-blunder");

.

phpQuery ou QueryPath?

Geralmente, o QueryPath é mais adequado para manipulação de documentos. Enquanto o phpQuery também implementa alguns métodos pseudo AJAX (apenas pedidos HTTP) para se assemelhar mais com o jQuery. Diz-se que o phpQuery é frequentemente mais rápido que o QueryPath (por causa de menos recursos gerais).

Para mais informações sobre as diferenças, veja esta comparação na máquina de wayback de tagbyte.org . (Fonte original desapareceu, então aqui está um link de arquivo da internet. Sim, você ainda pode localizar páginas ausentes, pessoas.)

E aqui está uma introdução abrangente ao QueryPath .

Vantagens

  • Simplicidade e Confiabilidade
  • Simples de usar alternativas ->find("a img, a object, div a")
  • Dados corretos sem escape (em comparação com a expressão regular grepping)
129
mario

HTML simples DOM é um ótimo analisador de código aberto:

simplehtmldom.sourceforge

Ele trata os elementos DOM de uma maneira orientada a objetos, e a nova iteração tem muita cobertura para códigos não compatíveis. Há também algumas funções excelentes, como a que você veria em JavaScript, como a função "find", que retornará todas as instâncias de elementos desse nome de tag.

Eu usei isso em várias ferramentas, testando-o em muitos tipos diferentes de páginas da Web, e acho que funciona muito bem.

88
Robert Elwell

Uma abordagem geral que eu não vi mencionada aqui é executar HTML através de Tidy , que pode ser configurado para citar XHTML com validade garantida. Então você pode usar qualquer biblioteca XML antiga nela.

Mas para o seu problema específico, você deve dar uma olhada neste projeto: http://fivefilters.org/content-only/ - é uma versão modificada do algoritmo Readability , que é projetado para extrair apenas o conteúdo textual (não cabeçalhos e rodapés) de uma página.

59
Eli

Para 1a e 2: eu votaria na nova classe DOMCrawler do Symfony Componet ( DomCrawler ). Esta classe permite consultas semelhantes aos Seletores de CSS. Dê uma olhada nesta apresentação para exemplos do mundo real: news-of-the-symfony2-world .

O componente foi projetado para funcionar de forma independente e pode ser usado sem o Symfony.

A única desvantagem é que ele só funcionará com PHP 5.3 ou mais recente.

55
Timo

Isso é comumente chamado de screen scraping , a propósito. A biblioteca que eu usei para isso é Simple HTML Dom Parser .

52
Joel Verhagen

Nós criamos alguns rastreadores para as nossas necessidades antes. No final do dia, geralmente são expressões regulares simples que fazem a coisa melhor. Embora as bibliotecas listadas acima sejam boas pelo motivo de serem criadas, se você sabe o que está procurando, expressões regulares são um caminho mais seguro, já que você pode manipular também não-válidas HTML / XHTML structures, que falharia, se carregado pela maioria dos analisadores.

41
jancha

Eu recomendo PHP Simple HTML DOM Parser .

Realmente tem recursos legais, como:

foreach($html->find('img') as $element)
       echo $element->src . '<br>';
38
Greg

Isso soa como uma boa descrição de tarefa do W3C XPath technology. É fácil expressar consultas como "retornar todos os atributos href em tags img que estão aninhados em <foo><bar><baz> elements." Não sendo um PHP buff, não posso dizer de que forma o XPath pode estar disponível. Se você puder chamar um programa externo para processar o arquivo HTML, poderá usar uma versão de linha de comando do XPath. Para uma introdução rápida, consulte http://en.wikipedia.org/wiki/XPath .

36
Jens

Alternativas de terceiros para SimpleHtmlDom que usam DOM em vez de String Parsing: phpQuery , Zend_Dom , QueryPath e FluentDom .

29
danidacar

Sim, você pode usar simple_html_dom para o efeito. No entanto, trabalhei bastante com o simple_html_dom, particularmente para o web scrapping e descobri que ele é muito vulnerável. Ele faz o trabalho básico, mas eu não vou recomendá-lo de qualquer maneira.

Eu nunca usei curl para o propósito, mas o que eu aprendi é que o curl pode fazer o trabalho com muito mais eficiência e é muito mais sólido.

Por favor, verifique este link: scraping-websites-with-curl

24
Rafay

QueryPath é bom, mas tome cuidado com "estado de rastreamento" porque se você não percebeu o que isso significa, pode significar que você desperdiça muito tempo de depuração tentando descobrir o que aconteceu e por que o código não trabalhos.

O que isso significa é que cada chamada no conjunto de resultados modifica o conjunto de resultados no objeto, não é disponível em cadeia como em jquery, onde cada link é um novo conjunto, você tem um único conjunto que é os resultados de sua consulta e cada chamada de função modifica esse conjunto único.

para obter um comportamento parecido com o jquery, você precisa se ramificar antes de fazer um filtro/modificar uma operação parecida, o que significa que ele irá espelhar o que acontece no jquery com muito mais atenção.

$results = qp("div p");
$forename = $results->find("input[name='forename']");

$results agora contém o conjunto de resultados para input[name='forename'] NÃO a consulta original "div p" isso me tropeçou muito, o que eu achei foi que QueryPath rastreia os filtros e localiza e tudo o que modifica seus resultados e os armazena no objeto. você precisa fazer isso

$forename = $results->branch()->find("input[name='forname']")

então $results não será modificado e você pode reutilizar o conjunto de resultados novamente e novamente, talvez alguém com muito mais conhecimento possa esclarecer isso um pouco, mas é basicamente assim do que eu encontrei.

23
Christopher Thomas

Advanced Html Dom é um simples HTML DOM substituição que oferece a mesma interface, mas é baseado em DOM, o que significa que nenhum dos problemas de memória associados ocorrem.

Ele também tem suporte total a CSS, incluindo jQuery extensions.

19
pguardiario

Para HTML5 , o html5 lib foi abandonado há anos. A única biblioteca HTML5 que eu posso encontrar com uma atualização recente e registros de manutenção é html5-php que acabou de ser lançada na versão beta 1.0 há pouco mais de uma semana.

18
Reid Johnson

Eu escrevi um analisador XML de uso geral que pode facilmente manipular arquivos GB. É baseado no XMLReader e é muito fácil de usar:

$source = new XmlExtractor("path/to/tag", "/path/to/file.xml");
foreach ($source as $tag) {
    echo $tag->field1;
    echo $tag->field2->subfield1;
}

Aqui está o repositório do github: XmlExtractor

17
Paul Warelis

Eu criei uma biblioteca chamada PHPPowertools/DOM-Query , que permite que você rastreie documentos HTML5 e XML como faz com o jQuery.

Sob o capô, ele usa symfony/DomCrawler para conversão de seletores CSS para XPath seletores. Ele sempre usa o mesmo DomDocument, mesmo ao passar um objeto para outro, para garantir um desempenho decente.


Exemplo de uso:

namespace PowerTools;

// Get file content
$htmlcode = file_get_contents('https://github.com');

// Define your DOMCrawler based on file string
$H = new DOM_Query($htmlcode);

// Define your DOMCrawler based on an existing DOM_Query instance
$H = new DOM_Query($H->select('body'));

// Passing a string (CSS selector)
$s = $H->select('div.foo');

// Passing an element object (DOM Element)
$s = $H->select($documentBody);

// Passing a DOM Query object
$s = $H->select( $H->select('p + p'));

// Select the body tag
$body = $H->select('body');

// Combine different classes as one selector to get all site blocks
$siteblocks = $body->select('.site-header, .masthead, .site-body, .site-footer');

// Nest your methods just like you would with jQuery
$siteblocks->select('button')->add('span')->addClass('icon icon-printer');

// Use a lambda function to set the text of all site blocks
$siteblocks->text(function( $i, $val) {
    return $i . " - " . $val->attr('class');
});

// Append the following HTML to all site blocks
$siteblocks->append('<div class="site-center"></div>');

// Use a descendant selector to select the site's footer
$sitefooter = $body->select('.site-footer > .site-center');

// Set some attributes for the site's footer
$sitefooter->attr(array('id' => 'aweeesome', 'data-val' => 'see'));

// Use a lambda function to set the attributes of all site blocks
$siteblocks->attr('data-val', function( $i, $val) {
    return $i . " - " . $val->attr('class') . " - photo by Kelly Clark";
});

// Select the parent of the site's footer
$sitefooterparent = $sitefooter->parent();

// Remove the class of all i-tags within the site's footer's parent
$sitefooterparent->select('i')->removeAttr('class');

// Wrap the site's footer within two nex selectors
$sitefooter->wrap('<section><div class="footer-wrapper"></div></section>');

[...]

Métodos suportados:


  1. Renomeado 'select', por razões óbvias
  2. Renomeado 'void', já que 'empty' é uma palavra reservada em PHP

NOTA :

A biblioteca também inclui seu próprio autoloader de configuração zero para bibliotecas compatíveis com o PSR-0. O exemplo incluído deve funcionar imediatamente sem nenhuma configuração adicional. Como alternativa, você pode usá-lo com o compositor.

17
John Slegers

Você poderia tentar usar algo como HTML Tidy para limpar qualquer HTML "quebrado" e converter o HTML em XHTML, que você pode então analisar com um analisador XML.

15
CesarB

Outra opção que você pode tentar é QueryPath . É inspirado pelo jQuery, mas no servidor em PHP e usado em Drupal .

15
Richard Le Poidevin

XML_HTMLSax é bastante estável - mesmo que não seja mais mantido. Outra opção poderia ser canalizar você HTML através de Html Tidy e, em seguida, analisá-lo com ferramentas XML padrão.

12
troelskn

O Symfony framework possui pacotes que podem analisar o HTML, e você pode usar o estilo CSS para selecionar os DOMs em vez de usar XPath .

11
Tuong Le

Há muitas maneiras de processar DOM HTML/XML, das quais a maioria já foi mencionada. Por isso, não farei nenhuma tentativa de listar os mesmos.

Eu só quero acrescentar que eu pessoalmente prefiro usar a extensão DOM e por quê:

  • faz o uso ideal da vantagem de desempenho do código C subjacente
  • é OO PHP (e me permite subclassificá-lo)
  • é um nível bastante baixo (o que me permite usá-lo como uma base não inchada para um comportamento mais avançado)
  • ele fornece acesso a todas as partes do DOM (ao contrário, por exemplo, SimpleXml, que ignora alguns dos recursos XML menos conhecidos)
  • ele tem uma sintaxe usada para o rastreamento do DOM que é semelhante à sintaxe usada no Javascript nativo.

E embora eu perca a capacidade de usar seletores CSS para DOMDocument, há uma maneira bastante simples e conveniente de adicionar esse recurso: subclassificando o DOMDocument e adicionando os métodos querySelectorAll e querySelector de JS à sua subclasse.

Para analisar os seletores, recomendo usar o componente muito minimalista CssSelector do framework Symfony . Esse componente apenas traduz seletores CSS para seletores XPath, que podem então ser alimentados em um DOMXpath para recuperar o Nodelist correspondente.

Você pode então usar esta subclasse (ainda de nível muito baixo) como base para mais classes de alto nível, destinadas a, por exemplo. analisar tipos muito específicos de XML ou adicionar mais comportamentos semelhantes a jQuery.

O código abaixo vem direto da minha biblioteca DOM-Query e usa a técnica que descrevi.

Para análise de HTML:

namespace PowerTools;

use \Symfony\Component\CssSelector\CssSelector as CssSelector;

class DOM_Document extends \DOMDocument {
    public function __construct($data = false, $doctype = 'html', $encoding = 'UTF-8', $version = '1.0') {
        parent::__construct($version, $encoding);
        if ($doctype && $doctype === 'html') {
            @$this->loadHTML($data);
        } else {
            @$this->loadXML($data);
        }
    }

    public function querySelectorAll($selector, $contextnode = null) {
        if (isset($this->doctype->name) && $this->doctype->name == 'html') {
            CssSelector::enableHtmlExtension();
        } else {
            CssSelector::disableHtmlExtension();
        }
        $xpath = new \DOMXpath($this);
        return $xpath->query(CssSelector::toXPath($selector, 'descendant::'), $contextnode);
    }

    [...]

    public function loadHTMLFile($filename, $options = 0) {
        $this->loadHTML(file_get_contents($filename), $options);
    }

    public function loadHTML($source, $options = 0) {
        if ($source && $source != '') {
            $data = trim($source);
            $html5 = new HTML5(array('targetDocument' => $this, 'disableHtmlNsInDom' => true));
            $data_start = mb_substr($data, 0, 10);
            if (strpos($data_start, '<!DOCTYPE ') === 0 || strpos($data_start, '<html>') === 0) {
                $html5->loadHTML($data);
            } else {
                @$this->loadHTML('<!DOCTYPE html><html><head><meta charset="' . $encoding . '" /></head><body></body></html>');
                $t = $html5->loadHTMLFragment($data);
                $docbody = $this->getElementsByTagName('body')->item(0);
                while ($t->hasChildNodes()) {
                    $docbody->appendChild($t->firstChild);
                }
            }
        }
    }

    [...]
}

Veja também Analisando documentos XML com seletores CSS pelo criador do Symfony Fabien Potencier em sua decisão de criar o componente CssSelector para Symfony e como usá-lo.

11
John Slegers

Com FluidXML você pode consultar e iterar XML usando XPath e Seletores CSS .

$doc = fluidxml('<html>...</html>');

$title = $doc->query('//head/title')[0]->nodeValue;

$doc->query('//body/p', 'div.active', '#bgId')
        ->each(function($i, $node) {
            // $node is a DOMNode.
            $tag   = $node->nodeName;
            $text  = $node->nodeValue;
            $class = $node->getAttribute('class');
        });

https://github.com/servo-php/fluidxml

9
Daniele Orlando

JSON e array de XML em três linhas:

$xml = simplexml_load_string($xml_string);
$json = json_encode($xml);
$array = json_decode($json,TRUE);

Ta da!

7
Antonio Max

Existem várias razões para não analisar HTML por expressão regular. Mas, se você tem controle total de qual HTML será gerado, então você pode fazer com a expressão regular simples.

Acima é uma função que analisa HTML por expressão regular. Note que esta função é muito sensível e exige que o HTML obedeça a certas regras, mas funciona muito bem em muitos cenários. Se você quiser um analisador simples e não quiser instalar bibliotecas, dê uma chance a isso:

function array_combine_($keys, $values) {
    $result = array();
    foreach ($keys as $i => $k) {
        $result[$k][] = $values[$i];
    }
    array_walk($result, create_function('&$v', '$v = (count($v) == 1)? array_pop($v): $v;'));

    return $result;
}

function extract_data($str) {
    return (is_array($str))
        ? array_map('extract_data', $str)
        : ((!preg_match_all('#<([A-Za-z0-9_]*)[^>]*>(.*?)</\1>#s', $str, $matches))
            ? $str
            : array_map(('extract_data'), array_combine_($matches[1], $matches[2])));
}

print_r(extract_data(file_get_contents("http://www.google.com/")));
7
Daniel Loureiro

Eu criei uma biblioteca chamada HTML5DOMDocument que está disponível gratuitamente em https://github.com/ivopetkov/html5-dom-document-php

Ele também suporta seletores de consulta que, acredito, serão extremamente úteis no seu caso. Aqui está algum código de exemplo:

$dom = new IvoPetkov\HTML5DOMDocument();
$dom->loadHTML('<!DOCTYPE html><html><body><h1>Hello</h1><div class="content">This is some text</div></body></html>');
echo $dom->querySelector('h1')->innerHTML;
2
Ivo Petkov

Se você estiver familiarizado com o seletor jQuery, você pode usar ScarletsQuery para PHP

<pre><?php
include "ScarletsQuery.php";

// Load the HTML content and parse it
$html = file_get_contents('https://www.lipsum.com');
$dom = Scarlets\Library\MarkupLanguage::parseText($html);

// Select meta tag on the HTML header
$description = $dom->selector('head meta[name="description"]')[0];

// Get 'content' attribute value from meta tag
print_r($description->attr('content'));

$description = $dom->selector('#Content p');

// Get element array
print_r($description->view);

Esta biblioteca geralmente leva menos de 1 segundo para processar o html off-line.
Também aceita HTML inválido ou falta de cotação nos atributos da tag.

0
StefansArya

O melhor método para analisar xml:

$xml='http://www.example.com/rss.xml';
$rss = simplexml_load_string($xml);
$i = 0;
foreach ($rss->channel->item as $feedItem) {
$i++;
echo $title=$feedItem->title;
echo '<br>';
echo $link=$feedItem->link;
echo '<br>';
if($feedItem->description !='') {$des=$feedItem->description;} else {$des='';}
echo $des;
echo '<br>';
if($i>5) break;
}
0
user8031209