アットホームなクラブイベント

こんばんわ。

9/25(土)、立ち飲みパーティをやるんで、
もしよかったら遊びに来てください。
お友達をお誘いの上、みんなで楽しく飲みましょう。


■日時
9月25日(土)
18:00開場
18:30開始
〜20:30終了
※約2時間を予定

■場所:〒141-0021 東京都品川区 上大崎2-11-10 Fujiwara building 5F
■最寄駅:各線「目黒駅」徒歩3分
■場所URL:
http://maps.google.co.jp/maps/place?cid=6878790382731382159

■会費:
男性:4000円
女性:2000円

・ドリンク飲み放題ビールワイングラスとっくり(おちょこ付き)、食事食べ放題ハンバーガーおにぎりさくらん
※但し、限りあり


■参加申込期日:
9月22日(水)

■定員:50名
(男性25名、女性25名)
※定員50名となり次第締め切りさせていただきます。

前回のご参加頂いた方は
年齢も職業も様々で21〜34歳くらいの社会人〜学生さんなど
40名以上のご参加を頂きました☆



ご連絡をお待ちしております。

Amazon Product Advertising APIを使って、「Flex × Python」からAmazonの商品データの検索するアプリを作ってみた

こんにちわ!兄貴とTwitter上でマジレス兄弟喧嘩をしたRyoAbeです。


今回は何を作ったかと言いますと表題にある通り、
AmazonAPI(Product Advertising API)を使った商品データの検索出来るアプリです。
(ほんとただそれだけですw)
かるーくAPI触ってどんな感じか探るだけだったんですが、
意外に手こずっちゃって、おかげでけっこう勉強になったんで
Blogにまとめがてら載せることにしました!


◎そもそもProduct Advertising APIってなんぞやって話から

Product Advertising API は、Amazon の商品情報や関連コンテンツをプログラムを通してアクセスできるサービスを提供することで、Web 開発者の皆様が、ご自分の Web サイトでAmazon の商品を紹介することによる紹介料の獲得を可能とします。

そもそもは、ブログとかのアフィリエイトとかに使うことが目的っぽい。
WordPressプラグインAmazonLinkとかもこのAPIを使ってるんだって。
まぁー、勉強ついでとはいえ今回の僕のAPIの使い方ちょっと間違ってますw

◎まずはアカウントの作成

まー上のリンク先(Product Advertising API)にも書いてあるんですが、アカウントを作らなきゃAPIは使えないんですわ。
アカウント作成が完了すると、"AWS アクセスキー ID"*1ってのを貰えて、それを使ってAPIにアクセスするわけです。
アカウントの作成手順については、下記のリンクを参考にして下さい
Product Advertising API アカウント作成 ヘルプ


◎アカウントも作成できたし、作り始めるか

今回使う言語はPythonです。
作り始めるって言ってもPythonのモジュールにPyAWSってのがあるんで、
大してプログラムは組まないんですが。。(そのつもりだった。。)

◎よーし、PyAWS使ってみよー

対話モードでとりあえず使ってみる。
(このときは、のん気なもんだった。。)

$ python

# まずはモジュールのインポート
>>> from pyaws import ecs

# アカウントを作成後にもらったキーをセット
>>> ecs.setLicenseKey('自分のアクセスキー')

# アクセス先は日本なので、setLocaleにjpをセット
>>> ecs.setLocale('jp')

# いざ、検索!! 試しに"Python"って調べてみる
>>> books = ecs.ItemSearch('Python', SearchIndex='Books', ResponseGroup='Images')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Python/2.6/site-packages/pyaws/ecs.py", line 339, in ItemSearch
    return pagedIterator(XMLItemSearch, argv, "ItemPage", 'Items', plugins)
  File "/Library/Python/2.6/site-packages/pyaws/ecs.py", line 216, in __init__
    dom = self.__search(** self.__arguments)
  File "/Library/Python/2.6/site-packages/pyaws/ecs.py", line 348, in XMLItemSearch
    return query(buildRequest(argv))
  File "/Library/Python/2.6/site-packages/pyaws/ecs.py", line 174, in query
    e = buildException(errors)
  File "/Library/Python/2.6/site-packages/pyaws/ecs.py", line 159, in buildException
    e = globals()[ class_name ](msg)
KeyError: u'ingParameter'

