html.parser — Простой парсер HTML и XHTML


Данный модуль определяет класс HTMLParser, который служит основой для анализа текстовых файлов, отформатированных в HTML (язык гипертекстовой разметки) и XHTML.

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

Создать экземпляр парсера, способный анализировать недопустимую разметку.

Если convert_charrefs имеет значение True (по умолчанию), все ссылки на символы (кроме ссылок в элементах script/style) автоматически преобразуются в соответствующие Юникод символы.

Экземпляр HTMLParser получает данные HTML и вызывает методы обработчика при обнаружении начальных тегов, конечных тегов, текста, комментариев и других элементов разметки. Пользователь должен создать подкласс HTMLParser и переопределить его методы для реализации желаемого поведения.

Данный парсер не проверяет соответствие конечных тегов начальным тегам и не вызывает обработчик конечных тегов для элементов, которые неявно закрываются путём закрытия внешнего элемента.

Изменено в версии 3.4: Добавлен ключевой аргумент convert_charrefs.

Изменено в версии 3.5: Значение по умолчанию для аргумента convert_charrefs теперь равно True.

Пример приложения парсера HTML

В качестве базового примера ниже приведен простой парсер HTML, который использует класс HTMLParser для вывода начальных тегов, конечных тегов и данных по мере их обнаружения:

from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("Столкнулся с начальным тегом: ", tag)

    def handle_endtag(self, tag):
        print("Столкнулся с конечным тегом :", tag)

    def handle_data(self, data):
        print("Столкнулся с некоторыми данными  :", data)

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

Выход тогда будет:

Столкнулся с начальным тегом: html
Столкнулся с начальным тегом: head
Столкнулся с начальным тегом: title
Столкнулся с некоторыми данными  : Test
Столкнулся с конечным тегом : title
Столкнулся с конечным тегом : head
Столкнулся с начальным тегом: body
Столкнулся с начальным тегом: h1
Столкнулся с некоторыми данными  : Parse me!
Столкнулся с конечным тегом : h1
Столкнулся с конечным тегом : body
Столкнулся с конечным тегом : html

Методы HTMLParser

У экземпляров HTMLParser следующие методы:

HTMLParser.feed(data)

Подать текст парсеру. Он обрабатывается, поскольку состоит из достаточных элементов; неполные данные буферизуются до тех пор, пока не будут переданы дополнительные данные или не будет вызван close(). data должен быть str.

HTMLParser.close()

Принудительная обработка всех буферизованных данных, как если бы за ними следовала метка конца файла. Данный метод может быть переопределён производным классом для определения дополнительной обработки в конце ввода, но переопределенная версия всегда должна вызывать метод базового класса HTMLParser close().

HTMLParser.reset()

Сбросить экземпляр. Теряет все необработанные данные. Это вызывается неявно во время создания экземпляра.

HTMLParser.getpos()

Возвращает текущий номер строки и смещение.

HTMLParser.get_starttag_text()

Возвращает текст последнего открытого начального тега. Обычно это не требуется для структурированной обработки, но может быть полезно при работе с HTML «как развернуто» или для повторной генерации ввода с минимальными изменениями (можно сохранить пробелы между атрибутами и т. д.).

Следующие методы вызываются, когда встречаются данные или элементы разметки, и они предназначены для переопределения в подклассе. Реализации базового класса ничего не делают (кроме handle_startendtag()):

HTMLParser.handle_starttag(tag, attrs)

Данный метод вызывается для обработки начала тега (например, <div id="main">).

Аргумент tag — это имя тега, преобразованное в нижний регистр. Аргумент attrs представляет собой список пар (name, value), содержащих атрибуты, находящиеся внутри скобок тега <>. name будет переведён в нижний регистр, кавычки в value удалены, а ссылки на символы и сущности заменены.

Например, для тега <A HREF="https://digitology.tech/"> данный метод будет называться handle_starttag('a', [('href', 'https://digitology.tech/')]).

Все ссылки на объекты из html.entities заменяются в значениях атрибутов.

HTMLParser.handle_endtag(tag)

Данный метод вызывается для обработки конечного тега элемента (например, </div>).

Аргумент tag — это имя тега, преобразованное в нижний регистр.

HTMLParser.handle_startendtag(tag, attrs)

Аналогичен handle_starttag(), но вызывается, когда парсер встречает пустой тег в стиле XHTML (<img ... />). Данный метод может быть переопределён подклассами, которым требуется лексическая информация; реализация по умолчанию просто вызывает handle_starttag() и handle_endtag().

HTMLParser.handle_data(data)

Данный метод вызывается для обработки произвольных данных (например, текстовых узлов и содержимого <script>...</script> и <style>...</style>).

HTMLParser.handle_entityref(name)

Данный метод вызывается для обработки ссылки на именованный символ в форме &name; (например, &gt;), где name — это общая ссылка на объект (например, 'gt'). Данный метод никогда не вызывается, если convert_charrefs равен True.

