楽天レシピAPIでレシピデータを大量に取得する

在宅で時間があるので、API連携というものを試してみたく。
レシピを検索するアプリを開発すればStayHomeのお供にピッタリと思ったので、今回は楽天レシピAPIにチャレンジしてみます。

楽天APIについて webservice.rakuten.co.jp

公式サイトを見ると楽天ブックス楽天トラベルなど多様なAPIがそろっているよう。
今回はレシピ系のAPIを使用します。


楽天レシピ系API


楽天レシピ系APIは2種類に分かれています。
楽天レシピカテゴリ一覧API
楽天レシピカテゴリ別ランキングAPI
楽天ウェブサービス: 楽天レシピカテゴリ別ランキングAPI(version:2017-04-26) | API一覧

楽天レシピカテゴリ一覧API」は楽天レシピに登録されているレシピカテゴリーを出力するAPIです。一方、「楽天レシピカテゴリ別ランキングAPI」はカテゴリー別のレシピをランキング上位から4つ出力するAPIです。
どちらも材料名を送るとレシピが返ってくるような仕様ではないため、直接検索には向いていません。一旦データベースに落とすなどして利用する必要がありそうですね。


楽天レシピカテゴリ別ランキングAPI」を試してみよう


①入力パラメータ

https://app.rakuten.co.jp/services/api/Recipe/CategoryRanking/20170426?[parameter]=[value]…

入力パラメータはURLに[parameter]=[value]の形で要求する形になります。
例) categoryId=10

②出力パラメータ (※長くなるため1件のみ表示)

{
 "result": [
  {
   "foodImageUrl": "https://image.space.rakuten.co.jp/d/strg/ctrl/3/fb9abefb60934de5d3667ef32f2ce34f7e74b698.49.2.3.2.jpg",
   "recipeDescription": "クックパットで『ホットケーキミックス』検索NO.1になり、NHK『あさイチ』で紹介&家族全員でテレビ出演させていただきました☆ボール1つでできる超簡単レシピ♪",
   "recipePublishday": "2011/02/28 13:52:42",
   "shop": 0,
   "pickup": 1,
   "recipeId": 1270000746,
   "nickname": "keikana♪",
   "smallImageUrl": "https://image.space.rakuten.co.jp/d/strg/ctrl/3/fb9abefb60934de5d3667ef32f2ce34f7e74b698.49.2.3.2.jpg?thum=55",
   "recipeMaterial": [
    "プレーンヨーグルト",
    "ホットケーキミックス",
    "お好みのジャム 又は砂糖",
    "卵",
    "サラダ油"
   ],
   "recipeIndication": "1時間以上",
   "recipeCost": "300円前後",
   "rank": "1",
   "recipeUrl": "https://recipe.rakuten.co.jp/recipe/1270000746/",
   "mediumImageUrl": "https://image.space.rakuten.co.jp/d/strg/ctrl/3/fb9abefb60934de5d3667ef32f2ce34f7e74b698.49.2.3.2.jpg?thum=54",
   "recipeTitle": "ヨーグルトとHMで超簡単濃厚チーズケーキ"
  }
 ]
}

このようにJSONで出力されます。またformat=xmlを入力パラメータに組み込めば、xml形式で出力させることも可能です。


データを取り出すにあたって


大量にデータを取得する際に注意する点は以下の2点。
1.楽天レシピAPIのバージョン制約
楽天レシピAPIのバージョンは、version:2017-04-26であるため、2017年4月26日のデータ断面(つまりはその日のランキングデータ)しか取り出せません。
つまり、入力パラメータのURLに日付を記載している部分(20170426)がありますが、この部分を変更しても結果が返ってきません。(前日付も同様)

2.カテゴリーには大分類・中分類・小分類がある
レシピには大分類・中分類・小分類が定義されています。
大分類だけを指定してレシピを取得することも可能ですが、取得できるレシピ数が少なくなるので、小分類まで指定してあげる必要があります。
大分類番号10(肉), 中分類番号69(その他のお肉), 小分類番号45(ラムチョップ・ラム肉)のランキングを取り出すパラメータはcategoryId=10-69-45と指定する必要があります。

以上の注意点を踏まえると、2017年4月26日時点のデータに対して、入力パラメータのカテゴリー分類のパターンを詳細に指定することで、最大量のデータを取得することが可能ということです。


カテゴリ分類をどうやって変化させるか


ここで登場するのが、とりあえず説明を割愛した「楽天レシピカテゴリ一覧API」です。下準備としてこちらからカテゴリーIDの一覧を取得しましょう。

