将pyparsing.ParseResults转换回html字符串

时间:2011-03-09 09:15:41

标签: python parsing html-parsing xml-parsing pyparsing

我对于pyparsing来说是全新的 如何将类pyparsing.ParseResults的实例转换回html字符串。

离。

>>> type(gcdata)
<type 'unicode'>
>>> pat
{<"div"> SkipTo:(</"div">) </"div">}
>>> type(pat)
<class 'pyparsing.And'>
>>> 
>>> l = pat.searchString( gcdata  )
>>> l[0]
(['div', ([u'class', u'shoveler'], {}), ([u'id', u'purchaseShvl'], {}), False, u'<div class="shoveler-heading">\n    <p>Customers Who Bought This Item Also Bought</p>\n    \n', '</div>'], {'startDiv': [((['div', ([u'class', u'shoveler'], {}), ([u'id', u'purchaseShvl'], {}), False], {u'class': [(u'shoveler', 1)], 'empty': [(False, 3)], u'id': [(u'purchaseShvl', 2)]}), 0)], 'endDiv': [('</div>', 5)], u'class': [(u'shoveler', 1)], 'empty': [(False, 3)], u'id': [(u'purchaseShvl', 2)]})
>>> 
>>> type(l[0])
<class 'pyparsing.ParseResults'>
>>> 
>>> divhtml = foo (l[0])

所以,我需要这个功能 foo 有什么建议吗?

2 个答案:

答案 0 :(得分:1)

使用返回DOM的HTML解析器会更好,例如lxml.html,但我怀疑你这样做是为了学习Pyparsing。由于您没有发布一段源代码,我已经进行了一些猜测并使用pyparsing.makeHTMLTags做了一个示例,如下所示。

import cgi
from pyparsing import makeHTMLTags, SkipTo

raw = """<body><div class="shoveler" id="purchaseShvl">
<p>Customers who bought this item also bought</p>
<div class="foo">
    <span class="bar">Shovel cozy</span>
    <span class="bar">Shovel rack</span>
</div>
</div></body>"""

def foo(parseResult):
    parts = []
    for token in parseResult:
        st = '<div id="%s" class="%s">' % \
             (cgi.escape(getattr(token, 'id')),
             cgi.escape(getattr(token, 'class')))
        parts.append(st + token.body + token.endDiv)
    return '\n'.join(parts)

start, end = makeHTMLTags('div')
anchor = start + SkipTo(end).setResultsName('body') + end
res = anchor.searchString(raw)
print foo(res)

答案 1 :(得分:1)

这是makeHTMLTags返回的表达式的一个问题,很多额外的分组和命名都在继续,如果您只想要标记文本,这会妨碍您。

Pyparsing包含方法originalTextFor以帮助解决此问题。基于@samplebias的示例代码:

start, end = makeHTMLTags('div')
#anchor = start + SkipTo(end).setResultsName('body') + end 
anchor = originalTextFor(start + SkipTo(end).setResultsName('body') + end)

通过将表达式包装在originalTextFor中,标记到其组成部分的所有分解都将被撤消,您只需从原始字符串中获取文本(还包括任何插入的空格)。默认行为是只返回此字符串,这会导致丢失所有结果名称的不幸副作用,因此返回解析的属性值可能会很麻烦。当我写originalTextFor时,我认为字符串是想要的,我无法将结果名称附加到字符串。所以我向asString添加了一个可选参数originalTextFor,默认为True,但如果传递为False,则会返回一个ParseResults,其中只包含整个匹配字符串的单个标记, plus 所有匹配的结果名称。因此,您仍然可以从结果中提取res.id,而res[0]会返回整个匹配的HTML。

其他一些评论:

<div>是一个非常常见的标记,只使用makeHTMLTags返回的标记就可以轻松匹配。它将匹配任何 div,可能还有许多您并不感兴趣。如果您可以使用withAttribute指定一些也应匹配的属性,则可以减少不匹配的数量。你可以这样做:

start.setParseAction(withAttribute(id="purchaseShvl"))

start.setParseAction(withAttribute(**{"class":"shovelr"}))

(使用'class'作为过滤属性可能是你想要做的最常见的事情,但由于'class'也是一个Python关键字,你可以像使用id一样使用命名参数形式,太糟糕了。)

最后,<div>的共性是嵌套的可能性。 div经常嵌套在div中,而且只是简单的SkipTo不够聪明,不能将其考虑在内。我们在重建您发布的结果时看到了这一点:

<div class='shovelr' id='purchaseShvl>
<div class='shovelr-heading'>
<p>Customers WhoBought This Item Also Bought</p>
</div>

第一个终止</div>结束表达式的匹配。我怀疑您可能需要扩展匹配表达式以考虑这些额外的div,而不仅仅是简单的SkipTo(结束)。