Python 楽天トラベルWeb API(REST)によるホテル情報の抽出「requests」

'22/04/13更新:堅牢性のためにエラー処理などを追加
 本記事では、下図のようにホテル情報を抽出する雛形コードを載せました。使用するライブラリは「requests」です。

f:id:HK29:20220409201224p:plain

 

 はじめに、楽天IDが必要なので、登録してない場合は登録します。そして、次のリンク先にアクセスして、楽天にログインします。https://webservice.rakuten.co.jp/documentation

 次に、APIを使うにはApplication IDを取得する必要があるため、下図右上の「New App」をクリックします。

f:id:HK29:20220409202124p:plain

 

 すると、下図のような登録画面に切り替わります。ここではWeb APIと連携するアプリ名を聞かれます。アプリ名は「excel」や「自分のHPのリンク」など適当でも構わないようで、必要事項を記入して進みます。

f:id:HK29:20220409202357p:plain

 

 最後に、下図のようにApplication IDが表示されます。web api利用時に使用します。

f:id:HK29:20220409203038p:plain

■本プログラム
 下記、冒頭にある app_id  = '' に上記で取得したIDを記入します。あとは実行するだけです。ホテル情報は、行き先の都道府県を乱数で決めて、抽出結果をcsvファイルに保存する仕様です。
 ちなみに、これはJupyterLabで作成したコードをスクリプトファイルに出力しました。そのため、上から順番にIn[1]~In[20]JupyterLabでインタラクティブに実行してゆくことも可能です。

#!/usr/bin/env python
# coding: utf-8

# In[1]:


import sys
import requests
import pandas as pd
import numpy as np
import random

# 取得したAPIのID
app_id = ''

# 楽天トラベルの地区コードを取得するためのURL
area_code_rquest_url = 'https://app.rakuten.co.jp/services/api/Travel/GetAreaClass/20131024'

# プロキシ設定(明示的に不要な場合はNoneを指定)
proxies = {
    'http': None,
    'https': None,
}

# リクエストするパラメータ
area_code_params = {
    'format': 'json',
    'formatVersion': 2,
    'applicationId': app_id,
}

# リクエストを実行する
res_area_code = requests.get(
    area_code_rquest_url,
    proxies = proxies,
    params = area_code_params)

print(res_area_code)
if res_area_code.status_code == 200:
    print('success')
    print(type(res_area_code))
else:
    print('check app_id,  and so on')
    sys.exit()


# In[2]:


# jsonへ変換
result_area_code_dict = res_area_code.json()
print(type(result_area_code_dict))


# In[3]:


# 都道府県データ
prefectures = result_area_code_dict['areaClasses']['largeClasses'][0][1]['middleClasses']
print(len(prefectures))
#print(prefectures)


# In[4]:


# 地区コードを抽出する
data_list = []
for i, data1 in enumerate(prefectures, start=1):
    #print(i, 'raw data')
    #print(data1['middleClass'])
    
    for j, data2 in enumerate(data1['middleClass']):
        if j == 0: # 都道府県名の抽出
            my_middle_class_code = data2['middleClassCode']
            my_middle_class_name = data2['middleClassName']
            #print('middleClass')
            #print('middleClassCode', my_middle_class_code)
            #print('middleClassName', my_middle_class_name)
        else: # 市以下の抽出
            for data3 in data2['smallClasses']:
                for key1, data4 in data3.items():
                    for data5 in data4:
                        for key3, data6 in data5.items():
                            if key3 != 'detailClasses': # 市地区
                                #print('smallClasses')
                                if key3 == 'smallClassCode':
                                    my_small_calss_code = data6
                                    data_list.append(['ClassCode', my_middle_class_code, my_small_calss_code, np.nan])
                                else:
                                    my_small_calss_name = data6
                                    data_list.append(['ClassName', my_middle_class_name, my_small_calss_name, np.nan])                                
                            else: # 詳細地区名                              
                                for data7 in data6:
                                    #print(data7['detailClass'])
                                    for key5, data7 in data7['detailClass'].items():
                                        #print('detailClasses')
                                        if key5 == 'detailClassCode':
                                            data_list.append(['ClassCode',
                                                  my_middle_class_code, \
                                                  my_small_calss_code, \
                                                  data7])
                                        else:
                                            data_list.append(['ClassName',
                                                  my_middle_class_name, \
                                                  my_small_calss_name, \
                                                  data7])

# pandasデータフレームへ格納
df = pd.DataFrame(data_list, columns = ['myclass', 'middle_name', 'small_name', 'detail_name'])
df


# In[5]:


# カテゴリがClassCodeのみ抽出する
df_class_code = df[df['myclass'] == 'ClassCode']
df_class_code.columns = ['mycode', 'middle_code', 'small_code', 'detail_code']
df_class_code.reset_index(drop = True, inplace = True)
df_class_code


# In[6]:


# カテゴリがClassNameのみ抽出する
df_class_name = df[df['myclass'] == 'ClassName']
df_class_name.reset_index(drop = True, inplace = True)
df_class_name


