Python 初学者が楽天APIを使って買い物リスト作成アプリを作った（データ収集編:1）

**この記事は以下の記事の続きです**

https://qiita.com/yuuauuy1/items/5bb4a53ab15bde354297


買い物リスト作成アプリを作るにあたって、
様々なレシピの材料と量を大量に集める必要があります。

今回は楽天のAPIを使ってデータを集めていきます。


**★このページは以下項目で構成されています★**

| 項番 | ページ内リンク |
|:----:|:-------------|
| 1 | [データのイメージ](#データのイメージ) |
| 2 | [データ収集で学んだこと](#データ収集で学んだこと) |
| 3 | [データ取得の流れ](#データ取得の流れ) |
| 4 | [楽天レシピのAPIを使って人気レシピを取得する](#楽天レシピのapiを使って人気レシピを約2000件取得する) |
| 5 | [楽天レシピのカテゴリ一覧を取得する](#楽天レシピ-カテゴリ一覧を取得する) |
| 6 | [レシピ名・レシピURL・レシピの材料一覧を取得する](#レシピ名レシピurlレシピの材料一覧を取得する) |



# データのイメージ

Excel をデータ保存先として活用しました。
理由は、純粋にSQLなどデータベース言語の勉強をまだ行っていなかったからです。

① こちらは、レシピ名やレシピURLなどの表です。
左から [index, レシピ名、レシピURL、何人分のレシピか、材料の一覧]　を表しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/92ccaebe-b519-147c-7946-b1e339d81962.png)

② こちらは、各レシピの材料と量が載っている表です。
　 各レシピは index の番号で対応しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/2d1bc6bb-a88c-b301-a134-29accf10ef45.png)

## データ収集で学んだこと

データを集める上で学んだことが沢山ありました。

## 1. どのようにデータを保持したいかしっかりとイメージを作る。

これをデータ収集を始める前に定めて（データの見本を作っていなかった）おらず、かなり迷走してしまいました。

①の表は、当初からこの型でしたが、②の表にするまでに難渋しました。
初期のころは、「index、レシピ名、レシピURL、材料、量」を　各項目１マスずつ使って
Excelの横１列に格納しようとしていました。（つまり、②の表は無し）
その後に、待っているデータ加工のやりやすさ等は考えておりませんでした・・・

さらに、材料と量を辞書型として持ち、Excelに格納する部分のコードが書けず、立ち止まってしまいました。

メンターさんにアドバイスを頂いて、indexが対応さえしていれば、データを取り出す表が２つになっても良いと気づき、コードを書き始めて２週間目でやっとデータ見本を作りました。
データ見本を作ってからは、多少試行錯誤はありましたが、見本があったのでプログラムが書きやすくなりました。

## 2. データを取る時に、ズレが起こらないようにプログラムを書く。

これは、メンターさんによく言われていたことです。

下の画像は楽天レシピの材料と量が載っている部分の画像です。
左の赤枠が材料、右の赤枠が量を表しています。
今回のアプリの場合は、材料と量を対応しなくてはいけません。
当初は、赤枠ごとにデータを取って、上から抽出し、材料と量をそれぞれ対応させるというプログラムだったのですが、
万が一、材料と量がズレてしまうとアプリの本来の機能が発揮できません。

ですので、青枠の様に、横一列であらかじめ対応している材料と量をデータとして抽出するのが良いです。
少しでもミスが起こってしまう機会を減らすという意味でも、重要な考え方だと思いました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/922a038b-9dfc-1bdd-1c65-e272419923fe.png)

## 3. API と jsonについて学習を深められた。
後述しますが、こちらのアプリは楽天レシピAPIを使用して作っています。
楽天レシピAPIはデータ返答方式がjsonで、jsonとは？？APIってなに？？といった状態でしたので、良い機会でした。

## 4. スクレイピングについて学習を深められた。
こちらも後述しますが、今回はseleniumとbeautifulsoupを使いスクレイピングを行いました。
スクレイピングは、なんて便利な技術なのだろうと思いましたが、著作権侵害のリスクや過度にアクセスし過ぎるとサーバーに負担をかけてしまういう注意点が多々あり、使用時は注意しなければならないと感じました。

スクレイピングの注意点に関しては以下のサイトを参考にさせて頂きました。

https://www.octoparse.jp/blog/10-myths-about-web-scraping/


# データ取得の流れ
アプリ作成にあたり、最終的には、各レシピの材料と量の情報が欲しいです。

データ取得の流れとして
1. 楽天レシピのAPIを使って、表①のデータ約2000件分のレシピの「レシピ名、レシピURL、レシピの材料」を取得
2. レシピURLを2000件分アクセスし、表②のレシピの材料と量を取得する。
が良いと考えました。

# 楽天レシピのAPIを使って人気レシピを約2000件取得する
## 引用・参考文献

初めに書きますが、引用・参考ともに以下のページです。
完全にこちらのURLに載っているコードを参考にさせて頂いていますので、
コード自体の詳細説明は省きますが、自身がどのように活用したのか書いていきます。

https://qiita.com/konitech913/items/7ffa7907a6c03c8909fc

## 楽天レシピのAPI
楽天レシピには、
「楽天レシピカテゴリ一覧API」：楽天レシピのカテゴリ一覧を返すAPI
「楽天レシピカテゴリ別ランキングAPI」：楽天レシピの各カテゴリで人気トップ4までのレシピを返すAPI
の２つがあります。


## レシピ取得の流れ
参考サイトによると、
1. 「楽天レシピカテゴリ一覧API」でレシピカテゴリ一覧を取得する。
2. 1で取得した一覧の中から、検索したいキーワードを含むレシピカテゴリを抽出する。
3. 2で抽出したレシピカテゴリに対して「楽天レシピカテゴリ別ランキングAPI」を使って人気レシピを取得する。
という方法が良いとのことです。

楽天レシピのAPIを使用する上で、「Rakuten Developers」に登録し、アプリID
を取得する必要があります。こちらに詳細があります。

https://qiita.com/anzuuuuu/items/be4e4845c21ac3334695#%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB%E3%81%AB%E8%A1%A8%E7%A4%BA%E3%81%97%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86

## 楽天レシピ カテゴリ一覧を取得する
まず、「楽天レシピカテゴリ一覧API」を使って、楽天レシピのカテゴリ一覧を取得しました。
写真は上部のみですが、全部で2160個のカテゴリーがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/2a1002f4-04d9-d694-6c18-a7fcf9a8855d.png)
コードはこちら
```python:Python
import requests
import json
import time
import pandas as pd
from openpyxl import load_workbook
import openpyxl

# 楽天レシピのレシピカテゴリ一覧を取得する（applicationIDには自身のものを入力）
res = requests.get('https://app.rakuten.co.jp/services/api/Recipe/CategoryList/20170426?applicationId=****')

#楽天APIはjson形式なのでpythonで読み取るためにloads関数使う
json_data = json.loads(res.text)

parent_dict = {} # mediumカテゴリの親カテゴリの辞書

df = pd.DataFrame(columns=['category1','category2','category3','categoryId','categoryName'])

for category in json_data['result']['large']:
    df = df.append({'category1':category['categoryId'],'category2':"",'category3':"",'categoryId':category['categoryId'],'categoryName':category['categoryName']}, ignore_index=True)

for category in json_data['result']['medium']:
    df = df.append({'category1':category['parentCategoryId'],'category2':category['categoryId'],'category3':"",'categoryId':str(category['parentCategoryId'])+"-"+str(category['categoryId']),'categoryName':category['categoryName']}, ignore_index=True)
    parent_dict[str(category['categoryId'])] = category['parentCategoryId']

for category in json_data['result']['small']:
    df = df.append({'category1':parent_dict[category['parentCategoryId']],'category2':category['parentCategoryId'],'category3':category['categoryId'],'categoryId':parent_dict[category['parentCategoryId']]+"-"+str(category['parentCategoryId'])+"-"+str(category['categoryId']),'categoryName':category['categoryName']}, ignore_index=True)
```

このカテゴリーをざっと見ると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/287f1c11-7c52-9613-06c2-e07e64278eeb.png)
こちらの写真の様に、海藻サラダ、トマトサラダ、キャベツサラダ…と細かくカテゴリーが分かれている部分があると分かります。
このカテゴリ一覧を「サラダ」で検索すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/82d9b855-484c-2268-d833-7e4d176fd548.png)
画像左下に [61 rows × 5 columnns] とあります。
1 rowにつき、１つのカテゴリ表示なので、「サラダ」関係だけでも61カテゴリーあり、
さらに「楽天レシピカテゴリ別ランキングAPI」は　各カテゴリー人気上位４つのレシピを取得できるので、
61 × 4 で 244 レシピ取得出来ることになります。

