ラベル Python の投稿を表示しています。 すべての投稿を表示
ラベル Python の投稿を表示しています。 すべての投稿を表示

2014/08/03

スクレイピングとWebクローリングの出来るフレームワークScrapyをCentOS6で試してみました。

Scrapyは、ウェブサイトをクロールし、そのページから構造化データの抽出が出来ます。高速で高レベルの画面スクレイピング、Webクローリングフレームワークです。Scrapyは全てPythonで書かれています。


http://scrapy.org/

CentOS6にScrapyの環境を用意して、下記の動画のチュートリアルを試しました。


ソースは、下記のページで公開されています。 
Scraping Web Pages With Scrapy 

 このソースで行っているのは、Craigslistの「NPO」のカテゴリで表示されているリストのタイトルと、そのURLを取得するというものです。

Craigslistとは - IT用語辞典 e-Words
実際に処理をする対象のページ 

データを実際に取得して格納をするスクリプト   
from scrapy.spider import BaseSpider
from scrapy.selector import HtmlXPathSelector
from craigslist_sample.items import CraigslistSampleItem

class MySpider(BaseSpider):
  name = "craig"
  allowed_domains = ["craigslist.org"]
  start_urls = ["http://sfbay.craigslist.org/npo/"]

  def parse(self, response):
      hxs = HtmlXPathSelector(response)
      titles = hxs.select("//span[@class='pl']")
      items = []
      for titles in titles:
          item = CraigslistSampleItem()
          item ["title"] = titles.select("a/text()").extract()
          item ["link"] = titles.select("a/@href").extract()
          items.append(item)
      return items

スクリプトを動かして結果をCSVに保存します。
$ scrapy crawl craig -o items.csv -t csv

2014/08/03 21:20 に実行して保存されたCSVの中身を確認
$ more items.csv
link,title
/sfc/npo/4599976929.html,✦FULL-TIME AND PART-TIME JOBS! EARN AVG OF $11-16/HR
/eby/npo/4599904588.html,█ CAMPAIGN STAFF POSITION to FIGHT FAC
/eby/npo/4599659276.html,"!!!What's in Your Water? Clean Water Action, Now Hiring!!"
/scz/npo/4599590859.html,*I'm an ACTIVIST. What Kind of Progressive Are You? Click To Find Out!
/eby/npo/4599575085.html,▲I'm an ACTIVIST. What Kind of Progressive Are You? Click To Find Out!
/sfc/npo/4599499777.html,FULL-TIME AND PART-TIME JOBS WITH CALPIRG! EARN AVG OF $11-16/HR
/eby/npo/4599307693.html,Join our winning team of rad activists!  PT phone work 12$ hr
/sfc/npo/4599088866.html,Resident Services Coordinator
/sfc/npo/4599069160.html,Stonestown Family YMCA - After School Program Leader
/eby/npo/4599024935.html,▲LGBT EQUALITY SUMMER JOBS W/ADVANCEMENT OPPORTUNITIES  $4700-7500
以下続く

Scrapyを使えば、クローリング、収集したデータの処理、整形まで、複雑な処理が簡単に出来そうです。

2013/09/21

CentOS6のZope2.10.9にpsycopg、ZPsycopgDAのインストールした際の備忘録

ZopeとPostgreSQLをつなぐ必要があり、psycopgを使って接続した際の備忘録です。


PostgreSQLのインストール状況の確認
$ sudo rpm -qa | grep postgres
postgresql-odbc-08.04.0200-1.el6.i686
postgresql-libs-8.4.13-1.el6_3.i686
postgresql-server-8.4.13-1.el6_3.i686
postgresql-devel-8.4.13-1.el6_3.i686
postgresql-8.4.13-1.el6_3.i686

postgresqlが、サービスに登録されていることを確認
$ ls /etc/init.d/
abrt-ccpp functions postgresql spice-vdagentd ...

OS起動時のPostgreSQL起動を設定
$ sudo chkconfig postgresql on
$ sudo chkconfig --list postgresql
postgresql      0:off   1:off   2:on    3:on    4:on    5:on    6:off

PostgreSQLの起動
$ sudo service postgresql initdb
データベースを初期化中: [  OK  ]
$ sudo service postgresql start
postgresql サービスを開始中: [  OK  ]
PostgreSQLで操作用のユーザーを作成しておきます。
$ sudo su - postgres
-bash-4.1$ createuser test_user
新しいロールをスーパーユーザとしますか?  (y/n)n
新しいロールにデータベース作成権限を与えますか? (y/n)y
新しいロールにロールを作成する権限を与えますか? (y/n)n

$ exit
logout

操作用のユーザーに切り替えて、データベースを作成して、そのデータベースに接続できることを確認しておきます。

$ su - test_user
$ createdb sampledb
$ psql sampledb
psycopg-1.1.6のインストール
psycopg-1.1.6.tar.gz をダウンロード、解凍して、インストール。
$ wget http://old.zope.org/Members/fog/psycopg/1.1.6/psycopg-1.1.6.tar.gz
$ tar vxzf psycopg-1.1.6.tar.gz
$ cd psycopg-1.1.6
$ ./configure 
--with-python=/opt/python2.4.6/bin/python 
--with-postgres-libraries=/usr/lib/pgsql 
--with-postgres-includes=/usr/include/pgsql/server 
--with-mxdatetime-includes=/opt/python2.4.6/lib/python2.4/site-packages/mx/DateTime/mxDateTime

$ make

$ sudo make install
Installing shared modules...
install -m 555 ./psycopgmodule.so /opt/python2.4.6/lib/python2.4/site-packages

下記のオプションを付けないと
--with-postgres-libraries=/usr/lib/pgsql 
--with-postgres-includes=/usr/include/pgsql/server

checking PostgreSQL version... configure: error: missing PostgreSQL headers
というエラーが表示されます。

下記のオプションを付けないと
--with-mxdatetime-includes=/opt/python2.4.6/lib/python2.4/site-packages/mx/DateTime/mxDateTime

checking for mxDateTime.h... configure: error: can't build without mx headers
というエラーが表示されます。

その解消のために,egenix-mx-base 3.2.6のインストールが必要
https://pypi.python.org/pypi/egenix-mx-base/

$ wget https://downloads.egenix.com/python/index/ucs2/egenix-mx-base/3.2.6/egenix-mx-base-3.2.6.tar.gz
$ tar vxzf egenix-mx-base-3.2.6.tar.gz
$ cd egenix-mx-base-3.2.6
$ sudo /opt/python2.4.6/bin/python setup.py build
$ sudo /opt/python2.4.6/bin/python setup.py install
ZPsycopgDAのインストール

/zope_instance_path/Products に ZPsycopgDA をコピーして、Zopeを再起動
ZopeからPostgreSQLに正常に接続、データを取得できることを確認できました。

2013/09/07

「集合知プログラミング」del.icio.usのリンクを推薦するシステムを作るのサンプルコードを試してみました。

「集合知プログラミング」では、インターネットを通じて集合知のモトとなるデータを集め、取り扱う方法が説明されています。


その2章「推薦を行う」にて「del.icio.usのリンクを推薦するシステム」の説明があります。delicious(デリーシャス)とは、ソーシャルブックマークサイトです。簡単に言えば、お気に入りのサイトを公開して共有するサービスです。

del.icio.us
https://delicious.com/

沢山の人が、お気に入りのブックマークを登録しています。登録されているブックマークを分析して、ある人と同じ嗜好を持つ人を探しだすプログラムを作ります。さらに、その人が好みそうなリンクを推薦するシステムを作ります。



「集合知プログラミング」では、Pythonのコードでの説明がされています。この「del.icio.usのリンクを推薦するシステム」では、Pythonのモジュール「pydelicious」を使います。

検証は、Mac OS X 10.7.5のPython 2.7.1で行います。
「pydelicious」のバージョンは、「pydelicious-0.6」を使用します。

下記のソースは、del.icio.usのデータセットを作る処理本体の deliciousrec.py の内容です。

書籍の記載のままでは、動作しない箇所があります。1行目「import time」の記載不足、8行目「for p2 in get_urlposts(p1['href']):」24行目「url=post['href']」は、del.icio.usのAPIの仕様変更により、「for p2 in get_urlposts(p1['url']):」「url=post['url']」への修正が必要です。
import time
from pydelicious import get_popular,get_userposts,get_urlposts

def initializeUserDict(tag,count=5):
 user_dict={}

 for p1 in get_popular(tag=tag)[0:count]:
  for p2 in get_urlposts(p1['url']):
   user=p2['user']
   user_dict[user]={}
 return user_dict

