6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Tinder APIで好みの女性芸能人に似た人を自動スワイプ

Last updated at Posted at 2019-10-09

毎日のTinder自動スワイプが面倒臭いことに加えて、せっかくなら好みの女性芸能人に似た人をLike(=それ以外はPass)したいと思ったことがキッカケである。類似度判定には、AWSの画像認識サービスであるRekognition APIを使用してみた。

スクリーンショット 2019-10-10 1.30.23.png

先ずは、ブラウザから以下URLへアクセスし、FaceBookのアクセストークンを取得するまでの流れを追ってみよう。

ログインが成功すると、リダイレクト後の/v2.10/dialog/oauth/confirm/のレスポンスを確認すると以下のようなデータが取得できる。

<script type="text/javascript">window.location.href="fb464891386855067:\/\/authorize\/#signed_request=LFt3F8b6JR0QYqlcFQQdaWWJ-xTtK-woHyIuQO1Dkug.eyJ1c2VyX2lkIjoiNTczMjQ4MjU2MTIzMjg3IiwiY29kZSI6IkFRQUc1SDRiZ2I4Z0hxZTVNbEtaLWxpWUhiTTN2cnVCeHRYejZWbXJaZ2ctVVJTdHByWE41NEZ1U3ZrYmpKTXN3TFpIalcxSktUc1FZaW5OTERLZ0FOaGRqdXBycVNxUUh3TE55T1FTeDRKck11c2xfZEpvOUFaQjNGUHJsZlgtanlCR05OQ2NMZk82blJSbXF0SGtjVmRDMG9xeVdDZmIxMW1QLTBPOHdhNnYxTHBuV0ZYWGhDUEgwM1p4WnVBQVFrYUZDQUNUUkpFVjBZdlpXT0dRWklXTG1GaUNROVBRbnV4ZUZPN0t2S045RXN5T3lBR2hzOGltWkhCNE9GMFdtY2VuWS1Gajd0QmhOU1FISFBEMC1wSHg5WE1yNXM2U1BrM0Y1OGR6QmdfekdHY2tGRzdiVE1EV0xWd3pQNXpHUHJ6Q1JDMnZ4X25zS1M3alhadkRpSDc1IiwiYWxnb3JpdGhtIjoiSE1BQy1TSEEyNTYiLCJpc3N1ZWRfYXQiOjE1NzA2MzE2NDV9&access_token=EAAGm0PX4ZCpsBAMGnFxdrE8bga4lW6rWBznvojpUjy8twmP1bTA8ZADATxALwveAKb58SZBj<省略>&data_access_expiration_time=1578407645&expires_in=5155";</script>

上記の「access_token=」の箇所がFaceBookの発行するアクセストークンである。Tinder APIを操作するには、X-Auth-Tokenが必要となるが、このトークンを発行するためにFaceBookのアクセストークンが必要となる。

######アクセストークン取得クラス

import requests
import json
from bs4 import BeautifulSoup
import boto3
from botocore.exceptions import ClientError

class GetAccessToken:
    def __init__(self):
        self.client_id = '464891386855067'
        self.fallback_redirect_uri = '221e1158-f2e9-1452-1a05-8983f99f7d6e'
        self.logger_id = '0fb8ab93-fab7-4f2c-b836-483bb9ae6595'
        self.fb_dtsg = 'AQEMLhbRCqkx:AQH2Bj3HImtl'
        self.email = '*****' #FaceBookログインメールアドレス
        self.password = '*****' #FaceBookパスワード
        self.redirect_uri = 'fb464891386855067://authorize/'
        self.jazoest = '22086'
        self.base_url = 'https://www.facebook.com'
        self.dialog_url = self.base_url + '/v2.10/dialog/oauth'
        self.confirm_url = self.base_url + '/v2.10/dialog/oauth/confirm'
        self.session = requests.Session()
        self.default_headers = {
            'Accept-Language': 'ja,en-US;q=0.9,en;q=0.8',
            'Content-Type': 'application/x-www-form-urlencoded, application/json',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
        }

    def __get_login_page(self):
        params = {
            'redirect_uri': self.redirect_uri,
            'scope': 'user_birthday,user_photos,user_education_history,email,user_relationship_details,user_friends,user_work_history,user_likes',
            'response_type': 'token,signed_request',
            'client_id': self.client_id,
            'ret': 'login',
            'fallback_redirect_uri': self.fallback_redirect_uri,
            'ext': '1556057433',
            'hash': 'Aea6jWwMP_tDMQ9y'
        }

        try:
            res = self.session.get(url=self.dialog_url, headers=self.default_headers, params=params)
            return res
        except Exception as e:
            return 'Caught Exception {}'.format(e)

    def __login(self):
        res = self.__get_login_page()
        login_form_html = BeautifulSoup(res.text, 'html.parser')
        uri = login_form_html.find(attrs={'id': 'login_form'}).get('action')

        data = {
            'email': self.email,
            'pass': self.password
        }

        try:
            res = self.session.post(url=self.base_url + uri, headers=self.default_headers, data=data, cookies=res.cookies.get_dict())
            return res
        except Exception as e:
            return 'Caught Exception {}'.format(e)

    def get_access_token(self):
        res = self.__login()

        data = {
            'jazoest': self.jazoest,
            'fb_dtsg': self.fb_dtsg,
            'from_post': '1',
            'app_id': self.client_id,
            'ret': 'login',
            'return_format': 'signed_request,access_token',
            'logger_id': self.logger_id,
            'sheet_name': 'initial',
            'redirect_uri': self.redirect_uri,
            'fallback_redirect_uri': self.fallback_redirect_uri,
            'display': 'page',
            '__CONFIRM__': '1'
        }

        try:
            response = self.session.post(url=self.confirm_url, headers=self.default_headers, data=data, cookies=res.cookies.get_dict())
            idx = response.text.find('&access_token=')
            end_idx = response.text.find('&data_access_expiration_time=')
            access_token = response.text[idx+14:end_idx]
            return access_token
        except Exception as e:
            return 'Caught Exception {}'.format(e)

