【问题标题】:How to parsing xml file with different elements using xml.sax python?如何使用 xml.sax python 解析具有不同元素的 xml 文件?
【发布时间】:2018-09-17 12:37:44
【问题描述】:

我正在寻找使用 python 解析大型 xml 文件。该文件包含几个不同的元素。这是我正在处理的文件的示例:

<movie title="Searching">
   <type>War, Thriller</type>
   <format>DVD</format>
   <year>2018</year>
   <rating>PG</rating>
   <stars>10</stars>
   <description>Missing girl</description>
</movie>
<song title="Need You Now">
   <type>Pop, Country</type>
   <format>MP4</format>
   <year>2010</year>
   <singer>Lady Antebellum</rating>
   <stars>8</stars>
</song>
   <movie title="Sidewalk of New York">
   <type>Romantic comedy</type>
   <format>DVD</format>
   <rating>PG</rating>
   <stars>7</stars>
   <description>Stories of people's perspective of love in NY</description>
</movie>

从上面的文件中,我想解析并存储与电影和歌曲元素相关的所有信息以列出。我正在使用 xml.sax 库,但遇到了如何区分元素的问题。例如,如果我解析类型、格式和年份标签,则无法区分它属于电影还是歌曲。这是我目前使用的一个 sn-p 代码:

import psycopg2
import xml.sax
from xml.sax.handler import ContentHandler

class MovieHandler( xml.sax.ContentHandler ):
   def __init__(self):
      self.CurrentData = ""
      self.type = ""
      self.format = ""
      self.year = ""
      self.data = {} #dict
      self.list = [] #list to store information
      self.list2 = []
      self.list3 = []
      self.list4 = []
      self.list5 = []

   # Call when an element starts
   def startElement(self, tag, attributes):
      self.CurrentData = tag
      if tag == "movie":
         title = attributes["title"]
         self.list.append(title)
         self.data['title'] = self.list
      elif tag == "song":
         title = attributes ["title"]
         self.list2.append(title)
         self.data['song_title'] = self.list2

   # Call when an elements ends
   def endElement(self, tag):
      if self.CurrentData == "type":
         type = self.type
         self.list3.append(type)
         self.data['type'] = self.list3
      elif self.CurrentData == "format":
         format = self.format
         self.list4.append(format)
         self.data['format'] = self.list4
      elif self.CurrentData == "year":
         year = int(self.year)
         self.list5.append(year)
         self.data['year'] = self.list5
      self.CurrentData = ""

   # Call when a character is read
   def characters(self, content):
      if self.CurrentData == "type":
         self.type = content
      elif self.CurrentData == "format":
         self.format = content
      elif self.CurrentData == "year":
         self.year = content

上面代码的结果是类型、格式、年份都会被重复计算。这是输出示例:

{'format': ['DVD', 'MP4', 'DVD'],
 'title': ['Searching', 'Need You Now', 'Sidewalk of New York'],
 'type': ['War, Thriller',
  'Pop, Country',
  'Romantic Comedy'],
 'year': [2018, 2010]}

关于如何解决这个问题的任何想法?谢谢!