def fillItems(user_dict):
 all_items={}
 for user in user_dict:
  for i in range(3):
   try:
    posts=get_userposts(user)
    break
   except:
    print "Failed user "+user+", retrying"
    time.sleep(4)
  for post in posts:
   url=post['url']
   user_dict[user][url]=1.0
   all_items[url]=1

 for ratings in user_dict.values():
  for item in all_items:
   if item not in ratings:
    ratings[item]=0.0
あと、推薦をするプログラム recommendations.py が必要です。
from math import sqrt

# Returns a distance-based similarity score for person1 and person2
def sim_distance(prefs,person1,person2):
  # Get the list of shared_items
  si={}
  for item in prefs[person1]: 
    if item in prefs[person2]: si[item]=1

  # if they have no ratings in common, return 0
  if len(si)==0: return 0

  # Add up the squares of all the differences
  sum_of_squares=sum([pow(prefs[person1][item]-prefs[person2][item],2) 
                      for item in prefs[person1] if item in prefs[person2]])

  return 1/(1+sum_of_squares)

# Returns the Pearson correlation coefficient for p1 and p2
def sim_pearson(prefs,p1,p2):
  # Get the list of mutually rated items
  si={}
  for item in prefs[p1]: 
    if item in prefs[p2]: si[item]=1

  # if they are no ratings in common, return 0
  if len(si)==0: return 0

  # Sum calculations
  n=len(si)
  
  # Sums of all the preferences
  sum1=sum([prefs[p1][it] for it in si])
  sum2=sum([prefs[p2][it] for it in si])
  
  # Sums of the squares
  sum1Sq=sum([pow(prefs[p1][it],2) for it in si])
  sum2Sq=sum([pow(prefs[p2][it],2) for it in si]) 
  
  # Sum of the products
  pSum=sum([prefs[p1][it]*prefs[p2][it] for it in si])
  
  # Calculate r (Pearson score)
  num=pSum-(sum1*sum2/n)
  den=sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n))
  if den==0: return 0

  r=num/den

  return r

# Returns the best matches for person from the prefs dictionary. 
# Number of results and similarity function are optional params.
def topMatches(prefs,person,n=5,similarity=sim_pearson):
  scores=[(similarity(prefs,person,other),other) 
                  for other in prefs if other!=person]
  scores.sort()
  scores.reverse()
  return scores[0:n]

# Gets recommendations for a person by using a weighted average
# of every other user's rankings
def getRecommendations(prefs,person,similarity=sim_pearson):
  totals={}
  simSums={}
  for other in prefs:
    # don't compare me to myself
    if other==person: continue
    sim=similarity(prefs,person,other)

    # ignore scores of zero or lower
    if sim<=0: continue
    for item in prefs[other]:
     
      # only score movies I haven't seen yet
      if item not in prefs[person] or prefs[person][item]==0:
        # Similarity * Score
        totals.setdefault(item,0)
        totals[item]+=prefs[other][item]*sim
        # Sum of similarities
        simSums.setdefault(item,0)
        simSums[item]+=sim

  # Create the normalized list
  rankings=[(total/simSums[item],item) for item,total in totals.items()]

  # Return the sorted list
  rankings.sort()
  rankings.reverse()
  return rankings

def transformPrefs(prefs):
  result={}
  for person in prefs:
    for item in prefs[person]:
      result.setdefault(item,{})
      
      # Flip item and person
      result[item][person]=prefs[person][item]
  return result


def calculateSimilarItems(prefs,n=10):
  # Create a dictionary of items showing which other items they
  # are most similar to.
  result={}
  # Invert the preference matrix to be item-centric
  itemPrefs=transformPrefs(prefs)
  c=0
  for item in itemPrefs:
    # Status updates for large datasets
    c+=1
    if c%100==0: print "%d / %d" % (c,len(itemPrefs))
    # Find the most similar items to this one
    scores=topMatches(itemPrefs,item,n=n,similarity=sim_distance)
    result[item]=scores
  return result

def getRecommendedItems(prefs,itemMatch,user):
  userRatings=prefs[user]
  scores={}
  totalSim={}
  # Loop over items rated by this user
  for (item,rating) in userRatings.items( ):

    # Loop over items similar to this one
    for (similarity,item2) in itemMatch[item]:

      # Ignore if this user has already rated this item
      if item2 in userRatings: continue
      # Weighted sum of rating times similarity
      scores.setdefault(item2,0)
      scores[item2]+=similarity*rating
      # Sum of all the similarities
      totalSim.setdefault(item2,0)
      totalSim[item2]+=similarity

  # Divide each total score by total weighting to get an average
  rankings=[(score/totalSim[item],item) for item,score in scores.items( )]

  # Return the rankings from highest to lowest
  rankings.sort( )
  rankings.reverse( )
  return rankings

def loadMovieLens(path='/data/movielens'):
  # Get movie titles
  movies={}
  for line in open(path+'/u.item'):
    (id,title)=line.split('|')[0:2]
    movies[id]=title
  
  # Load data
  prefs={}
  for line in open(path+'/u.data'):
    (user,movieid,rating,ts)=line.split('\t')
    prefs.setdefault(user,{})
    prefs[user][movies[movieid]]=float(rating)
  return prefs
用意ができたので、Pythonのインタラクティブシェルで動作を確認します。
#必要なモジュールの読み込み
>>> import recommendations
>>> from deliciousrec import *
>>> import random

# 「design」というタグを付けているユーザーの取得
>>> delusers=initializeUserDict('design')

# 取得した各ユーザーのリンクを取得
>>> fillItems(delusers)

# 取得したユーザー達の中からランダムに1人を取得
>>> user=delusers.keys()[random.randint(0,len(delusers)-1)]

# このユーザーに似ているユーザーをリストアップ
>>> recommendations.topMatches(delusers,user)

[
(0.5344262295081967, u'thehrisworld'), 
(-0.16393442622950818, u'yaax'), 
(-0.16393442622950818, u'tchiule'), 
(-0.16393442622950818, u'sneurgaonkar'), 
(-0.16393442622950818, u'sethkontny')
]

# このユーザーが好みそうなリンクを推薦
>>> recommendations.getRecommendations(delusers,user)[0:10]

[
(1.0, u'http://www.youtube.com/watch?v=C5tOEBmBAHg'), 
(1.0, u'http://www.lifeisstory.com/'), 
(1.0, u'http://techcrunch.com/2013/09/06/what-to-do-if-you-get-a-troll-demand/'), 
(1.0, u'http://on.mash.to/1dJDvYC'), 
(0.0, u'https://www.facebook.com/sethkontny/posts/10201536626993865'), 
(0.0, u'https://communities.vmware.com/community/vmtn/vcloud-automation-center'), 
(0.0, u'http://www.yorkdispatch.com/breaking/ci_24031648/more-sleep-better-kids-but-too-much-trouble'), 
(0.0, u'http://www.wpcentral.com/geophoto-windows-phone-8-helping-you-remember-where-you-took-photo'), 
(0.0, u'http://www.wired.com/wiredscience/2013/09/wired-space-photo-of-the-day-saturn-storm-circles-planet/'), 
(0.0, u'http://www.wired.com/wiredscience/2013/09/what-exactly-is-astrovirology/')
]


2013/08/31

PythonのPDFライブラリPyPDF2で、PDFのページを抽出して保存する方法

PyPDF2というPythonのPDFライブラリがあります。Pythonを使って、PDFファイルを操作することの出来るライブラリです。PDF内のページを抽出したり、ページをマージしたりすることが出来ます。

PyPDF2
http://www.blog.pythonlibrary.org/2012/07/11/pypdf2-the-new-fork-of-pypdf/


下記のスクリプトで、PDFの抽出、保存が出来ます。

someone.pdfというPDFを読み込み、読み込んだPDFの5ページ目を取り出します。取り出したPDFをdocument-output.pdfというPDFで別途保存します。
from PyPDF2 import PdfFileWriter, PdfFileReader

output = PdfFileWriter()
input1 = PdfFileReader(open("someone.pdf", "rb"))

page5 = input1.getPage(4)
output.addPage(page5)

