Jak działają parsy HTML, jeśli nie używają wyrażenia regularnego?

Codziennie widzę pytania pytające, jak parsować lub wyodrębnić coś z jakiegoś ciągu HTML i pierwsza odpowiedź / komentarz jest zawsze " nie używaj RegEx do parsowania HTML, bo nie czujesz gniew!"(ta ostatnia część jest czasami pomijana).

Jest to dla mnie dość mylące, zawsze myślałem, że ogólnie rzecz biorąc, najlepszym sposobem parsowania skomplikowanych łańcuchów jest użycie wyrażenia regularnego. Jak więc działa parser HTML? Czy nie używa wyrażeń regularnych do analizy.

One szczególnym argumentem przemawiającym za użyciem wyrażenia regularnego jest to, że nie zawsze istnieje alternatywa do parsowania (np. JavaScript, gdzie DOMDocument nie jest powszechnie dostępną opcją). jQuery, na przykład, wydaje się dobrze zarządzać za pomocą wyrażenia regularnego do konwersji łańcucha HTML do węzłów DOM.

Nie wiem, czy to CW, czy nie, to jest prawdziwe pytanie, na które chcę być odpowiedział i tak naprawdę nie ma być wątkiem dyskusyjnym.

Author: Martin., 2010-03-08

5 answers

Zwykle za pomocą tokenizera. Szkic specyfikacji HTML5 posiada rozbudowany algorytm do obsługi "rzeczywistego świata HTML".

 65
Author: Quentin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-08 10:45:39

Jak więc działa parser HTML? Czy nie używa wyrażeń regularnych do analizy?

Nie.

Jeśli sięgniesz w swoim mózgu do kursu teorii obliczeń, jeśli go wybrałeś, kursu kompilatorów lub czegoś podobnego, możesz sobie przypomnieć, że istnieją różne rodzaje języków i modeli obliczeniowych. Nie jestem wykwalifikowany, aby wchodzić w szczegóły, ale mogę przejrzeć kilka głównych punktów z Tobą.

Najprostszy typ języka & obliczanie (do tych celów) jest językiem regularnym. Mogą one być generowane za pomocą wyrażeń regularnych i rozpoznawane za pomocą automatów skończonych. Zasadniczo oznacza to, że" parsowanie " łańcuchów w tych językach używa pamięci stanu, ale nie pamięci pomocniczej. HTML z pewnością nie jest zwykłym językiem. Jeśli się nad tym zastanowić, lista tagów może być zagnieżdżona arbitralnie głęboko. Na przykład tabele mogą zawierać tabele, a każda tabela może zawierać wiele zagnieżdżonych znaczników. Za pomocą wyrażeń regularnych możesz wybierz parę znaczników, ale na pewno nie coś arbitralnie zagnieżdżonego.

Klasyczny prosty język, który nie jest regularny, jest poprawnie dopasowany do nawiasów. Spróbuj, jak możesz, nigdy nie będziesz w stanie zbudować wyrażenia regularnego (lub automatu skończonego), które zawsze będzie działać. Potrzebujesz pamięci, aby śledzić głębokość zagnieżdżania.

Maszyna stanowa ze stosem pamięci jest kolejną siłą modelu obliczeniowego. Nazywa się to automatem push-down i rozpoznaje języki generowane przez gramatyki bezkontekstowe. Tutaj możemy rozpoznać prawidłowo dopasowane nawiasy-rzeczywiście, stos jest idealnym modelem pamięci dla niego.

Czy to wystarczy do HTML? Niestety nie. Może dla super-duper dokładnie sprawdzony XML, w którym wszystkie tagi zawsze pasują idealnie. W rzeczywistym HTML możesz łatwo znaleźć urywki, takie jak <b><i>wow!</b></i>. To oczywiście nie zagnieżdża się, więc w celu poprawnego przetworzenia, stos nie jest wystarczająco potężny.

Następny poziom z obliczeń są języki generowane przez ogólne gramatyki i rozpoznawane przez maszyny Turinga. Ogólnie przyjmuje się, że jest to najsilniejszy model obliczeniowy jaki istnieje-maszyna stanowa z pamięcią pomocniczą, której pamięć może być modyfikowana w dowolnym miejscu. To właśnie potrafią języki programowania. Jest to poziom złożoności, na którym żyje HTML.

Podsumowując wszystko w jednym zdaniu: aby analizować ogólny HTML, potrzebujesz prawdziwego języka programowania, a nie zwykłego ekspresja.

HTML jest przetwarzany tak samo jak inne języki: lexing i parsing. Krok lexing rozkłada strumień poszczególnych znaków na znaczące tokeny. Krok parsowania montuje tokeny, używając stanów i pamięci, w logicznie spójny dokument, na którym można działać.

 133
Author: JXG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-08 11:18:54

Wyrażenia regularne są tylko jedną formą parsera. Rzetelny parser HTML będzie znacznie bardziej skomplikowany niż może być wyrażony w wyrażeniach regularnych, używając rekurencyjnego zejścia, przewidywania i kilku innych technik poprawnej interpretacji tekstu. Jeśli naprawdę chcesz się do niego dostać, możesz sprawdzić lex & yacc i podobne narzędzia.

Zakaz używania wyrażeń regularnych do parsowania HTML powinien być napisany bardziej poprawnie jako: "Don' t use naiwne wyrażenia regularne do analizy HTML..."(abyście nie poczuli gniewu) "...i traktuj wyniki ostrożnie."Dla pewnych konkretnych celów, wyrażenia regularne mogą być doskonale odpowiednie, ale musisz być bardzo ostrożny, aby być świadomym ograniczeń wyrażeń regularnych i tak ostrożny, jak jest to odpowiednie do źródła tekstu, który analizujesz(np. jeśli jest to wejście użytkownika, bądź bardzo ostrożny).

 22
Author: T.J. Crowder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-08 10:46:14

Parsowanie HTML jest przekształceniem tekstu liniowego w strukturę drzewiastą. Wyrażenia regularne nie mogą zasadniczo obsługiwać struktur drzewiastych. Wyrażenie regularne, którego potrzebujesz w każdym punkcie, aby uzyskać następny token, zmienia się cały czas. Możesz używać wyrażeń regularnych w parserze, ale będziesz potrzebował całej tablicy wyrażeń regularnych dla każdego możliwego stanu parsowania.

 6
Author: Svante,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-08 11:21:38

Jeśli chcesz mieć 100% rozwiązanie: musisz napisać własny kod, który jest powtarzany przez znak po znaku HTML i musisz mieć ogromną ilość logiki, aby określić, czy powinieneś zatrzymać bieżący węzeł i rozpocząć następny.

Powodem jest to, że jest to poprawny HTML:

<ul>
<li>One
<li>Two
<li>Three
</ul>

Ale tak jest:

<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>

Jeśli nie masz nic przeciwko "rozwiązaniu 90%": użycie parsera XML do załadowania dokumentu jest w porządku. Lub za pomocą Regex (chociaż xml jest łatwiejszy, jeśli jesteś następnie master of the content).

 2
Author: Timothy Khouri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-08 11:01:21