Как запретить lxml добавлять тип документа по умолчанию

lxml, похоже, добавляет тип документа по умолчанию, когда он отсутствует в html-документе.

Посмотрите этот демонстрационный код:

import lxml.etree
import lxml.html


def beautify(html):
    parser = lxml.etree.HTMLParser(
        strip_cdata=True,
        remove_blank_text=True
    )

    d = lxml.html.fromstring(html, parser=parser)
    docinfo = d.getroottree().docinfo

    return lxml.etree.tostring(
        d,
        pretty_print=True,
        doctype=docinfo.doctype,
        encoding='utf8'
    )


with_doctype = """
<!DOCTYPE html>
<html>
<head>
  <title>With Doctype</title>
</head>
</html>
"""

# This passes!
assert "DOCTYPE" in beautify(with_doctype)

no_doctype = """<html>
<head>
  <title>No Doctype</title>
</head>
</html>"""

# This fails!
assert "DOCTYPE" not in beautify(no_doctype)

# because the returned html contains this line
# <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# which was not present in the source before

Как я могу сказать lxml не делать этого?

Первоначально эта проблема была поднята здесь: https://github.com/mitmproxy/mitmproxy/issues/845

Цитирование комментария на Reddit может оказаться полезным:

lxml основан на libxml2, который делает это по умолчанию, если только вы не передадите параметр HTML_PARSE_NODEFDTD, я полагаю. Код здесь.

Я не знаю, можете ли вы указать lxml передать эту опцию. libxml имеет привязки python, которые вы, возможно, могли бы использовать напрямую, но они кажутся очень сложными.

РЕДАКТИРОВАТЬ: еще немного покопался, и эта опция появляется в источнике lxml здесь. Эта опция делает именно то, что вы хотите, но я пока не знаю, как ее активировать, если это вообще возможно.


person dufferZafar    schedule 12.08.2016    source источник
comment
Параметр HTML_PARSE_NODEFDTD не обрабатывается в HTMLParser. конструктор, так что вам, вероятно, не повезло.   -  person nwellnhof    schedule 12.08.2016
comment
@nwellnhof Может быть, мы могли бы исправить это и создать PR @ lxml? Параметр присутствует здесь хотя.   -  person dufferZafar    schedule 13.08.2016
comment
Я также задал этот вопрос в списке рассылки lxml . Пока нет решения.   -  person dufferZafar    schedule 13.08.2016
comment
Я не думаю, что исправить это будет слишком сложно, но у меня просто недостаточно опыта работы с cython и т. д., поэтому я не смогу скомпилировать его самостоятельно.   -  person dufferZafar    schedule 13.08.2016


Ответы (1)


В настоящее время нет возможности сделать это в lxml, но я создал запрос на извлечение в lxml, который добавляет логическое значение default_doctype к HTMLParser.

После слияния кода необходимо создать синтаксический анализатор следующим образом:

parser = lxml.etree.HTMLParser(
    strip_cdata=True,
    remove_blank_text=True,
    default_doctype=False,
)

Все остальное остается прежним.

person dufferZafar    schedule 19.08.2016