Używanie wyrażeń regularnych do analizowania HTML: dlaczego nie?

207

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?

ntownsend
źródło
3
myślę, że to dupe stackoverflow.com/questions/133601
jcrossley3
23
Ponieważ tylko Chuck Norris może analizować HTML za pomocą wyrażenia regularnego (jak wyjaśniono w tej słynnej rzeczy Zalgo: stackoverflow.com/questions/1732348/... ).
trwa
1
To pytanie skłoniło mnie do zadania kolejnego, które jest w jakiś sposób powiązane. Jeśli jesteś zainteresowany: Dlaczego nie można użyć wyrażenia regularnego do parsowania HTML / XML: formalne wyjaśnienie w kategoriach laika
Mac
Uważaj na Zalgo
Kelly S. French
To pytanie zostało dodane do często zadawanych pytań związanych z przepełnieniem stosu , w części „Typowe zadania sprawdzania poprawności”.
aliteralmind

Odpowiedzi:

212

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.

Johannes Weiss
źródło
26
Najlepsza jak dotąd odpowiedź. Jeśli może pasować tylko do zwykłych gramatyk, potrzebowalibyśmy nieskończenie dużego wyrażenia regularnego do parsowania gramatyki bezkontekstowej, takiej jak HTML. Uwielbiam, gdy te rzeczy mają jasne teoretyczne odpowiedzi.
ntownsend
2
Zakładałem, że omawiamy wyrażenia regularne typu Perl, w których tak naprawdę nie są wyrażeniami regularnymi.
Hank Gay
5
W rzeczywistości wyrażenia regularne .Net mogą do pewnego stopnia dopasowywać otwieranie do tagów zamykających, używając grup równoważących i starannie spreparowanego wyrażenia. Zawierające wszystko o tym w regexp jest wciąż szalone oczywiście to będzie wyglądać wielkiego kodu Chtulhu i prawdopodobnie przywołać prawdziwego również. I ostatecznie nie będzie działać we wszystkich przypadkach. Mówią, że jeśli napiszesz wyrażenie regularne, które może poprawnie parsować dowolny HTML, wszechświat zapadnie się na sobie.
Alex Paven,
5
Niektóre biblioteki wyrażeń regularnych mogą wykonywać rekurencyjne wyrażenia regularne (skutecznie czyniąc je wyrażeniami nieregularnymi :)
Ondra Žižka
43
-1 Ta odpowiedź wyciąga właściwy wniosek („Parsowanie HTML za pomocą Regex” to zły pomysł) na podstawie niewłaściwych argumentów („Ponieważ HTML nie jest zwykłym językiem”). To, co dziś większość ludzi ma na myśli, mówiąc „regex” (PCRE), jest w stanie nie tylko parsować gramatyki bezkontekstowe (w rzeczywistości jest to banalne), ale także gramatyki kontekstowe (patrz stackoverflow.com/questions/7434272/ … ).
NikiC,
35

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?

kmkaplan
źródło
1
Niektóre biblioteki wyrażeń regularnych mogą wykonywać rekurencyjne wyrażenia regularne (skutecznie czyniąc je wyrażeniami nieregularnymi :)
Ondra Žižka
23

(Od http://htmlparsing.com/regexes )

Załóżmy, że masz plik HTML, w którym próbujesz wyodrębnić adresy URL z tagów <img>.

<img src="http://example.com/whatever.jpg">

Więc piszesz takie wyrażenie regularne w Perlu:

if ( $html =~ /<img src="(.+)"/ ) {
    $url = $1;
}

W takim przypadku $urlrzeczywiście będzie zawierać http://example.com/whatever.jpg. Ale co się stanie, gdy zaczniesz otrzymywać HTML w ten sposób:

<img src='http://example.com/whatever.jpg'>

lub

<img src=http://example.com/whatever.jpg>

lub

<img border=0 src="http://example.com/whatever.jpg">

lub

<img
    src="http://example.com/whatever.jpg">

lub zaczniesz otrzymywać fałszywe alarmy od

<!-- // commented out
<img src="http://example.com/outdated.png">
-->

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.

Andy Lester
źródło
4
To wydaje się być prawdziwą odpowiedzią - chociaż prawdopodobnie możliwe jest parsowanie dowolnego kodu HTML za pomocą wyrażenia regularnego, ponieważ dzisiejsze wyrażenia regularne są czymś więcej niż tylko skończonymi automatami, aby przeanalizować dowolny plik HTML, a nie tylko konkretną stronę, należy ponownie zaimplementować parser HTML w wyrażeniu regularnym i wyrażenia regularne z pewnością staną się 1000 razy nieczytelne.
Smit Johnth,
1
Hej, Andy, nie spieszyłem się z wyrażeniem, które popiera twoje wspomniane przypadki. stackoverflow.com/a/40095824/1204332 Daj mi znać, co myślisz! :)
Ivan Chaer
2
Rozumowanie w tej odpowiedzi jest sposób nieaktualne, a zastosowanie nawet mniej niż to miało miejsce dzisiaj (pierwotnie które myślę, że nie). (Cytując OP: „jeśli robisz coś prostego, szybkiego lub brudnego ...”.)
Sz.
16