【问题讨论】:

    标签: python xml postgresql parsing xml-parsing


    【解决方案1】:

    您可以将当前类型保存在startElement中,然后您可以将数据相应地存储在endElement()

    def startElement(self, tag, attributes):
        if tag == "movie":
            self.currentEntryType = "movie"
        elif tag == "song":
            self.currentEntryType = "song"
        ...
    
    def endElement(self, tag):
        if self.currentEntryType == "movie":
            ... # store as movie data
    

    (我不太确定您的输出列表应该是什么样子,也许您可​​以为示例 xml 发布完美的输出列表)

    编辑: 有 2 个字典,一个用于电影,一个用于歌曲,会更好吗?这取决于您的数据库的外观。

    我完全不知道你为什么想要列表,使用字典列表而不是列表字典不是更好吗?

    例如像这样使用 2 个字典:

    {   
    'format': ['DVD', 'DVD'],
    'title': ['Searching', 'Sidewalk of New York'],
    'type': ['War, Thriller', 'Romantic Comedy'],
    'year': [2018]
    }
    {   
    'format': ['MP4'],
    'title': ['Need You Now'],
    'type': ['Pop, Country'],
    'year': [2010]
    }
    

    问题在于,您无法决定 2018 年是属于 Searching 还是属于 Sidewalk of New York。将列表提交到数据库时会出现问题吗?

    这是一个包含两个 dicts 的解决方案,一个用于电影,一个用于歌曲。 基本上它使用 8 个列表而不是 5 个列表来分别存储所有内容。

    class MovieHandler( xml.sax.ContentHandler ):
       def __init__(self):
          self.CurrentData = ""
          self.type = ""
          self.format = ""
          self.year = ""
          self.dataMovies = {} #dict
          self.dataSongs = {}
          self.list = [] # movie title
          self.list2 = [] # song title
          self.list3 = [] # movie type
          self.list4 = [] # movie format
          self.list5 = [] # movie year
          self.list6 = [] # song type
          self.list7 = [] # song format
          self.list8 = [] # song year
    
          self.currentEntryType = None
    
       # Call when an element starts
       def startElement(self, tag, attributes):
          self.CurrentData = tag
          if tag == "movie":
             title = attributes["title"]
             self.list.append(title)
             self.dataMovies['title'] = self.list
             self.currentEntryType = "movie"
          elif tag == "song":
             title = attributes ["title"]
             self.list2.append(title)
             self.dataSongs['song_title'] = self.list2
             self.currentEntryType = "song"
    
       # Call when an elements ends
       def endElement(self, tag):
    
          if tag == "movie":
             # Make movie lists the same length
             self.list3.extend([None]*(len(self.list)-len(self.list3)))
             self.list4.extend([None]*(len(self.list)-len(self.list4)))
             self.list5.extend([None]*(len(self.list)-len(self.list5)))   
          elif tag == "song":
             # Make movie lists the same length
             self.list6.extend([None]*(len(self.list2)-len(self.list6)))
             self.list7.extend([None]*(len(self.list2)-len(self.list7)))
             self.list8.extend([None]*(len(self.list2)-len(self.list8))) 
    
          if self.currentEntryType == "movie":
             if self.CurrentData == "type":
                type = self.type
                self.list3.append(type)
                self.dataMovies['type'] = self.list3
             elif self.CurrentData == "format":
                format = self.format
                self.list4.append(format)
                self.dataMovies['format'] = self.list4
             elif self.CurrentData == "year":
                year = int(self.year)
                self.list5.append(year)
                self.dataMovies['year'] = self.list5
             self.CurrentData = ""
          elif self.currentEntryType == "song":
             if self.CurrentData == "type":
                type = self.type
                self.list6.append(type)
                self.dataSongs['type'] = self.list6
             elif self.CurrentData == "format":
                format = self.format
                self.list7.append(format)
                self.dataSongs['format'] = self.list7
             elif self.CurrentData == "year":
                year = int(self.year)
                self.list8.append(year)
                self.dataSongs['year'] = self.list8
             self.CurrentData = ""
    
       # Call when a character is read
       def characters(self, content):
          if self.CurrentData == "type":
             self.type = content
          elif self.CurrentData == "format":
             self.format = content
          elif self.CurrentData == "year":
             self.year = content
    

    【讨论】:

    • 我编辑了这篇文章,你能给出更多解释吗?谢谢!
    • 你能不能也发布一下输出应该是什么样子的
    • 输出应该是元素列表的字典(我编辑了帖子)。想法是从这个字典中,我想将它插入到 postgres 数据库中。这就是为什么我想先把它全部存储到字典中,然后用 psycopg2 将它连接到 postgres。谢谢!
    • 对不起,我不明白输出有什么问题?应该有什么不同?
    • 对不起,我的意思是不同的是,如何区分列表中的类型、格式或年份属于电影或歌曲?
    猜你喜欢
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-07
    • 1970-01-01
    相关资源
    最近更新 更多