feedparserで、media コンテンツを取る
2008/11/24 @ 21:12feedparserで、どうやってビデオを取れるかをずっと悩みましたけど、今日少しだけ、進展した。問題の核心はyoutubeや、vimeoは Yahoo! RSS モジュールを使って、RSS拡張ネームスペースにデータを入れている。この拡張データの処理はfeedparserが中途半端でやってる。見てみよう。
YoutubeのGDATA APIで取ったデータはこうなる
| ... | |
| <entry> | |
| <id>http://gdata.youtube.com/feeds/api/videos/R3orQKBxiEg</id> | |
| <published>2008-07-17T23:58:51.000Z</published> | |
| <updated>2008-11-24T02:32:25.000Z</updated> | |
| ... | |
| <title type="text">Official Watchmen Trailer</title> | |
| <content type="text">Title speaks for itself | |
| so people don't have to keep answerin | |
| the name of the song is: | |
| smashing pumpkins- the beginning is the end is the beginning</content> | |
| ... | |
| <author> | |
| <name>Garrettheparrot</name> | |
| <uri>http://gdata.youtube.com/feeds/api/users/garrettheparrot</uri> | |
| </author> | |
| <media:group> | |
| <media:title type="plain">Official Watchmen Trailer</media:title> | |
| <media:description type="plain">Title speaks for itself | |
| so people don't have to keep answerin | |
| the name of the song is: | |
| smashing pumpkins- the beginning is the end is the beginning</media:description> | |
| <media:keywords>2009, Comic, Men, Movie, Watch, Watches, Who</media:keywords> | |
| <yt:duration seconds="140"/> | |
| <media:category label="Film & Animation" scheme="http://gdata.youtube.com/schemas/2007/categories.cat">Film</media:category> | |
| <media:content url="http://www.youtube.com/v/R3orQKBxiEg&f=gdata_user_favorites" type="application/x-shockwave-flash" medium="video" isDefault="true" expression="full" duration="140" yt:format="5"/> | |
| <media:content url="rtsp://rtsp2.youtube.com/CjALENy73wIaJwlIiHGgQCt6RxMYDSANFEgGUhRnZGF0YV91c2VyX2Zhdm9yaXRlcww=/0/0/0/video.3gp" type="video/3gpp" medium="video" expression="full" duration="140" yt:format="1"/> | |
| <media:content url="rtsp://rtsp2.youtube.com/CjALENy73wIaJwlIiHGgQCt6RxMYESARFEgGUhRnZGF0YV91c2VyX2Zhdm9yaXRlcww=/0/0/0/video.3gp" type="video/3gpp" medium="video" expression="full" duration="140" yt:format="6"/> | |
| <media:thumbnail url="http://i.ytimg.com/vi/R3orQKBxiEg/2.jpg" height="97" width="130" time="00:01:10"/> | |
| <media:thumbnail url="http://i.ytimg.com/vi/R3orQKBxiEg/1.jpg" height="97" width="130" time="00:00:35"/> | |
| <media:thumbnail url="http://i.ytimg.com/vi/R3orQKBxiEg/3.jpg" height="97" width="130" time="00:01:45"/> | |
| <media:thumbnail url="http://i.ytimg.com/vi/R3orQKBxiEg/0.jpg" height="240" width="320" time="00:01:10"/> | |
| <media:player url="http://www.youtube.com/watch?v=R3orQKBxiEg"/> | |
| </media:group> | |
| ... | |
| </entry> | |
| ... |
media という名前空間の下に結構データが入ってる。なのに、feedparserはちょっとしか取らない。
| >>> d = feedparser.parse("http://gdata.youtube.com/feeds/api/users/IanLewisInJapan/favorites") | |
| >>> e = d['entries'][0] | |
| >>> filter(lambda x: x.startswith('media_'), e.keys()) | |
| ['media_category', 'media_player', 'media_group', 'media_keywords', 'media_description', 'media_content', 'media_thumbnail'] | |
| >>> e['media_content'] | |
| u'' | |
| >>> e['media_thumbnail'] | |
| u'' | |
| >>> e['media_player'] | |
| u'' | |
| >>> e['media_description'] | |
| u'Last.fm/presents Yellow Magic Orchestra Interview at Royal Festival Hall in London.\nCheck out http://www.last.fm/Presents to find out about all of our other interviews or upcoming/past events.' | |
| >>> |
ビデオのURLはどこかにない。原因はfeedparserの拡張ネームスペース処理に入ってるけども、一言いうと、<media:content>というタグの属性は取れてない。こう見たら、どうやって、とれるかを調べたら、unknown_starttag()というメソッドの中にこのコードを見つけた。
feedparser.py
| ... | |
| # call special handler (if defined) or default handler | |
| methodname = '_start_' + prefix + suffix | |
| try: | |
| method = getattr(self, methodname) | |
| return method(attrsD) | |
| except AttributeError: | |
| return self.push(prefix + suffix, 1) | |
| ... |
じゃ、XMLを解析するときに、タグを見つけたら、unknown_starttag()という関数が実行されて、タグの名前に一致するメソッドがあれば、実行する処理やってる。それで、start_media_content()というメソッドがあれば実行してくれるわけだね。でも、どうやって、パーサークラスに付けるのか。feedparserは_StrictFeedParserというクラスを名前で使ってるから、自分が作ったクラスを_ScrictFeedParserと交換。
| feedparser._StrictFeedParser_old = feedparser._StrictFeedParser | |
| class DlifeFeedParser(feedparser._StrictFeedParser_old): | |
| | |
| def _start_media_content(self, attrsD): | |
| self.entries[-1]['media_content_attrs'] = copy.deepcopy(attrsD) | |
| feedparser._StrictFeedParser = DlifeFeedParser |
それで、またparse()を実行すると、
| >>> import feedparser | |
| >>> import copy | |
| >>> feedparser._StrictFeedParser_old = feedparser._StrictFeedParser | |
| >>> class DlifeFeedParser(feedparser._StrictFeedParser_old): | |
| ... | |
| ... def _start_media_content(self, attrsD): | |
| ... self.entries[-1]['media_content_attrs'] = copy.deepcopy(attrsD) | |
| ... return self.push('media_content', 1) | |
| ... | |
| >>> feedparser._StrictFeedParser = DlifeFeedParser | |
| >>> d = feedparser.parse("http://gdata.youtube.com/feeds/api/users/IanLewisInJapan/favorites") | |
| >>> filter(lambda x: x.startswith("media_"), d['entries'][0].keys()) | |
| ['media_category', 'media_player', 'media_group', 'media_content_attrs', 'media_keywords', 'media_description', 'media_content', 'media_thumbnail'] | |
| >>> d['entries'][0]['media_content_attrs'] | |
| ['medium', 'format', 'url', 'expression', 'duration', 'type', 'yt:format'] |
それで、media:contentの属性を取れた。 media:groupの下にcontentが複数ある場合もあるから、もうちょっとまとめないといけないけど、やり方が少し分かってきた。
Django サイトマップ フレームワーク
2008/11/18 @ 21:51Django sitemap frameworkを使うのが簡単過ぎる。下記のようにサイトマップクラスを作って、urls.pyに登録するだけ。サイトマップに載るURLを取るのに、サイトマップフレームワークが自分が作ったクラスのitems()を呼び出して、アイテムのget_absolute_url()を順番に呼び出す感じ。
models.py
| from django.db import models | |
| ... | |
| class Entry(models.Model): | |
| ... | |
| @permalink | |
| def get_absolute_url(self): | |
| return ... | |
| ... |
sitemap.py
| from django.contrib.sitemaps import Sitemap | |
| from mysite.blog.models import Entry | |
| from django.contrib.sitemaps import Sitemap | |
| from mysite.blog.models import Entry | |
| class BlogSitemap(Sitemap): | |
| priority = 0.5 | |
| def items(self): | |
| return Entry.objects.filter(is_draft=False) | |
| def lastmod(self, obj): | |
| return obj.pub_date | |
| # changefreq can be callable too | |
| def changefreq(self, obj): | |
| return "daily" if obj.comments_open() else "never" |
urls.py
| from mysite.blog.sitemap import BlogSitemap | |
| ... | |
| sitemaps = { | |
| "blog": BlogSitemap | |
| } | |
| (r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}) | |
| ... |
サイトマップインデクスも作れる。GoogleのURL 50,000件の制限があるため、サイトマップをPagenatorで自動的にURLを振り分けてくれる。urls.pyをこう変えるだけ
| from mysite.blog.sitemap import BlogSitemap | |
| ... | |
| sitemaps = { | |
| "blog": BlogSitemap | |
| } | |
| (r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', {'sitemaps': sitemaps}), | |
| (r'^sitemap-(?P&amp;lt;section&amp;gt;.+)\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps':sitemaps}) | |
| ... |
素敵だな。
後、サイトのコンテンツが変更された時、Googleが新しいコンテンツをインデクスするために ping_googleというmanage.pyコマンドが用意してある。
python manage.py ping_googleでも、他の検索エンジンが同じようなサービスがあるのに、ping_googleしかないので、上記のようなコマンドをいくつも作っていた。
DjangoGraphviz
2008/11/08 @ 11:40今日、Djangoアプリケーションのモデルの構成を分かりやすく見たくて、モデル構成からGraphviz ドットファイルを生成できれば、いいなと思って、DjangoGraphvizを見つけた。ただ、ここからダウンロードして、こう実行する。
PYTHONPATH=$PYTHONPATH:. DJANGO_SETTINGS_MODULE=appmodule.settings python modelviz.py applabel > app.dot
dot app.dot -Tpng app.png
最近作った dlifeのモデル構成イメージを作るとこれがでる。
よくできてるね。
初出勤日
2008/11/04 @ 21:57Python温泉
2008/10/27 @ 16:31先週末、 Python温泉第4回に行ってきました。Python温泉は金土日なんですけど、前回と同じく土日、1泊にやってきました。今回は集中できたけど、前回より友達つくりや、会話ができてなかったという感覚だった。いろいろ寂しかった。でも、ビープラウドのid:tokibitoさんと、jQuery Internalsのdata()関数の実装や、feedparserや, Djangoの話をして、少し仲良くなった。ブログに俺について何も書いてくれなかったけど ^^;
Python温泉参加者のブログにまとめ(id:voluntasのまとめ, id:tokibitoのまとめ)が多いから、
まとめ:
- すぐ転職がばれた
- FriendFeedや、sweetcronみたいなlifestreamをdjangoで頑張って作った。(dlife)
- 温泉一人で入って寂しかった。
- id:tokibitoさんにjQuery Internalsのdata()関数を紹介
- いつも土日で来るから、自己紹介に出ないので、誰が誰か分からなくなっちゃう ( 俺のせい? )
- feedparserを分かりやすくすぐ使えるから感動。しかし、写真、ビデオの処理悩み中
- ブログを見てくれて、リンクする人が以外に少ない。( 何も書いてないからかな?^^; )
- 夕食の時間の抽選で、アクセンス・テクノロジーの増田さん(id:whosaysni)から、Python シャツを頂きました!!
- 駅から、一人で帰って寂しかった。
ウェブホスティング移動
2008/10/13 @ 13:02最近PythonやDjangoの開発をだんだんやっていてる僕が、このサイトを新しいウェブホスティングプロバイダーに移動することにした。 今は Haswebという安いプロバイダーなんですけど、Pythonにあまり相性がよくないので、Pythonアプリをサーバーに動かすのが難しすぎる。 簡単なPython cgiを対応してるだけで、コンパイルもできないから、Pure Pythonライブラリしかインストールできない。 なので、PylonsがおすすめしてるWebfactionに移動することにした。 Django,Pylonsに結構やさしくて、ライブラリをコンパイルできるから、使いやすいと思ってる。 それで、このサイトでいろいろなPythonウェブアプリをだんだん作っていきたいと思ってる。