#####類似度判定クラス (※折角なので、マッチのユーザが見つかった際に、SNSでメール通知を行うようにした)

S3バケットの「src」フォルダに候補女性の画像一式を自動でダウンロードしアップロードするようにしている。
「target」フォルダには好みの女性の写真を格納してね。

class AWSAPI:
    def __init__(self):
        self.sns = boto3.client('sns')
        self.rekognition = boto3.client('rekognition')
        self.src_bucket_name = '*****' #画像データを格納するS3バケット名
        self.s3 = boto3.client('s3')
        self.topicArn = '*****' #トピックARN

    def compare_images(self, bucketName, srcKeyName, targetKeyName):
        try:
            response = self.rekognition.compare_faces(
                SourceImage={
                    'S3Object': {
                        'Bucket': bucketName,
                        'Name': srcKeyName,
                    }
                },
                TargetImage={
                    'S3Object': {
                        'Bucket': bucketName,
                        'Name': targetKeyName,
                    }
                },
                SimilarityThreshold=0
            )
        except ClientError as e:
            return 'Caught ClientError {}'.format(e)

        return response['FaceMatches'][0]['Similarity']

    def create_obj_list(self, bucketName, targetfolderName):
        try:
            response = self.s3.list_objects(
                Bucket=bucketName,
                )
            target_key_list = []
            for i in range(len(response['Contents'])):
                if targetfolderName in response['Contents'][i]['Key']:
                    target_key_list.append(response['Contents'][i]['Key'])
            target_key_list.remove(targetfolderName + '/')
            return target_key_list
        except ClientError as e:
            return 'Caught ClientError {}'.format(e)

    def upload_images(self, bucketName, localfile, keyName):
        with open(localfile, 'rb') as data:
            self.s3.upload_fileobj(data, self.src_bucket_name, keyName)

    def delete_images(self, bucketName, keyName):
        try:
            response = self.s3.delete_object(
                Bucket=bucketName,
                Key=keyName
            )
            return response
        except ClientError as e:
            return 'Caught ClientError {}'.format(e)

    def publish_sns(self):
        try:
            response = self.sns.publish(
                TopicArn=self.topicArn,
                Message='マッチしたユーザが見つかりました。',
                Subject='Tinder Match Notification.'
            )
        except ClientError as e:
            print('Caught exception: %s' % e)

#####Tinder API実行クラス
"<類似度の閾値>"に閾値を入れる。可愛い女性芸能人で閾値を70(%)とかにすると、殆どライク判定がされないので注意。w
まぁ、そりゃそうだ。

class TinderAPI(AWSAPI):
    def __init__(self, access_token):
        super().__init__()
        facebook_id = '**********'
        self.base_url = 'https://api.gotinder.com'
        auth_url = self.base_url + '/v2/auth/login/facebook'
        data = {
            'facebook_id': facebook_id,
            'token': access_token
        }

        self.session = requests.Session()
        res = self.session.post(url=auth_url, data=json.dumps(data))
        self.x_auth_token = res.json()['data']['api_token']

        self.api_headers = {
            'X-Auth-Token': self.x_auth_token,
            'Content-type': 'application/json',
            'User-agent': 'Tinder/3.0.4 (iPhone; iOS 7.1; Scale/2.00)'
        }

    def swipe_like_user(self):
        uri = self.base_url + '/user/recs'
        response = self.session.get(url=uri, headers=self.api_headers)
        res = json.loads(response.text)

        for i in range(len(res['results'])):
            for j in range(len(res['results'][i]['photos'][0]['processedFiles'])):
                img_url = res['results'][i]['photos'][0]['processedFiles'][j]['url']
                self.__get_images(img_url, j)
                self.upload_images(bucketName=self.src_bucket_name, localfile=str(j)+'.jpg', keyName='src/'+str(j)+'.jpg')
                target_key_list = self.create_obj_list(self.src_bucket_name, 'target')
                for k in range(len(target_key_list)):
                    response = self.compare_images(self.src_bucket_name, 'src/' + str(j) + '.jpg', target_key_list[k])
                    like_res = self.__like_user(res['results'][i]['_id']) if response > <類似度の閾値> else self.__pass_user(res['results'][i]['_id'])
                    self.publish_sns() if (self.__like_user(res['results'][i]['_id'])) else print('Not matched.')
            src_key_list = self.create_obj_list(self.src_bucket_name, 'src')
            for delete_object in src_key_list:
                self.delete_images(self.src_bucket_name, delete_object)

    def __get_images(self, url, prefix):
        img = requests.get(url, stream=True)
        with open(str(prefix) + '.jpg', 'wb') as f:
            f.write(img.content)

    def __like_user(self, uid):
        uri = self.base_url + '/like/' + uid
        res = self.session.get(url=uri, headers=self.api_headers)
        if json.loads(res.text)['match'] == True:
            return True
        else:
            return False

    def __pass_user(self, uid):
        uri = self.base_url + '/pass/' + uid
        res = self.session.get(url=uri, headers=self.api_headers)

        return res
6
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?