将HTML样式文本注释解析为字典列表

时间:2013-11-19 15:39:26

标签: python html parsing html-parsing nlp

目前我遇到以下问题:

给定一个字符串

"<a>annotated <b>piece</b></a> of <c>text</c>" 

构造一个列表,结果是

[{"annotated": ["a"]}, {"piece": ["a", "b"]}, {"of": []}, {"text": ["c"]}].

我之前的尝试看起来像

open_tag = '<[a-z0-9_]+>'
close_tag = '<\/[a-z0-9_]+>'
tag_def = "(" + open_tag + "|" + close_tag + ")"

def tokenize(str):
    """
    Takes a string and converts it to a list of words or tokens
    For example "<a>foo</a>, of" -> ['<a>', 'foo', '</a>', ',' 'of']
    """
    tokens_by_tag = re.split(tag_def, str)
    def tokenize(token):
        if not re.match(tag_def, token):
            return word_tokenize(token)
        else:
            return [token]
    return list(chain.from_iterable([tokenize(token) for token in tokens_by_tag]))

def annotations(tokens):
    """
    Process tokens into a list with {word : [tokens]} items
    """
    mapping = []
    curr = []
    for token in tokens:
        if re.match(open_tag, token):
            curr.append(re.match('<([a-z0-9_]+)>',token).group(1))
        elif re.match(close_tag, token):
            tag = re.match('<\/([a-z0-9_]+)>',token).group(1)
            try:
                curr.remove(tag)
            except ValueError:
                pass
        else:
            mapping.append({token: list(curr)})
    return mapping

不幸的是,这有一个缺陷,因为(n=54)解析为{"n=54" : []}(n=<n>52</n>)解析为[{"n=": []}, {52: ["n"]}]因此两个列表的长度不同,因此无法合并两个不同的列表稍后的。

是否有一种很好的策略来解析HTML / SGML样式注释,使得两个不同注释(但在其他方面相同)的字符串产生相同大小的列表?

请注意,我很清楚regexp不适合这种类型的解析,但在这种情况下也不是问题。

编辑修正了示例中的错误

1 个答案:

答案 0 :(得分:1)

您的xml数据(或html)格式不正确。

假设输入文件的xml数据格式良好:

<root><a>annotated <b>piece</b></a> of <c>text</c></root>

您可以在开始和结束元素事件中使用sax解析器到appendpop标记:

from xml.sax import make_parser
from xml.sax.handler import ContentHandler
import sys 

class Xml2PseudoJson(ContentHandler):

    def __init__(self):
        self.tags = []
        self.chars = []
        self.json = []

    def startElement(self, tag, attrs):
        d = {''.join(self.chars): self.tags[:]}
        self.json.append(d)
        self.tags.append(tag)
        self.chars = []

    def endElement(self, tag):
        d = {''.join(self.chars): self.tags[:]}
        self.chars = []
        self.tags.pop()
        self.json.append(d)

    def characters(self, content):
        self.chars.append(content)

    def endDocument(self):
        print(list(filter(lambda x: '' not in x, self.json)))

parser = make_parser()
handler = Xml2PseudoJson()
parser.setContentHandler(handler)
parser.parse(open(sys.argv[1]))

像以下一样运行:

python3 script.py xmlfile

产量:

[
    {'annotated ': ['root', 'a']}, 
    {'piece': ['root', 'a', 'b']}, 
    {' of ': ['root']}, 
    {'text': ['root', 'c']}
]