HTMLParser.handle_charref(name)

Данный метод вызывается для обработки десятичных и шестнадцатеричных числовых ссылок на символы в форме &#NNN; и &#xNNN;. Например, десятичный эквивалент для &gt;&#62;, а шестнадцатеричный — &#x3E;; в этом случае метод получит '62' или 'x3E'. Данный метод никогда не вызывается, если convert_charrefs равен True.

HTMLParser.handle_comment(data)

Данный метод вызывается при обнаружении комментария (например, <!--comment-->).

Например, комментарий <!-- comment --> вызовет данный метод с аргументом ' comment '.

Содержимое условных комментариев (condcom’ы) Internet Explorer также будет отправлено в данный метод, поэтому для <!--[if IE 9]>IE9-specific content<![endif]--> данный метод получит '[if IE 9]>IE9-specific content<![endif]'.

HTMLParser.handle_decl(decl)

Данный метод вызывается для обработки объявления типа документа HTML (например, <!DOCTYPE html>).

Параметр decl будет всем содержимым объявления внутри разметки <!...> (например, 'DOCTYPE html').

HTMLParser.handle_pi(data)

Метод вызывается, когда встречается оператор обработки. Параметр data будет содержать всю инструкцию по обработке. Например, для инструкции обработки <?proc color='red'> данный метод будет вызываться handle_pi("proc color='red'"). Он предназначен для переопределения производным классом; реализация базового класса ничего не делает.

Примечание

Класс HTMLParser использует синтаксические правила SGML для обработки инструкций. Оператор обработки XHTML, использующая завершающий '?', приведёт к включению '?' в data.

HTMLParser.unknown_decl(data)

Данный метод вызывается, когда парсер читает нераспознанное объявление.

Параметр data будет всем содержимым объявления внутри разметки <![...]>. Иногда полезно быть переопределенным производным классом. Реализация базового класса ничего не делает.

Примеры

Следующий класс реализует парсер, который будет использоваться для иллюстрации других примеров:

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

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("Начальный тег:", tag)
        for attr in attrs:
            print("     attr:", attr)

    def handle_endtag(self, tag):
        print("Конечный тег  :", tag)

    def handle_data(self, data):
        print("Данные   :", data)

    def handle_comment(self, data):
        print("Комментарий   :", data)

    def handle_entityref(self, name):
        c = chr(name2codepoint[name])
        print("Имя элемента   :", c)

    def handle_charref(self, name):
        if name.startswith('x'):
            c = chr(int(name[1:], 16))
        else:
            c = chr(int(name))
        print("Номер элемента   :", c)

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

parser = MyHTMLParser()

Разбор типа документа:

>>> 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"

Разбор элемента с несколькими атрибутами и заголовком:

>>> parser.feed('<img src="python-logo.png" alt="The Python logo">')
Начальный тег: img
     attr: ('src', 'python-logo.png')
     attr: ('alt', 'The Python logo')
>>>
>>> parser.feed('<h1>Python</h1>')
Начальный тег: h1
Данные   : Python
Конечный тег  : h1

Содержимое элементов script и style возвращается как есть, без дальнейшего анализа:

>>> parser.feed('<style type="text/css">#python { color: green }</style>')
Начальный тег: style
     attr: ('type', 'text/css')
Данные   : #python { color: green }
Конечный тег  : style

>>> parser.feed('<script type="text/javascript">'
...             'alert("<strong>hello!</strong>");</script>')
Начальный тег: script
     attr: ('type', 'text/javascript')
Данные   : alert("<strong>hello!</strong>");
Конечный тег  : script

Разбор комментариев:

>>> parser.feed('<!-- a comment -->'
...             '<!--[if IE 9]>IE-specific content<![endif]-->')
Комментарий   :  a comment
Комментарий   : [if IE 9]>IE-specific content<![endif]

Разбор ссылок на именованные и числовые символы и преобразование их в правильный символ (примечание: все данные 3 ссылки эквивалентны '>'):

>>> parser.feed('&gt;&#62;&#x3E;')
Имя элемента   : >
Номер элемента   : >
Номер элемента   : >

Передача неполных фрагментов в feed() работает, но handle_data() может вызываться более одного раза (если для convert_charrefs не установлено значение True):

>>> for chunk in ['<sp', 'an>buff', 'ered ', 'text</s', 'pan>']:
...     parser.feed(chunk)
...
Начальный тег: span
Данные   : buff
Данные   : ered
Данные   : text
Конечный тег  : span

Парсинг недопустимого HTML (например, атрибутов без кавычек) также работает:

>>> parser.feed('<p><a class=link href=#main>tag soup</p ></a>')
Начальный тег: p
Начальный тег: a
     attr: ('class', 'link')
     attr: ('href', '#main')
Данные   : tag soup
Конечный тег  : p
Конечный тег  : a