うげぇーーーー、エラー発生。。
APIから返却値(XML)にErrorが返ってきてるため、
PyAWS内のqueryメソッドでエラーが起きてしまっている様子。。
なんでErrorで返ってくるんだぁーーー!?!?!?!?

早速、調べてみた!!





◎なにやら、去年(2009年08月15日)APIの仕様が変わったらしい

前までは"Access Key ID"のみでAPIの使用は出来たんだが、認証方法が変わったらしい。

名称変更にともない、Product Advertising API にリクエストを送信いただく都度、認証のための電子署名を含めていただくことが必要になります。この変更は、2009年5月11日より3ヶ月の間の移行期間の後、2009年8月15日には、Product Advertising API へ送信されるリクエストは全て認証されることとなり、認証されない場合、リクエストは処理されなくなります。Product Advertising API へのリクエストに署名認証を含めるための簡単な方法については、こちらの開発者向けガイドをご覧ください。
Amazon アソシエイト Web サービスの名称変更および署名認証についてのお知らせ より

PyAMFのリリースを見てみると、2007/04/08で終わってる。。
そりゃ対応してないわ。。
どうしよっかな。。。





◎じゃあ、自分で作っちゃえ。

対応してないなら、自分で作っちゃえ。
PyAWSを直接修正するってのも手ではあったが、一から作った方が勉強になるしねb


# だったら初めから一から作れって話だし、
# 探せば新しい仕様に対応したモジュールがあるような気もするけど無視!


■プログラム概要

■処理フローの説明

 ----------------------------
 | Product Advertising API  |
 ----------------------------
    ↑②③         ↓④
 =========================================
 | [Python]サーバサイド(main.py, aws.py) |
 =========================================
    ↑①           ↓⑤
 ======================================
 | [Flex]クライアントサイド(main.swf) |
 ======================================
  1. テキストボックスに入力した検索文字を自作のPythonプログラムにAMF*3通信で投げる
  2. Product Advertising APIへのREST URLをアクセスキーやシークレットアクセスキーを元に生成する*4
    1. パラメータ(アクセスキーや検索したワード、タイムスタンプなど)をURLエンコード
    2. シークレットアクセスキーをHMAC-SHA256形式でハッシュ化
    3. 最後にBase64エンコードをし、REST URLの生成完了
  3. 生成したREST URLからリクエストを投げる
  4. urlopenXMLを取得する
  5. XMLをクライアントサイド(Flex)に返し、DataGridに表示する(バインディング*5使用)

■実際に返ってくるXMLはこんな感じ

  • keyword
  • ResponseGroup
    • ItemAttributes
  • SearchIndex
    • Books

で検索した場合のXML↓↓

<ItemSearchResponse>
	<OperationRequest>
		<HTTPHeaders>
			<Header Name="UserAgent" Value="Python-urllib/1.17 AppEngine-Google; (+http://code.google.com/appengine)"/>
		</HTTPHeaders>
		<RequestId>11111111-2222-3333-4444-555555555555</RequestId>
		<Arguments>
			<Argument Name="Operation" Value="ItemSearch"/>
			<Argument Name="Service" Value="AWSECommerceService"/>
			<Argument Name="Signature" Value="1111111111111111111111111111"/>
			<Argument Name="Version" Value="2009-01-06"/>
			<Argument Name="Keywords" Value="python"/>
			<Argument Name="AWSAccessKeyId" Value="AAAAAAAAAAAAAAAAAAAAAAA"/>
			<Argument Name="Timestamp" Value="2010-04-16T05:17:19Z"/>
			<Argument Name="ResponseGroup" Value="ItemAttributes"/>
			<Argument Name="SearchIndex" Value="Books"/>
		</Arguments>
	<RequestProcessingTime>0.1920510000000000</RequestProcessingTime>
	</OperationRequest>

