html.parser — Einfacher HTML- und XHTML-Parser

Quellcode: Lib/html/parser.py


Dieses Modul definiert eine Klasse HTMLParser, die als Grundlage für das Parsen von Textdateien im HTML-Format (HyperText Mark-up Language) und XHTML dient.

class html.parser.HTMLParser(*, convert_charrefs=True)

Erstellt eine Parser-Instanz, die ungültige Markups parsen kann.

Wenn convert_charrefs auf True (Standard) gesetzt ist, werden alle Zeichenreferenzen (außer denen in script/style Elementen) automatisch in die entsprechenden Unicode-Zeichen umgewandelt.

Eine Instanz von HTMLParser wird mit HTML-Daten gefüttert und ruft Handler-Methoden auf, wenn Start-Tags, End-Tags, Text, Kommentare und andere Markup-Elemente gefunden werden. Der Benutzer sollte von HTMLParser erben und seine Methoden überschreiben, um das gewünschte Verhalten zu implementieren.

Dieser Parser prüft nicht, ob End-Tags zu Start-Tags passen, oder ruft den End-Tag-Handler für Elemente auf, die implizit durch das Schließen eines äußeren Elements geschlossen werden.

Geändert in Version 3.4: Das Schlüsselwortargument convert_charrefs wurde hinzugefügt.

Geändert in Version 3.5: Der Standardwert für das Argument convert_charrefs ist nun True.

Anwendungsbeispiel für einen HTML-Parser

Als grundlegendes Beispiel folgt hier ein einfacher HTML-Parser, der die Klasse HTMLParser verwendet, um gefundene Start-Tags, End-Tags und Daten auszugeben.

from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("Encountered a start tag:", tag)

    def handle_endtag(self, tag):
        print("Encountered an end tag :", tag)

    def handle_data(self, data):
        print("Encountered some data  :", data)

parser = MyHTMLParser()
parser.feed('<html><head><title>Test</title></head>'
            '<body><h1>Parse me!</h1></body></html>')

Die Ausgabe wird dann wie folgt lauten:

Encountered a start tag: html
Encountered a start tag: head
Encountered a start tag: title
Encountered some data  : Test
Encountered an end tag : title
Encountered an end tag : head
Encountered a start tag: body
Encountered a start tag: h1
Encountered some data  : Parse me!
Encountered an end tag : h1
Encountered an end tag : body
Encountered an end tag : html

Methoden von HTMLParser

HTMLParser-Instanzen haben die folgenden Methoden:

HTMLParser.feed(data)

Füttert einen Text in den Parser. Er wird verarbeitet, soweit er vollständige Elemente enthält; unvollständige Daten werden gepuffert, bis weitere Daten gefüttert werden oder close() aufgerufen wird. data muss vom Typ str sein.

HTMLParser.close()

Erzwingt die Verarbeitung aller gepufferten Daten, als ob ihnen ein End-of-File-Markierung folgen würde. Diese Methode kann von einer abgeleiteten Klasse überschrieben werden, um zusätzliche Verarbeitung am Ende der Eingabe zu definieren. Die überschriebene Version sollte jedoch immer die Methode der Basisklasse HTMLParser close() aufrufen.

HTMLParser.reset()

Setzt die Instanz zurück. Alle unverarbeiteten Daten gehen verloren. Dies wird implizit beim Instanziieren aufgerufen.

HTMLParser.getpos()

Gibt die aktuelle Zeilennummer und den Offset zurück.

HTMLParser.get_starttag_text()

Gibt den Text des zuletzt geöffneten Start-Tags zurück. Dies sollte normalerweise nicht für eine strukturierte Verarbeitung benötigt werden, kann aber nützlich sein, um mit "eingesetztem" HTML umzugehen oder um die Eingabe mit minimalen Änderungen wieder zu generieren (Leerzeichen zwischen Attributen können erhalten bleiben usw.).

Die folgenden Methoden werden aufgerufen, wenn Daten oder Markup-Elemente angetroffen werden, und sie sind dazu gedacht, in einer Unterklasse überschrieben zu werden. Die Implementierungen der Basisklasse tun nichts (außer für handle_startendtag())

