Документация Python неофициальный перевод
Содержание страницы

20.2. html.parser – Простой парсер HTML и XHTMLhtml.parser – Simple HTML and XHTML parser

Исходный код: Lib/html/parser.py


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

class html.parser.HTMLParser(strict=False)

Создаёт экземпляр парсера. Если strict равно False (по умолчанию), парсер принимает и разбирает некорректную разметку. Если strict равно True, парсер возбуждает исключение HTMLParseError вместо [1], когда не может разобрать разметку. Использование strict=True не рекомендуется, а аргумент strict устарел.

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

Этот парсер не проверяет, соответствуют ли закрывающие теги открывающим, и не вызывает обработчик закрывающего тега для элементов, которые неявно закрываются при закрытии внешнего элемента.

Изменено в версии 3.2: добавлен ключевой аргумент strict.

Устарело с версии 3.3, будет удалено в версии 3.5: Аргумент strict и строгий режим устарели. Теперь парсер также способен принимать и разбирать некорректную разметку.

Также определено исключение:

exception html.parser.HTMLParseError

Исключение, возбуждаемое классом HTMLParser, когда он обнаруживает ошибку при разборе и strict равен True. Это исключение предоставляет три атрибута: msg – краткое сообщение, объясняющее ошибку; lineno – номер строки, в которой обнаружен некорректный элемент; offset – количество символов от начала строки, где начинается этот элемент.

Устарело с версии 3.3, будет удалено в версии 3.5: Это исключение было объявлено устаревшим, поскольку оно никогда не возбуждается парсером (при использовании режима нестрогого разбора по умолчанию).

20.2.1. Пример приложения парсера HTMLExample HTML Parser Application

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

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(strict=False)
parser.feed('<html><head><title>Test</title></head>'
            '<body><h1>Parse me!</h1></body></html>')

Результат будет таким:

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

20.2.2. Методы HTMLParserHTMLParser Methods

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

HTMLParser.feed(data)

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

HTMLParser.close()

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

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="http://www.cwi.nl/"> этот метод был бы вызван как handle_starttag('a', [('href', 'http://www.cwi.nl/')]).

Все ссылки на сущности из 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').

HTMLParser.handle_charref(name)

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

HTMLParser.handle_comment(data)

Этот метод вызывается, когда встречается комментарий (например, <!--comment-->).

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

Содержимое условных комментариев Internet Explorer (condcoms) также будет передаваться этому методу, так что для <!--[if IE 9]>IE9-specific content<![endif]--> этот метод получит '[if IE 9]>IE-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 будет содержать всё содержимое объявления внутри разметки <![...]>. Иногда его полезно переопределять в производном классе. Реализация базового класса вызывает исключение HTMLParseError, когда strict имеет значение True.

20.2.3. ПримерыExamples

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

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(strict=False)

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

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

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

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

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

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

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

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

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

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

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

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

Сноски

[1]По причинам обратной совместимости режим strict не вызывает исключений для всего несоответствующего HTML. То есть некоторый некорректный HTML допускается даже в режиме strict.