<Items>
	<Request>
		<IsValid>True</IsValid>
		<ItemSearchRequest>
			<Condition>New</Condition>
			<DeliveryMethod>Ship</DeliveryMethod>
			<Keywords>python</Keywords>
			<MerchantId>Amazon</MerchantId>
			<ResponseGroup>ItemAttributes</ResponseGroup>
			<ReviewSort>-SubmissionDate</ReviewSort>
			<SearchIndex>Books</SearchIndex>
		</ItemSearchRequest>
	</Request>
	<TotalResults>62</TotalResults>
	<TotalPages>7</TotalPages>
	<Item>
		<ASIN>4797353953</ASIN>
		<DetailPageURL>http://www.amazon.co.jp/%E3%81%BF%E3%82%93%E3%81%AA%E3%81%AEPython-%E6%94%B9%E8%A8%82%E7%89%88-%E6%9F%B4%E7%94%B0-%E6%B7%B3/dp/4797353953%3FSubscriptionId%3DAKIAJD5ODXRC4SZWODZA%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D4797353953
		</DetailPageURL>
		<ItemLinks>
			<ItemLink>
				<Description>Add To Wishlist</Description>
			<URL>http://www.amazon.co.jp/gp/registry/wishlist/add-item.html%3Fasin.0%3D4797353953%26SubscriptionId%3DAKIAJD5ODXRC4SZWODZA%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D5143%26creativeASIN%3D4797353953
			</URL>
			</ItemLink>
			<ItemLink>
				<Description>Tell A Friend</Description>
				<URL>http://www.amazon.co.jp/gp/pdp/taf/4797353953%3FSubscriptionId%3DAKIAJD5ODXRC4SZWODZA%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D5143%26creativeASIN%3D4797353953
				</URL>
			</ItemLink>
			<ItemLink>
				<Description>All Customer Reviews</Description>
				<URL>http://www.amazon.co.jp/review/product/4797353953%3FSubscriptionId%3DAKIAJD5ODXRC4SZWODZA%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D5143%26creativeASIN%3D4797353953
				</URL>
			</ItemLink>
			<ItemLink>
				<Description>All Offers</Description>
				<URL>http://www.amazon.co.jp/gp/offer-listing/4797353953%3FSubscriptionId%3DAKIAJD5ODXRC4SZWODZA%26tag%3Dws%26linkCode%3Dxm2%26camp%3D2025%26creative%3D5143%26creativeASIN%3D4797353953
				</URL>
			</ItemLink>
		</ItemLinks>
	
		<ItemAttributes>
		<Author>柴田 淳</Author>
		<Binding>単行本</Binding>
		<EAN>9784797353952</EAN>
		<Edition>改訂版</Edition>
		<ISBN>4797353953</ISBN>
		<Label>ソフトバンククリエイティブ</Label>
		<ListPrice>
			<Amount>2940</Amount>
			<CurrencyCode>JPY</CurrencyCode>
			<FormattedPrice>¥ 2,940</FormattedPrice>
		</ListPrice>
		<Manufacturer>ソフトバンククリエイティブ</Manufacturer>
		<NumberOfPages>484</NumberOfPages>
		<PackageDimensions>
		<Height Units="hundredths-inches">110</Height>
		<Length Units="hundredths-inches">827</Length>
		<Weight Units="hundredths-pounds">150</Weight>
		<Width Units="hundredths-inches">591</Width>
		</PackageDimensions>
		<ProductGroup>Book</ProductGroup>
		<ProductTypeName>ABIS_BOOK</ProductTypeName>
		<PublicationDate>2009-04-11</PublicationDate>
		<Publisher>ソフトバンククリエイティブ</Publisher>
		<Studio>ソフトバンククリエイティブ</Studio>
		<Title>みんなのPython 改訂版</Title>
		</ItemAttributes>
	</Item>
		:
		:
	
</Items>
</ItemSearchResponse>

詳しくはこちら