outputStream = file("document-output.pdf", "wb")
output.write(outputStream)
outputStream.close()
今回、やりたい事は、PDFを抽出したあと、PDF内のテキストを抽出して、その中にある特定の数字情報を使って、そのPDFの保存名に使いたい というものです。その操作をしたい該当のPDFを使って、上記のスクリプトを実行させると、下記のエラーが発生します。
Traceback (most recent call last):
  File "", line 1, in 
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 274, in write
    self._sweepIndirectReferences(externalReferenceMap, self._root)
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 351, in _sweepIndirectReferences
    self._sweepIndirectReferences(externMap, realdata)
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 327, in _sweepIndirectReferences
    value = self._sweepIndirectReferences(externMap, value)
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 351, in _sweepIndirectReferences
    self._sweepIndirectReferences(externMap, realdata)
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 327, in _sweepIndirectReferences
    value = self._sweepIndirectReferences(externMap, value)
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 336, in _sweepIndirectReferences
    value = self._sweepIndirectReferences(externMap, data[i])
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 351, in _sweepIndirectReferences
    self._sweepIndirectReferences(externMap, realdata)
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 327, in _sweepIndirectReferences
    value = self._sweepIndirectReferences(externMap, value)
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 366, in _sweepIndirectReferences
    newobj = self._sweepIndirectReferences(externMap, newobj)
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 327, in _sweepIndirectReferences
    value = self._sweepIndirectReferences(externMap, value)
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 327, in _sweepIndirectReferences
    value = self._sweepIndirectReferences(externMap, value)
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 357, in _sweepIndirectReferences
    newobj = data.pdf.getObject(data)
  File "/Library/Python/2.7/site-packages/PyPDF2/pdf.py", line 900, in getObject
    retval = readObject(self.stream, self)
  File "/Library/Python/2.7/site-packages/PyPDF2/generic.py", line 847, in readObject
    return DictionaryObject.readFromStream(stream, pdf)
  File "/Library/Python/2.7/site-packages/PyPDF2/generic.py", line 1348, in readFromStream
    value = readObject(stream, pdf)
  File "/Library/Python/2.7/site-packages/PyPDF2/generic.py", line 838, in readObject
    return ArrayObject.readFromStream(stream, pdf)
  File "/Library/Python/2.7/site-packages/PyPDF2/generic.py", line 933, in readFromStream
    arr.append(readObject(stream, pdf))
  File "/Library/Python/2.7/site-packages/PyPDF2/generic.py", line 867, in readObject
    return NumberObject.readFromStream(stream)
  File "/Library/Python/2.7/site-packages/PyPDF2/generic.py", line 1032, in readFromStream
    return NumberObject(name)
ValueError: invalid literal for int() with base 10: ''
他のPDFでは、問題なく処理できるのに、処理をしたい該当のPDFでエラーが発生します。PDFの内容自体に問題がありそうな感じです。



その後の検証にて、問題のPDFを保存し直してみました。保存中に「重複するバックグラウンドを統合中」というメッセージが表示されました。そのPDFを使うと、エラーは出なくなりました。

その後の実装で、PDF内のテキストを取得して処理をしたかったのですが、日本語の取得が上手くできませんでした。英語のテキストでは問題なく取得できます。

PDF内テキストを取得するスクリプト
temp = input1.getPage(0).extractText()
日本語を含むPDFをいくつか検証したのですが、全く取得できないものから、部分的に取得できるもの、取得できるけど出現位置がバラバラなものとかがありました。日本語エンコードの問題は根が深そうなので、PyPDF2を使った処理は、あきらめて、別の実装で処理をしました。

2012/10/20

Python Google Chartを使って大量に作成したQRコードをスライドショーで眺めると、QRコードの中に色々な形が見えてきます

QRコードを大量に作る必要があり、Google Chart APIを使って作成しました。QRコードには、URLの情報を入れています。URLの一部が連番になっていて、プログラムで大量に生成できる構成のURLになっています。

Google Chart Tools
https://developers.google.com/chart/?hl=ja


Google Chart APIを使うだけでも簡単にQRコードは作成できるのですが、更に楽をしたいので、Google Chart APIをPythonから使うためのラッパーがあるかを調べてみました。

Python Google Chartというラッパーがありました。

Python Google Chart
http://pygooglechart.slowchop.com/


下記のPythonスクリプトにて、任意の数の連番情報を持つテキストのQRコードが作成されます。100個のQRコードも数秒で作成できました。実際のスクリプトの「chart.add_data」の箇所はURL を指定していますが、ここでは、テキストだけにしています。
from pygooglechart import QRChart

for i in range(100):
    # Create a 125 x 125 QR code chart
    chart = QRChart(125, 125)

    # Add the text
    chart.add_data('Hello, World!' + str(i))

    # "Level H" error correction with a 0 pixel margin
    chart.set_ec('H', 0)

    # Download
    chart.download('qr-hello' + str(i) + '.png')
 
    i = i + 1
大量に作成したQRコードをチェックの為に次々と眺めていると、QRコードがアニメーションのように見えてきました。スライドショーのように、更に眺めていると、QRコードの白黒の模様が色々な形に見えてきました。人の顔や動物などが見えてきます。

作成したQRコードをスライドショーにして、YouTubeにアップしてみました。

2012/10/08

Pythonのマイクロウェブフレームワークの「Flask」のチュートリアルを試してみました。

Pythonのマイクロウェブフレームワークの「Flask」のチュートリアルを試してみました。


Flaskへ ようこそ - Flask v0.5.1 documentation
http://a2c.bitbucket.org/flask/

のサイト内のチュートリアルを基に試してみます。
但し、日本語に訳されてるこのバージョンは古く、最新版のFlaskは、0.9です。

Welcome to Flask - Flask 0.9dev documentation
http://flask.pocoo.org/docs/

しかし、チュートリアルの内容は、変わっていないようでした。

MacBook AirのMac OSXの10.7.4の環境で行います。
まず、virtualenvの環境構築を行います。

Flaskのインストールについて - virtualenv
http://a2c.bitbucket.org/flask/installation.html#virtualenv

Flaskのチュートリアルにvirtualenvの環境が必須という訳ではありません。しかし、virtualenvを使った方が、MacBook AirのPythonの環境に影響を与えず、仮想環境内のPythonで完結するので、virtualenvを使います。

Linuxでは、システムの管理自体にPythonを使っていることが多く、Pythonのバージョンを上げてしまうと、システム自体に支障が出ることもあります。以前、間違ってPythonのバージョンを上げてしまい。yumが動かなくなって、困ったことがありました。

virtualenvについては、下記でのサイトの説明が分かりやすいです。

今日のPython - VIRTUALENV について
http://blog1.erp2py.com/2011/07/virtualenv.html

virtualenvをインストールします。
$ pip install virtualenv
仮想環境の入る前に、Pythonの環境について確認しておきます。
$ pip freeze
Django==1.3.1
PIL==1.1.7
PyRSS2Gen==1.0.0
Twisted==11.0.0
altgraph==0.7.1
bdist-mpkg==0.4.4
bonjour-py==0.3
distribute==0.6.26
include-server==3.1-toolwhip.1
macholib==1.3
modulegraph==0.8.1
numpy==1.5.1
py2app==0.5.3
pyOpenSSL==0.12
pyobjc-core==2.3.2a0
pyobjc-framework-AddressBook==2.3.2a0
pyobjc-framework-AppleScriptKit==2.3.2a0
pyobjc-framework-AppleScriptObjC==2.3.2a0
pyobjc-framework-Automator==2.3.2a0
pyobjc-framework-CFNetwork==2.3.2a0
pyobjc-framework-CalendarStore==2.3.2a0
pyobjc-framework-Cocoa==2.3.2a0
pyobjc-framework-Collaboration==2.3.2a0
pyobjc-framework-CoreData==2.3.2a0
pyobjc-framework-CoreLocation==2.3.2a0
pyobjc-framework-CoreText==2.3.2a0
pyobjc-framework-DictionaryServices==2.3.2a0
pyobjc-framework-ExceptionHandling==2.3.2a0
pyobjc-framework-FSEvents==2.3.2a0
pyobjc-framework-InputMethodKit==2.3.2a0
pyobjc-framework-InstallerPlugins==2.3.2a0
pyobjc-framework-InstantMessage==2.3.2a0
pyobjc-framework-InterfaceBuilderKit==2.3.2a0
pyobjc-framework-LatentSemanticMapping==2.3.2a0
pyobjc-framework-LaunchServices==2.3.2a0
pyobjc-framework-Message==2.3.2a0
pyobjc-framework-OpenDirectory==2.3.2a0
pyobjc-framework-PreferencePanes==2.3.2a0
pyobjc-framework-PubSub==2.3.2a0
pyobjc-framework-QTKit==2.3.2a0
pyobjc-framework-Quartz==2.3.2a0
pyobjc-framework-ScreenSaver==2.3.2a0
pyobjc-framework-ScriptingBridge==2.3.2a0
pyobjc-framework-SearchKit==2.3.2a0
pyobjc-framework-ServerNotification==2.3.2a0
pyobjc-framework-ServiceManagement==2.3.2a0
pyobjc-framework-SyncServices==2.3.2a0
pyobjc-framework-SystemConfiguration==2.3.2a0
pyobjc-framework-WebKit==2.3.2a0
pyobjc-framework-XgridFoundation==2.3.2a0
python-dateutil==1.5
virtualenv==1.7.1.2
wsgiref==0.1.2
xattr==0.6.1
zope.interface==3.5.1
次に、myprojectというフォルダを作って、envと言う名前の仮想環境を作成します。
$ mkdir myproject
$ cd myproject
$ virtualenv env
New python executable in env/bin/python
Installing setuptools............done.
envという仮想環境をアクティブにします。
$  . env/bin/activate
あるいは
$ source env/bin/activate
シェルの前に括弧付きで仮想環境名が表示されれば、仮想環境に入っています。
(env)$ 
仮想環境を抜け出すためには、deactivateコマンドを使います。
(env)$ deactivate
仮想環境の状態で、Pythonの環境を確認します。
(env)$ pip freeze
wsgiref==0.1.2
別のPython環境に入っていることが分かります。 Flaskをインストールします。
(env)$ pip install flask
インストール後、Pythonの環境を確認します。
(env)$ pip freeze
Flask==0.9
Jinja2==2.6
Werkzeug==0.8.3
wsgiref==0.1.2
Flaskは、2つのライブラリの依存しているので、その2つのライブラリも一緒にインストールされています。「Jinja2(ジンジャ2)」というテンプレートシステムと、Werkzeug(ウェルクツォィグ)というWSGIツールキットです。