Dwa szybkie powody:

  • napisanie wyrażenia regularnego, które może wytrzymać złośliwe dane wejściowe, jest trudne; o wiele trudniejsze niż korzystanie z gotowego narzędzia
  • napisanie wyrażenia regularnego, które może pracować z absurdalnym znacznikiem, z którym nieuchronnie utkniesz, jest trudne; o wiele trudniejsze niż korzystanie z gotowego narzędzia

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?

Hank Gay
źródło
2
Łał? Opinia negatywna po ponad 2 latach? Na wypadek, gdyby ktoś się zastanawiał, nie powiedziałem „Ponieważ to teoretycznie niemożliwe”, ponieważ pytanie jasno brzmiało „szybko i brudno”, a nie „poprawnie”. PO wyraźnie przeczytał już odpowiedzi, które obejmowały teoretycznie niemożliwe terytorium i nadal nie były spełnione.
Hank Gay
1
Wyraź opinię po ponad 5 latach. :) Jeśli chodzi o to, dlaczego mógłeś otrzymać głos negatywny, nie jestem uprawniony do powiedzenia, ale osobiście wolałbym zobaczyć kilka przykładów lub wyjaśnień niż końcowe pytanie retoryczne.
Adam Jensen,
3
Zasadniczo wszystkie szybkie i brudne parsowanie HTML, które odbywa się w wysyłce produktów lub narzędzi wewnętrznych, kończy się dziurą w zabezpieczeniach lub błędem, który czeka. Trzeba go zniechęcić z rozmachem. Jeśli można użyć wyrażenia regularnego, można użyć odpowiedniego parsera HTML.
Przywróć Monikę
16

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).

Vatine
źródło
8

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.

Tamas Czinege
źródło
1
Bardzo prawda, większość dostępnych HTML wydaje się okropna. Nie rozumiem, w jaki sposób brakujące wyrażenie regularne może wprowadzić poważne luki w zabezpieczeniach. Czy możesz podać przykład?
ntownsend
4
ntownsend: Na przykład uważasz, że usunąłeś wszystkie znaczniki skryptu z kodu HTML, ale wyrażenie regularne nie obejmuje specjalnego przypadku (który, powiedzmy, działa tylko na IE6): boom, masz podatność na XSS!
Tamas Czinege
1
Był to ściśle hipotetyczny przykład, ponieważ większość przykładów ze świata rzeczywistego jest zbyt skomplikowana, aby zmieścić się w tych komentarzach, ale można znaleźć kilka, szybko przeglądając ten temat.
Tamas Czinege
3
+1 za wzmiankę o kącie bezpieczeństwa. Kiedy łączysz się z całym Internetem, nie możesz sobie pozwolić na pisanie hackerskiego kodu „działa przez większość czasu”.
j_random_hacker
7

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.

okoman
źródło
6

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.

Peter Boughton
źródło
6

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 książkę.

tagi
źródło
1
Właśnie przeczytałem tę książkę. Po prostu nie przyszło mi do głowy, że HTML jest językiem bezkontekstowym.
ntownsend
4

To wyrażenie pobiera atrybuty z elementów HTML. To wspiera:

  • atrybuty niewymienione / cytowane,
  • pojedyncze / podwójne cytaty,
  • uniknął cytatów wewnątrz atrybutów,
  • odstępy wokół są równe znakom,
  • dowolna liczba atrybutów,
  • sprawdź tylko atrybuty w tagach,
  • unikaj komentarzy i
  • zarządzać różnymi ofertami w ramach wartości atrybutu.

(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)

Sprawdź to . Działa lepiej z flagami „gisx”, jak w wersji demo.

Ivan Chaer
źródło
1
To bardzo interesujące. Nieczytelne, prawdopodobnie trudne do debugowania, ale wciąż: Imponująca robota!
Eric Duminil,
To nadal niejasno zakłada, że ​​HTML jest dobrze sformułowany. Bez dopasowania kontekstu spowoduje to dopasowanie widocznych adresów URL w kontekstach, w których zwykle nie chcesz ich dopasowywać, na przykład w kodzie JavaScript wewnątrz <script>tagu.
tripleee
4

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

 (?:
      " [\S\s]*? " 
   |  ' [\S\s]*? ' 
   |  [^>]? 
 )+

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.

 <     
 (?:
      [\w:]+ 
      \s+ 
      (?:
           " [\S\s]*? " 
        |  ' [\S\s]*? ' 
        |  [^>]? 
      )+
      \s* /?
 )
 >

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

 <
 (?:
      (?:
           (?:
                # Invisible content; end tag req'd
                (                             # (1 start)
                     script
                  |  style
                  |  object
                  |  embed
                  |  applet
                  |  noframes
                  |  noscript
                  |  noembed 
                )                             # (1 end)
                (?:
                     \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]*? )
           )
      )
 )
 >