■ソース載っけます

  • main.swf(クライアントサイド)
    • main.pyへAMFで検索文字を渡す
    • main.pyからXMLを受け取る
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*">
	<mx:Script>
		<![CDATA[
			import mx.utils.ObjectUtil;
			import mx.messaging.AbstractConsumer;
			import mx.collections.ArrayCollection;
			import mx.collections.XMLListCollection;
			import mx.controls.Alert;
			import flash.net.*;

			[Bindable]
			public var xml_data:XML;
			[Bindable]
			public var xElementCollection:XMLListCollection;
			
			// 検索時のプルダウン用
			[Bindable]
			public var search_indexes:ArrayCollection = new ArrayCollection(
				[ {label:"全て", data:"All"},
				  {label:"和書", data:"Books"},
				  {label:"洋書", data:"ForeignBooks"},
				  {label:"DVD", data:"DVD"},
				  {label:"曲名", data:"MusicTracks"},
				  {label:"ソフトウェア", data:"Software"},
				  {label:"ゲーム", data:"VideoGames"}
				]);


                        // データ送信
			private function doRequest():void{
				// レスポンダーを作成
				var responder:Responder = new Responder(onSuccess, onFault);
				// コネクションの作成
				var connection:NetConnection = new NetConnection();

				// コネクト先のPythonのURL
				connection.connect("http://searchamazondatarabe.appspot.com/");
				connection.objectEncoding = ObjectEncoding.AMF3;

				// サーバサイドに渡す値
				var data:Object = new Object();
				data['search_word'] = nameText.text;
				data['search_index'] = myComboBox.selectedItem.data;

				// サーバサイド(main.py)のitemSearchメソッドを呼ぶ
				connection.call("Service.itemSearch", responder, data);
			}
			
			// データ取得成功
			private function onSuccess(e:*):void{
				// 受け取ったXMLをバインドする
				xml_data =  new XML(e.toString());
				xElementCollection = new XMLListCollection(xml_data.Items.Item.ItemAttributes);
			}

			// データ取得失敗
			private function onFault(e:*):void{
				Alert.show("通信失敗");
			}

			// 実ページ遷移用 add 2010/4/26
			private function onItemClicked(e:*):void{
				var i:int =  e.rowIndex;
				var urls:String = xml_data.Items.Item[i].DetailPageURL;
				var url:URLRequest = new URLRequest(urls);
				navigateToURL(url, "_blank");
			}
			
		]]>
	</mx:Script>
	<mx:TextInput x="10" y="43" id="nameText" enter="doRequest()" />
	<mx:Button label="検索" id="idTest" click="doRequest()" x="286" y="43"/>
	<mx:Label x="10" y="10" text="Amazonでデータを検索" color="#FFFFFF" fontSize="16"/>

	<mx:DataGrid right="10" left="10" top="84" bottom="30" itemClick="onItemClicked(event)"
				 id="myDataGrid" dataProvider="{xElementCollection}">
		<mx:columns >
			<mx:DataGridColumn headerText="タイトル" dataField="Title"/>
			<mx:DataGridColumn headerText="著者名" dataField="Author"/>
			<mx:DataGridColumn headerText="商品カテゴリー" dataField="ProductGroup"/>
			<mx:DataGridColumn headerText="Binding" dataField="Binding"/>
			<mx:DataGridColumn headerText="ページ数" dataField="NumberOfPages"/>
			<mx:DataGridColumn headerText="発行年月日" dataField="PublicationDate"/>
			<mx:DataGridColumn headerText="発売元" dataField="Publisher"/>
		</mx:columns>
	</mx:DataGrid>
	<mx:ComboBox x="178" y="43" width="100" dataProvider="{search_indexes}" id="myComboBox"/>

</mx:Application>

  • main.py(サーバサイド)
    1. main.swf(クライアントサイド)からのリクエストを受ける
    2. アクセスキー、シークレットアクセスキーをセット
    3. aws.pyへ検索ワードを渡す
    4. aws.pyからXMLを受け取る
    5. クライアントサイド(Flex)へXMLを渡す
# -*- coding: utf-8 -*-
import logging
import wsgiref.handlers
from pyamf.remoting.gateway.wsgi import WSGIGateway
from aws import AWS


def search(data):
	aws = AWS(select_access_key_id='自分のシークレットアクセスキー',
	          aws_access_key_id='自分のアクセスキー')
	result =  aws.doItemSearch(keyword=data['search_word'],
	          search_index=data['search_index'])

	return result

def main():

	services = {
		'Service.itemSearch': search,
	}

	gateway = WSGIGateway(services, logger=logging, debug=True)
	wsgiref.handlers.CGIHandler().run(gateway)	

if __name__ == '__main__':
	main()
  • aws.py(こいつがProduct Advertising APIとの連携をする)
    1. main.pyから受け取った値(アクセスキー、シークレットアクセスキー)をコンストラクタでセット
    2. doItemSearchがmain.pyから呼ばれ、検索ワード(keyword)を受け取る
    3. パラメータのURLエンコードや暗号化をしてREST URLを生成
    4. XMLを取得し、main.pyへ返す
# -*- coding: utf-8 -*-