①入力パラメータ

https://app.rakuten.co.jp/services/api/Recipe/CategoryList/20170426?[parameter]=[value]…

categoryTypeで分類ごとのカテゴリー情報が取得できます。
(大分類:large / 中分類:medium / 小分類:small)

②出力パラメータ (※量が多いので一部抜粋)

{
 "result": {
  "small": [],
  "medium": [
   {
    "categoryName": "牛肉",
    "parentCategoryId": "10",
    "categoryId": 275,
    "categoryUrl": "https://recipe.rakuten.co.jp/category/10-275/"
   },
   {
    "categoryName": "豚肉",
    "parentCategoryId": "10",
    "categoryId": 276,
    "categoryUrl": "https://recipe.rakuten.co.jp/category/10-276/"
   },
   {
    "categoryName": "鶏肉",
    "parentCategoryId": "10",
    "categoryId": 277,
    "categoryUrl": "https://recipe.rakuten.co.jp/category/10-277/"
   }
  ]
 }
}

※中分類・小分類の場合は、parentCategoryIdとして親分類のカテゴリーIDが出力されます。


カテゴリを取得してみた


カテゴリーの取得プログラムはpythonで書きました。

def main():
 api_url = CategoryAPI.FORM
 res = requests.get(api_url)
 json_data = res.json()

 loop_count = 1
 for category_kind in json_data['result']:
  for category in json_data['result'][category_kind]:

   category = Category()
   category.category = loop_count
   category.category_id = category['categoryId']
   category.category_name = category['categoryName']
   category.category_url = category['categoryUrl']
   if loop_count != 3:
    category.parent_category = category['parentCategoryId']

   session.add(category_dto)
   session.commit()

  loop_count = loop_count + 1

※categoryの区分けは次の通り。(1:小分類 2:中分類 3:大分類)
※ORマッパー(SQLAlchemy)を使用しています。

このようにカテゴリーレコードが取得できました。

f:id:lc-xx16:20200502001214p:plain
カテゴリー一覧


カテゴリ一覧viewを作成する


親カテゴリーIDを利用して、自己結合でカテゴリー一覧viewを作ってみます。

f:id:lc-xx16:20200502001658p:plain
カテゴリー一覧
カテゴリー一覧viewはこのような感じです。


カテゴリ一覧viewを利用し、レシピを取得する


それでは、最後です。カテゴリー一覧viewを使用して、レシピを取得してみましょう。

def main():
 categories = session.query(CategoryAll.large_id, CategoryAll.medium_id, CategoryAll.small_id). \
  all()

 for category in categories:
  category_id = str(category[0]) + "-" + str(category[1]) + "-" + str(category[2])
  
  # API.FROM 及び API.APPは入力パラメータのURLの一部
  api_url = API.FORM + category_id + API.APP
  res = requests.get(api_url)
  sleep(1.5)
  json_data = res.json()

  for rcp in json_data['result']:
   # Recipe existence check
   recipes = session.query(Recipe.recipe_name). \
    filter(Recipe.recipe_name == rcp['recipeTitle']). \
    first()

   if recipes is None:
    # Recipe insert
    recipe = Recipe()
    recipe.recipe_id = rcp['recipeId']
    recipe.recipe_name = rcp['recipeTitle']
    recipe.recipe_url = rcp['recipeUrl']
    recipe.recipe_photo = rcp['foodImageUrl']
    recipe.material = rcp['recipeMaterial']
    recipe.large_id = category[0]
    recipe.medium_id = category[1]
    recipe.small_id = category[2]
    recipe.register_date = get_datetime()
    session.add(recipe)
    session.commit()

viewから抜き出した大分類ID, 中分類ID, 小分類IDを-で結合し、URLに組み込んでいます。
実際試したところ、アクセス速度がレスポンスを上回り、データが上手く取り出せていないことが頻発したので、プログラム内にsleep(1.5)を入れています。

f:id:lc-xx16:20200502002301p:plain
レシピ一覧

しっかり取れました。重複なしの4773レコード。


まとめ


今回は楽天レシピAPIでレシピデータを取得してみました。
レシピを数百件取得するだけであれば「楽天レシピカテゴリ一覧API」を経由しなくとも、「楽天レシピカテゴリ別ランキングAPI 」だけで可能です。
今回は4774レコード取れたので、これをもとに何かアプリを作れたらと思います。
またせっかく取得したカテゴリーも検索のキーなどに再利用できそうなので、そこら辺の設計も考えていければ。