正如 Ivar 所说,HTML 解析器确实是正确处理此类问题的唯一方法:
from html.parser import HTMLParser
class MyHTMLParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.indent = -1
def handle_starttag(self, tag, attrs):
self.indent += 1
print(2 * self.indent * ' ', sep='', end='')
print(f'<{tag}', sep='', end='')
for attr in attrs:
print(f' {attr[0]}="{attr[1]}"', sep='', end='')
print('>', sep='')
def handle_endtag(self, tag):
print(2 * self.indent * ' ', sep='', end='')
print(f'</{tag}>')
self.indent -= 1
parser = MyHTMLParser()
parser.feed("""<html>
<head>
<title>Test</title>
</head>
<body>
<h1>Heading!</h1>
<p style="font-weight: bold; color: red;">
Some text
<BR/>
Some more text
</p>
<ol>
<li>Item 1</li>
<li>Item 2</li>
</ol>
</body>
</html>
""")
打印:
<html>
<head>
<title>
</title>
</head>
<body>
<h1>
</h1>
<p style="font-weight: bold; color: red;">
<br>
</br>
</p>
<ol>
<li>
</li>
<li>
</li>
</ol>
</body>
</html>
See Python Demo
更新
如果 HTML 是一个不太大的文件,那么将整个文件读入内存并传递给解析器是有意义的:
parser = MyHTMLParser()
with open('test.html') as f:
html = f.read()
parser.feed(html)
如果输入在一个非常大的文件中,那么逐行或分块“馈送”解析器而不是尝试将整个文件读入内存可能是有意义的:
逐行:
parser = MyHTMLParser()
with open('test.html') as f:
for line in f:
parser.feed(line)
甚至更有效:
以 32K 为单位阅读:
CHUNK_SIZE = 32 * 1024
parser = MyHTMLParser()
with open('test.html') as f:
while True:
chunk = f.read(CHUNK_SIZE)
if chunk == '':
break
parser.feed(chunk)
当然,您可以选择更大的块大小。