freshmeat.net: Project details for pytumblr - ロックスターになりたいそう言えば前に「Windowsのエクスプローラで「送る」からShareOnTumblr」なんてのも作ったなぁ。 pythonで作ってあってlinuxなんかでも動くように作ったはず。
pytumblr is a Python library for the tumblr.com API. freshmeat.net: Project details for pytumblr
でpytumblrですが、ソース見たら簡単なソースだったのでGoogle App Engineで動くように改造してみました。以下パッチ
--- pytumblr.py.orig Thu Apr 24 03:15:26 2008
+++ pytumblr.py Thu Apr 24 12:02:32 2008
@@ -1,7 +1,9 @@
#!/usr/bin/env python
+#!-*- coding:utf-8 -*-
-import string, httplib, urllib2, urllib
-from xml.dom import minidom
+import urllib2, urllib
+from google.appengine.api import urlfetch
+from BeautifulSoup import BeautifulSoup
class pytumblr(object):
"""Tumblr API Object.
@@ -75,14 +77,11 @@
data = urllib.urlencode(values)
headers = {"Content-type": "application/x-www-form-urlencoded"}
- conn = httplib.HTTPConnection(self.url)
- conn.follow_all_redirects = True
- conn.request("POST",'/api/write', data, headers)
- response = conn.getresponse()
- if ( int(response.status) == 201):
+ response = urlfetch.fetch("http://%s/api/write" % self.url, headers=headers, method='POST', payload=data)
+ if ( int(response.status_code) == 201):
return 'Success'
- elif( int(response.status) != 201):
- raise 'Error - Status %s (%s) returned' %(response.status, response.reason)
+ elif( int(response.status_code) != 201):
+ raise 'Error - Status %s (%s) returned' %(response.status_code, 'something wrong')
def auth(self):
values = {
@@ -92,13 +91,10 @@
}
data = urllib.urlencode(values)
headers = {"Content-type": "application/x-www-form-urlencoded"}
- conn = httplib.HTTPConnection(self.url)
- conn.follow_all_redirects = True
- conn.request("POST", '/api/write', data, headers)
- response = conn.getresponse()
- if ( int(response.status) != 200 ):
- return '< There was a tiny problem: %s (%s) >' %(response.status, response.reason)
- if ( int(response.status) == 200 ):
+ response = urlfetch.fetch("http://%s/api/write" % self.url, headers=headers, method='POST', payload=data)
+ if ( int(response.status_code) != 200 ):
+ return '< There was a tiny problem: %s (%s) >' %(response.status_code, 'something wrong')
+ if ( int(response.status_code) == 200 ):
return '< Authenticated! >'
@@ -112,25 +108,8 @@
opts = opts + "?type=" + type
if ( id != 'None' ):
opts = opts + "?id=" + id
- return urllib.urlopen('http://%s.tumblr.com/api/read%s' %(self.user, opts)).read()
+ return urlfetch.fetch('http://%s.tumblr.com/api/read%s' %(self.user, opts)).content
def getblog(self):
- rxml = minidom.parseString(self.blogread())
- titles = rxml.getElementsByTagName('regular-title')
- postid = rxml.getElementsByTagName('post')
- i = 0
- n = 0
- posts = {}
- while ( i < len(postid)):
- if ( postid[i].attributes['type'].value == 'regular' ):
- t = titles [n]
- t = t.toxml()
- t = t.replace('<regular-title)', '')
- t = t.replace('</regular-title)', '')
- poid = postid[i].attributes["id"].value
- posts[ poid ] = t
- n = n + 1
- if ( postid[i].attributes['type'].value != 'regular' ):
- pass
- i = i + 1
- return posts
+ soap = BeautifulSoup(self.blogread())
+ return soap('post')
思いっきり弄ってますね。。。minidomの代わりにBeautifulSoupを、urllib2の代わりにurlfetchを使っています。したがってgetblogは自分のregular情報のdictだけを返すのではなくBeautifulSoupを使ってlink、photo、quoteを返す様にしてあります。
実際に動くよって所は以下のサイトで確認して下さい。
pytumblrこのサイトのURLの後ろに
http://mattn.appspot.com/tumblr/mattn
と言った感じにtumblrアカウント名を付けてみて下さい。今回のデモにはweb.pyというフレームワークを使用してみました。
以下スクリプトソースです。
#!-*- coding:utf-8 -*-
import web
from pytumblr import pytumblr
from BeautifulSoup import BeautifulSoup
def unescape(str):
return BeautifulSoup(str, convertEntities=BeautifulSoup.HTML_ENTITIES).contents[0].encode('utf-8', 'replace')
urls = (
'/tumblr/(.*)', 'tumblr'
)
render = web.template.render('templates/')
class tumblr:
def GET(self, name):
web.header("Content-Type", "text/html; charset=utf-8")
template_values = { 'name': '', 'posts': [] }
name = name.replace('/', '')
if name:
pt = pytumblr(name, None, None)
template_values['name'] = name
for blog in pt.getblog():
if blog['type'] == 'link':
template_values['posts'].append({
'type': 'link',
'text': unescape(blog('link-text')[0].string),
'desc': blog('link-description') and unescape(blog('link-description')[0].string) or '',
'link': unescape(blog('link-url')[0].string),
})
if blog['type'] == 'photo':
template_values['posts'].append({
'type': 'photo',
'text': unescape(blog('photo-caption')[0].string),
'link': unescape(blog('photo-url')[0].string),
})
if blog['type'] == 'quote':
template_values['posts'].append({
'type': 'quote',
'text': unescape(blog('quote-text')[0].string),
'link': unescape(blog('quote-source')[0].string),
})
return render.tumblr(template_values)
if __name__ == "__main__":
web.application(urls, globals()).cgirun()
案外短く書けますね。そしてテンプレートHTML
$def with (res)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="/static/css/tumblr.css" type="text/css" />
$if res['name']:
<title>pytumblr - $res['name']</title>
$else:
<title>pytumblr</title>
</head>
<body>
$if res['name']:
<h1>pytumblr - $res['name']</h1>
$else:
<h1>pytumblr</h1>
<img src="http://b.hatena.ne.jp/entry/image/http://mattn.appspot.com/tumblr/" title="はてなブックマーク" />
<div id="content">
$if res['name']:
<h2>$res['name']'s tumblr</h2>
$for post in res['posts']:
$if post['type'] == 'photo':
<b>PHOTO:</b>$:post['text']<br />
<blockquote><img src="$post['link']" /></blockquote>
$elif post['type'] == 'link':
<b>LINK:</b><a href="$post['link']">$:post['text']</a><br />
<blockquote class="link">$:post['desc']</blockquote>
$elif post['type'] == 'quote':
<b>QUOTE:</b><br />
<blockquote class="quote">
$:post['text']<br />
<cite>$:post['link']</cite>
</blockquote>
</div>
<hr clear="all" />
<p style="text-align: center">provided by <a href="http://mattn.kaoriya.net">mattn</a>, hosted on google app server.</p>
</body>
</html>
このweb.pyのテンプレートって癖があってpythonインデント方式なのですが、これってpreとかcodeで先頭が入っちゃったら不味いんじゃないかと思ったり...解決方あるのか、調べてみます。pytumblrで面白いもの作ってみて下さい。