Flaskのインストールが出来たので、チュートリアルの内容を行います。
http://a2c.bitbucket.org/flask/tutorial/index.html

完全なソースは、下記からダウンロードできるので、まず動作確認からしたい人は、そのソースを基に動作を確認できます。
https://github.com/mitsuhiko/flask/tree/master/examples/flaskr/

ダウンロードしたファイルを解凍して、flaskrフォルダの中に入ります。
まずデータベースの作成と初期化を行います。
(env)$ python
>>> from flaskr import init_db
>>> init_db()
あとは、flaskr.pyを起動すれば、動作確認が出来ます。
(env)$ python flaskr.py
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader
http://127.0.0.1:5000/にアクセスをすると、トップページが表示されます。ログインのリンクと、まだ記事が何も無い旨のメッセージが表示されます。


ソースに記載されているログイン情報で、ログインします。


ログインできたメッセージ表示と、タイトルと内容を投稿するフォームが表示されます。


タイトルと内容を記載して、「Share」ボタンを押します。


投稿した記事が表示されます。


ソースの内容については、下記のページにて詳しく説明されています。特に迷うこともなく、分かりやすい説明で、その通り動作しました。
http://a2c.bitbucket.org/flask/tutorial/index.html

Djangoのようなフルスタックのフレームワークではないので、覚えることが少なく、気軽にウェブアプリケーションが作れそうなフレームワークだと思います。

2012/09/08

Amazonインスタントストアにて、Python関連書籍のまとめページを作成しました。



Amazonアソシエイトには、Amazon内の商品を使って、簡単に自分だけの商品ページを作成する機能があります。その機能の中の「インスタントストア」を使って、Pythonに関連する書籍だけのページを作成しました。ウィザードに従って、作成するだけなので、特別な知識は必要なく作れます。

プログラミング Python関連書籍
http://astore.amazon.co.jp/ariki4160-22


 インスタントストアは、簡単に作成できますが、カスタマイズできる要素は少ないです。 もっとカスタマイズがしたいのであれば、Product Advertising API を使えば、完全オリジナルの見栄えを作成することができます。

2011/10/23

Djangoの画像管理プラグインアプリdjango-photologueを試してみました。



django-photologueは、画像管理に必要な機能が、おおよそ用意されているプラグインです。Djangoなので、この画像管理の機能を自分のアプリ用にカスタマイズして利用することも可能です。

django-photologue
A customizable plug-in photo management application for the Django web framework.
http://code.google.com/p/django-photologue/