potrójny
źródło
3

„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.

karma dla kotów
źródło
3

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.

Jason
źródło
3

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.

alpheus
źródło
2

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ą, strrposaby 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.

Deji
źródło
2
-1 za zasugerowanie STRASZNEGO pomysłu. Czy rozważałeś spacje między tagiem a nawiasem zamykającym? (Np. <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?
rmunn
1
W konkretnym przypadku kilku niestandardowych tagów tak, wyrażenia regularne działają dobrze. Więc nie jest tak, że użycie ich było błędem w twoim konkretnym przypadku . To jednak nie jest HTML, a powiedzenie „parsowanie HTML za pomocą wyrażenia regularnego jest w PHP całkowicie możliwe” jest po prostu fałszywym i OGROMNYM pomysłem. Niespójności prawdziwego HTML (a jest o wiele więcej niż kilka wymienionych na liście) powodują, że nigdy nie powinieneś analizować prawdziwego HTML za pomocą wyrażeń regularnych. Zobacz wszystkie inne odpowiedzi na to pytanie, a także te, do których odsyłam w innym komentarzu powyżej.
rmunn
2
PHP jest kompletnym językiem Turinga, więc nie jest to wcale nieprawda. Możliwe jest wszystko obliczeniowo, w tym parsowanie HTML. Odstępy w znacznikach NIGDY nie stanowiły problemu i od tego czasu dostosowałem je do wyświetlania listy elementów znaczników w kolejności. Moje użycie automatycznie poprawia tagi z niespójną obudową, rozbiera komentowane rzeczy na pierwszym etapie, a po kilku późniejszych dodaniach można łatwo dodawać wszelkiego rodzaju tagi (choć rozróżnia wielkość liter, według własnego wyboru). I jestem prawie pewien, że CDATA to tak naprawdę element XML, a nie HTML.
Deji,
2
Moja stara metoda (którą tutaj opisałem) była dość nieefektywna i niedawno zacząłem ponownie pisać wiele edytorów treści. Jeśli chodzi o robienie tych rzeczy, możliwość nie jest problemem; najlepszy sposób jest zawsze najważniejszy. Prawdziwa odpowiedź brzmi: „nie ma ŁATWEGO sposobu, aby to zrobić w PHP”. ŻADNY NIE mówi, że nie ma sposobu, aby to zrobić w PHP lub że jest to okropny pomysł, ale że jest to niemożliwe z regexem, którego szczerze mówiąc nigdy nie próbowałem, ale jedną z głównych wad mojej odpowiedzi jest założenie, że pytanie dotyczyło regexu w kontekście PHP, co niekoniecznie ma miejsce.
Deji,
2

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:

(?P<content>.*?)                # Content up to next tag
(?P<markup>                     # Entire tag
  <!\[CDATA\[(?P<cdata>.+?)]]>| # <![CDATA[ ... ]]>
  <!--(?P<comment>.+?)-->|      # <!-- Comment -->
  </\s*(?P<close_tag>\w+)\s*>|  # </tag>
  <(?P<tag>\w+)                 # <tag ...
    (?P<attributes>
      (?P<attribute>\s+
# <snip>: Use this part to get the attributes out of 'attributes' group.
        (?P<attribute_name>\w+)
        (?:\s*=\s*
          (?P<attribute_value>
            [\w:/.\-]+|         # Unquoted
            (?=(?P<_v>          # Quoted
              (?P<_q>['\"]).*?(?<!\\)(?P=_q)))
            (?P=_v)
          ))?
# </snip>
      )*
    )\s*
  (?P<is_self_closing>/?)   # Self-closing indicator
  >)                        # End of tag

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:

  • Otwórz tag - <div ...>
  • Zamknij tag - </div>
  • Komentarz - <!-- ... -->
  • CDATA - <![CDATA[ ... ]]>
  • Tag samozamykający - <div .../>
  • Opcjonalne wartości atrybutów - <input checked>
  • Nienotowane / cytowane wartości atrybutów - <div style='...'>
  • Pojedyncze / podwójne cytaty - <div style="...">
  • Escaped Quotes - <a title='John\'s Story'>
    (to naprawdę nie jest poprawny HTML, ale jestem miłym facetem)
  • Odstępy wokół znaków równości - <a href = '...'>
  • Nazwane zdjęcia dla interesujących bitów

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 renie (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.
  • _qi _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 attributesgrupy, aby każdy attribute, attribute_namea attribute_valuepoza nim.

Demo tutaj: https://regex101.com/r/mH8jSu/11

Hounshell
źródło
1

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.

Gumbo
źródło
0

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.

Erutan409
źródło