如何使用Python sub函数去除<p></p>标签

3

我有一个html文件,想要将空段落替换为一个空格。

mystring = "This <p></p><p>is a test</p><p></p><p></p>"
result = mystring.sub("<p></p>" , "&nbsp;")

这个不起作用。


请记住,正则表达式在处理某些格式错误的HTML时可能会失败,例如'<p>This<p>is a</p><p class="red"><P>test</P></ p>'。 - Hugh Bothwell
6个回答

10
请使用适当的解析模块(例如htmlparser或BeautifulSoup)来解析HTML,不要尝试使用正则表达式进行解析。现在遭受一段短暂的学习曲线,您将获得以下好处:
  1. 您的解析代码将更加健壮,处理可能未考虑过的边角情况,这些情况将无法使用正则表达式失败
  2. 对于未来的HTML解析/修改任务,您将能够更快地完成任务,因此时间投资也会有所回报。

您不会后悔!利润保证!


这是我加入公司时已经存在的代码,他们想要修改正则表达式...我怀疑不久它会出现问题。我将尝试转向更强大的解决方案。 - topless

5
我认为给出一个真实解析器的示例总是很好的,而不仅仅是重复Eli Bendersky在他的回答中给出的建议。
这里有一个使用lxml来删除空的<p>元素的示例。lxml的HTMLParser非常擅长处理HTML。
from lxml import etree
from StringIO import StringIO

input = '''This <p> </p><p>is a test</p><p></p><p><b>Bye.</b></p>'''

parser = etree.HTMLParser()
tree = etree.parse(StringIO(input), parser)

for p in tree.xpath("//p"):
    if len(p):
        continue
    t = p.text
    if not (t and t.strip()):
        p.getparent().remove(p)

print etree.tostring(tree.getroot(), pretty_print=True)

...会产生输出:

<html>
  <body>
    <p>This </p>
    <p>is a test</p>
    <p>
      <b>Bye.</b>
    </p>
  </body>
</html>

请注意,当我回复这个问题时,我误读了问题,我只是删除空的<p>元素,而不是用&nbsp替换它们。使用lxml,我不确定有没有简单的方法来做到这一点,所以我创建了另一个问题来询问:

++ 仅为一个真实的例子,但你没有理由诋毁 BeautifulSoup。你链接的页面明确说明它是旧版本和历史版本,而且该模块已经不存在这些问题了。 - Eli Bendersky
@Eli Bendersky:感谢您纠正了我的误解,我已经从我的答案中删除了那一部分。我没有意识到情况已经改变,也没有重新阅读我链接的页面——我应该为此负责。 - Mark Longair
这段代码存在一个错误。如果p的所有文本内容都被包裹在另一个元素中 - <p><b>This will be dropped</b></p>,上述代码将会删除它,因为node.text仅包含位于开始标签和第一个子元素之间的文本。 - abhaga

2
我认为对于这个特定的问题,使用解析模块会过度复杂。只需使用以下函数:
>>> mystring = "This <p></p><p>is a test</p><p></p><p></p>"

>>> mystring.replace("<p></p>","&nbsp;")
'This &nbsp;<p>is a test</p>&nbsp;&nbsp;'

2
如果输入的是<P>< p >或者加有属性,或者是使用空标签语法 <P/>的情况下,会发生什么呢? Pyparsing 的 HTML 标签支持可以处理所有这些变化:
from pyparsing import makeHTMLTags, replaceWith, withAttribute

mystring = 'This <p></p><p>is a test</p><p align="left"></p><P> </p><P/>'

p,pEnd = makeHTMLTags("P")
emptyP = p.copy().setParseAction(withAttribute(empty=True))

null_paragraph = emptyP | p+pEnd
null_paragraph.setParseAction(replaceWith("&nbsp;"))

print null_paragraph.transformString(mystring)

输出:

This &nbsp;<p>is a test</p>&nbsp;&nbsp;&nbsp;

1

使用正则表达式吗?

import re
result = re.sub("<p>\s*</p>","&nbsp;", mystring, flags=re.MULTILINE)

如果你经常使用正则表达式,就把它编译一下。


0

我写了那段代码:

from lxml import etree
from StringIO import StringIO

html_tags = """<div><ul><li>PID temperature controller</li> <li>Smart and reliable</li> <li>Auto-diagnosing</li> <li>Auto setting</li> <li>Intelligent control</li> <li>2-Rows 4-Digits LED display</li> <li>Widely applied in the display and control of the parameter of temperature, pressure, flow, and liquid level</li> <li>     </li> <p> </p></ul> <div> </div></div>"""

document = etree.iterparse(StringIO(html_tags), html=True)

for a, e in document:
    if not (e.text and e.text.strip()) and len(e) == 0:
        e.getparent().remove(e)

print etree.tostring(document.root)

为什么要踩这个答案?它是一个可行的解决方案。不过需要注意的是,lxml会返回一个有效的HTML字符串。因此,输入字符串将被包装在<html>和<body>标签中。因此,给定示例字符串的输出为:" <html> <body> <p> This </p> <p> is a test </p> </body> </html>"。 - Simon Steinberger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接