上記のサイトよりソースをダウンロードしてインストールします。さくらのVPSサーバにインストールします。インストール方法は、READMEに従いました。ソースフォルダの中で、以下のコマンドでインストールします。
$ python setup.py install
インストールを確認します。
$ python
Python 2.4.3 (#1, May  5 2011, 16:39:10)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-50)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> import photologue
>>> photologue.VERSION
(2, 2)
setting.pyに設定を追加していきます。
INSTALLED_APPS に photologue を追加します。
    INSTALLED_APPS = (
         # ...other installed applications,
         'photologue',
    )
django-photologue は、PIL(Python Imaging Library)を利用するので、インストールします。PIL(Python Imaging Library)のインストールの前に、libjpeg-develをインストールしておきます。
$ sudo yum install libjpeg-devel
PIL(Python Imaging Library)の1.1.7をソースからインストールします。
$ wget http://effbot.org/downloads/Imaging-1.1.7.tar.gz
$ cd Imaging-1.1.7
$ python setup.py build
$ sudo python setup.py install
PIL(Python Imaging Library)インストールを確認します。
$ python selftest.py
--------------------------------------------------------------------
PIL 1.1.7 TEST SUMMARY
--------------------------------------------------------------------
Python modules loaded from ./PIL
Binary modules loaded from /usr/lib64/python2.4/site-packages/PIL
--------------------------------------------------------------------
*** PIL CORE support not installed
*** TKINTER support not installed
--- JPEG support ok
--- ZLIB (PNG/ZIP) support ok
*** FREETYPE2 support not installed
*** LITTLECMS support not installed
--------------------------------------------------------------------
Running selftest:
--- 57 tests passed.
syncdbを行います。
$ python manage.py syncdb
/usr/lib/python2.4/site-packages/django/db/__init__.py:60: DeprecationWarning: Short names for ENGINE in database configurations are deprecated. Prepend default.ENGINE with 'django.db.backends.'
  DeprecationWarning
Creating tables ...
Creating table photologue_gallery_photos
Creating table photologue_gallery
Creating table photologue_galleryupload
Creating table photologue_photo
Creating table photologue_photoeffect
Creating table photologue_watermark
Creating table photologue_photosize
Installing custom SQL ...
Installing indexes ...
No fixtures found.
Photologueを初期化します。サムネール画像等のサイズ指定、エフェクトの初期指定を行います。ここで行った設定は、後でも変更可能です。

This command initiates the Photologue initialization routine and prompts you to create a number of default photo sizes and effects. This command takes no arguments.
$ python manage.py plinit
urls.pyにphotologueを追加します。
    # urls.py:
    urlpatterns += patterns('',
        (r'^admin/(.*)', admin.site.root),
        (r'^photologue/', include('photologue.urls')),
    )
ソースに含まれているphotologue用のtemplatesを配置します。
    myproject/
        myapp/
            ...
        templates/
            photologue/
                ...
Djangoを起動して、管理画面にアクセスします。

管理画面に「photologue」の項目が追加されています。


Gallery uploadsを使うと、サーバにアップしたい複数の画像を一度にアップできます。アップしたい複数の画像をフォルダにまとめて、zipに圧縮します。そのzipをアップすることで、画像ギャラリーが自動で作成されます。


画像ギャラリーが作成されました。


Photoには、それぞれの画像がサムネール付きで登録されています。


管理画面でプレビューを確認できます。


メインに表示される画像サイズ、サムネールの画像のサイズ、管理画面に表示するサムネール画像のサイズは、変更可能です。これ以外の画像サイズの設定項目を作ることも可能です。



画像にエフェクトを掛けることも、管理画面からの操作で可能です。メインの画像に「Display Reflection」のエフェクトを掛けた様子です。


エフェクトは、photo effectの画面で追加、変更、削除が可能です。


上記の画像には、ウォーターマークを設定しています。ウォーターマーク用のpngを用意して、Watermarksにアップすることによって、ウォーターマークの管理、運用が可能です。

2011/10/15

Amazon Web Service(AWS)には、1年間、無料で使用できる範囲があるので、その範囲内の使用を試してみました。



Amazon Web Service(AWS)には、1年間、無料で使用できる範囲があるので、その範囲内の使用を試してみました。

AWS 無料使用範囲
http://aws.amazon.com/jp/free/


上記のページで、次のように説明されています。
AWS の新規お客様のクラウド使用開始のお役に立てるよう、AWS は無料使用範囲を提供しています。AWS の新規のお客様は、Amazon EC2 マイクロインスタンスを1年間無料でご利用いただけると共に、Amazon S3、Amazon Elastic Block Store、Amazon Elastic Load Balancing、および AWS データ転送の無料使用範囲もご活用いただけます。AWS 無料使用範囲は、新しいアプリケーションの起動、既存アプリケーションのクラウドでのテスト、または単なる AWS 実地経験など、クラウドで実行するどのようなものにもご利用いただけます。
Amazon EC2であれば、Linux マイクロインスタンス使用(613 MB メモリと、32ビットと 64ビットプラットフォームサポート)750時間 毎月継続的に実行するのに十分な時間が無料で使用できます。

まず最初に、AWSのサインアップをします。クレジットカードが必要です。本人確認のため、電話も掛かってきます。

登録が完了すると、自動返信メール「Welcome to Amazon Web Services」が届きます。その後「AWS Unified Registration Sign-Up Confirmation」が届くと、AWS Management Consoleへのアクセスが可能になります。

Amazon EC2の無料範囲の使用をしてみます。

設定の仕方については、下記のページを参照して進めました。

Get Started with EC2
http://docs.amazonwebservices.com/AWSEC2/latest/GettingStartedGuide/

Amazon EC2の画面にて、まずインスタンスを作成します。
「Launch Instance」のボタンをクリック


どのOSをインストールするかを選びます。お勧めの星のついている「Basic 64-bit Amazon Linux」にしてみます。


インストールのタイプは、無料使用範囲の「Micro」を選びます。


その他のインスタンスのオプションはデフォルトのままにしました。


暗号キーを作成して、ダウンロードしておきます。


セキュリティもデフォルトのままで設定しました。デフォルトでは、SSHのみ設定されています。

インスタンスが作成されました。


次に作成したインスタンスに接続します。

PeTTYgenを起動して、先ほどダウンロードしたキーを読み込みます。「鍵のパスフレーズ」を設定した後、「秘密鍵の保存」をします。


次にPuTTYを起動して、ホスト名(またはIPアドレス)にAmazon EC2のインスタンスページに表示されている「Public DNS」の内容を設定します。


「接続」---「SSH」---「認証」の「認証のためのプライベートキーファイル」に先ほど用意した「秘密鍵」を指定します。


接続します。ユーザー名は「ec2-user」でログインします。「鍵のパスフレーズ」を尋ねられますので、先ほど設定した「鍵のパスフレーズ」を入れます。ログインできました。pythonのバージョンを確認しました。

2011/10/09

さくらVPSにMongoDBとPyMongoインストールして、動作を確認しました。



PyCon JP 2011の開催報告のUstreamで、「PythonとMongoDBでWEB開発 / Web」を拝見しました。MongoDBが面白そうなので、MongoDBのPythonのドライバ、PyMongoを一緒にインストールして、動作を確認してみました。

PythonとMongoDBでWEB開発 / Web
http://www.ustream.tv/recorded/16898050


これも、さくらVPSで試しています。
スキーマレスのデータベース「MongoDB」って面白そうです。

MongoDBのインストール
$ sudo vi /etc/yum.repos.d/10gen.repo 

[10gen]

name=10gen Repository
baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64
gpgcheck=0

$ sudo yum install mongo-10gen* --enablerepo=10gen
Installed:
  mongo-10gen.x86_64 0:2.0.0-mongodb_1
  mongo-10gen-server.x86_64 0:2.0.0-mongodb_1
関連するファイル、ディレクトリは下記のように設定されます。
設定ファイル:/etc/mongod.conf
ログファイル:/var/log/mongo/mongod.log
データディレクトリ:/var/lib/mongo/
起動は起動スクリプトでOK
$ sudo /etc/init.d/mongod start
Starting mongod: all output going to: /var/log/mongo/mongod.log
forked process: 31800
           [  OK  ]
PyMongoのインストール

pipを使ってインストールします。pip自体が入っていないので、pipをまずインストールします。その後、pipでPyMongoをインストールします。
$ sudo easy_install pip
$ sudo pip install pymongo
PyMongoを使って、MongoDBを操作してみます
$ python
Python 2.4.3 (#1, May  5 2011, 16:39:10)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-50)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pymongo

コネクションを作成して
>>>con = Connection('localhost', 27017)

コネクションからvivoデータベースを取得
>>>db = con.vivo

vivoデータベースからbarコレクションを取得
>>>col = db.bar
事前に、データベースのスキーマ作ることも、データベースを作成することもなしに、データベースに接続出来ました。

辞書を渡してやることで、データの登録が出来ます。
>>>col.insert({'ariki' : 2011})
ObjectId('4e91a2648eacfc7c8c000001')
>>> col.insert({'fujii' : 2010,'suwa' : 2009})
ObjectId('4e91a2d98eacfc7c8c000002')
>>> print col.find_one()
{u'_id': ObjectId('4e91a2648eacfc7c8c000001'), u'ariki': 2011}
>>> for data in col.find():
...     print data
...
{u'_id': ObjectId('4e91a2648eacfc7c8c000001'), u'ariki': 2011}
{u'fujii': 2010, u'_id': ObjectId('4e91a2d98eacfc7c8c000002'), u'suwa': 2009}
>>>
データの更新をします。
>>>data = col.find_one({'ariki' : 2011})
>>>data['ariki'] = 2008
>>>col.save(data)
>>>for data in col.find():
...     print data
...     
{u'_id': ObjectId('4e91a2648eacfc7c8c000001'), u'ariki': 2008}
{u'fujii': 2010, u'_id': ObjectId('4e91a2d98eacfc7c8c000002'), u'suwa': 2009}
>>>
データを削除します。
>>>col.remove({'ariki' : 2008})
>>> for data in col.find():
...     print data
...
{u'fujii': 2010, u'_id': ObjectId('4e91a2d98eacfc7c8c000002'), u'suwa': 2009}
>>>
データディレクトリの /var/lib/mongo/ に、下記のファイルが出来ていることを確認しました。
vivo.ns
vivo.0
vivo.1

Django Google Maps API v3 and Static Maps API helper appのサンプルを試してみました。



さくらVPSのDjango1.3にdjango-gmapi(Django Google Maps API v3 and Static Maps API helper app)のインストールをして、サンプルアプリの動作を確認しました。


Django Google Maps API v3 and Static Maps API helper app
http://django-gmapi.googlecode.com/

django-gmapi-1.0.1 のダウンロードとインストール。
sample_project のダウンロードとDjangoのプロジェクトフォルダへの設置をします。

サンプルプロジェクトの「gmapitest」でdjangoを起動します。
下記のエラーが表示されます。
SyntaxError at /

invalid syntax (maps.py, line 129)

Request Method:  GET
Request URL:  http://hogehost:8000/
Django Version:  1.3
Exception Type:  SyntaxError
Exception Value:  

invalid syntax (maps.py, line 129)

Exception Location:  /path/to/django/gmapitest/myapp/views.py in ?, line 3
調べてみると、現在のPython 2.4.3では動かないようです。
Python 2.6.7を別にインストールします。

別にインストールをしたPython 2.6.7で再度djangoをインストールします。
インストールの確認をします。
$ python2.6.7
Python 2.6.7 (r267:88850, Oct  8 2011, 17:43:16)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.VERSION
(1, 3, 0, 'final', 0)
django-gmapi-1.0.1 をPython 2.6.7でインストールした後
再度、サンプルプロジェクトの「gmapitest」で、Python 2.6.7を使ってdjangoを起動します。
Google mapの表示を確認できました。


地図表示のポイントとなるのは、/path/to/django/gmapitest/myapp/views.py になります。この中のindexの内容を基に修正、機能を作っていけばよさそうです。
# -*- coding: utf-8 -*-
from django import forms
from django.shortcuts import render_to_response
from gmapi import maps
from gmapi.forms.widgets import GoogleMap


class MapForm(forms.Form):
    map = forms.Field(widget=GoogleMap(attrs={'width':900, 'height':600}))


def index(request):
    gmap = maps.Map(opts = {
        'center': maps.LatLng(34.687428,133.916473),
        'mapTypeId': maps.MapTypeId.ROADMAP,
        'zoom': 13,
        'mapTypeControlOptions': {
             'style': maps.MapTypeControlStyle.DROPDOWN_MENU
        },
    })
    marker = maps.Marker(opts = {
        'map': gmap,
        'position': maps.LatLng(34.687428,133.916473),
    })
    maps.event.addListener(marker, 'mouseover', 'myobj.markerOver')
    maps.event.addListener(marker, 'mouseout', 'myobj.markerOut')
    info = maps.InfoWindow({
        'content': 'Hello! 岡山の地図',
        'disableAutoPan': True
    })
    info.open(gmap, marker)
    context = {'form': MapForm(initial={'map': gmap})}
    return render_to_response('index.html', context)

2011/10/06

さくらVPSにDjango1.3(PostgerSQL使用)をインストールしました。開発用のサーバでCSSが有効になるまでの手順の続きです。



前回の続きです。DjangoアプリPollのチュートリアルの内容で、開発用サーバでCSSが有効になる方法のまとめです。


はじめての Django アプリ作成、その 3
http://djangoproject.jp/doc/ja/1.0/intro/tutorial03.html

公開用のインタフェース、ビュー(view) の作成をする際、サンプルのままだと、CSSの効いていない画面しか表示されません。


CSSの効いた画面を表示したいと思った時、どうすればよいか分かりませんでした。実際に公開する際は、静的なファイルはApacheで提供するようになりますが、開発用のサーバで、CSSを有効にするには、どうすればいいのでしょうか。

Googleで色々と調べて、下記の設定で、開発用サーバでCSSが有効になることが分かりました。


/path/to/django/mysite/setting.py の該当部分
MEDIA_ROOT = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'media')
MEDIA_URL = '/m/'
/path/to/django/mysite/urls.py
from django.conf.urls.defaults import *
from mysite import settings

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',

    (r'^polls/$', 'mysite.polls.views.index'),
    (r'^polls/(?P\d+)/$', 'mysite.polls.views.detail'),
    (r'^polls/(?P\d+)/results/$', 'mysite.polls.views.results'),
    (r'^polls/(?P\d+)/vote/$', 'mysite.polls.views.vote'),

    url(r'^admin/', include(admin.site.urls)),
)