コードはこちら
```python:Python
# キーワードからカテゴリを抽出する(queryでデータフレームから条件式を抽出する)
df_keyword = df.query('categoryName.str.contains("サラダ")', engine='python')
print(df_keyword)
```

# レシピ名・レシピURL・レシピの材料一覧を取得する

このようにして、
__卵・揚げ物・野菜・麺・肉・鍋・豚肉・中華・節約・煮物・鶏肉・魚・簡単
ラーメン・パスタ・スープ・サラダ・カレー・うどん・イタリアン・鍋__　　
のワードが含まれるカテゴリーのレシピをExcelに出力し、レシピ名・レシピURL・そのレシピの材料一覧 を取得しました。
（これらのワードはレシピ数が多そうだったので選びました）

ちなみに、それぞれのカテゴリのレシピの数はまちまちです。

例えば・・・

こちらは **魚** 関連のレシピ一覧(83件)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/210fd9ae-ab99-ce99-5cc1-55f696b3a952.png)
こちらは　**パスタ** 関連のレシピ一覧(143件)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/e7f5f362-ac39-a847-bfd3-44c6e764d1be.png)
このような感じです。

コードはこちら
```python:Python
# 欲しいレシピデータを指定する
df_recipe = pd.DataFrame(columns=['recipeTitle', "recipeUrl","recipeMaterial"])

#上の表の１行ごとのデータを以下の処理に当てはめる
for index, row in df_keyword.iterrows():
    time.sleep(3) # 連続でアクセスすると先方のサーバに負荷がかかるので少し待つ
    #applicationIDには自身のものを入力
    url = 'https://app.rakuten.co.jp/services/api/Recipe/CategoryRanking/20170426?applicationId=****&categoryId='+row['categoryId']
    res = requests.get(url)
    json_data = json.loads(res.text)
    recipes = json_data['result']

    for recipe in recipes:
        df_recipe = df_recipe.append({'recipeTitle':recipe['recipeTitle'],'recipeUrl':recipe['recipeUrl'],"recipeMaterial":recipe["recipeMaterial"]},ignore_index=True)

#excelに保存する
df_recipe.to_excel("サラダ.xlsx")
```
ちなみに、上のコードの２行目と下から２行目で
```python:Python
#上から２行目
df_recipe = pd.DataFrame(columns=['recipeTitle', "recipeUrl","recipeMaterial"])
#下から２行目
for recipe in recipes:
        df_recipe = df_recipe.append({'recipeTitle':recipe['recipeTitle'],'recipeUrl':recipe['recipeUrl'],"recipeMaterial":recipe["recipeMaterial"]},ignore_index=True)
```
ここで、columnとして[recipeTitle] [recipeUrl] [recipeMaterial]の3つを指定していますが、
楽天レシピカテゴリ別ランキングAPIでは、他に

