Hatena::Grouppython

Pythonで遊ぶよ

|

2010-02-24

順序を保持するuniq

16:34

こういうのもありますよー。

[x for i,x in enumerate(a) if a.index(x) == i]

>>> a = [4,3,2,5,2,4,2,4,5,3,2,1,6]
>>> [x for i,x in enumerate(a) if a.index(x) == i]
[4, 3, 2, 5, 1, 6]

set とか別のリストを作る方法と比べて速いのかは知りませんが。

トラックバック - http://python.g.hatena.ne.jp/edvakf/20100224

2010-01-16

djangoでGoogle App Engineのプロジェクトを作成

18:13

Using Django with Google Appengine を見ながら自分のやったことをメモ。

まず django を入れる。easy_install (setuptools) が入ってれば easy_install Django で。

次に、アプリを作りたいディレクトリで、

% django-admin.py startproject myproject

とすると、myproject なるディレクトリが出来る。

次に、Running Django on Google App Engine - Google App Engine - Google Code から main.py をコピーしてきて、main.py として保存。

ただし、os.environ['DJANGO_SETTINGS_MODULE'] の値だけは project.settings から myproject.settings に変える。

% cat main.py
import logging, os

# Google App Engine imports.
from google.appengine.ext.webapp import util

# Force Django to reload its settings.
from django.conf import settings
settings._target = None

# Must set this env var before importing any part of Django
# 'project' is the name of the project created with django-admin.py
os.environ['DJANGO_SETTINGS_MODULE'] = 'myproject.settings'

import logging
import django.core.handlers.wsgi
import django.core.signals
import django.db
import django.dispatch.dispatcher

def log_exception(*args, **kwds):
    logging.exception('Exception in request:')

# Log errors.
django.dispatch.dispatcher.connect(
    log_exception, django.core.signals.got_request_exception)

# Unregister the rollback event handler.
django.dispatch.dispatcher.disconnect(
    django.db._rollback_on_exception,
    django.core.signals.got_request_exception)

def main():
    # Create a Django application for WSGI.
    application = django.core.handlers.wsgi.WSGIHandler()

    # Run the WSGI CGI handler with that application.
    util.run_wsgi_app(application)

if __name__ == '__main__':
    main()

次に、app.yamlGoogle の解説ページからコピー。application: my_application となってるのを、アンダースコア等を含まないものに変える。

application: myproject
version: 1
runtime: python
api_version: 1

handlers:
- url: /static
  static_dir: static

- url: /.*
  script: main.py

この時点でディレクトリ構造は

  • hoge/
    • main.py
    • app.yaml
    • myproject/
      • __init__.py
      • manage.py
      • settings.py
      • urls.py

となっている。

あとは dev_appserver.py . とやればサーバーが起動する。


とりあえず今日はここまで。

トラックバック - http://python.g.hatena.ne.jp/edvakf/20100116

2009-10-30

短縮URLをJSON(P)で返すAPIを作ってみた

06:42

twitter で140字を超える投稿は失敗するようになってしまったので、短縮 URL を返す API を作った。

僕は短縮 URL は嫌いなのだけど、必要に迫られて仕方なく、といったところ。

短縮してない URL だったとしても、投稿が140字以下だったらちゃんと投稿できるし、もし twitter に勝手に bit.ly に変換されても公式検索ではちゃんと元投稿の URLインデックスされてるので、本来なら URL は短縮しなでそのまま投稿するのが良いと思う。


とりあえず作ったので置いとく。

from gaeo.controller import BaseController
from google.appengine.api import urlfetch,memcache

import hashlib,re,urllib,logging
logging.getLogger().setLevel(logging.DEBUG)