from time import strftime, gmtime # GMTIME取得用
import urllib        # URLエンコード
import hmac, hashlib # HMAC-SHA256の算出用
import base64        # Base64エンコード用
import types		 # 型を調べるときに使用

AWS_DOMAIN = "ecs.amazonaws.jp"

class AWS:

	def __init__(self, **params):

		# パラメータセット
		self.__setParams(params)


	def __setParams(self, params):
		""" パラメータセット """

		self.setSelectAccessKey( params.get("select_access_key_id") )
		self.setAWSAccessKeyId( params.get("aws_access_key_id") )
		self.setResponseGroup( params.get("response_group") or "ItemAttributes")
		self.setTimeStamp( params.get("timestamp") or strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) )
		self.setOperation( params.get("operation") or "ItemSearch")
		self.setSearchIndex( params.get("search_index") or "Books" )
		self.setService( params.get("service") or "AWSECommerceService")
		self.setVersion( params.get("version") or "2009-01-06")
		self.setKeyWord( params.get("keyword") )


	def doItemSearch(self, **params):
		""" 検索 """

		# パラメータセット
		if params.get("keyword")      : self.setKeyWord( params.get("keyword") )
		if params.get("operation")    : self.setOperation( params.get("operation") )
		if params.get("search_index") : self.setSearchIndex( params.get("search_index") )
	    
		# パラメータをURLエンコード
		enc_params = self.__doParamEncode()
		# Signature作成
		signature = self.__createSignature(enc_params)
		# リクエストURLを生成
		request_url = 'http://' + AWS_DOMAIN + "/onca/xml?" + enc_params + "&Signature=" + signature
		# urlopen
		result = urllib.urlopen(request_url)

		return result.read()


	def __doParamEncode(self):
		""" URLのパラメータをURLエンコード """

		params = { 
				    "Service"        : self.getService(),
				    "AWSAccessKeyId" : self.getAWSAccessKeyId(),
				    "Operation"      : self.getOperation(),
				    "SearchIndex"    : self.getSearchIndex(),
				    "ResponseGroup"  : self.getResponseGroup(),
				    "Version"        : self.getVersion(),
				    "Timestamp"      : self.getTimeStamp(),
				    "Keywords"       : self.getKeyWord()
				  }

		# ソート
		sorted(params.iteritems())

		# URLエンコードした文字列を格納するタプル
		enc_param_list = []

		# パラメータをURLエンコード
		for key in sorted(params.keys()):
			if type(params[key]) == types.UnicodeType: 
				params[key] = params[key].encode("UTF-8")
			enc_param = "%s=%s" % (key, urllib.quote(params[key]) )
			enc_param_list.append( enc_param )


		return "&".join(enc_param_list)




	def __createSignature(self, enc_params):
		""" Signatureの作成 """

		# 署名用文字列の作成
		message = "\n".join(["GET", AWS_DOMAIN, "/onca/xml", enc_params])	
		# Secret Access KeyをHMAC-SHA256形式でハッシュ化
		hmac_digest = hmac.new(self.getSelectAccessKey(), message, hashlib.sha256).digest()
		# Base64エンコード
		base64_encoded = base64.b64encode(hmac_digest)
		# URLエンコード(2回目)
		signature = urllib.quote(base64_encoded)

		return signature		


	# KeyWord
	def setKeyWord(self, keyword):
		self.__keyword = keyword
		
	def getKeyWord(self):
		return self.__keyword

	# TimeStamp
	def setTimeStamp(self, timestamp):
		self.__timestamp = timestamp

	def getTimeStamp(self):
		return self.__timestamp

	# Service
	def setService(self, service):
		self.__service = service

	def getService(self):
		return self.__service

	# AWSAccessKeyId
	def setAWSAccessKeyId(self, aws_access_key_id):
		self.__aws_access_key_id = aws_access_key_id

	def getAWSAccessKeyId(self):
		return self.__aws_access_key_id

	# SelectAccessKey
	def setSelectAccessKey(self, select_access_key_id):
		self.__select_access_key_id = select_access_key_id

	def getSelectAccessKey(self):
		return self.__select_access_key_id

		
	# Operation
	def setOperation(self, operation):
		self.__operation = operation

	def getOperation(self):
		return self.__operation

	# SearchIndex
	def setSearchIndex(self, search_index):
		self.__search_index = search_index

	def getSearchIndex(self):
		return self.__search_index

	# ResponseGroup
	def setResponseGroup(self, response_group):
		self.__response_group = response_group

	def getResponseGroup(self):
		return self.__response_group

	# Version
	def setVersion(self, version):
		self.__version = version

	def getVersion(self):
		return self.__version
	

