Come funzionano le analisi HTML se non utilizzano regexp?


96

Vedo domande ogni giorno che chiedono come analizzare o estrarre qualcosa da una stringa HTML e la prima risposta / commento è sempre "Non usare RegEx per analizzare l'HTML, per non sentire l'ira!" (quell'ultima parte a volte è omessa).

Questo è piuttosto confuso per me, ho sempre pensato che in generale, il modo migliore per analizzare qualsiasi stringa complicata è usare un'espressione regolare. Allora come funziona un parser HTML? Non usa espressioni regolari per analizzare.

Un argomento particolare per l'utilizzo di un'espressione regolare è che non c'è sempre un'alternativa di analisi (come JavaScript, dove DOMDocument non è un'opzione universalmente disponibile). jQuery, ad esempio, sembra funzionare bene usando un'espressione regolare per convertire una stringa HTML in nodi DOM.

Non sono sicuro se farlo o meno in CW, è una domanda genuina a cui voglio rispondere e non intesa come filo di discussione.


Ricodificato per aggiungere analisi e analisi html - @ Andy E, spero che ti vada bene - Ho pensato che sarebbe stato utile.
JXG

@JXG: Per me va bene, grazie :-)
Andy E

Risposte:


65

Di solito utilizzando un tokenizzatore. La bozza della specifica HTML5 dispone di un ampio algoritmo per la gestione del "codice HTML del mondo reale".


1
Buona scoperta ... tra virgolette "Per gestire questi casi, i parser hanno un livello di annidamento dello script, che deve essere inizialmente impostato su zero, e un flag di pausa del parser, che deve essere inizialmente impostato su false." - In altre parole, devi iterarlo da solo e avere molta logica personalizzata: P
Timothy Khouri

1
Voto positivo. È meglio enfatizzare la complessità algoritmica invece di una certa tecnologia.
Arnis Lapsa

1
Iterarlo da soli con molta logica personalizzata non è una grande idea. Usa una libreria che supporti l'algoritmo standard, se puoi. ad es. search.cpan.org/~tobyink/HTML-HTML5-Parser-0.03/lib/HTML/HTML5/… / code.google.com/p/html5lib
Quentin

8
Il problema principale con i parser HTML è che quando si verifica un errore, non sei autorizzato a sputare "Parse error" e lasciarlo così. Entra in modalità stranezze e cerchi di distinguere il meglio che puoi dal disordine che hai incontrato, inclusi tag non corrispondenti, interlacciamento di stile [{]} e tutti i tipi di stranezze, cercando di rendere il risultato il migliore possibile e l'inevitabile fallimento il meno doloroso ... questo non è qualcosa che puoi fare con le regex.
SF.

7
@Timothy K: "Nota: a causa del modo in cui questo algoritmo fa sì che gli elementi cambino i genitori, è stato soprannominato" l'algoritmo dell'agenzia di adozione "(in contrasto con altri possibili algoritmi per la gestione di contenuti non annidati, che includevano" l'algoritmo dell'incesto ", l '"algoritmo degli affari segreti" e l' "algoritmo di Heisenberg"). "
JXG

133

Allora come funziona un parser HTML? Non usa espressioni regolari per analizzare?

Beh no.

Se torni nel tuo cervello a un corso di teoria del calcolo, se ne hai seguito uno, o un corso per compilatori, o qualcosa di simile, potresti ricordare che ci sono diversi tipi di linguaggi e modelli computazionali. Non sono qualificato per entrare in tutti i dettagli, ma posso rivedere alcuni dei punti principali con te.

Il tipo più semplice di linguaggio e calcolo (per questi scopi) è un linguaggio normale. Questi possono essere generati con espressioni regolari e riconosciuti con automi finiti. Fondamentalmente, ciò significa che le stringhe di "analisi" in questi linguaggi utilizzano lo stato, ma non la memoria ausiliaria. L'HTML non è certamente un linguaggio normale. Se ci pensi, l'elenco dei tag può essere annidato arbitrariamente in profondità. Ad esempio, le tabelle possono contenere tabelle e ogni tabella può contenere molti tag nidificati. Con le espressioni regolari, potresti essere in grado di scegliere un paio di tag, ma certamente non qualcosa di nidificato arbitrariamente.

Un linguaggio semplice classico che non è regolare è rappresentato correttamente dalle parentesi. Per quanto provi, non sarai mai in grado di costruire un'espressione regolare (o un automa finito) che funzionerà sempre. È necessaria la memoria per tenere traccia della profondità di annidamento.

Una macchina a stati con uno stack per la memoria è il prossimo punto di forza del modello computazionale. Questo è chiamato automa push-down e riconosce i linguaggi generati da grammatiche prive di contesto. Qui, possiamo riconoscere le parentesi abbinate correttamente: in effetti, uno stack è il modello di memoria perfetto per questo.

Bene, questo è abbastanza buono per HTML? Purtroppo no. Forse per super-duper XML accuratamente convalidato, in realtà, in cui tutti i tag si allineano sempre perfettamente. Nell'HTML del mondo reale, puoi facilmente trovare snippet come <b><i>wow!</b></i>. Questo ovviamente non si annida, quindi per analizzarlo correttamente, uno stack non è abbastanza potente.

Il livello successivo di calcolo sono i linguaggi generati da grammatiche generali e riconosciuti dalle macchine di Turing. Questo è generalmente accettato come effettivamente il modello computazionale più potente che esista: una macchina a stati, con memoria ausiliaria, la cui memoria può essere modificata ovunque. Questo è ciò che possono fare i linguaggi di programmazione. Questo è il livello di complessità in cui vive l'HTML.

Per riassumere tutto qui in una frase: per analizzare l'HTML generale, è necessario un vero linguaggio di programmazione, non un'espressione regolare.

L'HTML viene analizzato nello stesso modo in cui vengono analizzati gli altri linguaggi: lexing e parsing. Il passaggio di lexing suddivide il flusso dei singoli personaggi in gettoni significativi. La fase di analisi assembla i token, utilizzando stati e memoria, in un documento logicamente coerente su cui è possibile agire.


22

Le espressioni regolari sono solo una forma di parser. Un parser HTML onesto sarà significativamente più complicato di quanto possa essere espresso nelle espressioni regolari, utilizzando la discesa ricorsiva , la previsione e molte altre tecniche per interpretare correttamente il testo. Se vuoi davvero approfondire, potresti dare un'occhiata a lex e yacc e strumenti simili.

Il divieto di usare le espressioni regolari per l'analisi HTML dovrebbe probabilmente essere scritto più correttamente come: "Non usare espressioni regolari ingenue per analizzare l'HTML ..." (per non sentire l'ira) "... e trattare i risultati con cautela." Per alcuni obiettivi specifici, una regex potrebbe essere perfettamente adeguata, ma devi stare molto attento a essere consapevole dei limiti della tua regex e tanto cauto quanto appropriato alla fonte del testo che stai analizzando (ad esempio, se è input dell'utente, state davvero molto attenti).


+1, una buona risposta. Devo ammettere che ho già usato le regex anche quando non avevo il controllo dell'HTML, ma non in alcun tipo di applicazione rilasciata pubblicamente. Anch'io ho "sentito l'ira", perché era ingenuo. Ma è stato molto tempo fa :-)
Andy E