class ShortenController(BaseController):
    def index(self):
        html = """  
        <html><head>
        <title>URL shortening API</title>
        </head>
        <body>
        <form action="get" method="get">
        <input type="text" name="url" />
        <input type="submit" value="Shorten URL by j.mp" />
        </form>
        </body>
        </html>
        """
        return self.render(html=html)

    def get(self):
        urls = self.params.get('url')
        if not urls: return self.bad_request()
        url_list = set(urls.split()[0:10])
        obj = {}
        r = re.compile(r'<input id="shortened-url" value="(.+?)"')
        for url in url_list:
            logging.info(u'shortening : ' + urls)
            id = 'shorten_' + hashlib.md5(repr(url)).hexdigest()
            cache = memcache.get(id)
            if cache == None:
                try:
                    response = urlfetch.fetch('http://j.mp/?url='+urllib.quote(url.encode('utf-8')))
                    if response.status_code == 200:
                        obj[url] = r.search(response.content).group(1)
                        memcache.set(id, obj[url])
                    else:
                        logging.info('request status : %d', response.status_code)
                        return self.not_found()
                except Exception, e:
                    logging.info(e)
                    return self.not_found()
            else:
                logging.info('cache found')
                obj[url] = cache
        json = self.to_json(obj)

        callback = self.params.get('callback')
        if callback == '': return self.bad_request()
        if callback == None:  return self.render(json=json) # json
        else:
            if re.search(r'[^\w.$]',callback):
                return self.bad_request()
            return self.render(text='%s(%s)' % (callback,json)) # jsonp

    def bad_request(self):
        self.response.set_status(400, 'Bad Request')
        self.render(text='Bad request')

    def not_found(self):
        self.response.set_status(404, 'Not Found')
        self.render(text='Not Found')

Google App Engine Oil を使った。

URL 短縮サービスは http://j.mp/ を使った。j.mp は bit.ly が運営してて、内部的にはまったく bit.ly と同じもの。他の短縮サービスに比べたら潰れにくいと思う。


仕様

/shorten/get?url=url1[+url2[+...]][&callback=CallbackFunctionName]

URL は 10 個まで渡せる。

返り値はこんな感じ。

{"http://python.g.hatena.ne.jp/edvakf/20091030/1256852551": "http://j.mp/2QIsRR"}

関連

トラックバック - http://python.g.hatena.ne.jp/edvakf/20091030

2009-10-28

Google App Engine SDKに付いてくるコマンド

03:39

f:id:edvakf:20091029033433p:image

  • appcfg.py
  • bulkupload_client.py
  • bulkloader.py
  • dev_appserver.py
  • remote_api_shell.py

このメッセージちゃんと読んだことなかった。

bulkupload_client.py と bulkloader.py のどっちかは手元の SQLite データベースを Datastore にアップロードできるツールだったような気がする。

remote_api_shell.py をあとで使ってみる。

トラックバック - http://python.g.hatena.ne.jp/edvakf/20091028

2009-09-13

Google Readerの履歴をぶっこ抜くためのクラス

14:16

このあたりに書いたような方法で、フィードの履歴を過去5000件まで(運が良ければ)抜き出せる。

そのためのクラスを作った。

使い方の例

#!/usr/bin/python
# coding=utf-8

import urllib
from lxml import etree
import re
import time
from GoogleReader import GoogleReader

def main():
  gr = GoogleReader('ユーザー名','パスワード')   # 自分のユーザー名とパスワードに置き換える 
  atom = gr.getFeedCache('http://netamichelin.blog68.fc2.com/?xml',num=5000) # atom 形式でキャッシュ取得

  et = etree.fromstring(atom, parser=etree.XMLParser())
  ns = {
      'atom':'http://www.w3.org/2005/Atom',
      'media':'http://search.yahoo.com/mrss/',
      'gr':'http://www.google.com/schemas/reader/atom/',
      'idx':'urn:atom-extension:indexing'}

  links = []
  for entry in et.xpath('/atom:feed/atom:entry',namespaces=ns):
    mg = entry.xpath(u'./atom:category[@term="眼鏡"]',namespaces=ns)
    if len(mg):
      link = entry.xpath('./atom:link',namespaces=ns)[0].get('href')
      links.append(link)

  for url in links:
    try:
      r = urllib.urlopen(url)
      et = etree.fromstring(r.read(), parser=etree.HTMLParser())
      src = et.xpath('//td[@class=\"left_outline\"]/../td[2]/*[position() < 3]/a/img')[0].get('src')
      print(src)
      time.sleep(3)
    except:
      pass

if __name__ == '__main__':
  main()

結果。

こんな感じで一括ダウンロードしたらいいんじゃないかな。

for url in `curl http://gist.github.com/186082.txt`; do curl -O $url; done
トラックバック - http://python.g.hatena.ne.jp/edvakf/20090913
|