Mezzanine で、サイトマップが出力したくなりました。

  1. Robot.txt作成&配置
  2. Mezzanine で Sitemap.xml生成
  3. python manage.py ping_googleGoogle Blog 更新を通知

というところまでやってみましたので、結果を記載します。


環境情報

  • OS
    CentOS release 6.7 (Final)

  • Python Version
    Python 2.7.8

  • Package (必要そうなものだけ抜粋)
    Django (1.9.6)
    Mezzanine (4.1.0)


検索したところ、以下のような記事が見つかります。

正直プラグイン入れてまでやるか? というところはありますが、
記事記載の、django-robots 2.0 : Python Package Indexインストール&設定しました。

1.1 pipインストール

pip install django-robots

1.2 INSTALLED_APPS robots 追加

INSTALLED_APPS = (
    "robots",
    "request",
    "admin_backup",

1.3 TEMPLATES django.template.loaders.app_directories.Loader追加

TEMPLATESの、OPTIONS loadersして追加します。
'APP_DIRS': True は、コメントアウトしないと動作しないようなので、コメントアウトし、
以下のように記載にしました。

# List of callables that know how to import templates from various sources.
TEMPLATES = [{
              #u'APP_DIRS': True,
              u'BACKEND': u'django.template.backends.django.DjangoTemplates',
              u'DIRS': (u'${YOUR_TEMPLATE_DIR}'),
              u'OPTIONS': {u'builtins': [u'mezzanine.template.loader_tags'],
                           u'context_processors': (u'django.contrib.auth.context_processors.auth',
                                                   u'django.contrib.messages.context_processors.messages',
                                                   u'django.core.context_processors.debug',
                                                   u'django.core.context_processors.i18n',
                                                   u'django.core.context_processors.static',
                                                   u'django.core.context_processors.media',
                                                   u'django.core.context_processors.request',
                                                   u'django.core.context_processors.tz',
                                                   u'mezzanine.conf.context_processors.settings',
                                                   u'mezzanine.pages.context_processors.page'),
                           u'loaders': [
                                  "django.template.loaders.filesystem.Loader",
                                  "django.template.loaders.app_directories.Loader"
                           ]
                }
             }]

1.4 The “sites” frameworkインストールされていることを確認

Mezzanine を使っていれば、デフォルトでインストールされているはずなので、
OFF にしていない限り確認不要となります。

1.5 マイグレーション

# マイグレーション COMMAND
python manage.py makemigrations
# マイグレーション 実行
python manage.py migrate

1.6 設定

  • urls.py以下の記述を追加する。
    urlpatterns += [
        # add robots.txt URL
        url(r'^robots\.txt$', include('robots.urls')),
    

1.7 確認

  • 以下、URLにアクセスする。

robots.txtURL

http://www.monotalk.xyz/robots.txt

上記URLにアクセスして出力される。 robots.txt

User-agent: *
Allow: /

Host: www.monotalk.xyz
Sitemap: http://www.monotalk.xyz/sitemap.xml

robots.txt出力されていることを確認できたので、一旦これで OK とします。
出力内容がセキュリティとして微妙な気がしますので、
そのあたりの調整は別途、行う必要がありそうです。


2. Mezzanine で Sitemap.xml生成

2.1 Mezzanine のデフォルト動作について

Mezzanine はデフォルトでSitemap.xml出力されます。
${YOUR_DOMAIN}/sitemap.xmlアクセスすると以下のようなSitemap.xmlが出力されます。 ですので、出力内容を変えたいという場合以外、特に何もしないでOKです。

  • Sitemap.xml

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<script/>
<url>
<loc>
${YOUR_DOMAIN}/blog/at-comfasterxmljacksondatabindexcunrecognizedpropertyexceptionfromunrecognizedpropertyexceptionjava51/
</loc>
<lastmod>2016-03-20</lastmod>
</url>
<url>
<loc>
${YOUR_DOMAIN}/blog/no-module-named-djangoutilslognullhandler-djangoutilslog-is-not-a-package/
</loc>
<lastmod>2016-01-11</lastmod>
</url>
<url>
<loc>
${YOUR_DOMAIN}/blog/javalangillegalargumentexception-invalid-bson-field-name-javaruntimename/
</loc>
<lastmod>2016-01-11</lastmod>
</url>
<url>
<loc>${YOUR_DOMAIN}/about/</loc>
</url>
<url>
<loc>${YOUR_DOMAIN}/blog/</loc>
</url>
<url>
<loc>
${YOUR_DOMAIN}/blog/uses-a-non-entity-orgeclipsepersistenceexceptionsvalidationexception/
</loc>
<lastmod>2016-01-14</lastmod>
</url>
<url>
<loc>
${YOUR_DOMAIN}/blog/gradle-could-not-find-method-provided-for-arguments/
</loc>
<lastmod>2016-03-14</lastmod>
</url>
<url>
<loc>${YOUR_DOMAIN}/blog/mezzanine-pagedown/</loc>
<lastmod>2016-01-11</lastmod>
</url>
</urlset>
</xml>

2.2 Mezzanine (4.1.0)不具合

Python 2.7.8 環境下においては、Mezzanine (4.1.0)バグがあるようで、
以下、エラーが発生し、対象のファイルにパッチをあてないとSitemap.xml出力されませんでした。
Python 3 でも発生するかもしれませんが、試してはいないため不明です。

  • エラー内容

TypeError at /sitemap.xml
isinstance() arg 2 must be a class, type, or tuple of classes and types
Request Method: GET
Request URL:    http://www.monotalk.xyz/sitemap.xml
Django Version: 1.9.6
Exception Type: TypeError
Exception Value:    
isinstance() arg 2 must be a class, type, or tuple of classes and types
Exception Location: /usr/local/lib/python2.7/site-packages/django/db/models/fields/related.py in get_default, line 905
Python Executable:  /usr/bin/python
Python Version: 2.7.8
Python Path:    

以下のパッチをあてることで解消しました。
Mezzanine の次のVersionで改修されるようです。

        # DEL
        #home = self.model(title=_("Home"))
        # ADD
        class Home:
            title = _("Home")
        home = Home()
        #----
        setattr(home, "get_absolute_url", home_slug)
        items = {home.get_absolute_url(): home}


3. python manage.py ping_googleGoogle に Blog 更新を通知する。

Sitemap.xml出力に使用している。The sitemap framework | Django documentation | Djangoは、
ping_googleいう、管理コマンドがあり、Google に Blog の ping 送信が行えます。
この管理コマンドは、google以外にも更新通知を送れるようなので、
別途管理コマンドを作成して、複数のサイトに更新通知を送るようにします。

3.1 Mezzanine のPROJECT_APP ディレクトリ配下に、管理コマンドディレクトリを作成する。

  • Applicationを作成

cd ${PROJECT_ROOT}
python manage.py startapp jobs

  • 管理コマンドディレクトリを作成

cd jobs
mkdir ./management
touch ./management/__init__.py
mkdir ./management/commands
touch ./management/commands/__init__.py
touch ./management/commands/ping_all_search_engines.py

3.2 settings.pyINSTALL_APPSjobs追加する

INSTALLED_APPS = (
    "jobs",
    "robots",
    "request",

3.3 ping_all_search_engines.py作成する。

  • 参考サイト

  • ping_all_search_engines.py (Django 1.8 で動作する)
    以下の内容で作成しました。 pingサーバーを参考サイトを元に記述してみましたが、RPC形式じゃないので、通信失敗します.. また、後日 Django 1.10 に Upgrade したら、django ImportError: cannot import name NoArgsCommand | Monotalk原因で動作しなくなり、実装を修正しています。

    # -*- coding: utf-8 -*-
    
    from django.core.management.base import NoArgsCommand
    
    class Command(NoArgsCommand):
        def handle_noargs(self, **options):
    
           print "--------------------------------------------"
           print "ping_all_search_engines START"
           print "-------------------------"
    
           """
           Pings the popular search engines, Google, Yahoo, ASK, and
           Windows Live, to let them know that you have updated your
           site's sitemap. Returns successfully pinged servers.
           """
           from django.contrib.sitemaps import ping_google
           SEARCH_ENGINE_PING_URLS = (
             ('google', 'http://www.google.com/webmasters/tools/ping'),
             ('live', 'http://webmaster.live.com/ping.aspx'),
           )
    
           successfully_pinged = []
           failure_pinged = []
           sitemap_url = "http://www.monotalk.xyz/sitemap.xml"
           for (site, url) in SEARCH_ENGINE_PING_URLS:
               try:
                   ping_google(sitemap_url=sitemap_url, ping_url=url)
                   successfully_pinged.append(site)
               except:
                   failure_pinged.append(site)
    
           print "--------------------------------------------"
           print "ping_all_search_engines END"
           print "successfully_pinged >>>"
           print  successfully_pinged
           print "failure_pinged >>>"
           print  failure_pinged
           print "-------------------------"
    

  • ping_all_search_engines.py (Django 1.10 で動作する)
    Django 1.10 にUpgrade したところ動作しなくなり、且つログをちゃんと出力するため、
    printをloggerに書き換えました。

    # -*- coding: utf-8 -*-                                                                                                                                                             
    
    from django.core.management.base import BaseCommand
    from logging import getLogger
    
    logger = getLogger(__name__)
    
    class Command(BaseCommand):
    
        def handle(self, *args, **options):
    
           logger.info("ping_all_search_engines START")
    
           """
           Pings the popular search engines, Google, Yahoo, ASK, and
           Windows Live, to let them know that you have updated your
           site's sitemap. Returns successfully pinged servers.
           """
           from django.contrib.sitemaps import ping_google
    
           SEARCH_ENGINE_PING_URLS = (
             ('google', 'http://www.google.com/webmasters/tools/ping'),
             ('live', 'http://webmaster.live.com/ping.aspx'),
             ('goo', 'http://blog.goo.ne.jp/XMLRPC'),
             ('with2', 'http://blog.with2.net/ping.php'),
             ('google_blog_search_co_jp', 'http://blogsearch.google.co.jp/ping/RPC2'),
             ('google_blog_search_com', 'http://blogsearch.google.com/ping/RPC2'),
             ('blo', 'http://ping.blo.gs/'),
             ('blogmura', 'http://ping.blogmura.com/xmlrpc/hidmbp2r256f'),
             ('blogranking', 'http://ping.blogranking.net'),
             ('cocolog-nifty', 'http://ping.cocolog-nifty.com/xmlrpc'),
             ('dendou', 'http://ping.dendou.jp'),
             ('fc2', 'http://ping.fc2.com'),
             ('feedburner', 'http://ping.feedburner.com'),
             ('freeblogranking', 'http://ping.freeblogranking.com/xmlrpc'),
             ('drecom', 'http://ping.rss.drecom.jp'),
             ('sitecms', 'http://ping.sitecms.net'),
             ('pingoo', 'http://pingoo.jp/ping'),
             ('kuruten', 'http://ranking.kuruten.jp/ping'),
             ('pingomatic', 'http://rpc.pingomatic.com'),
             ('livedoor', 'http://rpc.reader.livedoor.com/ping'),
             ('weblogs', 'http://rpc.weblogs.com/RPC2'),
             ('serenebach', 'http://serenebach.net/rep.cgi'),
             ('newsgator', 'http://services.newsgator.com/ngws/xmlrpcping.aspx'),
             ('taichistereo', 'http://taichistereo.net/xmlrpc'),
             ('sourceforge', 'http://wpdocs.sourceforge.jp/Update_Services'),
             ('blogpeople', 'http://www.blogpeople.net/ping'),
             ('blogpeople_servlet', 'http://www.blogpeople.net/servlet/weblogUpdates'),
             ('blogstyle', 'http://www.blogstyle.jp'),
             ('i-learn', 'http://www.i-learn.jp/ping'),
             ('pubsub', 'http://xping.pubsub.com/ping'),       
           )
    
           SITEMAP_URLS = (
               "https://www.monotalk.xyz/sitemap.xml",
               "https://www.monotalk.xyz/blog/feeds/rss",
               "https://www.monotalk.xyz/blog/feeds/atom",
           )
    
           successfully_pinged = {}
           failure_pinged = {} 
    
           for elem_url in SITEMAP_URLS:
               for (site, url) in SEARCH_ENGINE_PING_URLS:
                   try:
                       ping_google(sitemap_url=elem_url, ping_url=url)
                       values = successfully_pinged.get(elem_url, None)
                       if values is None:
                           values = []
                       values.append(site)
                       successfully_pinged.update({elem_url:values})
    
                   except:
                       fail_values = failure_pinged.get(elem_url, None)
                       if fail_values is None:
                           fail_values = []
                       fail_values.append(site)
                       failure_pinged.update({elem_url:fail_values})
    
           import pprint
           logger.info("ping_all_search_engines END")
           logger.info("successfully_pinged >>>" + pprint.pformat(successfully_pinged, indent=4))
           logger.info("failure_pinged >>>" + pprint.pformat(failure_pinged, indent=2))
    

3.4 crontab実行可能にする。

あとは、1日1回くらい実行されるようにcron設定しておきます。
signal使用して、Blog 記事更新時に更新通知ができるとかっこいいと思いましたが、
それはまた別の機会に試してみようと思います。

以上です。

コメント