【问题标题】:LXML's etree.tostring escaping urls in link href attributesLXML 的 etree.tostring 在链接 href 属性中转义 url
【发布时间】:2015-02-21 00:07:49
【问题描述】:

当使用 LXML 解析 html 文档,然后使用 etree.tostring() 时,我注意到链接中的 & 符号正在转换为 html 转义实体。

这会破坏链接,原因很明显。这是该问题的一个简单的独立示例:

>>> from lxml import etree
>>> parser = etree.HTMLParser()
>>> tree = etree.fromstring("""<a href="https://www.example.com/?param1=value1&param2=value2">link</a>""", parser)
>>> etree.tostring(tree)
'<html><body><a href="https://www.example.com/?param1=value1&amp;param2=value2">link</a></body></html>'

我希望输出是:

<html><body><a href="https://www.example.com/?param1=value1&param2=value2">link</a></body></html>

【问题讨论】:

    标签: python html xml lxml elementtree


    【解决方案1】:

    虽然 & 编码应该是standard way。如果由于某些原因您确实需要避免转换,那么您可以这样做:

    第 1 步。 查找不应存在于您的 html 源代码中的唯一字符串。如果您确信“ANDamp;”,您可以简单地使用 ANDamp; 作为您的 reserved_amp 变量字符串不会出现在您的 html 源代码中。否则,您可能会考虑生成随机字母并检查以确保您的 html 源中不存在此字符串:

    >>> import random
    >>> import string
    >>> length = 15 #increase the length if it's still seems to be collide
    >>> reserved_amp = "&amp;"
    >>> html = """<a href="https://www.example.com/?param1=value1&param2=value2">link</a>"""
    >>> while reserved_amp in [html, "&amp;"]: 
    ...     reserved_amp = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(length)) + "amp;" #amp; is for you easy to spot on
    ... 
    >>> print reserved_amp
    2eya6oywxg5z7q5amp;
    

    第 2 步。 在解析之前替换所有出现的 &:

    >>> html = html.replace("&", reserved_amp)
    >>> html
    '<a href="https://www.example.com/?param1=value12eya6oywxg5z7q5amp;param2=value2">link</a>'
    >>> 
    

    第 3 步。仅当您需要原始表单时才将其替换回来:

    >>> from lxml import etree
    >>> parser = etree.HTMLParser()
    >>> tree = etree.fromstring(html, parser)
    >>> etree.tostring(tree).replace(reserved_amp, "&")
    '<html><body><a href="https://www.example.com/?param1=value1&param2=value2">link</a></body></html>'
    >>> 
    

    [更新]:

    reserved_amp 末尾的冒号是安全防护

    如果我们生成这样的reserved_amp 会怎样?

    ampXampXampXampX + amp;

    而html包含:

    yyYampX&amp;

    它将以这种形式编码:

    yyYampXampXampXampXampXamp;

    由于最后一个字符的 冒号 安全防护是非 ASCII 字母,因此无法返回/解码错误的反转结果,例如 yy&amp;YampX(原始为 yyYampX&amp;)它永远不会从上面的string.ascii_lowercase + string.digits 生成为reserved_amp

    因此,确保随机不使用冒号(或其他非 ASCII 字符),然后将其附加在末尾(必须是最后一个字符)将无需担心 yyYampX&amp; 会返回到 yy&amp;YampX 陷阱。

    【讨论】:

    • 修补 lxml 比这个 hack 更好
    【解决方案2】:

    根据lxml's tostring() docs,可以通过method='xml'来避免html的细节

    etree.tostring(tree, method='xml')
    

    在我使用的项目中:

    from lxml import html
    html.tostring(node, with_tail=False, method='xml', encoding='unicode')
    

    【讨论】:

    • 这个方法对我不起作用。我需要漂亮地打印一棵 XML 树,但有些元素的文本中有 &amp;#160;。使用stree.tostring(element, encoding='unicode', pretty_print=True, method='xml') 我得到了&amp;amp#160;
    猜你喜欢
    • 2019-04-17
    • 2015-05-09
    • 1970-01-01
    • 1970-01-01
    • 2014-10-21
    • 1970-01-01
    • 2010-11-17
    • 2014-06-18
    • 1970-01-01
    相关资源
    最近更新 更多