PHP DOM - Zpracování HTML

Pavel PHP, HTML

Jak na lehkou úpravu nebo získání části HTML pomocí PHP. Selektování je prováděno podobně jako v případě CSS nebo jQuery. Pomocí ID elementu, tříd nebo zanoření.

PHP DOM - Zpracování HTML

Generování HTML v PHP už jednodušší být nemůže. Stačí nějaké to echo a případně output buffer. Pokud je ale potřeba zpracovat již hotové HTML, může se to trochu komplikovat. Lze samozřejmě použít knihovny třetích stran, pro jednodušší úlohy ale naprosto dostačí základní PHP DOM.

DOMDocument

Základní třídou je DOMDocument, která načte HTML a následně jej lze zpracovávat jako XML. Ostatně tak je i HTML načítáno, s drobnými výjimkami. Níže je ukázaný příklad, jak HTML soubor načíst a selektovat jednotlivé elementy. Lze i traverzovat stromem pomocí referencí na rodiče, potomky a sourozence.

HTML na rozdíl od XML má i jiné typy uzlů, jako například TextNode. Pokud je tedy mezi tagy v HTML mezera, nové řádky apod., při traverzování pomocí next/previousSibling se narazí právě na tyto elementy.

// Bez tohoto volání při načítání HTML5 vyskakují podobné chyby:
// Warning:  DOMDocument::loadHTML(): Tag header invalid in Entity
libxml_use_internal_errors(true);

$data = file_get_contents("test.html");

$dom = new DOMDocument();
$dom->loadHtml(mb_convert_encoding($data, 'HTML-ENTITIES', 'UTF-8'));
$finder = new DomXPath($dom);


// Elementy
$nodesByElement = $finder->query("//a");       # CSS: a
$nodesByElement = $finder->query("//a/span");  # CSS: a > span
$nodesByElement = $finder->query("//a//span"); # CSS: a span

// ID a atributy
$nodeById = $finder->query("//*[@id='testId']");   # CSS: #testId
$nodeById = $finder->query("//div[@id='testId']"); # CSS: div#testId
$nodeByAttr = $finder->query("//*[@data-city]");   # CSS: [data-city]

// Třídy - těch může být více v jednom atributu, trochu se to komplikuje
// CSS: .inactive
$classToFind = "inactive";
$byClass = $finder->query("//*[contains(concat(' ', normalize-space(@class), ' '), ' ".$classToFind." ')]");

// XPath se v těchto případech nechová úplně stejně jako CSS
$byIndex = $finder->query("//div/a[2]");      # CSS: div > a:nth-child(2)
$lastNode = $finder->query("//div/a[last()]");# CSS: div > a:last-child

// Vyhledávání pouze v dříve vyhledaném elementu
$finder->query("//a", $nodeById);

// Rodič, potomci, předchozí sourozenec, následující sourozenec
// POZOR: Počítá i TextNode
$parentNode = $nodeById->item(0)->parentNode;
$previousSibling = $nodeById->item(0)->previousSibling;
$nextSibling = $nodeById->item(0)->nextSibling;
$children = $nodeById->item(0)->childNodes;

Zpracování HTML

Nalezené elementy lze i zpracovávat. Například získat HTML pouze určitého uzlu, získání a úpravy atributů, odstraňování nebo vložení uzlů a mnoho dalšího. Více je popsáno v dokumentaci k PHP DOM.

// Získání kusu HTML nalezeného selektorem
$htmlPart = $dom->saveHtml($nodeById->item(0));

//Získání hodnotu atributu
$linkNode->item(0)->getAttribute("href");

// Změnu atributu
$linkNode->item(0)->setAttribute("href", "/");

// Smazání tagu img, který je přímým potomkem
$toRemove = $finder->query("img", $nodeList->item(2));
$removedItem = $nodeList->item(2)->removeChild($toRemove->item(0));

//Vložení nového (nyní odstraněného) elementu
$nodeList->item(0)->appendChild($removedItem);

Selektory XPath

Pro vyhledávání v DOMDocument se nepoužívají CSS selektroy, ale XPath. Ty se značně liší, ale dokáží toho mnohem více. Malý přehled a tutoriál například na stránce interval.cz. Pro základy ale budou dostačovat nejspíše výše zmíněné selektory.

Důležité je používat na začátek vždy //, jinak bude vyhledávání probíhat pouze v kořenovém adresáři. Výjimkou je, pokud se určí aktuální uzel, pak se vyhledává v aktuálním uzlu ale pouze v přímých potomcích.


Osobní zkušenosti s DOMDocument, nebo dalšími knihovnami sdílejte v komentářích.

Přidat komentář

Právě odpovídáte na existující komentář. Zrušit

Komentáře

Roman Janko

http://rjwebdesign.cz 1.2.2018 15:08

Hezký článek, já teda k DOMu nikdy moc netíhnul, radši používam parsery třetích stran, např. SimpleHtmlDom, ale někdy se to může hodit a povědomí by o tom měl mít každý programátor.

Odpovědět

Roman Janko

http://rjwebdesign.cz 1.2.2018 15:09

PS. Celkově zajímavý blog, přidávám do RSS.

Odpovědět

Roman Janko

http://rjwebdesign.cz 1.2.2018 15:09

PS. Do třetice, něco o Nette by nebylo?

Odpovědět

Pavel

http://www.kutac.cz/blog/ 1.2.2018 19:07

Díky. Parsery třetích stran jsou OK, ale pro jednoduché věci naprosto dostačuje i ten základní.

O Nette se nic psát nechystám převážně z důvodu, že ho neumím a nikdy jsem jej nepoužil. Nejsem tedy dost znalý, abych o něm mohl psát.

Novinky z blogu

Sjednocení času ve Windows a Linuxu při dual bootu

Každý, kdo používá dual boot mezi Windows a Linuxem, zaznamenal špatný čas při nabootování do Windows. Přitom není moc těžké problém vyřešit ani v jednom...

Další články