Pythonで楽天市場の情報を取得して、画像付きで表示する

(Python 2.7.13, iTerm2 Build 3.1.3)

成果物

以下のようにランキングを取得し、画像付きで表示してくれるプログラム
f:id:teriyaki398:20171016120943j:plain

商品情報を取得するAPIは、やはりAmazonの "Product Advertising API" が有名だが、
これは非常に使いづらい上に、ブログの審査が非常に厳しい……

Rakuten Web Service が提供するAPIはとっても簡単に使えるので、そちらを利用する

規模ではAmazonに劣るかもしれないが、ランキングなどの情報は大差ないだろう

アプリIDの発行

webservice.rakuten.co.jp

  • アプリID発行から行う
    • アプリ名は適当でオッケー
    • ブログで紹介したいときはブログのURLでも貼っておけば良いと思う
  • アプリ情報の確認からちゃんと出来てるか確認する

PythonでジャンルIDを取得する

APIの使い方は、紹介ページに詳しく書いてあるので問題ないはず

例えばランキングを取得したいときは

https://app.rakuten.co.jp/services/api/IchibaItem/Ranking/20170628?[parameter]=[value]…

にリクエストを送れば良い
URLに含まれる日付はAPIのバージョンを表す

今回は requests と BeautifulSoup を使う
Requests の使い方 (Python Library) - Qiita
PythonとBeautiful Soupでスクレイピング - Qiita

指定したジャンルのランキングを取得するためには、ジャンルIDなるものが必要なので、
まずはジャンルIDを取得するAPIを使ってみる

# coding:utf-8

import requests
from bs4 import BeautifulSoup

URL = "https://app.rakuten.co.jp/services/api/IchibaGenre/Search/20140222"
APPLICATION_ID = [APPLICATION ID]
GENRE_ID = input()
    
payload = {
    "applicationId": APPLICATION_ID,
    "genreId": GENRE_ID,
    "format": "xml",  # 出力形式
    "genrePath": 0, # 0:出力結果に祖先ジャンルを含めない 1:含める
}

response = requests.get(URL, params = payload).text
soup = BeautifulSoup(response, "lxml")

# genreId, 名前, 階層の深さ のリストを作る
id_lis  = map(lambda x: x.text.encode("utf-8"), soup.findAll("genreid"))
name_lis    = map(lambda x: x.text.encode("utf-8"), soup.findAll("genrename"))
level_lis   = map(lambda x: int(x.text), soup.findAll("genrelevel"))

# 現在の階層を調べる
id_index        = id_lis.index(str(genre_id))
current_level   = level_lis[id_index]

# 現在の階層より下の階層にある[ID, 名前]のリストを作る
ans = [[i,j] for (i,j,k) in zip(id_lis, name_lis, level_lis) if k > current_level]

入力した番号の階層と、その一つ下の階層にあるIDを返してくれる
一番上のジャンルを表示するときは"0"を指定する

入力した番号の階層が表示されるのは煩わしいので、現在の階層より下のものだけを抽出している
他に取得したい情報があるときは payload = {} 部分に追記すれば良い

terminal上に画像を表示する

せっかくなのでランキング情報は画像付きで取得したい

Mac OS で使える iTerm2 は、アップデートにより画像も表示させられるようになった
【iTerm2】ターミナル上で画像を表示する方法 - Qiita

% imgcat [file name]

だけで画像を表示できる

プログラムから画像を表示するには、以下のようにすれば良い

import commands

print commands.getoutput("imgcat [file_name]")

ランキングを取得する

ランキング取得APIでは、画像のURIも取得できる
requests で画像をダウンロードし、imgcatで表示させてみよう

その他の処理はジャンルID取得とほぼ同じ

# coding: utf-8

import requests
import commands
from bs4 import BeautifulSoup
from StringIO import StringIO
from PIL import Image

URL = "https://app.rakuten.co.jp/services/api/IchibaItem/Ranking/20170628"
APPLICATION_ID = [APPLICATION ID]
GENRE_ID = [GENRE ID]

payload = {
    "applicationId": APPLICATION_ID,
    "genreId": GENRE_ID,
    "format": "xml"
}

response = requests.get(URL, params = payload).text
soup = BeautifulSoup(response, "lxml")

# 各パラメータのリストを作ってみる
rank_lis    = map(lambda x: x.text.encode("utf-8"), soup.findAll("rank"))
name_lis    = map(lambda x: x.text.encode("utf-8"), soup.findAll("itemname"))
price_lis   = map(lambda x: x.text.encode("utf-8"), soup.findAll("itemprice"))
url_lis     = map(lambda x: x.text.encode("utf-8"), soup.findAll("itemurl"))
im_flag_lis = map(lambda x: int(x.text), soup.findAll("imageflag"))      # 1:画像あり, 0:画像なし
im_url_lis  = map(lambda x: x.text.encode("utf-8"), soup.findAll("mediumimageurls"))

# 結果出力
for i in range(10):    # 表示したいランキング数
    print "%2s位 %s" % (rank_lis[i], name_lis[i])
    print "価格 : %s円" % price_lis[i]
    
    if im_flag_lis[i] == 1:      # 画像が存在するならダウンロードして表示する
        for im_url in im_url_lis[i].split("\n")[1:-1]:
            try:
                im_response = requests.get(im_url, allow_redirects = False, timeout = 1)
                Image.open(StringIO(im_response.content)).save("image.jpg")
                print commands.getoutput("imgcat image.jpg")
                break
            except:
                print "unavailable"

このままではそんなに利用価値はないと思う笑

定期的におすすめ本を紹介してくれる Slack Bot などが捗りそう