◎完成したものがこれ

GAEに載っけました。
http://searchamazondatarabe.appspot.com/main.swf

◎残タスク

  • XMLの返却値でAutherタグ(著者名)が複数ある場合に、まんまAutherタグが表示されちゃう

◎まだまだやりたいこと

  • デフォルトで10件しばりになってるんで、それを外す
  • 画像とかも表示したりしたい
  • 実ページへの遷移のクリックイベントの追加

◎作ってみて思ったこと

  • Product Advertising APIをちゃんとした使い方しよw
    • 今回は本当の用途と全然違う使い方をしてしまったw アフィリエイトでもやってみっか。
  • ってかPythonって楽しいね
    • なんかもっと綺麗な書き方(そもそも作りがおかしいとか)もあると思うけど、書いてて楽しかった

参考サイト

今日はこんなとこで!

※追記
・実ページへの遷移のクリックイベントの追加 add 2010/4/26

*1:AWS アクセスキー IDとは、AWSの開発者アカウントに関連付けられた識別子です。AWSで提供されているすべてのWebサービスにアクセスするときに、AWS アクセスキー IDが必要になります。

*2:クライアント(Flex)側でAPIに直接アクセスするって手もあったけどね。Python触りたかったしw

*3:AMFについてはこちらを参照

*4:詳細はこちら(認証(Timestamp及びSignature)@AjaxTower)もしくは、こっち(Product Advertising API)

*5:詳しくはこちら(Flex の Binding 具体例と内部事情の覗き見CommentsAdd Star@くって煮ブログ)を参照

Mac OS X 10.6.2(Snow Leopard)のGoogle App Engine for Python(ローカル)で、urllibが使えない...??

GAE for Python(ローカル)にてurllibが動かない!

  • 11.4 urllib -- URL による任意のリソースへのアクセス

http://www.python.jp/doc/2.4/lib/module-urllib.html
PythonをGAE、経由させなければ動くのに。。なぜ・・?

めちゃめちゃハマって、なんとかフィックス出来たので、
メモ代わり解決策を載せます。

  • なにも変哲もないただのurlをopenするプログラム
import urllib
def main():
	url = "http://www.google.com/"
	result = urllib.urlopen(url)
	print result.read()

if __name__ == '__main__':
	main()

しかし、こんなエラーが↓↓↓↓↓↓

  • エラーログ
Traceback (most recent call last):
  File "/Library/WebServer/CGI-Executables/python/google_appengine/google/appengine/tools/dev_appserver.py", line 3177, in _HandleRequest
    self._Dispatch(dispatcher, self.rfile, outfile, env_dict)
  File "/Library/WebServer/CGI-Executables/python/google_appengine/google/appengine/tools/dev_appserver.py", line 3120, in _Dispatch
    base_env_dict=env_dict)
  File "/Library/WebServer/CGI-Executables/python/google_appengine/google/appengine/tools/dev_appserver.py", line 515, in Dispatch
    base_env_dict=base_env_dict)
  File "/Library/WebServer/CGI-Executables/python/google_appengine/google/appengine/tools/dev_appserver.py", line 2379, in Dispatch
    self._module_dict)
  File "/Library/WebServer/CGI-Executables/python/google_appengine/google/appengine/tools/dev_appserver.py", line 2289, in ExecuteCGI
    reset_modules = exec_script(handler_path, cgi_path, hook)
  File "/Library/WebServer/CGI-Executables/python/google_appengine/google/appengine/tools/dev_appserver.py", line 2185, in ExecuteOrImportScript
    exec module_code in script_module.__dict__
  File "/Library/WebServer/CGI-Executables/python/google_appengine/aws/main.py", line 10, in <module>
    main()
  File "/Library/WebServer/CGI-Executables/python/google_appengine/aws/main.py", line 5, in main
    result = urllib.urlopen(url)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/urllib.py", line 82, in urlopen
    opener = FancyURLopener()
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/urllib.py", line 611, in __init__
    URLopener.__init__(self, *args, **kwargs)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/urllib.py", line 129, in __init__
    proxies = getproxies()
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/urllib.py", line 1558, in getproxies
    return getproxies_environment() or getproxies_macosx_sysconf()
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/urllib.py", line 1448, in getproxies_macosx_sysconf
    from ctypes import cdll
  File "/Library/WebServer/CGI-Executables/python/google_appengine/google/appengine/tools/dev_appserver.py", line 1264, in Decorate
    return func(self, *args, **kwargs)
  File "/Library/WebServer/CGI-Executables/python/google_appengine/google/appengine/tools/dev_appserver.py", line 1914, in load_module
    return self.FindAndLoadModule(submodule, fullname, search_path)
  File "/Library/WebServer/CGI-Executables/python/google_appengine/google/appengine/tools/dev_appserver.py", line 1264, in Decorate
    return func(self, *args, **kwargs)
  File "/Library/WebServer/CGI-Executables/python/google_appengine/google/appengine/tools/dev_appserver.py", line 1816, in FindAndLoadModule
    description)
  File "/Library/WebServer/CGI-Executables/python/google_appengine/google/appengine/tools/dev_appserver.py", line 1264, in Decorate
    return func(self, *args, **kwargs)
  File "/Library/WebServer/CGI-Executables/python/google_appengine/google/appengine/tools/dev_appserver.py", line 1767, in LoadModuleRestricted
    description)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/ctypes/__init__.py", line 10, in <module>
    from _ctypes import Union, Structure, Array
