如何使用Python检测字符串中是否包含HTML代码?

36
如何检测字符串中是否包含HTML(可以是HTML4、HTML5,或者仅为文本中的部分HTML)?我不需要HTML的版本,而只是想知道该字符串是否只包含文本或者其中是否包含HTML。该文本通常为多行,并且包含空行。

更新:

示例输入:

html:

<head><title>I'm title</title></head>
Hello, <b>world</b>
non-html
<ht fldf d><
<html><head> head <body></body> html

你为什么需要知道这个?你打算做些什么不同的事情吗? - Eevee
仅检测字符串是否包含HTML,然后要么删除字符串,要么将其打印出来。 - static
6个回答

55

您可以使用HTML解析器,例如BeautifulSoup。请注意,它会尝试解析HTML,即使是损坏的HTML,它的严格程度取决于底层解析器,因此可能会有所不同。

>>> from bs4 import BeautifulSoup
>>> html = """<html>
... <head><title>I'm title</title></head>
... </html>"""
>>> non_html = "This is not an html"
>>> bool(BeautifulSoup(html, "html.parser").find())
True
>>> bool(BeautifulSoup(non_html, "html.parser").find())
False

这个功能基本上尝试在字符串中找到任何HTML元素。如果找到了-结果是True

另一个带有HTML片段的例子:

>>> html = "Hello, <b>world</b>"
>>> bool(BeautifulSoup(html, "html.parser").find())
True

或者,您可以使用lxml.html


>>> import lxml.html
>>> html = 'Hello, <b>world</b>'
>>> non_html = "<ht fldf d><"
>>> lxml.html.fromstring(html).find('.//*') is not None
True
>>> lxml.html.fromstring(non_html).find('.//*') is not None
False

那么对于 non-html = "<html><head> head <body></body> html" bool(BeautifulSoup(non_html, "html.parser").find()) => True 呢?它不是一个HTML片段。 - static
即使 non_html = "<html head> head <body jdkjs /body> html dkslfjglangaiowmgiowe" 也会通过测试 :( - static
@static 是的,在这种情况下,它认为 fldfd 是属性,而 ht 标签没有关闭。很好的例子,谢谢 :) - alecxe
请注意,BeautifulSoup('This is not an html','lxml').find()返回<html><body><p>This is not an html</p></body></html> - 因此请使用html.parser - Vitaly Zdanevich
lxml.html.fromstring('<img src="http://example.com"/>').find('.//*') is not None 返回 false。难道不应该是 true 吗? - cointreau
显示剩余3条评论

8

我想到的一种方法是通过尝试将文本解析为HTML并相交找到起始和结束标记,然后将此集合与已知的可接受HTML元素集合进行相交。

示例:

#!/usr/bin/env python

from __future__ import print_function

from HTMLParser import HTMLParser


from html5lib.sanitizer import HTMLSanitizerMixin


class TestHTMLParser(HTMLParser):

    def __init__(self, *args, **kwargs):
        HTMLParser.__init__(self, *args, **kwargs)

        self.elements = set()

    def handle_starttag(self, tag, attrs):
        self.elements.add(tag)

    def handle_endtag(self, tag):
        self.elements.add(tag)


def is_html(text):
    elements = set(HTMLSanitizerMixin.acceptable_elements)

    parser = TestHTMLParser()
    parser.feed(text)

    return True if parser.elements.intersection(elements) else False


print(is_html("foo bar"))
print(is_html("<p>Hello World!</p>"))
print(is_html("<html><head><title>Title</title></head><body><p>Hello!</p></body></html>"))  # noqa

输出:

$ python foo.py
False
True
True

这适用于包含HTML元素子集的部分文本。

NB:这使用了html5lib,因此可能不适用于其他文档类型,但该技术可以轻松地进行调整。


基本上和你的解决方案一样,只是使用了不同的库,并使用已知元素的集合交集:P - James Mills
我在html5lib中找不到HTMLSanitizerMixin了。 - Sumit

1
你可以轻松地扩展内置的HTML解析器HTMLParser,该解析器已经处理了解析,并收集(开始/结束)标记属性数据。要确定文档是否有效,应确保开始标记的数量与结束标记的数量匹配:
from html.parser import HTMLParser


class MyHTMLParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.start_tags = list()
        self.end_tags = list()
        self.attributes = list()
    
    def is_text_html(self):
        return len(self.start_tags) == len(self.end_tags)

    def handle_starttag(self, tag, attrs):
        self.start_tags.append(tag)
        self.attributes.append(attrs)

    def handle_endtag(self, tag):
        self.end_tags.append(tag)

    def handle_data(self, data):
        print("Encountered some data  :", data)

那么

>>> parser = MyHTMLParser()
>>> parser.feed("<head><title>I'm title</title></head>"
                "Hello, <b>world</b>")
>>> parser.is_text_html()

True

>>> parser.feed("<ht fldf d><"
                "<html><head> head <body></body> html")
>>> parser.is_text_html()

False

1
如果您只需要知道一个字符串是否包含HTML文本,那么另一种未列出的解决方案是使用正则表达式,如下所示: </?\s*[a-z-][^>]*\s*>|(\&(?:[\w\d]+|#\d+|#x[a-f\d]+);) 请注意,尽管这比使用HTML解析器要快得多,但根据您期望的HTML标记的复杂性,它可能存在潜在的不准确性。
此处进行了测试,以便大致了解其覆盖范围。

-4

检查结束标签。我认为这是最简单和最稳健的方法。

"</html>" in possibly_html

如果有一个结束的html标签,那么它就看起来像是html,否则就不太像。

这是一个很好的答案,假设输入是完整的HTML页面(其中包含</html>)。它不适用于部分HTML(例如<b>some word</b>)。 - okoboko
这种方法可以扩展到搜索任何HTML结束标记,例如b。正则表达式可能会使它更快,但基本原理仍然相同。 - Andrew Johnson
1
由于您事先不知道文本中可能存在哪些HTML标签,因此这种技术效果不太好 :/ - James Mills

-6

在之前的帖子中,我会为快速简单的操作做类似于这样的事情:

import sys, os

if os.path.exists("file.html"):
    checkfile=open("file.html", mode="r", encoding="utf-8")
    ishtml = False
    for line in checkfile:
        line=line.strip()
        if line == "</html>"
            ishtml = True
    if ishtml:
        print("This is an html file")
    else:
        print("This is not an html file")

关于部分和非样式化的HTML(即</html>不是整行),怎么办?(好的,这里可以使用“包含”代替“==”) - static
你说得对,我没有想到那个。在这种情况下,我可能会修改 if line == "</html>":if "</html>" in line: - Donkyhotay

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