HTMLParser.handle_starttag(tag, attrs)

Diese Methode wird aufgerufen, um den Start-Tag eines Elements zu behandeln (z. B. <div id="main">).

Das Argument tag ist der Name des Tags, umgewandelt in Kleinbuchstaben. Das Argument attrs ist eine Liste von (name, value) Paaren, die die Attribute enthalten, die sich innerhalb der <> Klammern des Tags befinden. Der name wird in Kleinbuchstaben übersetzt, Anführungszeichen im value wurden entfernt, und Zeichen- und Entitätsreferenzen wurden ersetzt.

Beispielsweise würde für den Tag <A HREF="https://www.cwi.nl/"> diese Methode als handle_starttag('a', [('href', 'https://www.cwi.nl/')]) aufgerufen.

Alle Entitätsreferenzen aus html.entities werden in den Attributwerten ersetzt.

HTMLParser.handle_endtag(tag)

Diese Methode wird aufgerufen, um den End-Tag eines Elements zu behandeln (z. B. </div>).

Das Argument tag ist der Name des Tags, umgewandelt in Kleinbuchstaben.

HTMLParser.handle_startendtag(tag, attrs)

Ähnlich wie handle_starttag(), wird aber aufgerufen, wenn der Parser einen leeren Tag im XHTML-Stil antrifft (<img ... />). Diese Methode kann von abgeleiteten Klassen überschrieben werden, die diese spezielle lexikalische Information benötigen; die Standardimplementierung ruft einfach handle_starttag() und handle_endtag() auf.

HTMLParser.handle_data(data)

Diese Methode wird aufgerufen, um beliebige Daten zu verarbeiten (z. B. Textknoten und den Inhalt von <script>...</script> und <style>...</style>).

HTMLParser.handle_entityref(name)

Diese Methode wird aufgerufen, um eine benannte Zeichenreferenz der Form &name; (z. B. &gt;) zu verarbeiten, wobei name eine allgemeine Entitätsreferenz ist (z. B. 'gt'). Diese Methode wird niemals aufgerufen, wenn convert_charrefs auf True gesetzt ist.

HTMLParser.handle_charref(name)

Diese Methode wird aufgerufen, um dezimale und hexadezimale numerische Zeichenreferenzen der Form &#NNN; und &#xNNN; zu verarbeiten. Zum Beispiel ist das dezimale Äquivalent von &gt; &#62;, während das hexadezimale &#x3E; ist; in diesem Fall erhält die Methode '62' oder 'x3E'. Diese Methode wird niemals aufgerufen, wenn convert_charrefs auf True gesetzt ist.

HTMLParser.handle_comment(data)

Diese Methode wird aufgerufen, wenn ein Kommentar angetroffen wird (z. B. <!--comment-->).

Beispielsweise wird der Kommentar <!-- comment --> dazu führen, dass diese Methode mit dem Argument ' comment ' aufgerufen wird.

Der Inhalt von Internet Explorer Conditional Comments (condcoms) wird ebenfalls an diese Methode gesendet. Für <!--[if IE 9]>IE9-specific content<![endif]--> empfängt diese Methode '[if IE 9]>IE9-specific content<![endif]'.

HTMLParser.handle_decl(decl)

Diese Methode wird aufgerufen, um eine HTML-Doctype-Deklaration zu behandeln (z. B. <!DOCTYPE html>).

Der Parameter decl ist der gesamte Inhalt der Deklaration innerhalb des <!...> Markups (z. B. 'DOCTYPE html').

HTMLParser.handle_pi(data)

Diese Methode wird aufgerufen, wenn eine Verarbeitungsanweisung angetroffen wird. Der Parameter data enthält die gesamte Verarbeitungsanweisung. Zum Beispiel würde die Verarbeitungsanweisung <?proc color='red'> dazu führen, dass diese Methode als handle_pi("proc color='red'") aufgerufen wird. Sie ist dazu gedacht, von einer abgeleiteten Klasse überschrieben zu werden; die Standardimplementierung tut nichts.