ImportError: No module named _ctypes
  • 早速、調べてみよ!!

ほうほう。同じ悩みの人発見!
Import Error: Failed to import ctypes to load dll on windows@Google Code
どうやらGAEではpython 2.6のurllibが動かないらしい。。
しかも「Mac OS X 10.6.2」限定っぽい?
(原因は現在も調査中。。)

  • 対策!!

ってことは、Python 2.5から動かせばいいってことね。
Pythonのパスを2.6から2.5に変更してもいいけど、
他のところで影響が出たらイヤなので、
GAEの起動時のPythonを2.5から起動するようにしよー

    • 通常
 $ ./dev_appserver.py src/
    • 今回
 $ /usr/bin/python2.5 ./dev_appserver.py src/

参考URL : Feed parser error - _ctypes - broke in 1.2.5 and Snow Leopard
これで動いたよー
あーよかったよかった。ハマったハマった。。
さー作業の続きやろw

Flex3からMacのWebカメラ使ってみた

猛烈にMacのWebカムを使いたくなったので、とりあえず実装してみた。
そしてハマった。。w


どこでハマったかって?

  • CameraクラスgetCameraメソッドでWebカメラが認識できない?

いえいえ。

  • VideoDisplayコンポーネントへのCameraオブジェクトの受け渡しがうまくいかない?

ノーノー


答えは、ブラウザ上のFlashPlayerの設定です。。
まさか、こんなところで。。
FlashPlayerのカメラの設定では、下記の三つのプルダウンが出てくるので、
任意のWebカメラのデバイスを設定してあげなけらばならない。

本来MacのWebカムを使う場合は、[USB Video Class Video]なのにも関わらず、
僕はデフォルト設定の[DV ビデオ]のまま、コーディングを続けてしまったことにより、
いつまでたっても表示がされなかったのです。。
うーなんと恥ずかしい。。



とりあえず、ソースのっけておきます。
ソース自体は、全然簡単ですけどね^^;

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
	<mx:VideoDisplay x="10" y="10" width="480" height="360" id="display_camera"/>
	<mx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import flash.media.Camera;
			
			public function init():void{
				var camera:Camera = Camera.getCamera();
				if( camera ){
					// 描画の設定
					camera.setMode(640, 480, 10, true);
					// VideoDisplayコンポーネントへアタッチ
					display_camera.attachCamera(camera);
				}else{
					Alert.show("Not Found Camera");
				}
			}
		]]>
	</mx:Script>
	
</mx:Application>

今日は、こんなところでw

python-twitter使ってみた

猛烈にPythonからTwitterへつぶやきたくなったので、やってみた。
まー何をいまさらって気もするが、
python-twitter」というクライアントアプリがあるそうなので、それを使ったよ。

■インストール

python-twitter-0.6.tar.gz

