将pyparsing.ParseResults转换回html字符串

2 投票
2 回答
1237 浏览
提问于 2025-04-16 13:19

我刚接触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 个回答

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

这个问题出在 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 的时候,我假设用户想要的是一个字符串,而我无法将结果名称附加到字符串上。因此,我在 originalTextFor 中添加了一个可选参数 asString,默认值为 True,但如果传入 False,它会返回一个 ParseResults,其中只包含整个匹配字符串的单个标记,加上 所有匹配的结果名称。所以你仍然可以从结果中提取 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(end)。

撰写回答