it-swarm.dev

Jak analyzujete a zpracováváte HTML/XML v PHP?

Jak lze analyzovat HTML/XML a extrahovat z něj informace?

2021
RobertPitt

Try Jednoduchý HTML analyzátor DOM

  • HTML DOM parser napsaný v PHP 5+, který umožňuje manipulovat s HTML velmi snadným způsobem!
  • Vyžadovat PHP 5+.
  • Podporuje neplatné HTML.
  • Najít tagy na stránce HTML s voliči jako jQuery.
  • Extrahovat obsah z HTML v jednom řádku.
  • Stažení


Příklady:

Jak získat prvky 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>';


Jak upravit prvky 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;


Extrahovat obsah z HTML:

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


Škrábání 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

Stačí použít DOMDocument-> loadHTML () a provést s ním. libxml HTML parsovací algoritmus je docela dobrý a rychlý, a na rozdíl od všeobecného přesvědčení neškrtí znetvořený HTML.

230
Edward Z. Yang

Proč byste neměli a když byste měli používat regulární výrazy?

Za prvé, obyčejný nesprávný název: Regexps nejsou pro " parsing " HTML. Regexes však může " extract " data. Vytahování je to, na co jsou určeny. Hlavní nevýhodou regex HTML extrakce přes správné SGML sady nástrojů nebo základní parsery XML jsou jejich syntaktické úsilí a různá spolehlivost.

Zvažte, že vytvoření poněkud spolehlivého regexu HTML extrakce:

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

je méně čitelný než jednoduchý ekvivalent phpQuery nebo QueryPath:

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

Existují však specifické případy, kdy mohou pomoci.

  • Mnoho DOM traversal frontends neodhalí HTML komentáře <!--, které jsou však někdy užitečnější kotvy pro extrakci. Zejména pseudo-HTML variace <$var> nebo SGML zbytky lze snadno zkrotit pomocí regexps.
  • Často regulární výrazy mohou ukládat následné zpracování. Subjekty HTML však často vyžadují ruční správu.
  • A konečně, pro e xtremely simple tasks jako extrahování <img src = urls, jsou ve skutečnosti pravděpodobným nástrojem. Rychlostní výhoda oproti SGML/XML parseru většinou přichází hrát jen pro tyto základní postupy extrakce.

Někdy je dokonce vhodné předem extrahovat útržek HTML pomocí regulárních výrazů /<!--CONTENT-->(.+?)<!--END-->/ a zbytek zpracovat pomocí jednodušších rozhraní HTML parseru.

Poznámka: Vlastně mám tento app , kde alternativně používám analýzu XML a regulární výrazy. Jen minulý týden se rozpadla PyQuery parsing a regex stále fungoval. Ano, zvláštní, a já to nedokážu vysvětlit. Ale tak se to stalo.
Takže prosím, neberte do úvahy reálné úvahy dolů, jen proto, že neodpovídají regex = zlé meme. Ale ani to moc nehlasujme. Je to jen sidenote pro toto téma.

143
mario

phpQuery a QueryPath jsou velmi podobné v replikaci plynulého jQuery API. To je také důvod, proč jsou dva nejjednodušší přístupy k správně parse HTML v PHP.

Příklady pro dotaz

V podstatě nejprve vytvoříte dotazovatelný strom DOM z řetězce HTML:

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

Výsledný objekt obsahuje kompletní stromovou reprezentaci HTML dokumentu. To může být procházeno přes DOM metody. Ale společným přístupem je použití CSS selektorů jako v jQuery:

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

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

Většinou chcete použít jednoduché #id a .class nebo DIV selektory tagů pro ->find(). Můžete také použít příkazy XPath , které jsou někdy rychlejší. Také typické metody jQuery jako ->children() a ->text() a zejména ->attr() zjednodušují extrahování správných útržků HTML. (A už mají své SGML entity dekódovány.)

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