$ cd python-twitter-0.6
$ sudo python setup.py install
$ python
>>> import twitter

エラーがでなければOK

◎simplejson 2.0.9

json形式で通信するため、必要らしい

$ cd simplejson-2.0.9
$ python setup.py install
$ python
>>> import simplejson

エラーがでなければOK

■つぶやいてみよ〜

$ python
>>> import twitter
>>> api = twitter.Api("ユーザー名", "パスワード")
>>> status = api.PostUpdate("Hello Twitter from python-twitter")

おっ出来た出来た。
次は、フォロアーとかも取得できるやつ作ろー

「Python4PHPer 第2回講習会」に行ってきた〜

お父さん、お母さん、最近twitterばっかやりすぎてブログの更新をおろそかにしていたことをお許し下さい。
ってなわけで、遅くなりましたが先月に行ってきた「Python4PHPer 第2回講習会」について書きたいと思います。

■主催者側の(桑田さん)目的

Pythonユーザを増やして、Pythonの仕事を増やしたいというのが目的らしいw

■内容

Pythonの基本的な構文から多少高度な部分、GAEでのPythonの実装までを行った。
PHPでの書き方と比較しながらの説明だった。
実際に作ったものがこちら
http://rabepython.appspot.com/
詳しい内容については、@kazumeatさんが書いて下さってるのでそちらを参照下さい

■時間がハンパなく長い、そして桑田さんのタフさに驚き

朝の10時から夜の9時過ぎまで。。あれだけ話し続けて疲れた表情のかけらも出さない桑田さんに脱帽。。

■テキストのページ数がハンパない

パワポの資料で200ページ以上。そりゃ講習が丸一日かかるわけだわw

■参加費が激安

破格の500円。あれだけ濃い内容でこの参加料の安さはすごい。




今回の講習でPythonの言語としての面白みがとても分かりました。
また、一緒に受講した方々に刺激を受けもっと勉強しなきゃなと思った。
桑田さん、本当にお疲れ様でした。
そして、ありがとうございました!!
次回もまた参加させて頂きます。

FlexとRubyでrubyamfを使ってAMF通信

またしても前回に引き続き、AMF通信をします。w
今回はRubyです。
使用するモジュールはrubyamfです。
(ほかにもMidnightCoders WebORB for Railsというものもあるそうです。)

RubyAMF

  • まずはRailsプロジェクトの作成
$ rails hellorubyamf
$ cd hellorubyamf
(Railsが入ってない方は、「$gem install rails」でインストールしてね)
  • インストール
$ ruby script/plugin install http://rubyamf.googlecode.com/svn/tags/current/rubyamf
  • Webサーバを起動し、ブラウザでチェック
$ ruby script/server

http://localhost:3000/rubyamf/gateway にアクセス!
正常にインストールされていれば、RubyAMFのロゴ画像が表示されます。

Rails

  • app/controllersに移動し、service_controller.rbを作成
$ vi service_controller.rb
class ServiceController < ApplicationController
        def returnName
                render :amf => params[0] + "さん、こんにちわ"
        end
end

Flex

今回も前回Flexのソースとほぼ一緒で、connection先が変わるだけ。

connection.connect("http://localhost:3000/rubyamf/gateway");

完全なソースは下記に記します

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*">
	<mx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import flash.net.*;

            // データ送信
			private function doRequest():void{
				// レスポンダーを作成
				var responder:Responder = new Responder(onSuccess, onFault);
				// コネクションの作成
				var connection:NetConnection = new NetConnection();
				
				// コネクト
				connection.connect("http://localhost:3000/rubyamf/gateway");

				connection.objectEncoding = ObjectEncoding.AMF3;

				// ServiceクラスのreturnNameメソッドを呼び出す
				connection.call("Service.returnName", responder, nameText.text);
			}
			
			// データ取得成功
			private function onSuccess(e:*):void{
				// 返却値をAlertで表示
				Alert.show(e.toString());
			}
			
			// データ取得失敗
			private function onFault(e:*):void{
				Alert.show("通信失敗");
			}
			
			
		]]>
	</mx:Script>
	<mx:TextInput x="10" y="36" id="nameText" />
	<mx:Button label="Submit" id="idTest" click="doRequest()" x="10" y="66"/>
	<mx:Label x="10" y="10" text="name" color="#FFFFFF"/>
</mx:Application>