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

19.1. HTMLParser – Простой парсер HTML и XHTMLHTMLParser – Simple HTML and XHTML parser

Примечание

Модуль HTMLParser переименован в html.parser в Python 3. Инструмент 2to3 автоматически адаптирует импорты при преобразовании исходных кодов в Python 3.

Новое в версии 2.2.

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


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

class HTMLParser.HTMLParser

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

Класс HTMLParser создаётся без аргументов.

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

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

exception HTMLParser.HTMLParseError

HTMLParser способен обрабатывать некорректную разметку, но в некоторых случаях может вызвать это исключение при обнаружении ошибки во время разбора. Это исключение предоставляет три атрибута: msg – краткое сообщение с описанием ошибки, lineno – номер строки, в которой была обнаружена некорректная конструкция, и offset – количество символов от начала строки до начала этой конструкции.

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

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

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

# создать экземпляр парсера и передать ему некоторый HTML
parser = MyHTMLParser()
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

19.1.2. HTMLParser МетодыHTMLParser Methods

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

HTMLParser.feed(data)

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

HTMLParser.close()

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

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

Изменено в версии 2.6: Все ссылки на сущности из htmlentitydefs теперь заменяются в значениях атрибутов.

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;), где имя – ссылка на общую сущность (например '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]>IE9-specific content<![endif]'.

HTMLParser.handle_decl(decl)

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

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

HTMLParser.handle_pi(data)

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

Примечание

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

HTMLParser.unknown_decl(data)

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

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

19.1.3. ПримерыExamples

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

from HTMLParser import HTMLParser
from htmlentitydefs 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 = unichr(name2codepoint[name])
        print "Named ent:", c

    def handle_charref(self, name):
        if name.startswith('x'):
            c = unichr(int(name[1:], 16))
        else:
            c = unichr(int(name))
        print "Num ent  :", 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">')
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