タイトルの通りです。
Pythonを勉強しているうちに、スクレイピングというものの存在を知りましたが、TwitterではAPIを経由しないスクレイピングは規約違反になってしまうので、まずはTwitter APIを触ってみました。
Twitter利用規約より
https://help.twitter.com/en/rules-and-policies/twitter-automation
Don’t!
・Use non-API-based forms of automation, such as scripting the Twitter website. The use of these techniques may result in the permanent suspension of your account.
禁止!
・Twitterウェブサイトのスクレイピングのような、APIを利用しないフォームでの自動操作。これらの操作によってアカウントが永久停止される可能性があります。
今回利用したものは以下の通り。
・OSはWindows10
・基本的にはPythonとFlaskを使って作成
・画面成形用にBootstrap4を利用
前準備1.Twitterデベロッパーアカウントの申請
Twitter APIを利用するには、通常のTwitterアカウントに加えてデベロッパー用アカウントの申請が必要です。
デベロッパー用アカウントの申請については、こちらの記事等を参考に行ってください。
参考:Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ ※2019年8月時点の情報
アクセストークンの生成まで行う必要があります。
ちなみに申請の承認はすぐに来ました!
前準備2.Pythonの導入
今回はPythonで作成するので予め導入する必要があります。
まだの方はこちらの記事等を参考に導入してください。
参考:Windows10 64bitにPython3.8をインストール~バージョン確認~実行まで
また、PythonのフレームワークとしてFlaskを利用しています。
参考:Windowds10にPythonフレームワークのFlaskをインストール
Twitter APIを利用した絞りこみ検索の実装
今回の実装は基本的にこちらの記事を参考にさせて頂きました。
それでは実装に入っていきます。
作業ディレクトリの作成
まずは空のディレクトリとファイルを作っていきます。
│ app.py
│
├─apis
│ getLimitApi.py
│ getSearchTweets.py
│ __init__.py
│
├─lib
│ config.py
│ __init__.py
│
└─templates
base.html
index.html
result.html簡単に各ディレクトリの内容を解説。
・apis・・・Twitter APIへリクエストを送る処理を格納
・lib・・・設定ファイル用(今回はtoken情報)
・templates・・・画面用(htmlファイル)
・app.py・・・サーバーの立ち上げや、ルーティングを行うメインファイル
また、__init__.pyはそれぞれ他のディレクトリからパッケージとして呼び出すために必要です。どのファイルも中身は空で大丈夫です。
Requests-OAuthlibライブラリのインストール
次に、認証用のライブラリをインストールします。
コマンドライン等を開き、”pip install requests requests-oauthlib“を行います。(場所はどこでも大丈夫です。)
pip install requests requests-oauthlibconfig.pyの作成
予め作成しておいたlib/config.pyの中身を埋めていきます。
こちらはTwitterデベロッパーサイトで生成された4つのキー&トークン情報を格納するだけです。
上から順に格納してください。
lib/config.py
CONSUMER_KEY = "*************************"
CONSUMER_SECRET = "***************************************************"
ACCESS_TOKEN = "***************************************************"
ACCESS_TOKEN_SECRET = "***************************************************"Twitter API利用部分の作成
次にapis/の中を作成していきます。
apis/getSearchTweets.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
from requests_oauthlib import OAuth1Session
# libフォルダからconfigファイルの読込
from lib import config
# OAuth認証部分
CK = config.CONSUMER_KEY
CS = config.CONSUMER_SECRET
AT = config.ACCESS_TOKEN
ATS = config.ACCESS_TOKEN_SECRET
twitter = OAuth1Session(CK, CS, AT, ATS)
# Twitter Endpoint(検索結果を取得する)
url = 'https://api.twitter.com/1.1/search/tweets.json'
def search_tweet(keywords, min_retweets):
keyword = keywords + ' min_retweets:' + min_retweets
params ={
'count' : 30, # 取得するtweet数
'q' : keyword, # 検索キーワード
'result_type': 'recent',
'lang': 'ja',
'locale': 'ja'
}
#検索して結果をreqに格納
req = twitter.get(url, params = params)
#reqからツイート情報を取り出し返却
tweets = json.loads(req.text)
result = tweets['statuses']
return result画面から来る情報を基に検索APIを呼び出すファイルです。
configから取得したキー情報を格納後、検索パラーメーターをセットし、検索のTwitter APIを実行しています。
下の方では、データを扱いやすいよう必要な部分を取り出しています。
apis/getLimitApi.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
import datetime
import time
import math
from requests_oauthlib import OAuth1Session
from pytz import timezone
from dateutil import parser
from lib import config
# OAuth認証部分
CK = config.CONSUMER_KEY
CS = config.CONSUMER_SECRET
AT = config.ACCESS_TOKEN
ATS = config.ACCESS_TOKEN_SECRET
twitter = OAuth1Session(CK, CS, AT, ATS)
# Twitter Endpoint(検索結果を取得する)
url = 'https://api.twitter.com/1.1/application/rate_limit_status.json'
def getLimit():
req = twitter.get(url)
if req.status_code == 200:
limit_api = json.loads(req.text)
# リクエスト制限回数
limit = limit_api['resources']['search']['/search/tweets']['limit']
# リクエスト残り回数
remaining = limit_api['resources']['search']['/search/tweets']['remaining']
# リクエスト回復までの時間
reset = limit_api['resources']['search']['/search/tweets']['reset']
reset_minute = math.ceil((reset - time.mktime(datetime.datetime.now().timetuple())) / 60)
return limit, remaining, reset_minute
else:
print("Failed: %d" % req.status_code)デフォルトではTwitter APIの利用回数に制限があるので、その情報を取得するファイルです。
(時間の細かい算出部分はよくわかっていません。。)
画面部分の作成
操作しやすいように簡単な画面も作ってみました。
画面はFlaskにデフォルトで入っているjinja2というテンプレートを利用しています。
templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous">
</head>
<body style="padding-top:4.5rem;">
{% block body %}
{% endblock %}
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous"></script>
</body>
</html>各htmlファイルのベースとなるファイルです。今回はbootstrap4とjqueryの読み込みしかしていません。
{% block body %} {% endblock %}の部分がテンプレートの機能で、中身は別のファイルに分けて書いています。
templates/index.html
{% extends "base.html" %}
{% block body %}
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
<a class="navbar-brand" href="/">Twitter Search</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-item nav-link">API Limit:{{ limit[0] }}</a>
<a class="nav-item nav-link">API Remaining:{{ limit[1] }}</a>
<a class="nav-item nav-link">Reset minutes:{{ limit[2] }}</a>
</div>
</div>
</nav>
<div class="container">
<form action="/search" method="POST">
<div class="form-group">
<label>検索ワード</label>
<input type="text" class="form-control" name="keywords">
</div>
<div class="form-group">
<label>最低リツイート</label>
<input type="text" class="form-control" name="retweet">
</div>
<button type="submit" class="btn btn-primary">送信する</button>
</form>
</div>
{% endblock %}1つ前に作成したbase.htmlというファイルを継承して作っています。今回は検索ワードと最低RT数だけ指定してサーバー側の/searchに送るようにしています。
また、ナビゲーション部分でAPIリクエストの残り回数などを表示しています。
templates/result.html
{% extends "base.html" %}
{% block body %}
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
<a class="navbar-brand" href="/">Twitter Search</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-item nav-link">API Limit:{{ limit[0] }}</a>
<a class="nav-item nav-link">API Remaining:{{ limit[1] }}</a>
<a class="nav-item nav-link">Reset minutes:{{ limit[2] }}</a>
</div>
</div>
</nav>
<div class="container" align="center">
{% for r in res %}
<blockquote class="twitter-tweet">
<a href="https://twitter.com/{{ r['user']['screen_name'] }}/status/{{ r['id'] }}"></a>
</blockquote>
{% endfor %}
<!-- Twitter埋め込み用 -->
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</div>
{% endblock %}検索結果を表示する画面です。
ただ表示するだけだと殺風景なので、Twitterの埋め込み機能を使って表示しています。
app.pyの作成
最後にメインファイルとなるapp.pyの作成です。
app.py
from flask import Flask, render_template, request
from apis import getSearchTweets
from apis import getLimitApi
app = Flask(__name__)
@app.route('/')
def main():
limits = getLimitApi.getLimit()
return render_template('index.html', limit=limits)
@app.route('/search', methods = ['POST'])
def confirm():
result = request.form['keywords']
min_retweets = request.form['retweet']
rest = getSearchTweets.search_tweet(result, min_retweets)
limits = getLimitApi.getLimit()
return render_template('result.html', res=rest, limit=limits)
if __name__ == '__main__':
app.run(debug=True)ルートにアクセスがあった場合は検索画面を、/searchへPOSTリクエストがあった場合はフォームの情報を渡して結果画面を表示するようにしています。
以上で実装は完了です。
実際に動かしてみる
コマンドライン等で、app.pyがあるディレクトリに移動し、”python app.py“を実行します。
C:\Users\tfjkv\Desktop\twitter_api>python app.py
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Restarting with stat
* Debugger is active!
* Debugger PIN: 307-612-845
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)Google Chromeなどのブラウザを立ち上げ、”http://127.0.0.1:5000/ “にアクセスしましょう。
フォーム画面が表示されていれば完了です。
検索したいワードと最低RT数をいれて送信してみてください。
・Twitter APIデフォルトの設定では、30日間までしかツイートを遡れないので、それ以前のツイートは表示されません。
・エラー処理等は書いていないので、クエリが不適切or結果がないと何も表示されません。
・ナビゲーションバーの”Twitter Search”をクリックすると検索画面に戻ります。
補足事項
いくつか補足事項をまとめておきます。
Twitter APIについて
今回は検索と、API残り回数の取得しか利用していませんが、他にも多くの機能があります。
詳しくは、Docをご覧ください。
検索条件について
検索条件として利用したのは最低RT数のみですが、メディアタイプや、いいね数、RTを除外等も可能です。
詳しくはこちらの記事が参考になります。
また、これらの検索条件はスマホ用のTwitterアプリでも利用することができます。(実装後に知りました。。)
活用方法
今回は検索結果をブラウザ上で確認するよう出力しましたが、pythonを使ってファイル等に書き出すことも可能です。
データを集める事で、どのようなツイートがRTされやすいのか等の傾向を把握することが出来ると思います。
以上、pythonを使ってTwitter APIを呼び出す実装についてまとめてみました。
不明点やご指摘等あればお気軽にコメントやお問い合わせお願いします!
コメント