yoshiislandblog.net
元営業の駆け出しアラサーSEが、休日にMACと戯れた際の殴り書きメモ。日々勉強。日々進歩。

この記事は3年以上前に書かれた記事で内容が古い可能性があります

flaskでAPIサーバを作成する

2018-03-21

flaskでAPIサーバを作成してみる
今回は、TwitterIDから、最近呟いた地名を取得するAPIを作成する
MeCabなど環境作成はサクッとAmazon LinuxにMecab導入を参考にしてください

 

【APIサーバ】flask-corsをインストール

# pyenv versions
  system
* 3.5.0 (set by /root/.python-version)


# pip install requests requests_oauthlib

# pip install flask-cors

# pip freeze
certifi==2018.1.18
chardet==3.0.4
click==6.7
Flask==0.12.2
Flask-Cors==3.0.3
idna==2.6
itsdangerous==0.24
Jinja2==2.10
MarkupSafe==1.0
mecab-python3==0.7
netifaces==0.10.6
oauthlib==2.0.6
requests==2.18.4
requests-oauthlib==0.8.0
six==1.11.0
urllib3==1.22
Werkzeug==0.14.1

【APIサーバ】各スクリプトの配置

最新版はこちら(https://github.com/yoshi-island/mecab-twitter-api)

「get_tweets_place_list.py」で、tweetを取得してMecabで地名を抽出、
「mecab-twitter-api.py」で、APIサーバとしてリクエストが来たら返信する

passwords.py

Twitter tokenは一応別ファイルに書いておいて、モジュールとして呼び出す。
Twetterの設定はこちらを参考に。
twitter botを作ってみた

passwords.py

oath_keys = {
 "consumer_key": "",
 "consumer_secret": "",
 "access_token": "",
 "access_token_secret": ""
 }

get_tweets_place_list.py

get_tweets_place_list.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# python 3.5.0


###################
# usage
###################
#
#user = "<tweetuser>"
#place_list_rank = get_tweets_place_list(user)
#
# passwords.py
# oath_keys = {
#  "consumer_key": "",
#  "consumer_secret": "",
#  "access_token": "",
#  "access_token_secret": ""
#  }


###################
# import modules
###################

from requests_oauthlib import OAuth1Session
import json
import MeCab
import collections
import passwords

###################
# variables
###################

oath_keys = passwords.oath_keys



###################
# create oath session
###################
def session_create(oath_keys):
  oath = OAuth1Session(
  oath_keys["consumer_key"],
  oath_keys["consumer_secret"],
  oath_keys["access_token"],
  oath_keys["access_token_secret"]
  )
  return oath



###################
# get tweets
###################
def get_tweets(user,oath):
  user = user
  url = "https://api.twitter.com/1.1/statuses/user_timeline.json?"
  params = {
    "screen_name": user,
    "count": "1000"
    }
  oath = oath
  responce = oath.get(url, params = params)

  if responce.status_code != 200:
    print("Error code: %d" %(responce.status_code))
    return None
  tweets = json.loads(responce.text)
  #tweets_format = json.dumps(tweets, indent=4, separators=(',', ': '))
  #print(tweets_format)

  tweets_text_list = ""
  for l in tweets:
    tweets_text_list += l['text'] + "\n"

  return tweets_text_list



###################
# mecab_analyze_tweets
###################
def mecab_analyze_tweets(tweets_text_list):
  tweets_text_list = tweets_text_list.split("\n")
  mecab = MeCab.Tagger("-Ochasen")
  place_list = []
  place_list_srctwt = []
  for l in tweets_text_list:
    if len(l) > 0:
      mecab_parsed = mecab.parse(l)
      items = mecab_parsed.split("\t")
      if len(items)>4:
        if items[3].find("地域") > -1:
          if len(items[0]) > 1:
            if items[0] != "日本":
              place_list.append(items[0])
              place_list_srctwt.append(l)

  count_dict = collections.Counter(place_list)
  place_list_rank = count_dict.most_common(10)

  return place_list_rank,place_list_srctwt



###################
# main
###################
#if __name__ == "__main__":
def get_tweets_place_list(user):
  oath = session_create(oath_keys)
  tweets_text_list = get_tweets(user,oath) # list
  place_list_rank,place_list_srctwt = mecab_analyze_tweets(tweets_text_list) # list
  return place_list_rank, place_list_srctwt

mecab-twitter-api.py

mecab-twitter-api.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# python 3.5.0


###################
# import modules
###################

import get_tweets_place_list
from flask import Flask, jsonify, abort, make_response
import netifaces
import json
from flask_cors import CORS, cross_origin
import ssl



###################
# get local address
###################

sv_ip = netifaces.ifaddresses('eth0')[netifaces.AF_INET][0]['addr']

###################
# api functions
###################

api = Flask(__name__)
CORS(api)
api.config['JSON_AS_ASCII'] = False # for Japanese language
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain('<cert path>', '<key path>')

@api.route('/getTweetsPlace/<string:userId>', methods=['GET'])
def get_user(userId):
  user = userId

  try:
    place_list_rank, place_list_srctwt = get_tweets_place_list.get_tweets_place_list(user) # error judge
    place_name = place_list_rank[0][0] # data existance judge
    place_cnt = place_list_rank[0][1] # data existance judge
  except:
    return make_response(jsonify({'error': 'Not found'}), 404)

  rank_list = [
    {
      "number": 1,
      "place": place_name,
      "count": place_cnt
    }]

  for i in range (2,6):
    place_name = ""
    place_cnt = ""
    try:
      place_name = place_list_rank[i][0]
      place_cnt  = place_list_rank[i][1]
      component = {
          "number": i,
          "place": place_name,
          "count": place_cnt
        }
      rank_list.append(component)
    except:
      break

  result = {
    "result":True,
    "data":{
      "userId":user,
      "rank":rank_list,
      "src_place_twt": place_list_srctwt
    }
  }

  return make_response(jsonify(result))

@api.after_request
def after_request(response):
  response.headers.add('Access-Control-Allow-Origin', '*')
  response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
  response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS')
  return response


@api.errorhandler(404)
def not_found(error):
    return make_response(jsonify({'error': 'Not found'}), 404)

if __name__ == '__main__':
    api.run(host=sv_ip, port=3000, ssl_context=context, threaded=True, debug=True)

【Mac】実行

ローカルMacから実行

今回はホリエモンのIDを利用

https://{API IP}:3000/getTweetsPlace/@{TwitterID}

きちんと返って来ている

% curl -k https://{API IP}:3000/getTweetsPlace/@takapon_jp
{
  "data": {
    "rank": [
      {
        "count": 1,
        "number": 1,
        "place": "押尾"
      },
      {
        "count": 1,
        "number": 2,
        "place": "大阪"
      },
      {
        "count": 1,
        "number": 3,
        "place": "藤沢"
      },
      {
        "count": 1,
        "number": 4,
        "place": "心斎橋"
      }
    ],
    "src_place_twt": [
      "エジプト古代人!",
      "藤沢数希さん(@kazu_fujisawa)のぼく愛もそう…",
      "押尾紗季プロ❣️",
      "心斎橋駅から徒歩1分です!",
      "大阪府 大阪市中央区 西心斎橋1-9-31 辻本ビル 4階"
    ],
    "userId": "@takapon_jp"
  },
  "result": true
}

Trouble Shooting

以下のようなエラーが出たので

The 'Access-Control-Allow-Origin' header has a value 'null' that is not equal to the supplied origin. Origin 'null' is therefore not allowed access.

以下を参考に、「@api.after_request」を追加したら、解消した
https://stackoverflow.com/questions/22181384/javascript-no-access-control-allow-origin-header-is-present-on-the-requested

@api.after_request
def after_request(response):
  response.headers.add('Access-Control-Allow-Origin', '*')
  response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
  response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS')
  return response

参考: 備忘:ログアウトしてもバックグランドでコマンドを実行し続ける