AIが選んだ本当に似ている有名人トップ10　～実装編～

#はじめに
前回、「[AIが選んだ本当に似ている有名人トップ10　～結果編～](https://qiita.com/Sinhalite/items/37a1c326cb2cd7034776)」という記事を書きまして、有名人そっくりさん選手権の優勝者を決定しました。今回はその時ランキング化するために利用したアプリケーションやコードについて書いていきたいと思います。

#構成図
![Untitled Diagram.jpg](https://qiita-image-store.s3.amazonaws.com/0/104983/fa574503-bfcf-761a-c18a-948a2947b7b5.jpeg)

はじめて[draw.io](https://www.draw.io/)で作ってみましたが…雑や…。
それでもエクセルで作るよりは、便利だなーという印象でした。

#手順
##1.「soKKuri?」をスクレイピング
まずは「[soKKuri?](https://sokkuri.net/)」よりそっくり度が高いと評価されている有名人のペアを取得するため、スクレイピングを行います。そっくりさんランキングというページがありますので、そちらのリストを取得した上で、さらに投票数が多いペアに絞って抽出します。
スクレイピングには使い慣れてるBeautifulSoupを利用しました。余計な文字などは、replaceメソッドで削っていきます。スクレイピングのお作法として、適宜sleepをはさみます。

```python:
import urllib.request
from time import sleep
from bs4 import BeautifulSoup
from icrawler.builtin import GoogleImageCrawler
import csv
import os

#そっくりさんリスト作成
pairList = []
for i in range(1, 2):
  print('ページ' + str(i) + 'を確認中')
  req = urllib.request.urlopen('https://sokkuri.net/list.php?m=rank&p=' + str(i))
  soup = BeautifulSoup(req,'html.parser')

  #liタグのリストを取得。先頭行は見出しなので削除。
  rows = soup.find_all('li')
  rows.pop(0)

  #似ているが1000票以上のペアをリストに追加
  for row in rows:
    voteCount = row.find('span', class_='lp-ok').string
    voteCount = int(voteCount[:-1].replace(',', ''))

    if voteCount >= 1000:
      pair = row.find('span', class_='lp-pair').text.replace('？','')
      pair = [pair.split('　')[0],pair.split('　')[2]]
      pairList.append(pair)
  sleep(4)

```

##2.画像の検索とダウンロード
配列に格納した有名人の名前をもとに、Google画像検索を行い、1人につき10枚の画像をローカルに保存します。
今回はiCrawlerというライブラリを利用しました。

```python:
#画像のダウンロード
for i, pair in enumerate(pairList):
  downloadBasePath = 'static/images/' + str(i)
  os.mkdir('static/images/' + str(i))
  #画像はそれぞれA/Bに振り分ける
  os.mkdir('static/images/' + str(i) + '/A')
  os.mkdir('static/images/' + str(i) + '/B')

  for j, person in enumerate(pair):
    if j == 0:
      optionPath = '/A'
    else:
      optionPath = '/B'

    crawler = GoogleImageCrawler(storage={'root_dir':  downloadBasePath  + optionPath})
    crawler.crawl(keyword=pair[j], max_num=10)
```
##3.比較に利用する画像を選択する
ダウンロードした10枚の画像の中から、比較に使えそうな画像を1枚選択します。
これはさすがに手作業で見るしかないので、相当骨が折れる作業となります。それなので今回は、画像選択を容易に実施するためのウェブアプリケーションを、Flaskを利用して作成しました。
Flaskを使うのは初めてでしたが、「Rails的な感じでいけるやろ!」とゴリ押ししていたら、まぁ案の定動きました:muscle_tone4:

機能としては、ダウンロードしてきた画像を一覧表示し、クリックすると青枠で囲んで選択状態になります。選択状態の判定やフォームへの値渡しにはJQueryを使っています。
2人の画像を1枚ずつ選んで、送信ボタンをクリックすることで、FaceAPIにリクエストを送信します。

![imageClassifyTool.png](https://qiita-image-store.s3.amazonaws.com/0/104983/2b74c3f2-0a22-d621-19cc-b9872d0d7e19.png)

##4.FaceAPIにリクエストを送信する
選択した2枚の画像をFaceAPIに投げて、そっくり度を取得していきます。
最初は純粋に画像2枚をバイナリデータとして送信すれば良いのだと思っていたのですが、どうやらそのやり方はできないようです。画像の比較を行うためには、FaceIDという値を2つ、リクエストボディに含める必要があります。まずはDetectという機能を使って1枚画像を送信すると、FaceIDが取得できるので、それぞれの画像でリクエストを行い、FaceIDを取得しておきます。

```python:
def verify(imgAFaceId,imgBFaceId):
    url = 'https://japaneast.api.cognitive.microsoft.com/face/v1.0/verify'
    headers = {
        'Content-Type': 'application/json',
        'Ocp-Apim-Subscription-Key': ''
    }
    body = {
        'faceId1':imgAFaceId,
        'faceId2':imgBFaceId
    }
    return requests.post(url ,headers = headers,json = body).json()['confidence']
```

##5.CSVに保存
FaceAPIより返却されたそっくり度を、CSVに追記する形で保存します。
あとはPandasなりExcelなりで分析可能です。

```python:
def writeCsv(num,confidence):
    df = pd.read_csv('data.csv', index_col=0, encoding='shift-jis')
    df.iat[int(num),1] = confidence
    df.to_csv('data.csv', encoding='shift_jis')
```

#おわりに
使ったことないライブラリを使えたので、作りながらとても勉強になった気がします。
私レベルで分かることはお答えしますので、不明点あればコメントいただけると幸いです:angel: