【问题标题】:Python code to remove HTML tags from a string [duplicate]从字符串中删除 HTML 标记的 Python 代码 [重复]
【发布时间】:2012-03-28 13:46:12
【问题描述】:

我有这样的文字:

text = """<div>
<h1>Title</h1>
<p>A long text........ </p>
<a href=""> a link </a>
</div>"""

使用纯 Python,没有外部模块我想要这个:

>>> print remove_tags(text)
Title A long text..... a link

我知道我可以使用 lxml.html.fromstring(text).text_content() 来做到这一点,但我需要在纯 Python 中使用 2.6+ 的内置或 std 库来实现相同的目标

我该怎么做?

【问题讨论】:

  • 您不想使用外部模块的任何具体原因。?
  • 没有权限在服务器上安装模块...

标签: python html xml string parsing


【解决方案1】:

使用正则表达式

使用正则表达式,您可以清理 &lt;&gt; 中的所有内容:

import re
# as per recommendation from @freylis, compile once only
CLEANR = re.compile('<.*?>') 

def cleanhtml(raw_html):
  cleantext = re.sub(CLEANR, '', raw_html)
  return cleantext

某些 HTML 文本还可以包含未括在括号中的实体,例如“&amp;nsbm”。如果是这种情况,那么您可能希望将正则表达式编写为

CLEANR = re.compile('<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});')

link 包含有关此的更多详细信息。

使用 BeautifulSoup

您也可以使用BeautifulSoup 附加包来查找所有原始文本。

调用 BeautifulSoup 时需要显式设置解析器 我推荐"lxml",正如替代答案中提到的那样(比默认答案(html.parser)更强大(即无需额外安装即可使用)。

from bs4 import BeautifulSoup
cleantext = BeautifulSoup(raw_html, "lxml").text

但这并不妨碍你使用外部库,所以我推荐第一种解决方案。

编辑:要使用lxml,您需要pip install lxml

【讨论】:

  • 如果你想编译正则表达式,最好的方法是在函数外编译。在你的例子中,每个调用 cleanhtml 必须再次编译正则表达式
  • BeautifulSoup 在标记很重的时候很好,否则尽量避免它,因为它很慢。
  • 很好的答案。不过你忘记了def cleanhtml(raw_html) 末尾的冒号 :)
  • FWIW,这也将删除 XML 另一个 XHTML 标记。
  • 不错的答案。您可能希望使用 cleantext = BeautifulSoup(raw_html, "html.parser").text 在 BeautifulSoup 中显式设置您的解析器
【解决方案2】:

Python 有几个内置的 XML 模块。对于您已经有一个包含完整 HTML 的字符串的情况,最简单的一个是 xml.etree,它的工作方式(有点)类似于您提到的 lxml 示例:

def remove_tags(text):
    return ''.join(xml.etree.ElementTree.fromstring(text).itertext())

【讨论】:

  • 这对我有用,但要小心自动关闭类型的 html 标签。示例: 我得到一个“ParseError:不匹配的标签:第 1 行,第 9 列”,因为这个标签是关闭的,而之前没有打开。这对于所有自动关闭的 html 标记都是一样的。
【解决方案3】:

请注意,这并不完美,因为如果您有类似 &lt;a title="&gt;"&gt; 之类的东西,它会损坏。但是,它是非库 Python 中最接近的,没有真正复杂的函数:

import re

TAG_RE = re.compile(r'<[^>]+>')

def remove_tags(text):
    return TAG_RE.sub('', text)

但是,正如 lvc 提到的,xml.etree 在 Python 标准库中可用,因此您可能只需将其调整为像您现有的 lxml 版本一样提供服务:

def remove_tags(text):
    return ''.join(xml.etree.ElementTree.fromstring(text).itertext())

【讨论】:

  • 我喜欢你的正则表达式方法,如果性能是一个重要因素可能会更好。
  • 此外,它适用于不以 xml 标签开头的字符串,情况就是这样
  • @DouglasCamata 正则表达式并不比 xml 解析器更高效。
  • 值得注意的是,如果您的文档中有文本 &lt;,这将中断。
  • @PatrickT 你需要导出它 - import xml.etree
【解决方案4】:

在任何类 C 语言中都有一个简单的方法。该风格不是 Pythonic,而是适用于纯 Python:

def remove_html_markup(s):
    tag = False
    quote = False
    out = ""

    for c in s:
            if c == '<' and not quote:
                tag = True
            elif c == '>' and not quote:
                tag = False
            elif (c == '"' or c == "'") and tag:
                quote = not quote
            elif not tag:
                out = out + c

    return out

这个想法基于一个简单的有限状态机,在这里有详细解释:http://youtu.be/2tu9LTDujbw

你可以在这里看到它的工作原理:http://youtu.be/HPkNPcYed9M?t=35s

PS - 如果你对课程感兴趣(关于使用 python 进行智能调试)我给你一个链接:https://www.udacity.com/course/software-debugging--cs259。免费!

【讨论】:

  • 这将中断不匹配的引号,并且由于逐个字符地添加到输出中而非常慢。但这足以说明,编写一个原始的逐字符解析器并不是什么大问题。
  • 这个答案非常适合教授 HTML 或 Python,但错过了生产使用的关键点:满足标准很难,使用支持良好的库可以避免数周的研究和/或寻找错误在一个健康的最后期限内。
【解决方案5】:
global temp

temp =''

s = ' '

def remove_strings(text):

    global temp 

    if text == '':

        return temp

    start = text.find('<')

    end = text.find('>')

    if start == -1 and end == -1 :

        temp = temp + text

    return temp

newstring = text[end+1:]

fresh_start = newstring.find('<')

if newstring[:fresh_start] != '':

    temp += s+newstring[:fresh_start]

remove_strings(newstring[fresh_start:])

return temp

【讨论】:

  • 你的答案是:a) 格式非常糟糕(例如违反 pep8),b) 过度杀伤,因为有工具可以做同样的事情,c) 容易失败(当 html 包含 > 字符时会发生什么情况属性?),d)二十一世纪的全球在这种微不足道的情况下?
猜你喜欢
  • 2019-12-04
  • 2010-09-19
  • 2017-09-06
  • 1970-01-01
相关资源
最近更新 更多