Hinweis

Die Klasse HTMLParser verwendet die SGML-Syntaxeregeln für Verarbeitungsanweisungen. Eine XHTML-Verarbeitungsanweisung, die das abschließende '?' verwendet, führt dazu, dass das '?' in data enthalten ist.

HTMLParser.unknown_decl(data)

Diese Methode wird aufgerufen, wenn eine nicht erkannte Deklaration vom Parser gelesen wird.

Der Parameter data ist der gesamte Inhalt der Deklaration innerhalb des <![...]> Markups. Es ist manchmal nützlich, sie von einer abgeleiteten Klasse überschreiben zu lassen. Die Implementierung der Basisklasse tut nichts.

Beispiele

Die folgende Klasse implementiert einen Parser, der zur Veranschaulichung weiterer Beispiele verwendet wird.

from html.parser import HTMLParser
from html.entities import name2codepoint

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("Start tag:", tag)
        for attr in attrs:
            print("     attr:", attr)

    def handle_endtag(self, tag):
        print("End tag  :", tag)

    def handle_data(self, data):
        print("Data     :", data)

    def handle_comment(self, data):
        print("Comment  :", data)

    def handle_entityref(self, name):
        c = chr(name2codepoint[name])
        print("Named ent:", c)

    def handle_charref(self, name):
        if name.startswith('x'):
            c = chr(int(name[1:], 16))
        else:
            c = chr(int(name))
        print("Num ent  :", c)

    def handle_decl(self, data):
        print("Decl     :", data)

parser = MyHTMLParser()

Parsen eines Doctypes

>>> parser.feed('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
...             '"http://www.w3.org/TR/html4/strict.dtd">')
Decl     : DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"

Parsen eines Elements mit einigen Attributen und einem Titel

>>> parser.feed('<img src="python-logo.png" alt="The Python logo">')
Start tag: img
     attr: ('src', 'python-logo.png')
     attr: ('alt', 'The Python logo')
>>>
>>> parser.feed('<h1>Python</h1>')
Start tag: h1
Data     : Python
End tag  : h1

Der Inhalt von script- und style-Elementen wird unverändert zurückgegeben, ohne weitere Verarbeitung.

>>> parser.feed('<style type="text/css">#python { color: green }</style>')
Start tag: style
     attr: ('type', 'text/css')
Data     : #python { color: green }
End tag  : style

>>> parser.feed('<script type="text/javascript">'
...             'alert("<strong>hello!</strong>");</script>')
Start tag: script
     attr: ('type', 'text/javascript')
Data     : alert("<strong>hello!</strong>");
End tag  : script

Parsen von Kommentaren

>>> parser.feed('<!--a comment-->'
...             '<!--[if IE 9]>IE-specific content<![endif]-->')
Comment  : a comment
Comment  : [if IE 9]>IE-specific content<![endif]

Parsen von benannten und numerischen Zeichenreferenzen und deren Umwandlung in das korrekte Zeichen (Hinweis: Diese 3 Referenzen sind alle äquivalent zu '>').

>>> parser = MyHTMLParser()
>>> parser.feed('&gt;&#62;&#x3E;')
Data     : >>>

>>> parser = MyHTMLParser(convert_charrefs=False)
>>> parser.feed('&gt;&#62;&#x3E;')
Named ent: >
Num ent  : >
Num ent  : >

Das Füttern unvollständiger Blöcke an feed() funktioniert, aber handle_data() kann mehr als einmal aufgerufen werden (es sei denn, convert_charrefs ist auf True gesetzt).

>>> for chunk in ['<sp', 'an>buff', 'ered', ' text</s', 'pan>']:
...     parser.feed(chunk)
...
Start tag: span
Data     : buff
Data     : ered
Data     :  text
End tag  : span

Auch das Parsen von ungültigem HTML (z. B. unquotierte Attribute) funktioniert.

>>> parser.feed('<p><a class=link href=#main>tag soup</p ></a>')
Start tag: p
Start tag: a
     attr: ('class', 'link')
     attr: ('href', '#main')
Data     : tag soup
End tag  : p
End tag  : a