【问题标题】:How do i get the text from a xml.dom.minidom Dom Element?如何从 xml.dom.minidom Dom 元素中获取文本?
【发布时间】:2023-03-24 16:55:01
【问题描述】:

如何获取标题元素的文本值? 这甚至可以使用 Dom 元素吗? 我需要手动解析文本吗?

#-*-coding:utf8;-*-
#qpy:3
#qpy:console

import re
import urllib.request
from xml.dom import minidom

def download(url):
    with urllib.request.urlopen(url) as res:
        return res.read().decode('latin-1')

class RSSFeed(object):
    def __init__(self, url):
        self.url = url
        self.raw_xml = download(url)
        self.dom = minidom.parseString(self.raw_xml)
        self.links = self.dom.getElementsByTagName('link')

    def entries(self):
        ret = {}
        for element in self.dom.getElementsByTagName('entry'):
            title = element.getElementsByTagName('title')[0]
            print(title.toprettyxml())


    def __str__(self):
        return self.dom.toprettyxml()

feed_url = 'https://rickys-python-notes.blogspot.com/atom.xml?redirect=false&start-index=1&max-results=500'
feed = RSSFeed(feed_url)
dom = feed.dom
print(feedHow totries())

【问题讨论】:

  • sn-p 的最后两行应该做什么?
  • 忽略那些我希望没人会注意到的行。
  • 您可以查看[SO]: Print all xml child node using python。您需要知道 xml 的结构才能提取相关数据。那么您希望提取哪个节点文本?
  • 我正在解析一个原子 RSS 提要,我需要所有 RSS 元素的文本值。注意我不能使用像 feedparser 或 lxml 这样的第 3 方模块。
  • “忽略那些我希望没人注意到的台词。”说什么?

标签: python parsing


【解决方案1】:

确定任何 XML 元素的节点值(即文本内容)的规范方法是

  • 获取它包含的所有文本节点的节点值,包括嵌套的节点值
  • 修剪它们
  • 用空格加入它们

minidom莫名其妙没有实现这个程序,所以如果一定要用minidom,就需要自己动手了。

所以我们需要一些辅助函数。

  • 获取满足特定条件的所有后代节点,例如作为文本节点。
  • 了解他们的价值观并加入他们的行列
  • 为方便起见,从节点获取特定名称的第一个元素。

让我们将它们收集到一个模块中。

# minidom_helpers.py

def get_descendant_nodes(context_node, predicate):
    if not context_node:
        yield None
    for child in context_node.childNodes:
        if predicate(child):
            yield child
        yield from get_descendant_nodes(child, predicate)

def get_text_value(context_node, default=None):
    texts_nodes = get_descendant_nodes(context_node, lambda n: n.nodeType == n.TEXT_NODE)
    text_value = ' '.join([str.strip(t.nodeValue) for t in texts_nodes])
    return text_value if text_value else default

def get_first_child(context_node, element_name):
    elems = context_node.getElementsByTagName(element_name)
    return elems[0] if elems else None

现在我们可以做

import re
import urllib.request
from xml.dom import minidom
from minidom_helpers import *

class RSSFeed(object):
    def __init__(self, url):
        self.url = url
        self.dom = minidom.parse(urllib.request.urlopen(url))
        self.links = self.dom.getElementsByTagName('link')

    def entries(self):
        for entry in self.dom.getElementsByTagName('entry'):
            yield {
                "title":  get_text_value(get_first_child(entry, 'title'))
            }

    def __str__(self):
        return self.dom.toprettyxml()

feed_url = 'https://rickys-python-notes.blogspot.com/atom.xml?redirect=false&start-index=1&max-results=500'
feed = RSSFeed(feed_url)

for entry in feed.entries():
    print(entry)

解析 XML 的一般说明。尝试养成将 XML 视为二进制数据而不是文本的习惯。

XML 解析器实现了一种自动确定文件编码的复杂机制。通过尝试自己提前将文件或 HTTP 响应解码为字符串来规避该机制既没有必要也不明智:

# BAD CODE, DO NOT USE
def download(url):
    with urllib.request.urlopen(url) as res:
        return res.read().decode('latin-1')

raw_xml = download(url)
dom = minidom.parseString(self.raw_xml)

以上内容对文件编码进行了硬编码(在您的情况下:错误)假设,并且当服务器出于某种原因决定开始以 UTF-16 发送文件时会中断。

如果您将 XML 视为二进制数据而不是文本,那么它会变得更容易且更健壮。

dom = minidom.parse(urllib.request.urlopen(url))

XML 解析器将嗅探字节并确定它们的编码。

从文件中读取 XML 也是如此。而不是

# BAD CODE, DO NOT USE
with open(path, 'r', encoding='latin-1') as fp:
    dom = minidom.parseString(fp.read())

使用

with open(path, 'rb') as fp:
    dom = minidom.parse(fp)

或者干脆

dom = minidom.parse(path)

【讨论】:

    【解决方案2】:
    def entries(self):
            for element in self.dom.getElementsByTagName('entry'):
                title = element.getElementsByTagName('title')[0].firstChild.nodeValue
                link = element.getElementsByTagName('link')[0].getAttribute('href')
                author = element.getElementsByTagName('name')[0].firstChild.nodeValue
                article = element.getElementsByTagName('content')[0].firstChild
                yield type('Entry', (object,), dict(title=title, link=link, author=author, article=article))
    

    【讨论】:

      【解决方案3】:
      #-*-coding:utf8;-*-
      #qpy:3
      #qpy:console
      
      import urllib.request
      from xml.dom import minidom
      
      
      def parse_feed(url):
          with urllib.request.urlopen(url) as res:
              dom = minidom.parseString(res.read().decode('latin-1'))
          for element in dom.getElementsByTagName('entry'):
              title = element.getElementsByTagName('title')[0].firstChild.nodeValue
              link = element.getElementsByTagName('link')[0].getAttribute('href')
              author = element.getElementsByTagName('name')[0].firstChild.nodeValue
              article = element.getElementsByTagName('content')[0].firstChild.nodeValue
              yield type('Entry', (object,), dict(title=title, link=link, author=author, article=article))
      
      
      feed_url = 'https://rickys-python-notes.blogspot.com/atom.xml?redirect=false&start-index=1&max-results=500'
      
      for entry in parse_feed(feed_url):
          print(entry.title, entry.link)
      

      【讨论】:

        猜你喜欢
        • 2011-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-21
        • 2022-12-05
        • 2014-11-24
        • 2022-01-17
        • 2021-05-28
        相关资源
        最近更新 更多