【问题标题】:SGML Parser in PythonPython 中的 SGML 解析器
【发布时间】:2011-01-09 00:06:20
【问题描述】:

我对 Python 完全陌生。我有以下代码:

class ExtractTitle(sgmllib.SGMLParser):

def __init__(self, verbose=0):

   sgmllib.SGMLParser.__init__(self, verbose)

   self.title = self.data = None

def handle_data(self, data):

  if self.data is not None:
    self.data.append(data)

def start_title(self, attrs):
 self.data = []

def end_title(self):

  self.title = string.join(self.data, "")

raise FoundTitle # abort parsing!

从 SGML 中提取标题元素,但它只适用于单个标题。我知道我必须重载 unknown_starttag 和 unknown_endtag 才能获得所有标题,但我一直弄错。请帮帮我!!!

【问题讨论】:

  • 你想做什么?解析html文件?
  • 我有一个带有 SGML 的大文本文件,其中有格式为 new title

    new text

    。我希望我的代码能够在另一个文件中给我这个结果: new text

标签: python parsing sgml


【解决方案1】:

Beautiful Soup 是您可以很好地解析它的一种方式(这是我总是这样做的方式,除非有一些非常好的理由不这样做,我自己)。它比使用 SGMLParser 更简单、更易读。

>>> from BeautifulSoup import BeautifulSoup
>>> soup = BeautifulSoup('''<post id='100'> <title> new title </title> <text> <p> new text </p> </text> </post>''')
>>> soup('post')  # soup.findAll('post') is equivalent
[<post id="100"> <title> new title </title> <text> <p> new text </p> </text> </post>]
>>> for post in soup('post'):
...     print post.findChild('text')
...
<text> <p> new text </p> </text>

一旦你在这个阶段得到它,你可以用它做各种事情,这取决于你想要它的方式。

>>> post = soup.find('post')
>>> post
<post id="100"> <title> new title </title> <text> <p> new text </p> </text> </post>
>>> post_text = post.findChild('text')
>>> post_text
<text> <p> new text </p> </text>

您可能想要去除 HTML。

>>> post_text.text
u'new text'

或者看看内容...

>>> post_text.renderContents()
' <p> new text </p> ']
>>> post_text.contents
[u' ', <p> new text </p>, u' ']

您可以做各种各样的事情。如果您更具体 - 特别是提供真实数据 - 它会有所帮助。

当涉及到操纵树时,您也可以这样做。

>>> post
<post id="100"> <title> new title </title> <text> <p> new text </p> </text> </post>
>>> post.title  # Just as good as post.findChild('title')
<title> new title </title>
>>> post.title.extract()  # Throws it out of the tree and returns it but we have no need for it
<title> new title </title>
>>> post  # title is gone!
<post id="100">  <text> <p> new text </p> </text> </post>
>>> post.findChild('text').replaceWithChildren()  # Thrown away the <text> wrapping
>>> post
<post id="100">   <p> new text </p>  </post>

所以,最后,你会得到这样的东西:

>>> from BeautifulSoup import BeautifulSoup
>>> soup = BeautifulSoup('''
... <post id='100'> <title> new title 100 </title> <text> <p> new text 100 </p> </text> </post>
... <post id='101'> <title> new title 101 </title> <text> <p> new text 101 </p> </text> </post>
... <post id='102'> <title> new title 102 </title> <text> <p> new text 102 </p> </text> </post>
... ''')
>>> for post in soup('post'):
...     post.title.extract()
...     post.findChild('text').replaceWithChildren()
... 
<title> new title 100 </title>
<title> new title 101 </title>
<title> new title 102 </title>
>>> soup

<post id="100">   <p> new text 100 </p>  </post>
<post id="101">   <p> new text 101 </p>  </post>
<post id="102">   <p> new text 102 </p>  </post>

【讨论】:

  • 美汤慢而死;)
  • @virhilo:“慢”?也许在处理过程中是这样,但在开发过程中它往往非常快。这通常是现在最重要的。和“死”?它几乎可以满足所有需要,没有什么额外的做。它没有任何积极的发展(我会同意你)这一事实根本不会打扰我。
  • 感谢现在正在工作的人 :) 关于如何将结果写入外部文件的任何想法?
  • @afg102:用我所拥有的,然后你可以用outfile = open('filename', 'w')、outfile.write(soup.renderContents()) 将它写入一个文件(unicode(soup) 也可以)
【解决方案2】:

每次调用 end_title() 时,您的代码都会重置“title”属性。因此,您最终得到的标题是文档中的最后一个标题。

您需要做的是存储您找到的所有标题的列表。在下文中,我还将数据重置为 None(因此您不会收集标题元素之外的文本数据)并且我使用 "".join 而不是 string.join,因为您使用后者被认为是过时的

class ExtractTitle(sgmllib.SGMLParser):
  def __init__(self, verbose=0):
    sgmllib.SGMLParser.__init__(self, verbose)
    self.titles = []
    self.data = None

  def handle_data(self, data):
    if self.data is not None:
      self.data.append(data)

  def start_title(self, attrs):
    self.data = []

  def end_title(self):
    self.titles.append("".join(self.data))
    self.data = None

这里正在使用:

>>> parser = ExtractTitle()
>>> parser.feed("<doc><rec><title>Spam and Eggs</title></rec>" +
...             "<rec><title>Return of Spam and Eggs</title></rec></doc>")
>>> parser.close()
>>> parser.titles
['Spam and Eggs', 'Return of Spam and Eggs']
>>> 

【讨论】:

  • 怎么没用?你的测试用例是什么,它是如何失败的?我添加了一个示例以表明它确实对我有用。
  • 好的,很好:我的代码有一个小错误。非常感谢!您对我发布的另一个问题有任何想法吗? stackoverflow.com/questions/4634787/freqdist-with-nltk
【解决方案3】:

使用 lxml 代替 SGMLParser:

>>> posts = """
... <post id='100'> <title> xxxx </title> <text> <p> yyyyy </p> </text> </post>
... <post id='101'> <title> new title1 </title> <text> <p> new text1 </p> </text> </post>
... <post id='102'> <title> new title2 </title> <text> <p> new text2 </p> </text> </post>
... """
>>> from lxml import html
>>> parsed = html.fromstring(posts)
>>> new_file = html.Element('div')
>>> for post in parsed:
...     post_id = post.attrib['id']
...     post_text = post.find('text').text_content()
...     new_post = html.Element('post', id=post_id)
...     new_post.text = post_text
...     new_file.append(new_post)
... 
>>> html.tostring(new_file)
'<div><post id="100"> yyyyy  </post><post id="101"> new text1  </post><post id="102"> new text2  </post></div>'
>>> 

【讨论】:

  • 感谢您的回复。我试图从文件中提取,所以我这样做了:filexy = open(fileurl) 和 posts = filexy.read() 然后是你的代码。但是由于某种原因,它只显示相同的文本(即它没有遍历所有标签)你知道吗?谢谢
  • 您能粘贴一些示例文档吗?
  • 我想知道你们是否喜欢 NLTK。我正在使用函数 FreqDist 来获取从我生成的文件中获得的文本中单词的频率。我试过这个: filey = open(fileurl") p= filey.read() fdist = FreqDist(p) vocab = fdist.keys() vocab[:30] -> 但结果是单个字母的列表,而在来自 nltk 网站的示例,这应该会导致整个单词的列表。请帮忙吗?
  • 这是一个新问题,不是吗?
猜你喜欢
  • 2012-08-23
  • 1970-01-01
  • 2022-08-03
  • 1970-01-01
  • 2011-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多