61413


Google News Sitemap をPythonの標準xmlライブラリであるElementTreeを使って作成しています。  
しかし、作成済みのxmlファイルを読み込んで、URLを追加したあとに、namespaceを付けて出力しようとすると期待した結果を得ることができません。

以下にコードを記載します。

main.py

[code]

    #!python3.7
    # -*- coding: utf-8 -*-
    # news-ranking.py
    
    from datetime import datetime, timedelta
    import xml.etree.ElementTree as ElementTree
    import os
    from pprint import pprint
    
    class NewsSitemapXMLGenerator:
    
      def __init__(self):
        self.xml = ElementTree.parse('/xml/path/news-sitemap.xml').getroot()
    
      def generate(self):
        self.addElement()
        ElementTree.register_namespace('', 'http://www.sitemaps.org/schemas/sitemap/0.9')
        ElementTree.register_namespace('news', 'http://www.google.com/schemas/sitemap-news/0.9')
        string = ElementTree.tostring(self.xml, 'unicode')
        with open('/xml/path/news-sitemap.xml', mode='w') as fo:
          fo.write("<?xml version='1.0' encoding='UTF-8'?>" + string)
    
      def addElement(self):
        url = ElementTree.SubElement(self.xml, 'url')
        ElementTree.SubElement(url, 'loc').text = 'loc'
        news = ElementTree.SubElement(url, 'news:news')
        publication = ElementTree.SubElement(news, 'news:publication')
        ElementTree.SubElement(publication, 'news:name').text = 'name'
        ElementTree.SubElement(publication, 'news:language').text = 'jp'
        ElementTree.SubElement(news, 'news:publication_date').text = 'date'
        ElementTree.SubElement(news, 'news:title').text = 'title'
        return self.xml
    
    def main():
      generator = NewsSitemapXMLGenerator()
      generator.generate()
    
    if __name__ == '__main__':
      main()
    
[/code]

news-sitemap.xml(実行前)

[code]

    <?xml version='1.0' encoding='UTF-8'?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" />
    
[/code]

news-sitemap.xml(実行後)

[code]

    <?xml version='1.0' encoding='UTF-8'?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" />
    
[/code]

xmlns:news="http://www.google.com/schemas/sitemap-news/0.9"
を宣言したままにしたいのですが、実行後には削除されてしまいます。

解決方法をご存じの方、よろしくお願いします。

* * *

12/16追記  
xmlの記述が既にあり、これに要素を追加するパターンにも対応したいと思っています。  
よろしくお願いします。

news-sitemap2.xml

[code]

    <?xml version='1.0' encoding='UTF-8'?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
            xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
    <url>
      <loc>loc</loc>
      <news:news>
        <news:publication>
          <news:name>name</news:name>
          <news:language>jp</news:language>
        </news:publication>
        <news:publication_date>date</news:publication_date>
        <news:title>title</news:title>
      </news:news>
    </url>
    </urlset>
    
[/code]


CC BY-SA 4.0
2019-12-13T07:02:27.803
61403
2019-12-16T07:00:25.887
2019-12-16T02:50:24.440
37057
37057
question
python xml
Python のElementTree を使ってGoogle News Sitemap を作成する方法


勘違いをしていたのですが、最初の `news-sitemap.xml` の場合にはノード内で `news` prefix
が使用されていないために削除されていた模様です(未使用の prefix は XML フォーマットとしては invalid になる様です)。`news-
sitemap2.xml` の場合は使用されているので `news` prefix が読み込まれているのですが、デフォルトの動作ではファイルへ書き出す際に
`ns1` などの文字列に変更されてしまいます。

そして、デフォルトで付与される prefix を変更するための関数が `ElementTree.register_namespace()` なのでした。

[code]

    ElementTree.register_namespace(ns_news['prefix'], ns_news['uri'])
    
[/code]

prefix が未使用の場合には新規に追加してくれる仕様であれば @kazu H さんが最初に書かれたコードで問題は無かったはずなのですが…。

以下が書き直した `generate()` 関数です。

[code]

    def generate(self):
      ns_news = {
        'prefix': 'news',
        'uri': 'http://www.google.com/schemas/sitemap-news/0.9'
      }
      self.addElement()
      ## namespaces
      ElementTree.register_namespace('', self.xml.tag[1:].rsplit('}', 1)[0])
      ElementTree.register_namespace(ns_news['prefix'], ns_news['uri'])
      ## there is no "news" prefix
      if ns_news['uri'] not in \
          [e.tag[1:].rsplit('}', 1)[0] for e in self.xml.iter()]:
        self.xml.set('xmlns:'+ns_news['prefix'], ns_news['uri'])
      ## output
      ElementTree.ElementTree(self.xml).write(
        'news-sitemap_new.xml', encoding='UTF-8', xml_declaration=True
      )
    
[/code]

~~  
ElementTree では prefix の付いた xmlns 属性(attribute)を扱えない様なので、`generate()`
メソッドを以下の様に書き換えました。`self.xml.set(...)` の部分で root ノード(`urlset`)の属性に `xmlns:news`
を追加しています。

[code]

      def generate(self):
        self.addElement()
        self.xml.set('xmlns:news', 'http://www.google.com/schemas/sitemap-news/0.9')
        ElementTree._namespace_map[self.xml.tag[1:].rsplit('}', 1)[0]] = ''
        ElementTree.ElementTree(self.xml).write(
          'news-sitemap_new.xml', encoding='UTF-8', xml_declaration=True
        )
    
[/code]

次の行の `self.xml.tag[1:].rsplit('}', 1)[0]` ですが、self.xml.tag は以下の様になっています。

[code]

    self.xml.tag
    => '{http://www.sitemaps.org/schemas/sitemap/0.9}urlset'
    
[/code]

つまり、デフォルトの namespace(prefix なし) URI になります。当初は、

[code]

    self.xml.set('xmlns', self.xml.tag[1:].rsplit('}', 1)[0])
    
[/code]

とすれば良いかと思っていたのですが、実行してみると `ns0` という prefix が自動で割り振られてしまいます。

[code]

    xmlns:ns0="http://www.sitemaps.org/schemas/sitemap/0.9"
    
[/code]

上記のコードはこれを回避するためのものです。

以下は実行結果です(本来は1行ですが、適当に改行を入れています)。

**news-sitemap_new.xml**

[code]

    <?xml version='1.0' encoding='UTF-8'?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
            xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
    <url>
      <loc>loc</loc>
      <news:news>
        <news:publication>
          <news:name>name</news:name>
          <news:language>jp</news:language>
        </news:publication>
        <news:publication_date>date</news:publication_date>
        <news:title>title</news:title>
      </news:news>
    </url>
    </urlset>
    
[/code]

~~


CC BY-SA 4.0
2019-12-13T11:17:53.317
61413
2019-12-16T07:00:25.887
2019-12-16T07:00:25.887
61403
answer