Содержание страницы
18.1.2. email.parser: Разбор сообщений электронной почты¶email.parser: Parsing email messages
Структуры объектов сообщений можно создать одним из двух способов: либо создать с нуля, создав экземпляры объектов Message и соединив их с помощью вызовов attach() и set_payload(), либо разобрать плоское текстовое представление сообщения.
Пакет email предоставляет стандартный парсер, который понимает большинство структур документов электронной почты, включая MIME-документы. Можно передать парсеру строку или файловый объект, и он вернет корневой экземпляр Message структуры объекта. Для простых сообщений, не являющихся MIME, полезная нагрузка этого корневого объекта, скорее всего, будет строкой, содержащей текст сообщения. Для MIME-сообщений корневой объект вернет True из своего метода is_multipart(), а подчасти можно получить с помощью методов get_payload() и walk().
На самом деле доступны два интерфейса парсера: классический API Parser и инкрементальный API FeedParser. Классический API Parser подходит, если весь текст сообщения находится в памяти в виде строки или если всё сообщение хранится в файле в файловой системе. FeedParser больше подходит для ситуаций, когда сообщение читается из потока, который может блокироваться в ожидании дополнительных данных (например, чтение сообщения электронной почты из сокета). FeedParser может потреблять и анализировать сообщение инкрементально, возвращая корневой объект только после закрытия парсера [1].
Обратите внимание, что парсер можно расширять ограниченными способами, и, конечно, можно реализовать собственный парсер полностью с нуля. Нет никакой магической связи между встроенным парсером пакета email и классом Message, поэтому ваш собственный парсер может создавать деревья объектов сообщений любым способом, который сочтёт необходимым.
18.1.2.1. API FeedParser¶FeedParser API
FeedParser, импортируемый из модуля email.feedparser, предоставляет API, подходящий для инкрементального разбора сообщений электронной почты, например, когда текст сообщения читается из источника, который может блокироваться (например, сокет). FeedParser, конечно, можно использовать для разбора сообщения, полностью содержащегося в строке или файле, но для таких случаев может быть удобнее классический API Parser. Семантика и результаты работы двух парсеров API идентичны.
API FeedParser прост: создаёте экземпляр, подаёте ему текст, пока есть что подавать, затем закрываете парсер, чтобы получить корневой объект сообщения. FeedParser чрезвычайно точен при разборе сообщений, соответствующих стандартам, и очень хорошо справляется с сообщениями, не соответствующими стандартам, предоставляя информацию о том, как сообщение было признано некорректным. Он заполняет атрибут defects объекта сообщения списком всех проблем, найденных в сообщении. Список дефектов, которые можно найти, приведён в модуле email.errors.
Вот API для FeedParser:
- class email.parser.FeedParser(_factory=email.message.Message)¶
Создаёт экземпляр FeedParser. Необязательный параметр _factory – это вызываемый объект без аргументов, который будет вызываться всякий раз, когда потребуется новый объект сообщения. По умолчанию используется класс email.message.Message.
- feed(data)¶
Передаёт парсеру FeedParser дополнительные данные. data должна быть строкой, содержащей одну или несколько строк. Строки могут быть неполными, и FeedParser правильно склеит такие неполные строки. Строки в строке могут иметь любой из трёх распространённых концевых символов: возврат каретки, перевод строки или возврат каретки с переводом строки (они могут даже быть смешаны).
- close()¶
Закрытие FeedParser завершает разбор всех ранее переданных данных и возвращает корневой объект сообщения. Поведение при попытке передать дополнительные данные закрытому FeedParser не определено.
- class email.parser.BytesFeedParser(_factory=email.message.Message)¶
Работает точно так же, как FeedParser, за исключением того, что входные данные для метода feed() должны быть байтами, а не строкой.
Новое в версии 3.2.
18.1.2.2. API класса Parser¶Parser class API
Класс Parser, импортируемый из модуля email.parser, предоставляет API, который можно использовать для разбора сообщения, когда все содержимое сообщения доступно в виде строки или файла. Модуль email.parser также предоставляет второй класс, называемый HeaderParser, который можно использовать, если интересуют только заголовки сообщения. HeaderParser в таких ситуациях может работать значительно быстрее, поскольку он не пытается разбирать тело сообщения, а вместо этого устанавливает в качестве полезной нагрузки необработанное тело в виде строки. HeaderParser имеет тот же API, что и класс Parser.
- class email.parser.Parser(_class=email.message.Message, strict=None)¶
Конструктор класса Parser принимает необязательный аргумент _class. Это должна быть вызываемая фабрика (например, функция или класс), которая используется всякий раз, когда необходимо создать объект вложенного сообщения. По умолчанию используется Message (см. email.message). Фабрика будет вызываться без аргументов.
Необязательный флаг strict игнорируется.
Устарело с версии 2.4: Поскольку класс Parser является обратно совместимой оберткой API вокруг появившегося в Python 2.4 FeedParser, весь разбор фактически является нестрогим. Следует просто перестать передавать флаг strict конструктору Parser.
Другие публичные методы Parser:
- parse(fp, headersonly=False)¶
Читает все данные из файлоподобного объекта fp, разбирает полученный текст и возвращает корневой объект сообщения. fp должен поддерживать как метод readline(), так и метод read() для файлоподобных объектов.
Текст, содержащийся в fp, должен быть отформатирован как блок заголовков в стиле RFC 2822 и строк продолжения заголовков, возможно, с предшествующим заголовком конверта. Блок заголовков завершается либо концом данных, либо пустой строкой. За блоком заголовков следует тело сообщения (которое может содержать MIME-кодированные подчасти).
Необязательный параметр headersonly – это флаг, указывающий, нужно ли остановить разбор после чтения заголовков. Значение по умолчанию – False, то есть разбирается всё содержимое файла.
- parsestr(text, headersonly=False)¶
Аналогичен методу parse(), но принимает строковый объект вместо объекта, подобного файлу. Вызов этого метода для строки полностью эквивалентен тому, чтобы сначала обернуть text в экземпляр StringIO, а затем вызвать parse().
Необязательный параметр headersonly работает так же, как в методе parse().
- class email.parser.BytesParser(_class=email.message.Message, strict=None)¶
Этот класс полностью аналогичен Parser, но работает с байтовым вводом. Аргументы _class и strict интерпретируются так же, как и для конструктора Parser. strict поддерживается только для упрощения переноса кода; он устарел.
- parse(fp, headeronly=False)¶
Читает все данные из двоичного файлоподобного объекта fp, разбирает полученные байты и возвращает объект сообщения. fp должен поддерживать как метод readline(), так и метод read() для файлоподобных объектов.
Байты, содержащиеся в fp, должны быть отформатированы как блок заголовков в стиле RFC 2822 и строк продолжения заголовков, возможно, с предшествующим заголовком конверта. Блок заголовков завершается либо концом данных, либо пустой строкой. За блоком заголовков следует тело сообщения (которое может содержать MIME-кодированные подчасти, включая подчасти с Content-Transfer-Encoding равным 8bit).
Необязательный параметр headersonly – это флаг, указывающий, нужно ли остановить разбор после чтения заголовков. Значение по умолчанию – False, то есть разбирается всё содержимое файла.
- parsebytes(bytes, headersonly=False)¶
Аналогичен методу parse(), но принимает объект байтовой строки вместо файлоподобного объекта. Вызов этого метода для байтовой строки полностью эквивалентен тому, чтобы сначала обернуть text в экземпляр BytesIO, а затем вызвать parse().
Необязательный параметр headersonly работает так же, как в методе parse().
Новое в версии 3.2.
Поскольку создание структуры объекта сообщения из строки или файлового объекта является довольно распространённой задачей, для удобства предоставлены четыре функции. Они доступны в пространстве имён пакета верхнего уровня email.
- email.message_from_string(s, _class=email.message.Message, strict=None)¶
Возвращает структуру объекта сообщения из строки. Это полностью эквивалентно Parser().parsestr(s). Необязательные аргументы _class и strict интерпретируются так же, как в конструкторе класса Parser.
- email.message_from_bytes(s, _class=email.message.Message, strict=None)¶
Возвращает структуру объекта сообщения из байтовой строки. Это полностью эквивалентно BytesParser().parsebytes(s). Необязательные аргументы _class и strict интерпретируются так же, как в конструкторе класса Parser.
Новое в версии 3.2.
- email.message_from_file(fp, _class=email.message.Message, strict=None)¶
Возвращает дерево структуры объекта сообщения из открытого файлового объекта. Это полностью эквивалентно Parser().parse(fp). Необязательные аргументы _class и strict интерпретируются так же, как в конструкторе класса Parser.
- email.message_from_binary_file(fp, _class=email.message.Message, strict=None)¶
Возвращает дерево структуры объекта сообщения из открытого двоичного файлового объекта. Это полностью эквивалентно BytesParser().parse(fp). Необязательные аргументы _class и strict интерпретируются так же, как в конструкторе класса Parser.
Новое в версии 3.2.
Вот пример использования этого в интерактивном приглашении Python:
>>> import email
>>> msg = email.message_from_string(myString)
18.1.2.3. Дополнительные примечания¶Additional notes
Вот некоторые замечания по семантике разбора:
- Большинство сообщений, не являющихся multipart, разбираются как одно сообщение со строковой полезной нагрузкой. Такие объекты будут возвращать False для is_multipart(). Их метод get_payload() будет возвращать строковый объект.
- Все сообщения типа multipart разбираются как контейнерное сообщение со списком вложенных сообщений в качестве полезной нагрузки. Внешнее контейнерное сообщение будет возвращать True для is_multipart(), а его метод get_payload() будет возвращать список подчастей Message.
- Большинство сообщений с типом содержимого message/* (например,\nmessage/delivery-status и message/rfc822) также будут\nразобраны как объект-контейнер, содержащий полезную нагрузку в виде списка длиной 1. Их\nметод is_multipart() будет возвращать True. Единственный элемент в\nсписке полезной нагрузки будет объектом подсообщения.
- Некоторые сообщения, не соответствующие стандартам, могут быть внутренне непоследовательны в отношении\nсвоей многокомпонентности. Такие сообщения могут иметь\nзаголовок Content-Type типа multipart, но их\nметод is_multipart() может возвращать False. Если такие сообщения были разобраны\nс помощью FeedParser, они будут содержать экземпляр класса\nMultipartInvariantViolationDefect в своем списке атрибутов defects.\nПодробнее см. email.errors.
Сноски
| [1] | Начиная с версии 3.0 пакета email, представленной в Python 2.4, классический\nParser был перереализован на основе FeedParser, поэтому\nсемантика и результаты двух анализаторов идентичны. |