バーコードの写メール送信でAmazonからデータ取得


jan2amazon@gmail.com

上記のメールアドレスにバーコード画像を送信することで、AMAZONからデータを取得するスクリプトを作ってみました。

(2009/01/19)現在とめています

環境準備

今回 Gmail を利用しましたので、アカウントを新規作成しました。
またアカウント設定にてPOPを利用可能にしておきます。

POPへの接続

今回は PHP 側のライブラリとして Zend_Mail を利用しました。
以下のコードでログインしています。

require_once 'Zend/Mail.php';
require_once 'Zend/Mail/Storage/Pop3.php';

// POPへの接続
$mail = new Zend_Mail_Storage_Pop3(array('host'     => 'pop.gmail.com',
                                         'ssl'      => 'SSL',
                                         'port'     => 995,
                                         'user'     => MAIL_USER_NAME,
                                         'password' => MAIL_USER_PASS));

// メールボックスのメール数表示
echo $mail->countMessages() . " messages found<br />\n";

ここの ssl は大文字じゃないとエラーになります。。。
送信部分は小文字みたいです。。。

メール取得処理

// 受信処理
$messageNum = 1;
foreach ($mail as $message) {
    echo "Mail from '{$message->from}'<br />\n";

	// 前回のファイルを消しておく
	@unlink( TEMP_FILE_NAME );

	// メッセージ取得
	$message = $mail->getMessage($messageNum);

	// マルチパートの個数取得
	$countParts = $message->countParts();

	// マルチパート分処理
	for( $i = 1 ; $i <= $countParts ; $i++ ){
		$part = $message->getPart($i);

		// jpegのみ処理します
		if( strtok($part->contentType, ';') == 'image/jpeg') {
			save_image( $part->getContent(), TEMP_FILE_NAME );
			break;
		}
	}

	// ファイルの存在チェック
	if( file_exists(TEMP_FILE_NAME) ){
		// JANコード解析
		$jan = image_to_jan(TEMP_FILE_NAME);
		if( $jan ){
			// amazonからデータ取得
			$data = get_amazon($jan);
			if( $data ){
				// メール送信
				preg_match("/<(.*)>/", $message->from, $matches);
				send_mail($data, $matches[1]);
			}
		}
	}

	$messageNum++;
}

メールアドレスだけ取得できないので、form から<>で囲まれているアドレスを利用しています。

画像保存

// 画像保存
function save_image( $image, $image_file ){
	$fp = fopen($image_file, 'w');
	$image = base64_decode($image);
	fwrite($fp, $image, 1024*1024);
	fclose($fp);
}

base64のみ対応です。

ZEBRA image を利用してJANコード取得

// ZEBRA image を利用してJANコード取得
function image_to_jan( $image_file ){
	$ret = getimagesize($image_file);
	$jan = "";
	if( isset( $ret[0] ) && 10 < $ret[0] ){
		$str = exec( "~/local/bin/zebraimg -q $image_file" );

		$data = split( ":", $str );
		if( isset( $data[0] ) && $data[0] == 'EAN-13' ){
			// パリティーコード付与
			$temp = $data[1];
			$jan0 = 0 + substr( $temp, 0, 1 ) + substr( $temp, 2, 1 ) + substr( $temp, 4, 1 ) + substr( $temp, 6, 1 ) + substr( $temp, 8, 1 ) + substr( $temp, 10, 1 );
			$jan1 = 0 + substr( $temp, 1, 1 ) + substr( $temp, 3, 1 ) + substr( $temp, 5, 1 ) + substr( $temp, 7, 1 ) + substr( $temp, 9, 1 ) + substr( $temp, 11, 1 );
			$jan = ( 10 - ( ( $jan0 + ( $jan1 * 3 ) ) % 10 ) ) % 10;
			$jan = $temp . $jan;
			$str = $str . " => " . $jan;
		}

		return $jan;
	}
}

Webからアップするコードを流用。
http://akira.info/labs/zebra/

amazonからデータ取得

