python:获取打开和关闭html标签的

时间:2019-01-24 17:17:36

标签: python html python-3.x beautifulsoup html-parsing

问题:

如何使用python(3.6)查找所有打开和关闭HTML标签的文本。 这必须是确切的文本,保留空格和可能非法的html:

# input
html = """<p>This <a href="book"> book </a  > will help you</p attr="e">"""

# desired output
output = ['<p>', '<a href="book">', '</a  >', '</p attr="e">']

尝试解决方案:

显然,这在Beautifulsoup中是不可能的,这个问题:How to get the opening and closing tag in beautiful soup from HTML string?链接到html.parser

实现自定义解析器很容易。您可以使用self.get_starttag_text()来获取与最后打开的标签相对应的文本。但是由于某种原因,没有类似的方法get_endtag_text()

这意味着我的解析器产生以下输出:

class MyHTMLParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.tags = []

    def reset_stored_tags(self):
        self.tags = []
    def handle_starttag(self, tag, attrs):
        self.tags.append(self.get_starttag_text())

    def handle_endtag(self, tag):
        self.tags.append(self.get_endtag_text())

    def handle_startendtag(self, data):
        self.tags.append(self.get_starttag_text())
# input
input_doc = """<p>This <a href="book"> book </a> will help you</p>"""

parser = MyHTMLParser()
parser.feed(input_doc)

print(parser.tags)
# ['<p>', '<a href="book">', '<a href="book">', '<a href="book">']

tag的{​​{1}}参数只是一个字符串handle_endtag"a",而不是某些可以提供整个标签的自定义数据类型。

2 个答案:

答案 0 :(得分:1)

您可以使用递归并在soup.contents属性上进行迭代:

from bs4 import BeautifulSoup as soup

html = """<p>This <a href="book"> book </a> will help you</p>"""

def attrs(_d):
  if _d.name != '[document]':
    _attrs = ' '.join(f'{a}="{b}"' for a, b in getattr(_d, 'attrs', {}).items())
    yield f'<{_d.name}>' if not _attrs else f'<{_d.name} {_attrs}>'
  for i in _d.contents:
    if not isinstance(i, str):
       yield from attrs(i)
  if _d.name != '[document]':
    yield f'</{_d.name}>'

print(list(attrs(soup(html, 'html.parser'))))

输出:

['<p>', '<a href="book">', '</a>', '</p>']

编辑:对于无效的HTML,您可以使用re

import re
html = """<p>This <a href="book"> book </a  > will help you</p attr="e">"""
new_results = re.findall('\<[a-zA-Z]+.*?\>|\</[a-zA-Z]+.*?\>', html)

输出:

['<p>', '<a href="book">', '</a  >', '</p attr="e">']

答案 1 :(得分:1)

虽然@ Ajax1234的答案包含一些不错的python + beautifulsoup,但我发现它非常不稳定。主要是因为我需要html标签的确切字符串。 该方法找到的每个标记必须出现在html文本中。这会导致以下问题:

  • 它从HTML解析标记名称和属性并将其插入 一起形成标签yield f'<{_d.name}>' if not _attrs else f'<{_d.name} {_attrs}>'的字符串。这样可以消除标记中多余的空格:<p >变成<p>

  • 即使标记中没有标记,它始终会生成一个结束标记

  • 对于列表属性,它失败:<p class="a b">变成<p class="[a, b]">

可以通过在处理HTML之前清除HTML来部分解决空白问题。我使用了bleach,但这可能太激进了。值得注意的是,在使用之前,必须先指定一个可接受标签的列表。

一种更好的方法是在html.parser.HTMLParser周围使用薄包装纸。 这是我已经开始提出的问题,不同之处在于我会自动添加生成一个结束标记。

from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
    def __init__(self):
        super().__init__()
        self.tags = []

    def handle_starttag(self, tag, attrs):
        self.tags.append(self.get_starttag_text())

    def handle_endtag(self, tag):
        self.tags.append(f"</{tag}>")

parser = MyHTMLParser();
parser.feed("""<p > Argh, whitespace and p is not closed </a>""")
parser.tags # ['<p >', '</a>']

这解决了上面提到的问题,但是它有一个缺点,它没有查看结束标记的实际文本。如果结束标记中包含多余的参数或空格,则解析不会显示它们。