Jestem funkcją poniżej, staram się wyprowadzić DOMDocument bez dołączania opakowań XML, HTML, body i znaczników p przed wyjściem zawartości. Sugerowana poprawka:
$postarray['post_content'] = $d->saveXML($d->getElementsByTagName('p')->item(0));
Działa tylko wtedy, gdy treść nie zawiera elementów blokowych. Jeśli jednak tak się stanie, jak w poniższym przykładzie z elementem h1, wynikowy wynik funkcji saveXML jest obcinany do ...
<p> Jeśli chcesz </p>
Wskazano mi ten post jako możliwe obejście, ale nie mogę zrozumieć, jak zaimplementować go w tym rozwiązaniu (zobacz zakomentowane próby poniżej).
Jakieś sugestie?
function rseo_decorate_keyword($postarray) {
global $post;
$keyword = "Jasmine Tea"
$content = "If you like <h1>jasmine tea</h1> you will really like it with Jasmine Tea flavors. This is the last ocurrence of the phrase jasmine tea within the content. If there are other instances of the keyword jasmine tea within the text what happens to jasmine tea."
$d = new DOMDocument();
@$d->loadHTML($content);
$x = new DOMXpath($d);
$count = $x->evaluate("count(//text()[contains(translate(., 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz'), '$keyword') and (ancestor::b or ancestor::strong)])");
if ($count > 0) return $postarray;
$nodes = $x->query("//text()[contains(translate(., 'ABCDEFGHJIKLMNOPQRSTUVWXYZ', 'abcdefghjiklmnopqrstuvwxyz'), '$keyword') and not(ancestor::h1) and not(ancestor::h2) and not(ancestor::h3) and not(ancestor::h4) and not(ancestor::h5) and not(ancestor::h6) and not(ancestor::b) and not(ancestor::strong)]");
if ($nodes && $nodes->length) {
$node = $nodes->item(0);
// Split just before the keyword
$keynode = $node->splitText(strpos($node->textContent, $keyword));
// Split after the keyword
$node->nextSibling->splitText(strlen($keyword));
// Replace keyword with <b>keyword</b>
$replacement = $d->createElement('strong', $keynode->textContent);
$keynode->parentNode->replaceChild($replacement, $keynode);
}
$postarray['post_content'] = $d->saveXML($d->getElementsByTagName('p')->item(0));
// $postarray['post_content'] = $d->saveXML($d->getElementsByTagName('body')->item(1));
// $postarray['post_content'] = $d->saveXML($d->getElementsByTagName('body')->childNodes);
return $postarray;
}
źródło
DOMDocument
który wpływa również na kod w tej odpowiedzi. Afaik,DOMDocument
zawsze interpretuje dane wejściowe jako latin-1, chyba że dane wejściowe określają inny zestaw znaków . Innymi słowy:<meta charset="…">
znacznik wydaje się być potrzebny dla danych wejściowych, które nie są latin-1. W przeciwnym razie wyjście zostanie przerwane na np. Wielobajtowe znaki UTF-8.Po prostu usuń węzły bezpośrednio po załadowaniu dokumentu za pomocą loadHTML ():
źródło
<!DOCTYPE
działa. Druga linia jest przerywana, jeśli<body>
ma więcej niż jedną notatkę podrzędną.Użyj
saveXML()
zamiast tego i przekaż documentElement jako argument do niego.http://php.net/domdocument.savexml
źródło
saveHTML
także ( przykład )loadHTML
libxml używa modułu parsera HTML, który wstawi brakujący szkielet HTML. W konsekwencji$dom->documentElement
będzie głównym elementem HTML. Naprawiłem Twój przykładowy kod. Powinien teraz zrobić to, o co prosi Scott.Problem z pierwszą odpowiedzią polega na tym, że
LIBXML_HTML_NOIMPLIED
jest niestabilna .Może zmieniać kolejność elementów (w szczególności przenosząc znacznik zamykający górnego elementu na dół dokumentu), dodawać losowe
p
znaczniki i być może wiele innych problemów [1] . Może usunąć tagihtml
ibody
za Ciebie, ale kosztem niestabilnego zachowania. W produkcji to czerwona flaga. W skrócie:Nie używaj
LIBXML_HTML_NOIMPLIED
. Zamiast tego użyjsubstr
.Pomyśl o tym. Długości
<html><body>
i</body></html>
są ustalone na obu końcach dokumentu - ich rozmiary nigdy się nie zmieniają, podobnie jak ich położenie. To pozwala namsubstr
je odciąć:( JEDNAK NIE JEST TO KOŃCOWE ROZWIĄZANIE! Pełna odpowiedź znajduje się poniżej , czytaj dalej, aby poznać kontekst)
Odcinamy
12
początek dokumentu, ponieważ<html><body>
= 12 znaków (<<>>+html+body
= 4 + 4 + 4), a cofamy się i odcinamy\n</body></html>
15 znaków na końcu, ponieważ = 15 znaków (\n+//+<<>>+body+html
= 1 + 2 + 4 + 4 + 4)Zauważ, że nadal używam
LIBXML_HTML_NODEFDTD
pomiń!DOCTYPE
przed dołączeniem. Po pierwsze, upraszcza tosubstr
usuwanie tagów HTML / BODY. Po drugie, nie usuwamy doctype z,substr
ponieważ nie wiemy, czy „default doctype
” zawsze będzie miał stałą długość. Ale co najważniejsze,LIBXML_HTML_NODEFDTD
powstrzymuje parser DOM przed zastosowaniem do dokumentu typu dokumentu innego niż HTML5 - co przynajmniej zapobiega traktowaniu przez parser elementów, których nie rozpoznaje, jako luźnego tekstu.Wiemy na pewno, że tagi HTML / BODY mają ustalone długości i pozycje, i wiemy, że stałe, takie jak,
LIBXML_HTML_NODEFDTD
nigdy nie są usuwane bez jakiegoś powiadomienia o wycofaniu, więc powyższa metoda powinna zostać zastosowana w przyszłości, ALE ...... jedynym zastrzeżeniem jest to, że implementacja DOM może zmienić sposób umieszczania znaczników HTML / BODY w dokumencie - na przykład, usuwając znak nowej linii na końcu dokumentu, dodając spacje między tagami lub dodając znaki nowej linii.
Można temu zaradzić, wyszukując pozycje otwierających i zamykających znaczników
body
i używając tych przesunięć, tak jak w przypadku naszych długości do przycięcia. Używamystrpos
i,strrpos
aby znaleźć przesunięcia odpowiednio z przodu iz tyłu:Na koniec powtórzenie ostatecznej, przyszłościowej odpowiedzi :
Bez doctype, bez tagu HTML, bez tagu body. Możemy mieć tylko nadzieję, że parser DOM wkrótce otrzyma nową warstwę farby i możemy bardziej bezpośrednio wyeliminować te niechciane tagi.
źródło
$html = $dom -> saveHTML();
zamiast$dom -> saveHTML();
kilkukrotnie?Sprytną sztuczką jest użycie
loadXML
i wtedysaveHTML
.html
Ibody
znaczniki są umieszczone naload
etapie, a niesave
etap.Uwaga, jest to trochę hakerskie i powinieneś użyć odpowiedzi Jonaha, jeśli możesz sprawić, że zadziała.
źródło
użyj DOMDocumentFragment
źródło
Jest rok 2017, a na to pytanie 2011 nie podoba mi się żadna z odpowiedzi. Wiele wyrażeń regularnych, duże klasy, loadXML itp.
Proste rozwiązanie, które rozwiązuje znane problemy:
Łatwe, proste, solidne, szybkie. Ten kod będzie działał w odniesieniu do tagów HTML i kodowania, takich jak:
Jeśli ktoś znajdzie błąd, powiedz proszę, sam tego użyję.
Edytuj , Inne prawidłowe opcje, które działają bez błędów (bardzo podobne do już podanych):
Możesz sam dodać ciało, aby zapobiec pojawianiu się dziwnych rzeczy na futrze.
Trzecia opcja:
źródło
mb_convert_encoding
a zamiast tego odpowiednio dodając<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body>
i modyfikującsubstr
. Przy okazji, twoje jest tutaj najbardziej eleganckim rozwiązaniem. Głosowano za.Jestem trochę spóźniony w klubie, ale nie chciałem nie dzielić się metodą, o której się dowiedziałem. Przede wszystkim mam odpowiednie wersje dla loadHTML (), aby zaakceptować te fajne opcje, ale
LIBXML_HTML_NOIMPLIED
nie działało w moim systemie. Również użytkownicy zgłaszają problemy z parserem (na przykład tutaj i tutaj ).Rozwiązanie, które stworzyłem, jest właściwie dość proste.
HTML do załadowania jest umieszczany w
<div>
elemencie, więc ma kontener zawierający wszystkie węzły do załadowania.Następnie ten element kontenera jest usuwany z dokumentu (ale jego DOMElement nadal istnieje).
Następnie wszystkie bezpośrednie elementy podrzędne z dokumentu są usuwane. Obejmuje to każdy dodany
<html>
,<head>
a<body>
znaczniki (skutecznieLIBXML_HTML_NOIMPLIED
opcja), jak również<!DOCTYPE html ... loose.dtd">
zgłoszenie (w praktyceLIBXML_HTML_NODEFDTD
).Następnie wszystkie bezpośrednie elementy podrzędne kontenera są ponownie dodawane do dokumentu i można je wyprowadzić.
XPath działa jak zwykle, po prostu uważaj, aby teraz było wiele elementów dokumentu, więc nie pojedynczy węzeł główny:
źródło
Żadne z innych rozwiązań w momencie pisania tego tekstu (czerwiec 2012) nie było w stanie w pełni zaspokoić moich potrzeb, dlatego napisałem jedno, które zajmuje się następującymi przypadkami:
<doctype>
,<xml>
,<html>
,<body>
, i<p>
znaczniki)<p>
spokoju.Oto rozwiązanie, które rozwiązuje te problemy:
Napisałem też kilka testów, które będą żyć w tej samej klasie:
Możesz sam sprawdzić, czy to działa.
DomDocumentWorkaround::testAll()
zwraca to:źródło
Ok, znalazłem bardziej eleganckie rozwiązanie, ale jest po prostu żmudne:
Dobra, mam nadzieję, że to niczego nie pominie i komuś pomoże?
źródło
Użyj tej funkcji
źródło
preg_replace
ponieważ użycie metod opartych na DOMDocument do usuwania tagów html i body nie zachowywało kodowania UTF-8 :(Jeśli rozwiązanie flagowe, na które odpowiedział Alessandro Vendruscolo , nie działa, możesz spróbować tego:
$bodyTag
będzie zawierać w pełni przetworzony kod HTML bez tych wszystkich opakowań HTML, z wyjątkiem<body>
tagu, który jest głównym elementem treści. Następnie możesz użyć wyrażenia regularnego lub funkcji przycinającej, aby usunąć je z końcowego ciągu (posaveHTML
) lub, jak w powyższym przypadku, iterować po wszystkich jego potomkach, zapisując ich zawartość w zmiennej tymczasowej$finalHtml
i zwracając ją (uważam, że jest bezpieczniejsze).źródło
Natknąłem się na ten temat, aby znaleźć sposób na usunięcie opakowania HTML. Używanie
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
działa świetnie, ale mam problem z utf-8. Po wielu staraniach znalazłem rozwiązanie. Publikuję to poniżej, ponieważ każdy ma ten sam problem.Problem spowodowany przez
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
Problem:
Rozwiązanie 1:
Rozwiązanie 2:
źródło
Walczę z tym na RHEL7 z PHP 5.6.25 i LibXML 2.9. (Wiem, że stare rzeczy w 2018 roku, ale to dla ciebie Red Hat).
Odkryłem, że rozwiązanie zasugerowane przez Alessandro Vendruscolo, cieszące się dużym uznaniem, łamie kod HTML poprzez zmianę kolejności tagów. To znaczy:
staje się:
Dotyczy to obu opcji, które sugeruje:
LIBXML_HTML_NOIMPLIED
iLIBXML_HTML_NODEFDTD
.Rozwiązanie zaproponowane przez Alexa idzie w połowie, aby go rozwiązać, ale nie działa, jeśli
<body>
ma więcej niż jeden węzeł podrzędny.Rozwiązanie, które działa dla mnie, to:
Najpierw, aby załadować DOMDocument, używam:
Aby zapisać dokument po masowaniu DOMDocument, używam:
Zgadzam się jako pierwszy, że to niezbyt eleganckie rozwiązanie - ale działa.
źródło
Dodanie
<meta>
tagu uruchomi działanie naprawcze programuDOMDocument
. Zaletą jest to, że nie musisz w ogóle dodawać tego tagu. Jeśli nie chcesz użyć wybranego przez siebie kodowania, przekaż je jako argument konstruktora.http://php.net/manual/en/domdocument.construct.php
Wynik
Dzięki @Bart
źródło
Ja też miałem to wymaganie i podobało mi się rozwiązanie opublikowane przez Alexa powyżej. Jest jednak kilka problemów - jeśli
<body>
element zawiera więcej niż jeden element podrzędny, wynikowy dokument będzie zawierał tylko pierwszy element podrzędny<body>
, a nie wszystkie. Poza tym potrzebowałem usuwania, aby obsługiwać rzeczy warunkowo - tylko wtedy, gdy masz dokument z nagłówkami HTML. Więc udoskonaliłem to w następujący sposób. Zamiast usuwać<body>
, przekształciłem go na a<div>
i usunąłem deklarację XML i<html>
.źródło
Podobnie jak inni członkowie, najpierw rozkoszowałem się prostotą i niesamowitą mocą odpowiedzi @Alessandro Vendruscolo. Możliwość prostego przekazania niektórych oznaczonych stałych do konstruktora wydawała się zbyt dobra, aby była prawdziwa. Dla mnie to było. Mam poprawne wersje zarówno LibXML, jak i PHP, jednak bez względu na to, co i tak dodałoby znacznik HTML do struktury węzłów obiektu Document.
Moje rozwiązanie działało znacznie lepiej niż korzystanie z ...
Flagi lub ....
Usuwanie węzłów, które staje się bałaganiarskie bez uporządkowanej kolejności w DOM. Ponownie fragmenty kodu nie mają możliwości wcześniejszego określenia struktury DOM.
Zacząłem tę podróż od szukania prostego sposobu na przechodzenie przez DOM, jak robi to JQuery, lub przynajmniej w jakiś sposób, który miał uporządkowany zestaw danych albo pojedynczo połączony, podwójnie połączony lub przechodzenie przez węzeł drzewa. Nie obchodziło mnie, jak długo mogę przeanalizować ciąg tak, jak robi to HTML, a także mam niesamowitą moc właściwości klasy encji węzła do wykorzystania po drodze.
Do tej pory obiekt DOMDocument sprawił, że chciałem ... Tak jak w przypadku wielu innych programistów wydaje się ... Wiem, że widziałem wiele frustracji w tym pytaniu, więc odkąd W KOŃCU ... (po około 30 godzinach prób i niepowodzeń testowanie typu) Znalazłem sposób, aby to wszystko uzyskać. Mam nadzieję, że to komuś pomoże ...
Po pierwsze, jestem cyniczny wobec WSZYSTKIEGO ... lol ...
Spędziłbym całe życie, zanim zgodziłbym się z kimkolwiek, że w tym przypadku i tak potrzebna jest klasa strony trzeciej. Bardzo byłem i NIE jestem fanem używania jakiejkolwiek struktury klas innej firmy, jednak natknąłem się na świetny parser. (około 30 razy w Google, zanim się poddałem, więc nie czuj się sam, jeśli tego unikasz, ponieważ w jakikolwiek sposób wyglądało to kiepsko lub nieoficjalnie ...)
Jeśli używasz fragmentów kodu i potrzebujesz, kod jest czysty i nie ma na niego żadnego wpływu parser, bez użycia dodatkowych tagów, użyj simplePHPParser .
Jest niesamowity i działa podobnie jak JQuery. Nie robiłem wrażenia, ale ta klasa korzysta z wielu dobrych narzędzi i jak dotąd nie miałem żadnych błędów parsowania. Jestem wielkim fanem robienia tego, co robi ta klasa.
Możesz znaleźć jego pliki do pobrania tutaj , instrukcje uruchamiania tutaj i jego API tutaj . Bardzo polecam używanie tej klasy z jej prostymi metodami, które mogą działać w
.find(".className")
ten sam sposób, w jaki byłaby używana metoda znajdowania JQuery, a nawet znane metody, takie jakgetElementByTagName()
lubgetElementById()
...Kiedy zapisujesz drzewo węzłów w tej klasie, nic nie dodaje. Możesz po prostu powiedzieć
$doc->save();
i przekazuje całe drzewo do łańcucha bez żadnego zamieszania.Będę teraz używać tego parsera we wszystkich projektach bez ograniczenia przepustowości w przyszłości.
źródło
Mam PHP 5.3 i odpowiedzi tutaj nie działają.
$doc->replaceChild($doc->firstChild->firstChild->firstChild, $doc->firstChild);
zastąpiłem cały dokument tylko pierwszym dzieckiem, miałem wiele akapitów i tylko pierwszy był zapisywany, ale rozwiązanie dało mi dobry punkt wyjścia do napisania czegoś bezregex
zostawiłem kilka komentarzy i jestem pewien, że można to poprawić, ale jeśli ktoś ma ten sam problem co ja to może być dobry punkt wyjścia.Wtedy moglibyśmy to wykorzystać w ten sposób:
Zwróć uwagę, że
appendChild
akceptuje a,DOMNode
więc nie musimy tworzyć nowych elementów, możemy po prostu ponownie wykorzystać istniejące, które implementująDOMNode
takie jakDOMElement
ten.źródło
LIBXML_HTML_NOIMPLIED
spełnia swojej roli, ponieważ robi to tylko częściowo. Usunięcie doctype jest skuteczneLIBXML_HTML_NODEFDTD
.Mam 3 problemy z
DOMDocument
zajęciami.1- Ta klasa ładuje HTML z kodowaniem ISO i znakami utf-8 nie wyświetlanymi na wyjściu.
2- Nawet jeśli damy
LIBXML_HTML_NOIMPLIED
flagę metody loadHtml, dopóki nasz html wejściowy nie zawiera znacznik korzeniowy, to nie będzie parse poprawnie.3- Ta klasa uważa tagi HTML5 za nieprawidłowe.
Więc nadpisałem tę klasę, aby rozwiązać te problemy i zmieniłem niektóre metody.
Teraz używam
DOMEditor
zamiastDOMDocument
i jak na razie działa dobrzeźródło
Trafiłem też na ten problem.
Niestety nie czułem się komfortowo korzystając z żadnego z rozwiązań przedstawionych w tym wątku, więc poszedłem sprawdzić takie, które by mnie satysfakcjonowało.
Oto, co wymyśliłem i działa bez problemów:
W istocie działa podobnie do większości przedstawionych tutaj rozwiązań, ale zamiast wykonywać pracę ręczną, używa selektora xpath, aby wybrać wszystkie elementy w treści i połączyć ich kod HTML.
źródło
descendant-or-self::body/p/*
.mój serwer ma php 5.3 i nie mogę zaktualizować, więc te opcje
nie są dla mnie.
Aby rozwiązać ten problem, mówię funkcji SaveXML, aby wydrukowała element Body, a następnie po prostu zamień „body” na „div”
oto mój kod, mam nadzieję, że komuś pomaga:
utf-8 służy do obsługi języka hebrajskiego.
źródło
Odpowiedź Alexa jest poprawna, ale może powodować następujący błąd na pustych węzłach:
Oto mój mały mod:
Dodanie trim () jest również dobrym pomysłem, aby usunąć białe znaki.
źródło
Może za późno. Ale może ktoś (jak ja) nadal ma ten problem.
Więc żadna z powyższych nie działała dla mnie. Ponieważ $ dom-> loadHTML również zamyka otwarte tagi, nie tylko dodaje tagi html i body.
Więc dodaj element <div> nie działa dla mnie, ponieważ czasami mam 3-4 niezamknięte div w kawałku html.
Moje rozwiązanie:
1.) Dodaj znacznik do wycięcia, a następnie załaduj kawałek HTML
2.) rób co chcesz z dokumentem
3.) zapisz html
4.) zanim go zwrócisz, usuń tagi <p> </ p> ze znacznika, dziwne jest to tylko na [MARK], ale nie na [/ MARK] ...!?
5.) usuń wszystko przed i po markerze
6.) zwrócić go
Byłoby dużo łatwiej, gdyby LIBXML_HTML_NOIMPLIED działał dla mnie. Powinien, ale tak nie jest. PHP 5.4.17, libxml wersja 2.7.8.
Wydaje mi się naprawdę dziwne, używam parsera HTML DOM, a potem, aby naprawić tę "rzecz", muszę użyć wyrażenia regularnego ... Chodziło o to, aby nie używać wyrażenia regularnego;)
źródło
< div >< div > ... < /div >
. Ciągle szukam rozwiązań.Dla każdego, kto korzysta z Drupala, jest do tego wbudowana funkcja:
https://api.drupal.org/api/drupal/modules!filter!filter.module/function/filter_dom_serialize/7.x
Kod odniesienia:
źródło
Możesz użyć porządku tylko z pokazem ciała:
Ale pamiętaj: porządnie usuń niektóre tagi, takie jak ikony Font Awesome: Problemy z wcięciem HTML (5) w PHP
źródło
źródło
Ta biblioteka ułatwia przechodzenie / modyfikowanie DOM, a także zajmuje się usuwaniem opakowań doctype / html:
https://github.com/sunra/php-simple-html-dom-parser
źródło