6

L'analisi dell'HTML è la trasformazione di un testo lineare in una struttura ad albero. Le espressioni regolari non possono generalmente gestire le strutture ad albero. L'espressione regolare di cui hai bisogno in ogni punto per ottenere il token successivo cambia continuamente. Puoi usare espressioni regolari in un parser, ma avrai bisogno di un intero array di espressioni regolari per ogni possibile stato di analisi.


2

Se vuoi avere una soluzione al 100%: devi scrivere il tuo codice personalizzato che itera attraverso l'HTML carattere per carattere e devi avere un'enorme quantità di logica per determinare se devi fermare il nodo corrente e avviare il Il prossimo.

Il motivo è che questo è HTML valido:

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

Ma lo è anche questo:

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

Se sei d'accordo con "soluzione al 90%": Quindi utilizzare un parser XML per caricare un documento va bene. O usando Regex (anche se l'xml è più facile se sei padrone del contenuto).


4
Un parser XML è più simile a una soluzione all'1%. Il numero di documenti HTML ben formati XML è esiguo.
Quentin

4
Sì, lo fanno ... non prendere letteralmente "carattere per personaggio", poiché puoi provare a trasmettere le cose in streaming. Ma il punto è che devi scrivere il tuo parser. I programmatori di nuova età non sono abituati a scrivere quel tipo di codice ... siamo abituati a "HtmlDocumentUtility.Load" e cose del genere :)
Timothy Khouri

4
@ Andy E: Le espressioni regolari non sono magiche, funzionano anche carattere per carattere, come qualsiasi altro tipo di analisi, o diamine, qualsiasi altra funzione di stringa.
Bart van Heukelom

1
BTW: Il tuo primo esempio non è solo "HTML semi-valido". In realtà è valido HTML 4.01 Strict. Puoi usare ad esempio il validatore W3C per verificarlo. Il tag di chiusura è ufficialmente facoltativo per <li> (vedi le specifiche HTML 4).
sleske

2
@ Bart: buon punto, a volte il mio cervello dimentica tutta la logica e pensa che le cose funzionino per magia.
Andy E
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.