if settings.DEBUG:
    urlpatterns += patterns('',
        (r'^m/(?P.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}),
    )
/path/to/django/mysite/templates/polls/index.html


2011/10/04

さくらVPSにDjango1.3(PostgerSQL使用)をインストールしました。開発用のサーバでCSSが有効になるまでの手順をまとめておきます。



さくらVPSにて、Django1.3で開発サーバを起動し、アプリを作成する際、テンプレートからCSSが参照できませんでした。しばらく調べて解決したので、備忘録として記録しておきます。


さくらのVPSに入っているPythonは、2.4.3です。
Python 2.4.3 (#1, May  5 2011, 16:39:10)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-50)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Django1.3をインストールします。
$ wget http://www.djangoproject.com/download/1.3/tarball/
$ tar vxzf Django-1.3.tgz
$ cd Django-1.3
$ sudo python setup.py install
Django1.3がインストールされたことを確認します。
Python 2.4.3 (#1, May  5 2011, 16:39:10)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-50)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.VIRSION
(1, 3, 0, 'final', 0)
PostGreSQLをインストールします。
# yum install postgresql-server postgresql postgresql-libs postgresql-devel
postgresql8.1.23-1がインストールされました。 PostgreSQL関係の操作をpostgresユーザで行います。
{user} su -
{root} su - postgres
{postgres}
データベースを初期化します。
{postgres}$ initdb --encoding=UTF8 --no-locale
・・・
Success. You can now start the database server using:
    postmaster -D /var/lib/pgsql/data
or
    pg_ctl -D /var/lib/pgsql/data -l logfile start
サーバプロセスの起動と停止のコマンドです。
{postgres}$ pg_ctl -w start  // 起動処理終了後プロンプトに戻る
{postgres}$ pg_ctl stop    // サーバプロセスの停止
ユーザーを作成します。
{postgres}$ createuser hoge
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) y
Shall the new role be allowed to create more new roles? (y/n) n
CREATE ROLE
psqlでデータベースに接続します。
$ psql postgres hoge
Welcome to psql 8.1.23, the PostgreSQL interactive terminal.
Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help with psql commands
       \g or terminate with semicolon to execute query
       \q to quit
postgres=>
$ psql template1 hoge
Welcome to psql 8.1.23, the PostgreSQL interactive terminal.
Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help with psql commands
       \g or terminate with semicolon to execute query
       \q to quit
template1=>
終了するには、\qコマンドを実行します。
PostGreSQLに用意されているテンプレート用のデータベース「template1」を使います。

パスワードを設定します。
$ psql
Welcome to psql 8.1.23, the PostgreSQL interactive terminal.
Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help with psql commands
       \g or terminate with semicolon to execute query
       \q to quit
postgres=# alter user hoge password '**********';
ALTER ROLE
postgres=# \q
PostgerSQLとPythonの接続をします。
psycopg2のインストール
$ wget http://initd.org/psycopg/tarballs/PSYCOPG-2-4/psycopg2-2.4.2.tar.gz
$ tar vxzf psycopg2-2.4.2.tar.gz
$ cd psycopg2-2.4.2
# python setup.py build
# python setup.py install
はじめての Django アプリ作成の内容を元にアプリを作成していきます。

はじめての Django アプリ作成
http://djangoproject.jp/doc/ja/1.0/intro/tutorial01.html#intro-tutorial01

Djangoのプロジェクトを作成します。
/path/to/django/にて
$ django-admin.py startproject mysite
Djangoアプリケーションを作成します。
/path/to/django/mysite/にて
$ python manage.py startapp polls
重要な設定箇所だけを記載します。
/path/to/django/mysite/settings.pyを編集
DATABASES = {
    'default': {
        'ENGINE': 'postgresql_psycopg2',
        'NAME': 'template1',
        'USER': 'hoge',
        'PASSWORD': '*************',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',
    'django.contrib.admindocs',
    'mysite.polls'
)
/path/to/django/mysite/polls/models.pyを編集
from django.db import models
class Poll(models.Model):
    question = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice = models.CharField(max_length=200)
    votes = models.IntegerField()
admin サイトの有効化
/path/to/django/mysite/urls.pyを編集
from django.conf.urls.defaults import patterns, include, url
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
    url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
    url(r'^admin/', include(admin.site.urls)),
)
モデルテーブルをデータベース上に作成します。
/path/to/django/mysiteにて
$ python manage.py syncdb
Djangoを起動
/path/to/django/mysiteにて
$ python manage.py runserver 0.0.0.0:8080
管理画面へのアクセスを確認。

次回へ続く。

2011/09/14

Pythonでunixtimeを日付のフォーマットに変換する方法



Pythonのインタープリター上で、unixtimeの1190385670を変換します。
>>> import time
>>> time.ctime(1190385670)
'Fri Sep 21 23:41:10 2007'

変換だけがしたいなら、下記のサイトで処理するのが簡単。

unixtime変換ツール
http://tools.fictionlife.com/unixtime/index.html?unixtime=1190385670

2011/07/04

地名と共に画像をアップすると、マップ上にコメント付きで画像を配置できるGoogle App Engineアプリを試してみました



地名と共に画像をアップすると、マップ上にコメント付きで画像を配置できるGoogle App Engineアプリを試してみました。

Google App Engine と Maps で作ったWebアプリのソースを公開されている方がいました。面白そうなソースなので、設置をして試してみました。このような動くサンプルを見せていただけると、とても良くGoogle App Engineの構造が分かります。感謝!

自分のアカウントでアップしてみました。Pythonの部分は1点のみ修正をしています。見栄えについては変更しました。

My Photo in Google Maps on GAE
http://vivo-map.appspot.com/maps


Pythonのソースの修正した部分について。ローカルで動作テストをすると、ジオコーディングは正常に動作するのですが、サーバ上に上げると、緯度経度の取得が出来ない現象がありました。サーバ上で何が起きているのかをステータスコードで確認をすると、「google.maps.GeocoderStatus.OVER_QUERY_LIMIT」というステータスを取得していることが分かりました。これは、「使用制限回数の上限を超えたことを示している」そうです。ローカルからのリクエストは問題なく、サーバからのリクエストで問題が起きるのが、よく分かりません。

ジオコーディングをしている下記の部分を変更してみました。
class PostPage(webapp.RequestHandler):

  def getGeometory(self, place):
    url = u"http://maps.google.com/maps/api/geocode/xml?address=%s&sensor=false" % urllib.quote(place.encode('utf8'))
    result = urlfetch.fetch(url)
    lat = 35.6895060 # 東京
    lng = 139.6917010 # 東京
    if result.status_code == 200:
      doc = xml.dom.minidom.parseString(result.content)
      if doc.getElementsByTagName('status').item(0).childNodes[0].data == "OK":
        lat = doc.getElementsByTagName('lat').item(0).childNodes[0].data
        lng = doc.getElementsByTagName('lng').item(0).childNodes[0].data

    return db.GeoPt(lat, lng)

Pythonのgooglemapsモジュールを使った検索にしてみました。内部では同じことをしているのだから、ダメかと思いましたが、サーバ上でも動作をしました。
# モジュールを追加
from googlemaps import GoogleMaps

class PostPage(webapp.RequestHandler):

  def getGeometory(self, place):
    gmaps = GoogleMaps()
    address = place.encode('utf8')
    lat, lng = gmaps.address_to_latlng(address)  

    return db.GeoPt(lat, lng)

でたらめな場所(地名/駅名/〒)の情報でも、何かしらの場所の緯度経度に変換をして地図にポイントをします。

下記の記事のソースを使わせていただきました。

Google App Engine と Maps でWebアプリ作ってみた
http://sakitake4.blogspot.com/2011/04/google-app-engine-maps-web.html?spref=tw


ありがとうございました。

2011/06/30

Google Map APIを使って、簡単に地名などから緯度、経度に変換が出来るPythonのモジュール「googlemaps」を使ってみました



Google Map APIを使って、簡単に geocoding や reverse geocoding が出来るPythonのモジュールがあります。

googlemaps 1.0.2
http://pypi.python.org/pypi/googlemaps/


ジオコーディングとは、住所や地名、駅名などの情報から、緯度・経度の座標値に変換する技術のことです。

住所から、緯度、経度への変換
> python
Python 2.5.2 [MSC v.1310 32 bit (Intel)] onwin32
>>> from googlemaps import GoogleMaps
>>> gmaps = GoogleMaps('API_Key')
>>> address = u'岡山市'
>>> lat, lng = gmaps.address_to_latlng(address)
>>> print lat, lng
34.6551456 133.9195019

サンプルでは、Google MapのAPIキーを使っての説明をしていますが、Google Maps API v3からは、APIキーは不要になったので、APIキーを渡さなくても操作できます。
>>> from googlemaps import GoogleMaps
>>> gmaps = GoogleMaps()
>>> address = u'岡山市'
>>> lat, lng = gmaps.address_to_latlng(address)
>>> print lat, lng
34.6551456 133.9195019

Linux上でも同様に動きます。

$ python
Python 2.6 [GCC 3.4.4 20050721 (Red Hat 3.4.4-2)] on linux2
>>> from googlemaps import GoogleMaps
>>> gmaps = GoogleMaps()
>>> address = u'岡山市'
>>> lat, lng = gmaps.address_to_latlng(address)
>>> print lat, lng
34.6551456 133.9195019

逆(緯度、経度から住所への変換)も可能です。
>>> destination = gmaps.latlng_to_address(34.6551456,133.9195019)
>>> print destination
岡山市役所

その近辺の検索

「秋葉原周辺のWifiスポットのある喫茶店」?の検索結果の最初のもの「」Japan town acupuncture & Oriental Medicine, Inc.」がヒット
>>> local = gmaps.local_search('cafes w. free power near akihabara')
>>> print local['responseData']['results'][0]['titleNoFormatting']
Japan town acupuncture & Oriental Medicine, Inc.
「Okayama」に変ると「Ryokan Misono Kurashiki」がヒット
>>> local = gmaps.local_search('cafes w. free power near Okayama')
>>> print local['responseData']['results'][0]['titleNoFormatting']
Ryokan Misono Kurashiki
「Okayamaの近くのschool」の検索結果の4番目のもの
>>> local = gmaps.local_search('school near Okayama')
>>> print local['responseData']['results'][3]['titleNoFormatting']
フラップアップゼミ
「岡山市」に変ると「鬼ヶ島宮ノ松原キャンプ場」がヒット
>>> local = gmaps.local_search(u'school near 岡山市')
>>> print local['responseData']['results'][3]['titleNoFormatting']
鬼ヶ島宮ノ松原キャンプ場
「岡山市の近くの学校」に変ると「KLCセミナー倉敷校」がヒット
>>> local = gmaps.local_search(u'学校 near 岡山市')
>>> print local['responseData']['results'][3]['titleNoFormatting']
KLCセミナー倉敷校

2011/06/26

「RSSの中に含まれる名詞の集計表を作成する」のRSSの読み込みの不具合解消しました。



「RSSの中に含まれる名詞の集計表を作成する」のRSSの読み込みの不具合解消しました。

mecab-pythonを使って、RSSの中に含まれる名詞の集計表を作るソースを試してみました。
http://vivo-design.blogspot.com/2011/06/mecab-pythonrss.html

の続きです。


Yahooの提供するRSSであれば、問題なく読めるのですが、それ以外は、「Failed to parse feed」となる点が解消しました。修正したソースで作成した集計表です。Yahoo以外のRSSを読み込んで、集計が出来ました。私が試行錯誤していた時に追記したものが、不要なだけでした。


修正したソース

analysis_rss_feed.py
本体
#coding:utf-8
import re
import csv
import MeCab
import feedparser
from extractKeyword  import extractKeyword


#=====================
# ----- set URLs -----
#=====================

"""
# IT関連
URLs = ['http://headlines.yahoo.co.jp/rss/itmedia_ait.xml',
        'http://headlines.yahoo.co.jp/rss/bcn.xml',
        'http://headlines.yahoo.co.jp/rss/impress_c_sci.xml',
        'http://headlines.yahoo.co.jp/rss/rbb.xml',
        'http://headlines.yahoo.co.jp/rss/scan.xml']

# 雑誌関連
URLs = ['http://zasshi.news.yahoo.co.jp/rss/sspa.xml',
        'http://zasshi.news.yahoo.co.jp/rss/sasahi.xml',
        'http://zasshi.news.yahoo.co.jp/rss/shincho.xml',
        'http://zasshi.news.yahoo.co.jp/rss/sbunshun.xml',
        'http://zasshi.news.yahoo.co.jp/rss/chuokou.xml',
        'http://zasshi.news.yahoo.co.jp/rss/playboyz.xml',
        'http://zasshi.news.yahoo.co.jp/rss/gendaibiz.xml',
        'http://zasshi.news.yahoo.co.jp/rss/bshunju.xml']
"""
# 一般のニュース関連
URLs = ['http://rss.asahi.com/f/asahi_newsheadlines',
        'http://rss.rssad.jp/rss/mainichi/flash.rss',
        'http://rss.yomiuri.co.jp/rss/yol/topstories',
        'http://sankei.jp.msn.com/rss/news/points.xml',
        'http://www.jiji.com/rss/ranking.rdf',
        'http://www.kyodo.co.jp/feed/']


#===============================
# ----- category selection -----
#===============================
class_num = 0
word_classes = [u'名詞',u'動詞',u'形容詞',u'副詞',u'助詞',u'助動詞']
word_class = word_classes[class_num]

#==============================
# ----- defined functions -----
#==============================
def getwordcounts(url):
    """RSSフィードのタイトルと、単語の頻度のディクショナリを返す"""
    # フィードをパースする
    d = feedparser.parse(url)
    wc={}
    
    # すべてのエントリをループする
    # RSSの種類によって,記事に相当する部分が異なることに対応する
    for e in d.entries:

        # 追加したこの部分が不要でした
        # if 'summary_detail' in e: summary = e.summary_detail
        if 'summary' in e: summary = e.summary

        elif 'description' in e: summary = e.description
        else: summary = e.link

        # 単語のリストを取り出す
        words = getwords(e.title+' '+summary)
        # extractKeywordを用いるため,単語を連結してテキスト化
        txt = ''
        for i in range(len(words)):
            txt += words[i]
        words_sub = extractKeyword(txt,word_class)
        
        for word in words_sub:
            # dict型のkeyにwordがなければ,value=0として新設
            wc.setdefault(word,0)
            wc[word] += 1
    return d.feed.title,wc

def getwords(html):
    """すべてのHTMLタグを取り除き,単語リストを返す"""

    # htmlから'<[^>]+>'を''に置き換える
    txt = re.compile(r'<[^>]+>').sub('',html)
    tagger = MeCab.Tagger('-Owakati')
    txt = txt.encode('utf-8')
    return tagger.parse(str(txt)).split(' ')
    # return tagger.parse(txt.encode('utf-8')).split(' ')
    # return tagger.parse(txt.encode('euc-jp')).split(' ')

def main():
    apcount = {}
    wordcounts = {}
    feedlist = [line for line in URLs]
    for feedurl in feedlist:
        wc = {}
        try:
            title,wc_ini = getwordcounts(feedurl)

            for w,bc in wc_ini.items():

                # csvで日本語を表示させるため,Shift_JIS化
                # この部分はエンコードをかけると、どの文字コード
                # でも、エラーになりました。
                # wc.setdefault(w.encode('sjis'),bc)

                wc.setdefault(w,bc)

            wordcounts[title] = wc

            # for word,count in wc.items():
            for word,count in wc.items():
                apcount.setdefault(word,0)
                if count>1:
                    # それぞれの単語が出現するフィードの数を数える
                    apcount[word]+=1
        except:
            # urlが認識できなければエラー表示
            print 'Failed to parse feed %s' % feedurl
    
    # でたらめな単語が存在するフィードがあることや
    # 一般的な単語の除外を考慮し,単語の出現率に敷居値を設定する
    wordlist = []
    for w,bc in apcount.items():
        frac = float(bc)/len(feedlist)
        if frac>0.1 and frac<0.5: wordlist.append(w)
    
    # csvへの書き込み用リストを生成
    header = ['Feed_name']+wordlist
    rows = []
    for blog,wc in wordcounts.items():
        # row = [blog.encode('sjis')]
        row = [blog.encode('utf-8')]
        for word in wordlist:
            if word in wc: row.append(wc[word])
            else: row.append('0')
        rows.append(row)
    
    # csvへ書き込み
    csv_writer = csv.writer(open('rss_data.csv','w'),lineterminator='\n')
    csv_writer.writerow(header)
    csv_writer.writerows(rows)    
    
if __name__ == "__main__":
    main()
修正したのは、下記の2箇所
# 一般のニュース関連
URLs = ['http://rss.asahi.com/f/asahi_newsheadlines',
        'http://rss.rssad.jp/rss/mainichi/flash.rss',
        'http://rss.yomiuri.co.jp/rss/yol/topstories',
        'http://sankei.jp.msn.com/rss/news/points.xml',
        'http://www.jiji.com/rss/ranking.rdf',
        'http://www.kyodo.co.jp/feed/']

途中略

# 追加したこの部分が不要でした
# if 'summary_detail' in e: summary = e.summary_detail
if 'summary' in e: summary = e.summary

この部分が無くても、どのRSSでも正常に動作しました。

mecab-pythonを使って、RSSの中に含まれる名詞の集計表を作るソースを試してみました。



mecab-pythonを使って、何か面白いサンプルは無いかなと探していたら、下記の記事を見つけました。

RSSフィードから単一品詞の単語別出現数を取得してみた.
http://d.hatena.ne.jp/r_e10/20110420/1303326055


面白そうなので、試してみました。今回は、自宅のサーバのPython2.6(fedoraの古いバージョン)で試します。

出来た集計表が下記のものです。

RSSの中に含まれる名詞を取り出し、その名詞の出現数を集計したCSVの表を作成するものです。


しかし、なかなか上手くいきませんでした。起こったエラーをまとめます。

エラーその1 そもそもRSSのフィードが読めない

「Failed to parse feed」というエラーが出ます。Yahooの提供するものであれば、問題なく読めるのですが、それ以外は、「Failed to parse feed」となります。各feedの構成を調べれば、解決するのかもしれませんが、現時点では、YahooのRSSだけで動作させてみます。

エラーその2 UnicodeEncodeError

色々なUnicode系のエラーが出ました。色々な対応を試しすぎて、結局、どれが有効なのか、はっきりとは分かっていません。まず、下記のサイトの記述を参考に、mecab-ipadic(辞書)のインストールをし直しました。

Python による日本語自然言語処理
http://nltk.googlecode.com/svn/trunk/doc/book-jp/ch12.html
MeCabの文字コードはデフォルトのEUC-JPからUTF-8に変更
Unix環境においては、辞書をインストールする際に、

% ./configure --with-charset=utf8
% make
% su
# make install

ソースコードの修正

analysis_rss_feed.py
本体のファイル
#coding:utf-8
import re
import csv
import MeCab
import feedparser
from extractKeyword  import extractKeyword


#=====================
# ----- set URLs -----
#=====================

"""
# IT関連
URLs = ['http://headlines.yahoo.co.jp/rss/itmedia_ait.xml',
        'http://headlines.yahoo.co.jp/rss/bcn.xml',
        'http://headlines.yahoo.co.jp/rss/impress_c_sci.xml',
        'http://headlines.yahoo.co.jp/rss/rbb.xml',
        'http://headlines.yahoo.co.jp/rss/scan.xml']
"""

# 雑誌関連
URLs = ['http://zasshi.news.yahoo.co.jp/rss/sspa.xml',
        'http://zasshi.news.yahoo.co.jp/rss/sasahi.xml',
        'http://zasshi.news.yahoo.co.jp/rss/shincho.xml',
        'http://zasshi.news.yahoo.co.jp/rss/sbunshun.xml',
        'http://zasshi.news.yahoo.co.jp/rss/chuokou.xml',
        'http://zasshi.news.yahoo.co.jp/rss/playboyz.xml',
        'http://zasshi.news.yahoo.co.jp/rss/gendaibiz.xml',
        'http://zasshi.news.yahoo.co.jp/rss/bshunju.xml']

#===============================
# ----- category selection -----
#===============================
class_num = 0
word_classes = [u'名詞',u'動詞',u'形容詞',u'副詞',u'助詞',u'助動詞']
word_class = word_classes[class_num]

#==============================
# ----- defined functions -----
#==============================
def getwordcounts(url):
    """RSSフィードのタイトルと、単語の頻度のディクショナリを返す"""
    # フィードをパースする
    d = feedparser.parse(url)
    wc={}
    
    # すべてのエントリをループする
    # RSSの種類によって,記事に相当する部分が異なることに対応する
    for e in d.entries:
        if 'summary' in e: summary = e.summary

        #  add for yahoo.co.jp_rss
        if 'summary_detail' in e: summary = e.summary_detail

        elif 'description' in e: summary = e.description
        else: summary = e.link

        # 単語のリストを取り出す
        words = getwords(e.title+' '+summary)
        # extractKeywordを用いるため,単語を連結してテキスト化
        txt = ''
        for i in range(len(words)):
            txt += words[i]
        words_sub = extractKeyword(txt,word_class)
        
        for word in words_sub:
            # dict型のkeyにwordがなければ,value=0として新設
            wc.setdefault(word,0)
            wc[word] += 1
    return d.feed.title,wc

def getwords(html):
    """すべてのHTMLタグを取り除き,単語リストを返す"""

    # htmlから'<[^>]+>'を''に置き換える
    txt = re.compile(r'<[^>]+>').sub('',html)
    tagger = MeCab.Tagger('-Owakati')
    txt = txt.encode('utf-8')
    return tagger.parse(str(txt)).split(' ')

def main():
    apcount = {}
    wordcounts = {}
    feedlist = [line for line in URLs]
    for feedurl in feedlist:
        wc = {}
        try:
            title,wc_ini = getwordcounts(feedurl)

            for w,bc in wc_ini.items():

                # csvで日本語を表示させるため,Shift_JIS化
                # この部分はエンコードをかけると、どの文字コード
                # でも、エラーになりました。
                # wc.setdefault(w.encode('sjis'),bc)

                wc.setdefault(w,bc)

            wordcounts[title] = wc

            # for word,count in wc.items():
            for word,count in wc.items():
                apcount.setdefault(word,0)
                if count>1:
                    # それぞれの単語が出現するフィードの数を数える
                    apcount[word]+=1
        except:
            # urlが認識できなければエラー表示
            print 'Failed to parse feed %s' % feedurl
    
    # でたらめな単語が存在するフィードがあることや
    # 一般的な単語の除外を考慮し,単語の出現率に敷居値を設定する
    wordlist = []
    for w,bc in apcount.items():
        frac = float(bc)/len(feedlist)
        if frac>0.1 and frac<0.5: wordlist.append(w)
    
    # csvへの書き込み用リストを生成
    header = ['Feed_name']+wordlist
    rows = []
    for blog,wc in wordcounts.items():
        # row = [blog.encode('sjis')]
        row = [blog.encode('utf-8')]
        for word in wordlist:
            if word in wc: row.append(wc[word])
            else: row.append('0')
        rows.append(row)
    
    # csvへ書き込み
    csv_writer = csv.writer(open('rss_data.csv','w'),lineterminator='\n')
    csv_writer.writerow(header)
    csv_writer.writerows(rows)    
    
if __name__ == "__main__":
    main()
修正したのは、下記の3箇所
#  add for yahoo.co.jp_rss
if 'summary_detail' in e: summary = e.summary_detail

# wc.setdefault(w.encode('sjis'),bc)
wc.setdefault(w,bc)

# row = [blog.encode('sjis')]
row = [blog.encode('utf-8')]
extractKeyword.py テキストを形態素解析して、名詞のみのリストを返すためファイル
#coding:utf-8
import MeCab

#=====================
# ----- set text -----
#=====================
test_txt = u'PythonからMeCabの形態素解析機能を使ってみました。'

#===============================
# ----- category selection -----
#===============================
class_num = 0
word_classes = [u'名詞',u'動詞',u'形容詞',u'副詞',u'助詞',u'助動詞']
word_class = word_classes[class_num]

#==============================
# ----- defined functions -----
#==============================

def extractKeyword(text,word_class):
    """textを形態素解析して、名詞のみのリストを返す"""
    
    tagger = MeCab.Tagger('-Ochasen')
    # node = tagger.parseToNode(text.encode('utf-8'))
    node = tagger.parseToNode(text)
    keywords = []
    while node:
        # if node.feature.split(",")[0] == word_class:
        if node.feature.split(",")[0] == word_class.encode('utf-8'):
            keywords.append(node.surface)
        node = node.next
    return keywords

def main():
    keywords = extractKeyword(test_txt)
    for w in keywords:
        print w,

if __name__ == "__main__":
    main()
修正したのは、下記の2箇所
# node = tagger.parseToNode(text.encode('utf-8'))
node = tagger.parseToNode(text)

# if node.feature.split(",")[0] == word_class:
if node.feature.split(",")[0] == word_class.encode('utf-8'):

人気の投稿 (過去 30 日間)