QueryPath také umožňuje vstřikování nových tagů do toku (->append) a pozdější výstup a předběžnou úpravu aktualizovaného dokumentu (->writeHTML). To může ne jen analyzovat malformed HTML, ale také různé XML dialekty (s jmennými prostory), a dokonce extrahovat data od HTML microformats (XFN, vCard).

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

.

phpQuery nebo QueryPath?

Obecně QueryPath je vhodnější pro manipulaci s dokumenty. Zatímco phpQuery také implementuje některé pseudo AJAX metody (jen HTTP požadavky), aby se více podobaly jQuery. Říká se, že phpQuery je často rychlejší než QueryPath (kvůli méně celkovým funkcím).

Další informace o rozdílech viz toto porovnání na trase ze serveru tagbyte.org . (Původní zdroj zmizel, takže zde je odkaz na internetový archiv. Ano, stále můžete najít chybějící stránky, osoby.)

A zde je komplexní úvod do dotazu .

Výhody

  • Jednoduchost a spolehlivost
  • Jednoduché alternativy ->find("a img, a object, div a")
  • Vlastní unescaping dat (ve srovnání s greppingem regulárních výrazů)
129
mario

Jednoduchý HTML DOM je skvělý parser:

simplehtmldom.sourceforge

Zachází s prvky DOM objektově orientovaným způsobem a nová iterace má spoustu pokrytí pro nevyhovující kód. Tam jsou také některé skvělé funkce, jako byste viděli v JavaScriptu, jako je funkce "najít", která vrátí všechny instance prvků tohoto názvu značky.

Použil jsem to v řadě nástrojů, testoval jsem je na mnoha různých typech webových stránek a myslím, že to funguje skvěle.

88
Robert Elwell

Jedním z obecných přístupů, které jsem zde neviděl, je spuštění HTML přes Tidy , které lze nastavit tak, aby vyplňoval garantovaný XHTML. Pak můžete použít libovolnou starou knihovnu XML.

Ale k vašemu konkrétnímu problému byste se měli podívat na tento projekt: http://fivefilters.org/content-only/ - je to upravená verze algoritmu čitelnosti , který je navržen tak, aby extrahovat ze stránky pouze textový obsah (nikoli záhlaví a zápatí).

59
Eli

Pro 1a a 2: Hlasoval bych pro novou třídu Symfony Componet DOMCrawler ( DomCrawler ). Tato třída umožňuje dotazy podobné CSS Selectorům. Podívejte se na tuto prezentaci pro reálné příklady: news-of-the-symfony2-world .

Komponenta je navržena tak, aby fungovala samostatně a lze ji používat bez aplikace Symfony.

Jedinou nevýhodou je, že bude fungovat pouze s PHP 5.3 nebo novějšími.

55
Timo

Toto je obyčejně odkazoval se na jak scraping obrazovky , mimochodem. Knihovna, kterou jsem použil, je Simple HTML Dom Parser .

52
Joel Verhagen

Pro naše potřeby jsme vytvořili poměrně málo prohledávačů. Na konci dne to jsou obvykle jednoduché regulární výrazy. Zatímco knihovny uvedené výše jsou dobré z toho důvodu, že jsou vytvořeny, pokud víte, co hledáte, regulární výrazy jsou bezpečnější cestou, protože můžete zpracovávat i neplatné HTML / XHTML structures, které by selhaly, pokud by byly načteny přes většinu analyzátorů.

41
jancha

Doporučuji PHP Jednoduchý HTML DOM Parser .

Má opravdu pěkné vlastnosti, jako například:

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

To zní jako dobrý popis technologie W3C XPath technology. Je snadné vyjádřit dotazy jako "vrátit všechny href atributy v img tagech, které jsou vnořeny v <foo><bar><baz> elements." Není to PHP buff, nemůžu vám říct, v jaké formě může být XPath k dispozici. Pokud můžete zavolat externí program ke zpracování souboru HTML, měli byste být schopni použít verzi příkazového řádku XPath. Rychlý úvod naleznete na stránkách http://en.wikipedia.org/wiki/XPath .