# In[7]:


# 列方向へ結合する
DF = pd.concat([df_class_code, df_class_name], axis=1)
DF


# In[8]:


# detail_codeがAである行を抽出
DF_buf = DF[DF['detail_code'] == 'A']
DF_buf


# In[9]:


# detail_codeがAである行のmiddle_code と small_codeの値を抽出して、detail_codeがNaNの行番号を抽出
target_index_list = []
for index, row in DF_buf.iterrows():
    df_buf1 = DF[
        (DF['middle_code'] == row['middle_code']) & \
        (DF['small_code'] == row['small_code'])
    ]
    df_buf2 = df_buf1[df_buf1['detail_code'].isnull()]
    print(df_buf2)
    target_index_list.append(df_buf2.index[0])
target_index_list


# In[10]:


# 行番号を指定して削除
DF2 = DF.drop(DF.index[target_index_list])
DF2.to_csv('01_area_list.csv', encoding='utf-8-sig')
DF2


# In[11]:


# # 地域を指定する場合 1/2 middle地域を指定
# DF3 = DF2[DF2['middle_name'] == '愛知県']
# DF3


# In[12]:


# # 地域を指定する場合 2/2 small地域を指定
# DF3 = DF3[DF3['small_name'] == '南知多・日間賀島・篠島']
# index_no = DF3.index[0]
# print(index_no)
# DF3


# In[13]:


# 地域を乱数で決定する場合
index_no = random.randint(0,len(DF2))
print(index_no)


# In[14]:


# インデックス番号を指定してデータを抽出
DF3 = DF.iloc[index_no]
DF3


# In[15]:


# データを変数へ格納
middle_name, small_name = DF3['middle_name'], DF3['small_name'] 
middle, small, detail = DF3['middle_code'], DF3['small_code'], DF3['detail_code']
print(middle_name, small_name, middle, small, detail)


# In[16]:


# 宿情報のリクエストURL
inn_request_url = 'https://app.rakuten.co.jp/services/api/Travel/SimpleHotelSearch/20170426'

df_hotel_list = []

i = 1
j = 0
while True:
    
    # リクエストするパラメータの設定
    if pd.isna(detail): # detailClassCode がない場合
        inn_params = {
            'format': 'json',
            'largeClassCode': 'japan',
            'middleClassCode': middle,
            'smallClassCode': small,
            'page': i,
            'applicationId': app_id,
        }
    else:
        inn_params = {
            'format': 'json',
            'largeClassCode': 'japan',
            'middleClassCode': middle,
            'smallClassCode': small,
            'detailClassCode': detail, 
            'page': i,
            'applicationId': app_id,
        }    

    # リクエストを実行する
    res = requests.get(
        inn_request_url,
        proxies = proxies,
        params = inn_params)
    
    if res.status_code != 200: # もしデータ取得に成功しなかった場合にループを抜ける
        break
    
    # jsonへ変換
    result = res.json()

    # ホテル情報をpandasデータフレームで取得する
    hotels = result["hotels"]
    for hotel in hotels:
        hotel_info = hotel["hotel"][0]["hotelBasicInfo"]
        df_buf = pd.DataFrame(hotel_info, index=[j])
        df_hotel_list.append(df_buf)
        j += 1
        if j > 50: # もし、検索したホテル数が50を超えたらループを抜ける
            break
    i += 1
#df_hotel_list


# In[17]:


# pandasデータフレームを結合する
df_hotels = pd.concat(df_hotel_list)
#df_hotels


# In[18]:


# カラム名一覧
df_hotels.columns


# In[19]:


# カラム名を指定して、データ抽出
df_hotels_simple = df_hotels[
    [
    'hotelName',
    'hotelInformationUrl',
    #'reviewUrl',
    'hotelSpecial',
    'hotelMinCharge',
    #'address2',
    'telephoneNo',
    #'access',
    'nearestStation',
    'reviewCount',
    'reviewAverage',
    #'userReview',
    ]
]

# 「最安値の目安」列に対して、降順にソートする
df_hotels_simple_s = df_hotels_simple.sort_values('hotelMinCharge', ascending=False)

# ファイルへ保存
#df_hotels_simple_s.to_csv(f'02_{middle_name}_{small_name}_hotel_list.csv', encoding='utf-8-sig')


# In[20]:


# 「最安値の目安」列に対して、指定区間内の値にあるホテルを抽出する
min_Charge = 6000
max_Charge = 10000
df_target_hotels = df_hotels_simple_s[(df_hotels_simple_s['hotelMinCharge'] >= min_Charge) &                                       (df_hotels_simple_s['hotelMinCharge'] <= max_Charge)]

print(len(df_target_hotels))
# ファイルへ保存
df_target_hotels.to_csv(f'02_{middle_name}_{small_name}_target_hotel_list.csv', encoding='utf-8-sig')
df_target_hotels

以上

<広告>