・レシピID
・画像のURL(サイズ:一般)		
・画像のURL(サイズ:中)		
・画像のURL(サイズ:小)	
・ピックアップレシピ　
・ユーザー区分		
・レシピ投稿者	
・レシピを考えた人のコメント材料名の一覧	
・調理時間目安	
・費用の目安	
・レシピ公開日	
・ランキング順位

を返してくれますので欲しい情報に合わせて、変更してみてください。

詳細はこちら

https://webservice.rakuten.co.jp/documentation/recipe-category-ranking

## Excel結合
それぞれのレシピ一覧をすべて統合して、約2000件のレシピ一覧の表を作りました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/e84d10aa-2110-1f59-e9cd-050fed881448.png)
※ 表①の最後の列部分を写しています

Excelを統合するコードは以下です。
```Python:Python
#ライブラリの読み込み
import glob　#特定の条件に従ったファイルを取得できる
import pandas 　#Excelやcsvといったデータフレームを扱える,インストールをする必要がある

#フォルダ、ファイルの種類を指定（あらかじめ結合したいすべてのファイルをフォルダに入れておく）
#"~~*.xlsx" は、ファイルのパスの *より前の部分が同じエクセルファイルという意味
folder = '/Users/class/Desktop/excel/*.xlsx'   

#結合後のExcelファイル名を指定する
excel_file_name = 'recipe_index.xlsx'

#フォルダ内のファイル一覧を読み込み。glob.glob()で()内のファイルを取得できる
file_list = glob.glob(folder)

#読み込んだファイルを入れるリストを準備する
lists = []
#ファイル一覧をExcelでリストへ追加
for i in file_list:
    lists.append(pandas.read_excel(i))

#リスト内のExcelを結合。pandas.concat([リスト])でリスト内のデータフレームが結合できる。
merge_data = pandas.concat(lists)

#結合したデータをExcelへ書き込み。
#●●.to_excel(○○, index=0)で●●を○○に書き込める。index=0でインデックス無しの表示となる。
merge_data.to_excel(excel_file_name)
```

長すぎるので、次回に続きます！

次回からは、何人分かのレシピを取得する部分を書きます。

https://qiita.com/yuuauuy1/items/c7b544eda29f90ee12e5