Wydaje się, że każde pytanie w przepełnieniu stosu, w którym pytający używa wyrażenia regularnego w celu pobrania informacji z HTML, nieuchronnie będzie miało „odpowiedź”, która mówi, aby nie używać wyrażenia regularnego do analizowania HTML.
Dlaczego nie? Wiem, że istnieją „prawdziwe” parsery HTML bez cudzysłowów, takie jak Piękna Zupa , i jestem pewien, że są potężne i przydatne, ale jeśli robisz coś prostego, szybkiego lub brudnego, to dlaczego przeszkadza Ci używanie czegoś tak skomplikowanego, gdy kilka wyrażeń regularnych będzie działać dobrze?
Co więcej, czy jest coś fundamentalnego, czego nie rozumiem w wyrażeniach regularnych, co czyni je złym wyborem do parsowania?
regex
html-parsing
ntownsend
źródło
źródło
Odpowiedzi:
Cała parsowanie HTML nie jest możliwe w przypadku wyrażeń regularnych, ponieważ zależy od dopasowania znacznika otwierającego i zamykającego, co nie jest możliwe w wyrażeniach regularnych.
Wyrażenia regularne mogą pasować tylko do zwykłych języków, ale HTML jest językiem bezkontekstowym, a nie językiem zwykłym (jak wskazał @StefanPochmann, języki regularne również są pozbawione kontekstu, więc kontekst nie musi oznaczać niekonieczności). Jedyne, co możesz zrobić z wyrażeniami regularnymi w HTML, to heurystyka, ale to nie zadziała pod każdym warunkiem. Powinno być możliwe przedstawienie pliku HTML, który zostanie nieprawidłowo dopasowany przez dowolne wyrażenie regularne.
źródło
Wyrażenie regularne „fast'n´irty” wystarczy. Ale podstawową rzeczą, którą należy wiedzieć, jest to, że nie można zbudować wyrażenia regularnego, które poprawnie parsuje HTML.
Powodem jest to, że wyrażenia regularne nie mogą obsłużyć wyrażeń zagnieżdżonych w sposób arbitralny. Zobacz Czy można używać wyrażeń regularnych do dopasowywania wzorców zagnieżdżonych?
źródło
(Od http://htmlparsing.com/regexes )
Załóżmy, że masz plik HTML, w którym próbujesz wyodrębnić adresy URL z tagów <img>.
Więc piszesz takie wyrażenie regularne w Perlu:
W takim przypadku
$url
rzeczywiście będzie zawieraćhttp://example.com/whatever.jpg
. Ale co się stanie, gdy zaczniesz otrzymywać HTML w ten sposób:lub
lub
lub
lub zaczniesz otrzymywać fałszywe alarmy od
Wygląda tak prosto i może być prosty dla pojedynczego, niezmiennego pliku, ale dla wszystkiego, co zamierzasz robić na dowolnych danych HTML, wyrażenia regularne to tylko przepis na przyszły ból serca.
źródło
Dwa szybkie powody:
Jeśli chodzi o przydatność wyrażeń regularnych do parsowania w ogóle: nie są one odpowiednie. Czy kiedykolwiek widziałeś rodzaje wyrażeń regularnych, których potrzebujesz, aby przeanalizować większość języków?
źródło
Jeśli chodzi o parsowanie, wyrażenia regularne mogą być przydatne na etapie „analizy leksykalnej” (lexer), gdzie dane wejściowe są dzielone na tokeny. Jest mniej przydatny na etapie „budowania drzewa analizy”.
W przypadku parsera HTML spodziewałbym się, że akceptuje on tylko poprawnie sformatowany HTML, który wymaga możliwości wykraczających poza to, co potrafi wyrażenie regularne (nie mogą one „policzyć” i upewnić się, że dana liczba elementów otwierających jest zrównoważona tą samą liczbą elementów zamykających).
źródło
Ponieważ istnieje wiele sposobów na „zepsucie” HTML, które przeglądarki będą traktować w dość liberalny sposób, ale zajmie to sporo wysiłku, aby odtworzyć liberalne zachowanie przeglądarki, aby objąć wszystkie przypadki wyrażeniami regularnymi, więc regex nieuchronnie zawiedzie w przypadku niektórych specjalnych przypadki, a to prawdopodobnie wprowadziłoby poważne luki w zabezpieczeniach w systemie.
źródło
Problem polega na tym, że większość użytkowników, którzy zadają pytanie dotyczące HTML i wyrażenia regularnego, robi to, ponieważ nie może znaleźć własnego wyrażenia regularnego, który działa. Następnie należy zastanowić się, czy wszystko byłoby łatwiejsze przy użyciu parsera DOM lub SAX lub czegoś podobnego. Są zoptymalizowane i zbudowane w celu pracy ze strukturami dokumentów podobnymi do XML.
Jasne, są problemy, które można łatwo rozwiązać za pomocą wyrażeń regularnych. Ale nacisk kładzie się łatwo .
Jeśli chcesz po prostu znaleźć wszystkie adresy URL, które wyglądają
http://.../
, jakbyś był w porządku z wyrażeniami regularnymi. Ale jeśli chcesz znaleźć wszystkie adresy URL w elemencie a, który ma klasę „mylink”, prawdopodobnie lepiej użyj odpowiedniego parsera.źródło
Wyrażenia regularne nie zostały zaprojektowane do obsługi zagnieżdżonej struktury znaczników, a w najlepszym razie skomplikowane (w najgorszym przypadku niemożliwe) jest obsługiwanie wszystkich możliwych przypadków brzegowych, które można uzyskać za pomocą prawdziwego HTML.
źródło
Uważam, że odpowiedź leży w teorii obliczeń. Aby język był analizowany za pomocą wyrażenia regularnego, musi on być z definicji „zwykły” ( link ). HTML nie jest zwykłym językiem, ponieważ nie spełnia wielu kryteriów dla zwykłego języka (wiele wspólnego z wieloma poziomami zagnieżdżania nieodłącznymi w kodzie HTML). Jeśli jesteś zainteresowany teorią obliczeń, poleciłbym tę książkę.
źródło
To wyrażenie pobiera atrybuty z elementów HTML. To wspiera:
(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)
Sprawdź to . Działa lepiej z flagami „gisx”, jak w wersji demo.
źródło
<script>
tagu.HTML / XML jest podzielony na znaczniki i treść. Regex jest użyteczny tylko podczas analizy tagów leksykalnych. Myślę, że możesz wydedukować treść. Byłby to dobry wybór dla parsera SAX. Tagi i treść mogą być dostarczane do funkcji zdefiniowanej przez użytkownika, w której można śledzić zagnieżdżanie / zamykanie elementów.
Jeśli chodzi o samą analizę tagów, można to zrobić za pomocą wyrażenia regularnego i użyć do usunięcia tagów z dokumentu.
Przez lata testów odkryłem sekret sposobu, w jaki przeglądarki analizują tagi, zarówno dobrze, jak i źle sformułowane.
Normalne elementy są przetwarzane w tej formie:
Rdzeń tych tagów używa tego wyrażenia regularnego
Zauważysz to
[^>]?
jako jedną z alternatyw. To dopasuje niezrównoważone cytaty z źle sformułowanych tagów.Jest także jednym z głównych źródeł wszelkiego zła źródeł wyrażeń regularnych. Sposób, w jaki jest używany, spowoduje wyboistość, aby zaspokoić jego zachłanny, dopasowany ilościowo pojemnik.
Jeśli użyjesz go biernie, nigdy nie będzie problemu. Ale jeśli wymusisz coś dopasować przez przeplatając je z kupna parę atrybut / wartość, i nie zapewniają odpowiedniej ochrony przed backtracking, to jest poza kontrolą koszmar.
Jest to ogólna forma zwykłych starych tagów. Zauważ, że
[\w:]
reprezentuje nazwę znacznika? W rzeczywistości znaki prawne reprezentujące nazwę znacznika to niesamowita lista znaków Unicode.Przechodząc dalej, widzimy również, że po prostu nie możesz wyszukać określonego tagu bez analizy WSZYSTKICH tagów. Mam na myśli, że możesz, ale musiałbyś użyć kombinacji czasowników takich jak (* SKIP) (* FAIL), ale nadal wszystkie tagi muszą zostać przeanalizowane.
Powodem jest to, że składnia znaczników może być ukryta w innych znacznikach itp.
Aby pasywnie przeanalizować wszystkie tagi, potrzebny jest regex, taki jak poniżej. Ten konkretny pasuje również do niewidzialnej treści .
Gdy nowy HTML lub xml lub jakikolwiek inny opracuje nowe konstrukcje, po prostu dodaj go jako jedną z alternatyw.
Informacja o stronie internetowej - Nigdy nie widziałem strony internetowej (lub xhtml / xml), z którą
miałby to problem. Jeśli znajdziesz, daj mi znać.
Uwaga dotycząca wydajności - jest szybka. To najszybszy parser tagów, jaki widziałem
(może być szybszy, kto wie).
Mam kilka konkretnych wersji. Jest również doskonały jako skrobak
(jeśli jesteś typem praktycznym).
Wypełnij wyrażenie regularne
<(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\s+(?>"[\S\s]*?"|'[\S\s]*?'|(?:(?!/>)[^>])?)+)?\s*>)[\S\s]*?</\1\s*(?=>))|(?:/?[\w:]+\s*/?)|(?:[\w:]+\s+(?:"[\S\s]*?"|'[\S\s]*?'|[^>]?)+\s*/?)|\?[\S\s]*?\?|(?:!(?:(?:DOCTYPE[\S\s]*?)|(?:\[CDATA\[[\S\s]*?\]\])|(?:--[\S\s]*?--)|(?:ATTLIST[\S\s]*?)|(?:ENTITY[\S\s]*?)|(?:ELEMENT[\S\s]*?))))>
Sformatowany wygląd
źródło
„To zależy”. To prawda, że wyrażenia regularne nie analizują i nie mogą parsować HTML z prawdziwą dokładnością, z wszystkich podanych tutaj powodów. Jeśli jednak konsekwencje popełnienia błędu (np. Brak obsługi zagnieżdżonych tagów) są niewielkie i jeśli wyrażenia regularne są bardzo wygodne w twoim środowisku (na przykład podczas hakowania Perla), śmiało.
Załóżmy, że może analizujesz strony internetowe, które prowadzą do Twojej witryny - być może znalazłeś je podczas wyszukiwania linków Google - i chcesz szybko uzyskać ogólne wyobrażenie o kontekście otaczającym link. Próbujesz uruchomić mały raport, który może ostrzec Cię o połączeniu spamu, coś w tym rodzaju.
W takim przypadku błędne parowanie niektórych dokumentów nie będzie wielkim problemem. Nikt oprócz ciebie nie zobaczy błędów, a jeśli będziesz miał szczęście, będzie ich na tyle mało, że będziesz mógł je śledzić indywidualnie.
Chyba mówię, że to kompromis. Czasami zaimplementowanie lub użycie poprawnego parsera - choć może to być tak proste - może nie być warte kłopotów, jeśli dokładność nie jest krytyczna.
Uważaj tylko na swoje założenia. Mogę wymyślić kilka sposobów, w jakie skrót regexp może zadziałać, jeśli próbujesz na przykład parsować coś, co zostanie pokazane publicznie.
źródło
Zdecydowanie istnieją przypadki, w których użycie wyrażenia regularnego do parsowania niektórych informacji z HTML jest właściwą drogą - zależy to w dużej mierze od konkretnej sytuacji.
Powyższy konsensus jest taki, że ogólnie jest to zły pomysł. Jeśli jednak struktura HTML jest znana (i mało prawdopodobne, że ulegnie zmianie), jest to nadal prawidłowe podejście.
źródło
Pamiętaj, że chociaż sam HTML nie jest regularny, niektóre strony, na które patrzysz, mogą być regularne.
Na przykład błąd
<form>
jest zagnieżdżany w tagach; jeśli strona internetowa działa poprawnie, użycie wyrażenia regularnego do przechwycenia<form>
byłoby całkowicie uzasadnione.Niedawno zrobiłem przeglądanie stron internetowych, używając tylko Selenium i wyrażeń regularnych. Dostałem od niej, ponieważ dane Chciałam umieścić w
<form>
, i umieścić w prostej formie tabeli (tak mogę nawet liczyć na to<table>
,<tr>
i<td>
aby nie być zagnieżdżone - co jest rzeczywiście bardzo nietypowy). W pewnym stopniu wyrażenia regularne były nawet prawie konieczne, ponieważ niektóre struktury, do których potrzebowałem uzyskać dostęp, zostały ograniczone komentarzami. (Piękna zupa może dać ci komentarze, ale trudno byłoby ją złapać<!-- BEGIN -->
i<!-- END -->
zablokować za pomocą pięknej zupy.)Gdybym jednak musiał się martwić o zagnieżdżone tabele, moje podejście po prostu by się nie udało! Musiałbym wrócić do Pięknej Zupy. Jednak nawet wtedy czasami możesz użyć wyrażenia regularnego, aby chwycić potrzebną porcję, a następnie przejść do następnego etapu.
źródło
W rzeczywistości parsowanie HTML za pomocą wyrażenia regularnego jest w PHP całkowicie możliwe. Musisz tylko przeanalizować cały łańcuch do tyłu za pomocą,
strrpos
aby znaleźć<
i powtórzyć regex stamtąd za pomocą nieokreślonych specyfikatorów za każdym razem, aby ominąć zagnieżdżone znaczniki. Nie jest to wymyślne i strasznie powolne w przypadku dużych rzeczy, ale użyłem go do mojego osobistego edytora szablonów dla mojej witryny. Właściwie nie analizowałem HTML, ale kilka niestandardowych tagów, które utworzyłem, do przeszukiwania baz danych w celu wyświetlenia tabel danych (mój<#if()>
tag mógł w ten sposób wyróżnić specjalne wpisy). Nie byłem przygotowany, aby przejść do parsera XML tylko dla kilku samodzielnie utworzonych znaczników (z bardzo nie-danymi XML) tu i tam.Tak więc, chociaż to pytanie jest znacznie martwe, nadal pojawia się w wyszukiwarce Google. Przeczytałem go i pomyślałem „wyzwanie przyjęte” i skończyłem naprawiać mój prosty kod bez konieczności wymiany wszystkiego. Postanowiłem przedstawić inną opinię każdemu, kto szuka podobnego powodu. Ostatnia odpowiedź została opublikowana 4 godziny temu, więc wciąż jest to gorący temat.
źródło
<tag >
) Czy rozważałeś skomentowane tagi zamykające? (Np.<tag> <!-- </tag> -->
) Czy rozważałeś CDATA? Czy bierzesz pod uwagę tagi niespójnych liter? (Np<Tag> </tAG>
) Czy uważają to za dobrze?Próbowałem też tego w regexie. Jest to szczególnie przydatne do znajdowania fragmentów treści sparowanych z następnym znacznikiem HTML i nie szuka pasujących bliskich znaczników, ale pobiera ścisłe znaczniki. Rzuć stos w swoim własnym języku, aby to sprawdzić.
Używaj z opcjami „sx”. „g” też, jeśli masz szczęście:
Ten jest przeznaczony dla Pythona (może działać w innych językach, nie wypróbowałem go, wykorzystuje pozytywne spojrzenia, negatywne spojrzenia i nazwane odwołania wsteczne). Obsługuje:
<div ...>
</div>
<!-- ... -->
<![CDATA[ ... ]]>
<div .../>
<input checked>
<div style='...'>
<div style="...">
<a title='John\'s Story'>
(to naprawdę nie jest poprawny HTML, ale jestem miłym facetem)
<a href = '...'>
Bardzo dobrze jest też nie uruchamiać zniekształconych tagów, na przykład gdy zapomnisz o
<
lub>
.Jeśli twój smak wyrażeń regularnych obsługuje wielokrotne przechwytywanie nazwane, oznacza to, że jesteś złoty, ale Python
re
nie (wiem, że wyrażenie regularne obsługuje, ale muszę używać waniliowego Pythona). Oto, co otrzymujesz:content
- Cała zawartość do następnego tagu. Możesz to pominąć.markup
- Cały tag ze wszystkim w nim.comment
- Jeśli jest to komentarz, treść komentarza.cdata
- Jeśli tak<![CDATA[...]]>
, zawartość CDATA.close_tag
- Jeśli jest to tag zamknięty (</div>
), nazwa tagu.tag
- Jeśli jest to tag otwarty (<div>
), nazwa tagu.attributes
- Wszystkie atrybuty wewnątrz znacznika. Użyj tego, aby uzyskać wszystkie atrybuty, jeśli nie otrzymujesz powtarzających się grup.attribute
- Powtarzane, każdy atrybut.attribute_name
- Powtarzane, każda nazwa atrybutu.attribute_value
- Powtarzane, każda wartość atrybutu. Obejmuje to cytaty, jeśli były cytowane.is_self_closing
- Dzieje się tak,/
jeśli jest to tag samozamykający, w przeciwnym razie nic._q
i_v
- zignoruj je; są używane wewnętrznie do odwołań wstecznych.Jeśli silnik wyrażeń regularnych nie obsługuje powtarzanych nazwanych przechwytywania, istnieje sekcja przywołana, za pomocą której można uzyskać każdy atrybut. Wystarczy uruchomić ten regex na
attributes
grupy, aby każdyattribute
,attribute_name
aattribute_value
poza nim.Demo tutaj: https://regex101.com/r/mH8jSu/11
źródło
Wyrażenia regularne nie są wystarczające dla takiego języka jak HTML. Jasne, istnieje kilka przykładów użycia wyrażeń regularnych. Ale generalnie nie nadaje się do analizowania.
źródło
Ty, wiesz ... masz dużo mentalności, NIE MOŻESZ tego zrobić i myślę, że wszyscy po obu stronach ogrodzenia mają rację. Ci MOŻE to zrobić, ale to wymaga trochę więcej niż tylko systemem przetwarzania jednego regex przed nim. Weź to (napisałem to w ciągu godziny) jako przykład. Zakłada, że HTML jest w pełni poprawny, ale w zależności od tego, jakiego języka używasz do wspomnianego wcześniej wyrażenia regularnego, możesz wprowadzić pewne poprawki HTML, aby upewnić się, że się powiedzie. Na przykład usunięcie tagów zamykających, które nie powinny tam być:
</img>
na przykład. Następnie dodaj zamykający pojedynczy ukośnik HTML do elementów, które ich brakuje, itp.Użyłbym tego w kontekście pisania biblioteki, która pozwoliłaby mi na wyszukiwanie elementów HTML
[x].getElementsByTagName()
, na przykład podobnych do JavaScript . Po prostu podzielę funkcjonalność, którą napisałem w sekcji DEFINE wyrażenia regularnego i wykorzystam ją do wejścia do drzewa elementów, pojedynczo.Czy to będzie ostateczna 100% odpowiedź na sprawdzenie poprawności HTML? Nie. Ale to początek i przy odrobinie pracy można to zrobić. Jednak próba wykonania tego w jednym wykonaniu wyrażenia regularnego nie jest ani praktyczna, ani wydajna.
źródło