36
Jens

Alternativy třetích stran k SimpleHtmlDom, které používají DOM namísto String Parsing: phpQuery , Zend_Dom , QueryPath a FluentDom .

29
danidacar

Ano, pro tento účel můžete použít simple_html_dom. Nicméně jsem hodně pracoval s simple_html_dom, zejména pro web šrotování a zjistili jsme, že je příliš zranitelný. To dělá základní práci, ale já to nedoporučuji stejně.

Nikdy jsem nepoužíval curl pro tento účel, ale to, co jsem se naučil, je, že curl může dělat práci mnohem efektivněji a je mnohem pevnější.

Podívejte se na tento odkaz: scraping-website-with-curl

24
Rafay

QueryPath je dobré, ale dávejte pozor na "sledovací stav", protože pokud jste si neuvědomili, co to znamená, může to znamenat, že ztrácíte spoustu času ladění a snažíte se zjistit, co se stalo a proč kód neznamená práce.

Znamená to, že každý hovor na výsledkové sadě modifikuje výsledný soubor v objektu, není to řetězitelný jako v jQuery, kde každý odkaz je nový soubor, máte jednu sadu, která je výsledkem vašeho dotazu a každé volání funkce modifikuje tento jediný soubor.

aby bylo možné získat jquery-like chování, musíte větvit dříve, než uděláte filtr/upravit podobnou operaci, to znamená, že bude zrcadlit to, co se děje v jquery mnohem podrobněji.

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

$results nyní obsahuje sadu výsledků pro input[name='forename'] NOT původní dotaz "div p" mě to hodně zakoplo, co jsem zjistil, bylo to, že QueryPath sleduje filtry a nálezy a vše, co mění vaše výsledky a ukládá je do objektu. musíte to udělat místo toho

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

pak $results nebude upraven a můžete znovu a znovu použít sadu výsledků, možná to někdo, kdo má mnohem více znalostí, to může trochu vyčistit, ale je to v podstatě takové, co jsem našel.

23

Advanced Html Dom je jednoduché HTML DOM nahrazení, které nabízí stejné rozhraní, ale je to DOM, což znamená, že se nevyskytují žádné problémy spojené s pamětí.

Má také plnou podporu CSS, včetně jQuery extensions.

19
pguardiario

Pro HTML5 , html5 lib byl již léta opuštěn. Jediná knihovna HTML5, kterou mohu najít s nedávnou aktualizací a záznamy o údržbě, je html5-php která byla právě před týdnem přivedena do beta verze 1.0.

18
Reid Johnson

Napsal jsem univerzální XML parser, který dokáže snadno zpracovat GB soubory. Je založen na XMLReaderu a je velmi snadno použitelný:

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

Zde je github repo: XmlExtractor

17
Paul Warelis

Vytvořil jsem knihovnu s názvem PHPPowertools/DOM-Query , která vám umožňuje procházet dokumenty HTML5 a XML stejně jako jQuery.

Pod kapotou používá symfony/DomCrawler pro převod CSS selektorů na XPath selectory. To vždy používá stejný DomDocument, dokonce když projde jeden objekt k jinému, zajistit slušný výkon.


Příklad použití:

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>');

[...]

Podporované metody:


  1. Přejmenováno na „select“, ze zřejmých důvodů
  2. Přejmenováno na 'void', protože 'empty' je vyhrazené slovo v PHP

POZNÁMKA :

Knihovna také obsahuje vlastní nulovou konfiguraci autoloaderu pro knihovny kompatibilní s PSR-0. Zahrnutý příklad by měl fungovat mimo krabici bez jakékoli další konfigurace. Alternativně jej můžete použít se skladatelem.

17
John Slegers

Můžete zkusit použít něco jako HTML Tidy k vyčištění jakéhokoliv "zlomeného" HTML a převést HTML na XHTML, který pak můžete analyzovat pomocí XML parseru.

