【问题标题】:Converting HTML list (<li>) to tabs (i.e. indentation)将 HTML 列表 (<li>) 转换为制表符(即缩进)
【发布时间】:2012-04-27 00:26:05
【问题描述】:

曾使用过数十种语言,但对 Python 不熟悉。

我在这里的第一个(也许是第二个)问题,所以要温柔...

试图有效地将类似 HTML 的 markdown 文本转换为 wiki 格式(特别是 Linux Tomboy/GNote 到 Zim 的注释),但一直卡在转换列表上。

对于这样的 2 级无序列表...

  • 第一层
    • 二级

Tomboy/GNote 使用类似...

&lt;list&gt;&lt;list-item&gt;First level&lt;list&gt;&lt;list-item&gt;Second level&lt;/list-item&gt;&lt;/list&gt;&lt;/list-item&gt;&lt;/list&gt;

但是,Zim 个人 wiki 希望这样......

* First level
  * Second level

...带有前导标签。

我探索了正则表达式模块函数 re.sub()、re.match()、re.search() 等,并发现 Python 能够将重复文本编码为...

 count * "text"

因此,看起来应该有一种方法可以做...

 newnote = re.sub("<list>", LEVEL * "\t", oldnote)

其中 LEVEL 是注释中&lt;list&gt; 的序数(出现)。因此,第一个遇到的&lt;list&gt;0,第二个是1,等等。

每次遇到&lt;/list&gt; 时,LEVEL 都会递减。

&lt;list-item&gt; 标记转换为项目符号的星号(在适当的情况下在换行符之前)和 &lt;/list-item&gt; 标记被删除。

最后...问题...

  • 如何获取 LEVEL 的值并将其用作制表符乘数?

【问题讨论】:

  • 别想了,使用 html/xml 解析器,例如 BeautifulSoup 或 xml.dom.minidom,使用递归函数或使用堆栈/队列来打开/关闭标签并计算表格。基本上,您希望将标记文本转换为可用数据。然后将此代码友好的数据转换为您的其他标记样式。
  • 不要使用re。它在处理嵌套标签时不是很有效。
  • 我将研究 html2text.py 程序的技术,但我要转换的实际上并不是 HTML。

标签: python html regex tabs


【解决方案1】:

您确实应该使用 xml 解析器来执行此操作,但要回答您的问题:

import re

def next_tag(s, tag):
    i = -1
    while True:
        try:
            i = s.index(tag, i+1)
        except ValueError:
            return
        yield i

a = "<list><list-item>First level<list><list-item>Second level</list-item></list></list-item></list>"

a = a.replace("<list-item>", "* ")

for LEVEL, ind in enumerate(next_tag(a, "<list>")):
    a = re.sub("<list>", "\n" + LEVEL * "\t", a, 1)

a = a.replace("</list-item>", "")
a = a.replace("</list>", "")

print a

这将适用于您的示例,并且仅适用于您的示例。使用 XML 解析器。你可以使用xml.dom.minidom(它包含在Python中(至少2.7),无需下载任何东西):

import xml.dom.minidom

def parseList(el, lvl=0):
    txt = ""
    indent = "\t" * (lvl)
    for item in el.childNodes:
        # These are the <list-item>s: They can have text and nested <list> tag
        for subitem in item.childNodes:
            if subitem.nodeType is xml.dom.minidom.Element.TEXT_NODE:
                # This is the text before the next <list> tag
                txt += "\n" + indent + "* " + subitem.nodeValue
            else:
                # This is the next list tag, its indent level is incremented
                txt += parseList(subitem, lvl=lvl+1)
    return txt

def parseXML(s):
    doc = xml.dom.minidom.parseString(s)
    return parseList(doc.firstChild)

a = "<list><list-item>First level<list><list-item>Second level</list-item><list-item>Second level 2<list><list-item>Third level</list-item></list></list-item></list></list-item></list>"
print parseXML(a)

输出:

* First level
    * Second level
    * Second level 2
        * Third level

【讨论】:

  • 我认为 re 解决方案将适用于这个小转换程序,但我肯定会坚持使用 xml 解析器解决方案来解决其他一些问题。感谢大家在这么短的时间内做出如此出色的回应。 (程序员确实拥有夜晚!)
  • @RobertC 是的,您也许可以为此使用 re 解决方案,但我相信如果您有超过 2 级的嵌套标签,它将无法工作。您可能需要对其进行一些更改才能使其正常工作。 xml 解析器解决方案应该适用于所有内容。不客气;)
【解决方案2】:

使用Beautifulsoup,它允许你在标签中迭代,即使它们是海关。做这种操作很实用

from BeautifulSoup import BeautifulSoup
tags = "<list><list-item>First level<list><list-item>Second level</list-item></list></list-item></list>"
soup = BeautifulSoup(tags)
print [[ item.text for item in list_tag('list-item')]  for list_tag in soup('list')]

Output : [[u'First level'], [u'Second level']]

我使用了嵌套列表推导,但您可以使用嵌套 for 循环

for list_tag in soup('list'):
     for item in list_tag('list-item'):
         print item.text

希望对你有帮助。

在我的示例中,我使用了 BeautifulSoup 3,但该示例应该与 BeautifulSoup4 一起使用,但只有导入更改。

from bs4 import BeautifulSoup

【讨论】:

  • 这看起来很棒!我会投票但还不够高的代表。我要试试这个。
猜你喜欢
  • 2021-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-27
  • 2014-01-30
  • 2021-07-12
  • 2020-06-20
  • 1970-01-01
相关资源
最近更新 更多