用Python从HTML生成目录
我正在尝试从一段HTML内容中生成一个目录,这段内容不是完整的文件,只是一些内容,主要是根据其中的 <h2>
和 <h3>
标签来生成。
到目前为止,我的计划是:
使用
beautifulsoup
提取出标题的列表。在内容上使用正则表达式,在标题标签前面或里面放置锚链接(这样用户可以点击目录)—— 不知道在
beautifulsoup
中有没有方法可以直接替换?在预定义的位置输出一个嵌套的链接列表,指向这些标题。
听起来好像很简单,但实际上做起来有点麻烦。
有没有什么工具可以一次性帮我完成这些工作,这样我就不用浪费几个小时去重新发明轮子了?
一个例子:
<p>This is an introduction</p>
<h2>This is a sub-header</h2>
<p>...</p>
<h3>This is a sub-sub-header</h3>
<p>...</p>
<h2>This is a sub-header</h2>
<p>...</p>
4 个回答
1
我这里有一个更详细的版本,基于Łukasz提出的解决方案。
def list_to_html(lst):
result = ["<ul>"]
for item in lst:
if isinstance(item, list):
result.append(list_to_html(item))
else:
result.append('<li><a href="#{}">{}</a></li>'.format(item[0], item[1]))
result.append("</ul>")
return "\n".join(result)
soup = BeautifulSoup(article, 'html5lib')
toc = []
h2_prev = 0
h3_prev = 0
h4_prev = 0
h5_prev = 0
for header in soup.findAll(['h2', 'h3', 'h4', 'h5', 'h6']):
data = [(slugify(header.string), header.string)]
if header.name == "h2":
toc.append(data)
h3_prev = 0
h4_prev = 0
h5_prev = 0
h2_prev = len(toc) - 1
elif header.name == "h3":
toc[int(h2_prev)].append(data)
h3_prev = len(toc[int(h2_prev)]) - 1
elif header.name == "h4":
toc[int(h2_prev)][int(h3_prev)].append(data)
h4_prev = len(toc[int(h2_prev)][int(h3_prev)]) - 1
elif header.name == "h5":
toc[int(h2_prev)][int(h3_prev)][int(h4_prev)].append(data)
h5_prev = len(toc[int(h2_prev)][int(h3_prev)][int(h4_prev)]) - 1
elif header.name == "h6":
toc[int(h2_prev)][int(h3_prev)][int(h4_prev)][int(h5_prev)].append(data)
toc_html = list_to_html(toc)
3
这是一些快速写出来的、看起来很糟糕的代码:
soup = BeautifulSoup(html)
toc = []
header_id = 1
current_list = toc
previous_tag = None
for header in soup.findAll(['h2', 'h3']):
header['id'] = header_id
if previous_tag == 'h2' and header.name == 'h3':
current_list = []
elif previous_tag == 'h3' and header.name == 'h2':
toc.append(current_list)
current_list = toc
current_list.append((header_id, header.string))
header_id += 1
previous_tag = header.name
if current_list != toc:
toc.append(current_list)
def list_to_html(lst):
result = ["<ul>"]
for item in lst:
if isinstance(item, list):
result.append(list_to_html(item))
else:
result.append('<li><a href="#%s">%s</a></li>' % item)
result.append("</ul>")
return "\n".join(result)
# Table of contents
print list_to_html(toc)
# Modified HTML
print soup
2
使用 lxml.html
。
- 它可以很好地 处理不规范的HTML。
- 它的速度 非常快。
- 它让你可以 轻松创建缺失的元素,并在不同的树之间移动元素。