15
CesarB

Další možností, kterou můžete vyzkoušet, je QueryPath . Je inspirován jQuery, ale na serveru v PHP a používán v Drupalu .

15

XML_HTMLSax je poměrně stabilní - i když už není udržován. Další možností by mohlo být připojení HTML pomocí Html Tidy a následná analýza pomocí standardních XML nástrojů.

12
troelskn

Symfony framework má svazky, které mohou analyzovat HTML, a můžete použít styl CSS pro výběr DOMs namísto použití XPath .

11
Tuong Le

Existuje mnoho způsobů, jak zpracovat HTML/XML DOM, z nichž většina již byla zmíněna. Proto se nebudu pokoušet o seznam těch samých.

Chci jen dodat, že osobně dávám přednost použití rozšíření DOM a proč:

  • iit optimálně využívá výkonnostní výhodu podkladového kódu C
  • je to OO PHP (a dovoluje mi jej podtřídy)
  • je to poněkud nízká úroveň (což mi umožňuje používat je jako ne-nafouklý základ pro pokročilejší chování)
  • poskytuje přístup ke každé části DOM (na rozdíl od např. SimpleXml, který ignoruje některé méně známé funkce XML)
  • má syntaxi používanou pro procházení DOM, která je podobná syntaxi použité v nativním jazyce Javascript.

A i když mi chybí možnost použití voličů CSS pro DOMDocument, existuje poměrně jednoduchý a pohodlný způsob, jak přidat tuto funkci: subclassing DOMDocument a přidání metod querySelectorAll a querySelector podobných JS do vaší podtřídy.

Pro analýzu selektorů doporučuji použít velmi minimalistickou komponentu CssSelector z Symfony framework . Tato komponenta pouze převádí CSS voliče na selektory XPath, které pak mohou být přiváděny do DOMXpath pro načtení odpovídajícího nodelistu.

Potom můžete použít tuto podtřídu (stále velmi nízkou úroveň) jako základ pro vyšší třídy, které mají být např. analyzovat velmi specifické typy XML nebo přidat další chování podobné jQuery.

Níže uvedený kód vychází přímo z mé DOM-Query knihovny a používá techniku, kterou jsem popsal.

Pro analýzu 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);
                }
            }
        }
    }

    [...]
}

Viz také Parsování XML dokumentů s voliči CSS tvůrcem Symfony Fabien Potencier o jeho rozhodnutí vytvořit komponentu CssSelector pro Symfony a jak ji používat.

11
John Slegers

S FluidXML můžete dotazovat a iterovat XML pomocí XPath a CSS Selectors .

$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 a pole z XML ve třech řádcích:

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

Ta da!

7
Antonio Max

Existuje několik důvodů, proč nechcete analyzovat HTML regulárním výrazem. Pokud však máte úplnou kontrolu nad tím, co bude HTML generováno, můžete to udělat s jednoduchým regulárním výrazem.

Nad ní je funkce, která analyzuje HTML regulárním výrazem. Všimněte si, že tato funkce je velmi citlivá a vyžaduje, aby HTML dodržoval určitá pravidla, ale v mnoha scénářích funguje velmi dobře. Pokud chcete jednoduchý analyzátor a nechcete instalovat knihovny, udělejte tento snímek:

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

Vytvořil jsem knihovnu nazvanou HTML5DOMDocument, která je volně dostupná na adrese https://github.com/ivopetkov/html5-dom-document-php

Podporuje také selektory dotazů, které podle mého názoru budou ve vašem případě velmi užitečné. Zde je příklad kódu:

$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

Pokud jste obeznámeni s voličem jQuery, můžete použít ScarletsQuery pro 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);

Tato knihovna obvykle trvá méně než 1 sekundu pro zpracování offline html.
Také přijímá neplatné HTML nebo chybí citaci na atributech tagů.

0
StefansArya

Nejlepší metoda pro analýzu 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