// amazonからデータ取得
function get_amazon( $isbn )
{
	$url  = "http://xml-jp.amznxslt.com/onca/xml?Service=AWSECommerceService&SubscriptionId=*****************&ContentType=text/html&Operation=ItemLookup&ResponseGroup=Large&IdType=ISBN&SearchIndex=Books&ItemId=$isbn";
	$response = get_page( $url, "UTF-8" );
	$xml = new SimpleXMLElement($response);

	// データがない場合には終了
	if( !$xml->Items->Item->ItemAttributes->EAN ){
		return;
	}

	$item['ISBN'] = $xml->Items->Item->ItemAttributes->EAN . "";

	$item['タイトル'] = $xml->Items->Item->ItemAttributes->Title . "";
	$item['作者'] = $xml->Items->Item->ItemAttributes->Author . "";
	$item['種類'] = $xml->Items->Item->ItemAttributes->Binding . "";
	$item['出版社'] = $xml->Items->Item->ItemAttributes->Publisher . "";

	$item['価格'] = $xml->Items->Item->ItemAttributes->ListPrice->FormattedPrice . "";
	$item['ページ数'] = $xml->Items->Item->ItemAttributes->NumberOfPages . "";

	$item['評価'] = $xml->Items->Item->CustomerReviews->AverageRating . "点(" . $xml->Items->Item->CustomerReviews->TotalReviews . '件)';

	if( isset( $xml->Items->Item->CustomerReviews->Review ) ){
		foreach( $xml->Items->Item->CustomerReviews->Review as $key => $data ){
			$preview = array();
			$preview['評価'] = $data->Rating . "";
			$preview['タイトル'] = $data->Summary . "";
			$preview['日付'] = $data->Date . "";
			$preview['内容'] = $data->Content . "";
	
			$item['レビュー'][] = $preview;
		}
	}

	$body = "";
	foreach( $item as $key => $data ){
		if( is_array( $data ) ){
			// 配列処理
			foreach( $data as $key2 => $data2 ){
				$body .= "\n";
				$body .= "----------------------\n";
				$body .= "(評価" . $data2['評価'] . ")" . $data2['タイトル'] . " " . $data2['日付'] ."\n\n";
				$body .= $data2['内容'] . "\n";
			}
		} else {
			// 通常
			$body .= $key . ":" . $data . "\n";
		}
	}

	$item['body'] = $body;

	return $item;
}

昔に利用したコードを流用して改造。

ページ取得

// ページ取得
function get_page( $url, $encode = "SJIS" ){
	$fp = fopen( $url , "r");
	// 接続に失敗した時の処理 
	if (!$fp) {
		return "";
	}
	
	$response = "";
	while (!feof($fp)){
	    $response .= fgets($fp, 4096);
	}
	
	fclose($fp);
	$response = @mb_convert_encoding( $response, "utf8", $encode );

	return $response;
}

流用したので利用していますが、もう少しスマートな取得方法でいいと思います。

メール送信

// メール送信
function send_mail( $book_info, $email ){
	require_once 'Zend/Mail.php';
	require_once 'Zend/Mail/Transport/Smtp.php';

	$config = array('ssl' => 'ssl',
		'auth' => 'login',
		'username' => MAIL_USER_NAME,
		'password' => MAIL_USER_PASS,
		'port' => 465);
	
	$transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com', $config);
	$mail = new Zend_Mail();

	$body = mb_convert_encoding( $book_info['body'], 'JIS', 'utf8' );
	$body=preg_replace("/<br>/iu","\r\n",$body);
	$body=preg_replace("/<br \/>/iu","\r\n",$body);
	$body=preg_replace("/<p>/iu","\r\n",$body);
	$mail->setBodyText($body, $charset='iso-2022-jp');

	$mail->setFrom('jan2amazon@gmail.com', 'jan2amazon');
	$mail->addTo( $email );

	$subject = mb_encode_mimeheader(mb_convert_encoding($book_info['タイトル'], 'JIS', 'utf8'), 'iso-2022-jp');
	$mail->setSubject($subject);

	$mail->send($transport);
}

長いタイトルの途中が文字化けするなど少し手を入れる必要があるコードです。
Zend_Mail はそれなりに利用できますが、細かいところを考えると自分で一から組んだ方が楽かもしれません。

たとえばタイトルが無いメールを受信した場合に $message->subject でアクセスしようとすると例外が発生します。
ちょっとした処理でも結構な行数になってしまいそうですね。

定期実行

今回は CORESERVER(XREA) を利用しているので、毎分 CRON で呼び出すようにしています。
結構エラーが発生してスクリプトが落ちている気がするのと、エラーチェックしていないのでメールが返信されない場合にはもう一度送信をしてください。

実際のサービスで利用する場合にはメールサーバーの受信アクションで連動してリアルタイムで起動した方がかっこいいと思います。
今回は最大1分近くラグがあるので、早くこないかなーって心配になります(笑)

(2009/01/19)現在とめています