Python 初学者が楽天APIを使って買い物リスト作成アプリを作る（レシピ検索システム編）


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

https://qiita.com/yuuauuy1/items/3448c41c0b684db76edb

今回は、楽天レシピカテゴリ一覧API と 楽天レシピカテゴリ別ランキングAPIを使って
集めたレシピから検索 ➡ 買い物リストに入れたいレシピを選択する
という機能を作っていきます！

**イメージは以下の通り**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/3a576d55-d8ac-d00b-65ff-53c031feda60.png)

このように、検索ワードを入力したら、以下の検索結果が出てきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/0b5369d7-c86f-0732-9556-853cb5b1d4d7.png)
この中から、以下の様に indexを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/9d509468-2e31-57e1-af15-4065e62d6ffd.png)
さらに再度レシピ検索を行うか選択する　ところまでを作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/96b6f755-649b-3c78-9f06-4dffcaa5b688.png)


**買い物リスト作成アプリの概要はこちら**

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

**目次**

| 項番 | ページ内リンク | 
|:-:|:-:|
| 1 |[レシピ検索機能を作るのに必要な手順](#レシピ検索機能を作るのに必要な手順)|
| 2 |[準備](#準備)|
| 3 |[検索ワードの入力](#検索ワードの入力)|
| 4 |[検索ワードがヒットするレシピを見つけて表示](#検索ワードがヒットするレシピを見つけて表示)|
| 5 |[indexを選択する](#indexを選択する)|
| 6 |[レシピ検索を継続するか問う関数をつくる](#レシピ検索を継続するか問う関数をつくる)|
| 7 |[全コード](#全コード)| 

# レシピ検索機能を作るのに必要な手順

1. ユーザが検索ワードを入力
2. 検索ワードが「レシピ名」「レシピの材料」から当てはまるレシピをピックアップ
3. ピックアップしたレシピの「レシピ名」「レシピindex」「何人分のレシピか」「URL」をそれぞれ表示
4. 何件レシピがヒットしたか表示 
5. 買い物リストに入れたいレシピのindexを選択
6. 検索を続けるか選択する

この手順を踏んでいきます。

## 準備

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/518e43af-e26f-b329-12e4-88db4be7d103.png)
検索ワードを、こちらのレシピ一覧のB列の「レシピ名」とE列の「レシピ材料」から探すのですが、２列分検索しなくてはいけなくなります。
検索時間を短くしたいので、F列にレシピ名とレシピ材料の列を合体させた列を作り、１列検索すれば良いようにします。

さらにこのあと、ヒットしたレシピの「レシピindex」や「何人分のレシピか」の情報を得たいので、この表自体をリストとして取得しておきます。

### 列を合体
```python:Python
#必要ライブラリのインポート
import openpyxl 

# ファイルを開き、シートを取得
filename = "test"
wb = openpyxl.load_workbook(filename +".xlsx")
ws = wb.worksheets[0]

for row in ws.iter_rows(min_row=2, max_col=6): #Excelの扱う範囲指定
    #2列目と5列目を足して、6列目に代入
    row[5].value = row[1].value + row[4].value #row[0] = 1列目
#保存
wb.save(filename + ".xlsx") 
```
これでF列に 「レシピ名」+「レシピ材料」の列が新たにできました。

### 表をリストとして取得する
```python:Python
#index.レシピ名.URL.人数.レシピ名と材料が一緒になった列のリストを作る
index_name_lists=[]
    #Excelの表の範囲指定
    for row in index_ws.iter_rows(min_row =2, min_col=1, max_col=6):
        index_name =[]
        index_name.append(row[0].value) #index列
        index_name.append(row[1].value) #レシピ名
        index_name.append(row[2].value) #URL
        index_name.append(row[3].value) #人数
        index_name.append(row[5].value) #レシピ名と材料が一緒になった列　

        index_name_lists.append(index_name)
```
結果は以下です。２重配列のリストになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/9832779c-c1aa-524b-0e88-9e834a9039e6.png)
同じようにこちらの「材料と量」のExcelもリスト化しておきます。index_ing_listsと名前を付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2983287/703fc5ba-38ea-3a1e-73e3-2a0948fc24e4.png)

## 検索ワードの入力
検索ワードはinput関数を使ってユーザーに入力してもらいます。
```python:Python
word = str(input("検索ワードを日本語で入力してください : "))
```
次に、`isalphaメソッド`を使って入力した文字が日本語であるかを判定します。
`isalphaメソッド`は主に指定したワードが英語で構成されている場合、Trueで返しますが、ひらがな・カタカナ・漢字で構成されていてもTrueを返します。
これを利用します。

>参考文献
[［解決！Python］文字列が英字（文字）のみで構成されているかどうかを判定するには（isalpha／isasciiメソッド、re.match／re.fullmatch関数）](https://atmarkit.itmedia.co.jp/ait/articles/2102/02/news013.html)
```python:Python
def input_word():  
    word = str(input("検索ワードを日本語で入力してください : "))
    word_result =  word.isalpha() #ひらがな／カタカナ／漢字が文字判定(True)される。
    if word_result : # Trueの場合、入力ワードを検索する
        search_word(word)
    else:
        print("日本語以外の文字が入力されました") 
        input_word() #Falseの時、再度検索ワード入力
```

## 検索ワードがヒットするレシピを見つけて表示

検索ワードがヒットするレシピを`レシピindex,レシピ名,URL,何人分,レシピ名+材料が格納されたリスト`から探します。
```python:Python
title_list = [] #検索件数を把握するためにtitle_list にtitle格納する
    for index_name in index_name_lists: #レシピindex,レシピ名,URL,何人分,レシピ名+材料が格納されたリスト、
         # 検索ワードがレシピ名+材料に含まれるとき        
        if word in index_name[4]:
          
            # それぞれの情報を取得
            index = index_name[0]
            title = index_name[1]
            url = index_name[2]
            people_n = index_name[3]
            title_list.append(title)

            print("+++++++++++++++++++++++++++++++++++++++++")
            print("index : "+ str(index))
            print("レシピ名 : "+ title)
            print(str(people_n)+ " 人分のレシピです")
            print("URL : "+ url)

            #indexが一致する材料と量を表示
            for index_ing in index_ing_lists: #index,材料,量のリスト
                if str(index) in str(index_ing[0]):
                    ingredient = index_ing[1] #材料
                    amount =  index_ing[2] #量          
                    print("{"+ str(ingredient)+"："+str(amount)+"}")

    print( str( len(title_list))+ "件のレシピが見つかりました。" )
```
次に、検索結果がある時、ない時の条件分岐を作ります。

検索結果があれば、次の動作へ
検索結果がなければ、再度検索ワード入力へ戻るようにします。

条件分岐を入れて、関数化するとこちら
```python:Python
def search_word(word):    
    title_list = [] #検索件数を把握するためにtitle_list にtitle格納する
    for index_name in index_name_lists: 
         # 検索ワードと一致した時        
        if word in index_name[4]:
          
            # index と URLを取得する
            index = index_name[0]
            title = index_name[1]
            url = index_name[2]
            people_n = index_name[3]
            title_list.append(title)

            message(index, title, url, people_n)

            for index_ing in index_ing_lists:   #リストとして持っておく
                if str(index) in str(index_ing[0]):
                    ingredient = index_ing[1]
                    amount =  index_ing[2]           
                    print("{"+ str(ingredient)+"："+str(amount)+"}")

    print( str( len(title_list))+ "件のレシピが見つかりました。" )

    if len(title_list) == 0: # 検索結果無い場合、検索ワード入力に戻る
        print("検索ワードの文字表記を変えると、検索結果が変わるかもしれません") 
        input_word() #再度検索ワード入力へ戻る
    
    else: # 検索結果がある場合、レシピのindex選択に進む
        input_index() 
```

## indexを選択する

このアプリで使うレシピには、すべてレシピindexという通し番号がついています。
ユーザーに検索結果から買い物リスト化したいレシピのindexを input関数を使って選択してもらいます。
```python:Python
recipe_index = input("買い物リストに入れるレシピの　indexを入力してください") 
```
また、このindexが正の整数であるか？
indexに無い数字を入力していないか?判定するために条件分岐を加えます。
正の整数であるかは、`indigistメソッド`で判定します。

isdigit メソッドは文字列中のすべての文字が数字であり、かつ 1 文字以上ある場合に、Trueを返します。
```python:isdigitメソッド
print("143234".isdigit())#正の数字(True)
print("-5".isdigit())#負の数(False)
print("1a234".isdigit())#アルファベット入り(False)
print("3.14".isdigit())#小数(False)
print("１２３４".isdigit())#全角数字(True)
```
>参考文献
>[文字列の中の文字が数を表す文字かどうかを判定する(isdecimal, isdigit, isnumeric)](https://www.javadrive.jp/python/string/index14.html)

さらに、検索できるレシピは全部で2031件なので、2031より上のindexが入力された時は、再度index選択に戻るように条件分岐を追加します。
```python:Python
index_list =[] # レシピindexのリスト    
def input_index(): # 買い物リストに入れるレシピの index が 正しい数字か判定
    recipe_index = input( word_list[3] ) #買い物リストに入れるレシピの　indexを入力してください
    index_result = recipe_index.isdigit() # 正の整数か判定
    
    if index_result: #indexが整数のとき
        if 0 <= int(recipe_index) <= 2031: #index内の数字を選択した時にindex_list に追加
            index_list.append(int(recipe_index))
            end()  #レシピ検索を継続するか終了するか選択する関数呼び出し      
        else:　#index範囲外の数字が入力された時、もう一度index選択を行う
            print("indexにない数字が入力されました") 
            input_index() 
    else: #数字以外の文字が入力された時、もう一度index選択を行う
        print("数字以外の文字が入力されました") 
        input_index()
```

## レシピ検索を継続するか問う関数をつくる
ユーザーにレシピ検索を継続するか、数字で入力してもらった後に、
indexを選択する関数と同じように、`isdigitメソッド`を使って入力された文字が整数か判断します。
```python:Python
input_n = input(word_list[6]) #まだレシピ検索しますか？Yes:1,No:0
n_result = input_n.isdigit() # 整数か判定
```
入力された文字が整数である場合、レシピ検索を続けるか、終了するかで条件分岐を作ります。
さらに、関数化すると以下
```python:Python
def end(): # レシピ検索継続するか 終了するか入力
    input_n = input(word_list[6]) #まだレシピ検索しますか？Yes:1,No:0
    n_result = input_n.isdigit() # 整数か判定
    if n_result:
        
        if int(input_n) == 1: #レシピ検索を継続する場合、レシピ検索関数を再度呼び出し
            input_word()
        
        elif int(input_n) == 0: #レシピ検索を終了する場合以下print
            print("レシピ選択を終わります。 現在" + str(len(index_list)) + "個のレシピが選択されています。")
            print("選択されたレシピ index : " + str(index_list))
            print("それでは買い物リストを作りましょう") 
        else: #0と1以外の数字が入力された時、再度レシピ検索継続か問う
            print("1 or 0で選択してください") 
            end()
    else: #数字以外の文字が入力された時、再度レシピ検索継続か問う
        print("数字以外が入力されました") 
        end()
```

# 全コード
```python:Python
#必要ライブラリのインポート
import openpyxl 
import csv
from fractions import Fraction  

#ファイルを開く
ing_wb = openpyxl.load_workbook("recipe_ingredients.xlsx") #レシピの材料と量の一覧表
ing_ws = ing_wb.worksheets[0] 

index_wb = openpyxl.load_workbook("recipe_index.xlsx") #レシピ名、ＵＲＬ、材料、人数分の表一覧
index_ws = index_wb.worksheets[0]
  
#index.レシピ名.URL.人数.レシピ名と材料が一緒になった列のリストを作る
index_name_lists=[]
def create_index_namelist(): 
    
    for row in index_ws.iter_rows(min_row =2, min_col=1, max_col=6):
        index_name =[]
        index_name.append(row[0].value) #index列
        index_name.append(row[1].value) #レシピ名
        index_name.append(row[2].value) #URL
        index_name.append(row[3].value) #人数
        index_name.append(row[5].value) #レシピ名と材料が一緒になった列　

        index_name_lists.append(index_name)

#index.材料.量が一緒になったリストを作る
index_ing_lists=[]
def create_index_inglist():   
    
    for row in ing_ws.iter_rows(min_row =2, min_col=1, max_col=3):
        index_ing =[]
        index_ing.append(row[0].value) #index列
        index_ing.append(row[1].value) #材料
        index_ing.append(row[2].value) #量
        index_ing_lists.append(index_ing)

# 検索文字が正しく入力されているか判定する
def input_word():  
    word = str(input("検索ワードを日本語で入力してください : "))
    word_result =  word.isalpha() #ひらがな／カタカナ／漢字が文字判定(True)される。数字は除外(False)
    if word_result :
        search_word(word)
    else:
        print("日本語以外の文字が入力されました  ")     
        input_word()  
    
#検索文字が一致するセルの座標をrecipe_indexファイルから検索
def search_word(word):    
    title_list = [] #検索件数を把握するためにtitle_list にtitle格納する
    for index_name in index_name_lists: 
         # 検索ワードと一致した時        
        if word in index_name[4]:
          
            # index と URLを取得する
            index = index_name[0]
            title = index_name[1]
            url = index_name[2]
            people_n = index_name[3]
            title_list.append(title)

            message(index, title, url, people_n)

            for index_ing in index_ing_lists:   #リストとして持っておく
                if str(index) in str(index_ing[0]):
                    ingredient = index_ing[1]
                    amount =  index_ing[2]           
                    print("{"+ str(ingredient)+"："+str(amount)+"}")

    print( str( len(title_list))+ "件のレシピが見つかりました。" )

    if len(title_list) == 0: # 検索結果無い場合、検索ワード入力に戻る
        print("検索ワードの文字表記を変えると、検索結果が変わるかもしれません") 
        input_word()
    
    else: # 検索結果がある場合、レシピのindex選択に進む
        input_index()

def message(index, title, url, people_n):
    print("+++++++++++++++++++++++++++++++++++++++++")
    print("index : "+ str(index))
    print("レシピ名 : "+ title)
    print(str(people_n)+ " 人分のレシピです")
    print("URL : "+ url)
    
index_list =[] # レシピindexのリスト    
def input_index(): # 買い物リストに入れるレシピの index が 正しい数字か判定
    recipe_index = input("買い物リストに入れるレシピの　indexを入力してください") 
    index_result = recipe_index.isdigit() # 正の整数か判定
    
    if index_result:
        if 0 <= int(recipe_index) <= 2031:
            index_list.append(int(recipe_index))
            end()        
        else:
            print("indexにない数字が入力されました") 
            input_index()
    else:
        print("数字以外の文字が入力されました") 
        input_index()

def end(): # レシピ検索継続するか 終了するか入力する関数
    input_n = input("まだレシピ検索しますか？Yes:1,No:0で選択してください") 
    n_result = input_n.isdigit() # 整数か判定
    if n_result:
        
        if int(input_n) == 1:
            input_word()
        
        elif int(input_n) == 0:
            print("レシピ選択を終わります。 現在" + str(len(index_list)) + "個のレシピが選択されています。")
            print("選択されたレシピ index : " + str(index_list))
            print(word_list[8]) #それでは買い物リストを作りましょう
        else:
            print("1 or 0で選択してください") 
            end()
    else:
        print("数字以外が入力されました") 
        end()
```

以上でレシピ検索システム編終了です。
次回は最終回、量の計算編です！

https://qiita.com/yuuauuy1/items/6f08d8d6d4b0b62f71b0#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%8C%E9%81%B8%E6%8A%9E%E3%81%97%E3%81%9F%E3%83%AC%E3%82%B7%E3%83%94%E3%82%92%E8%A1%A8%E7%A4%BA