見出し画像

【118日目】Django×Tweepy_絵文字を削除してテキストを保存&複数の例外処理

tweepyを用いたアプリ作りの続きです。
tweepyで取得したツイートデータを、Djangoを活用してデータベースに保存していく仕組みを作っています。

実装していて気が付いたのですが、データベースに保存する際、絵文字が入っていると「OperationalError」が出て保存ができません。

そのため今回は絵文字を削除したうえでデータベースに保存する方法をまとめたいと思います。


emojiライブラリのインストール

便利なもので、Pythonには絵文字を扱うためのライブラリが存在しています。これを用いると簡単に今回の目的が達成できるので、インストールが必要ですが使っていきたいと思います。

pip install emoji


get_emoji_regexpメソッドを活用する

emojiライブラリのうち、get_emoji_regexpメソッドを活用します。regexpとはregular_expressionの略で、正規表現の意です。正規表現とは文字列を表現する一つの方式で、特定の文字列や似たような特徴を持つ文字列を検索するときに特に威力を発揮します。

今回は取得したテキストやユーザー名(=文字列)の中から、emojiの正規表現パターンにマッチするものを検索するために活用します。

import emoji


モジュールreのsubメソッドを活用する

モジュールreとは、Pythonで正規表現を行うためのモジュールです。Pythonの標準ライブラリに含まれています。

このうちsubメソッドを活用します。subメソッド使い方は以下です。

re.sub(pattern, repl, string, count=0, flags=0)
string 中に出現する最も左の重複しない pattern を置換 repl で置換することで得られる文字列を返します。 パターンが見つからない場合、 string がそのまま返されます。 repl は文字列または関数です。 repl が文字列の場合は、その中の全てのバックスラッシュエスケープが処理されます。 \n は 1 つの改行文字に変換され、 \r はキャリッジリターンに変換される、などです。

https://docs.python.org/ja/3/library/re.html

今回はpatternにemojiの正規表現を、replには空(=削除)を、stringには取得したユーザー名やテキストを渡します。

import re
import emoji

user_name = re.sub(emoji.get_emoji_regexp(), "", tweet_data[i][2]) 
text      = re.sub(emoji.get_emoji_regexp(), "", tweet_data[i][3])



例外処理を複数回使ってエラーに対処する

上記で大体の絵文字は削除できると思いますが、世の中に存在する全てを削除しきれるわけではないと思います。そのため、絵文字が削除しきれずにエラーが出た場合の例外処理を定義しておきます。また、そもそもデータベースにはツイートを重複して保存できないようにしているので、重複した場合の例外処理もしておきます。

前者は「OperationalError」と出て、後者は「IntegrityError」と出ます。例外処理を定義するtry except文は複数の組み合わせが可能です。

コード全文は以下のようになっています。

[views.py]
from .models import Tweets
from .forms import GetTweetForm
from . import tweepy
import re
import emoji
 

def get_tweets_view(request):
    template_name = 'tweepy_test/twitter_get.html'
    ctx = {}
    if request.method == "GET":
        form = GetTweetForm
        ctx['form'] = form
        return render(request, template_name, ctx)
    
    if request.method == "POST":
        search_form = GetTweetForm(request.POST)
        if search_form.is_valid():
            search_criteria = search_form.cleaned_data
            searchkey       = search_criteria['searchkey']
            location        = ""
            # get_tweet()に渡す引数を準備
            search     ="{0} {1}".format(searchkey, location)
            item_num   = 10
            # ツイートを取得する関数を呼び出し
            tweet_data = tweepy.get_tweets(search, item_num)

            # 取得したツイートデータをDBに保存 & 保存した数を数えて表示する
            tweet_count = 0
            for i in range(len(tweet_data)):
                try:
                    user_name = re.sub(emoji.get_emoji_regexp(), "", tweet_data[i][2]) 
                    text      = re.sub(emoji.get_emoji_regexp(), "", tweet_data[i][3])
                    Tweets.objects.create(
                        tweet_id   =str(tweet_data[i][0]),
                        user_id    =str(tweet_data[i][1]),
                        user_name  =str(user_name),
                        text       =str(text),
                        favorite   =int(tweet_data[i][4]),
                        retweet    =int(tweet_data[i][5]),
                        created_at =tweet_data[i][6],
                        )
                    tweet_count += 1
                except OperationalError:
                    try:
                        Tweets.objects.create(
                            tweet_id   =str(tweet_data[i][0]),
                            user_id    =str(tweet_data[i][1]),
                            favorite   =int(tweet_data[i][4]),
                            retweet    =int(tweet_data[i][5]),
                            created_at =tweet_data[i][6],
                            )
                        tweet_count += 1
                    except IntegrityError:
                        pass
                except IntegrityError:
                    pass

            ctx = {
                'form': search_form,
                'searchkey': searchkey,
                'tweet_count': tweet_count, 
                }

            return render(request, 'tweepy_test/search_criteria.html', ctx)
        
        else:
            ctx['form'] = search_form
            return render(request, template_name, ctx)

中盤以降に例外処理の記述をしています。

  1. ツイート一つ一つに対して以下を繰り返し処理する

  2. フォームからの入力がバリデーションOKならツイートデータを取得

  3. 絵文字を除外してTweetsモデル(DB)に保存

  4. 3がOperationalErrorなら(=絵文字が残ったら)ユーザー名とテキストは除いて他の項目のみ保存

  5. 4がIntegrityErrorなら(=保存済みのツイートと重複したら)何もしない

  6. 3がIntegrityErrorなら(=保存済みのツイートと重複したら)何もしない


これでかなり思い通りのデータが集められるようになってきました。

ここまで整理して見えてきたのですが、取得したツイートデータを見てみると、単なるリツイートも1つのツイートとして拾っているみたいです。

引用リツイートならまだしも、単純なリツイートは1件とカウントしたくないので、ここにフィルターをかける方法がないかを今探っています。


ここまでお読みいただきありがとうございました!


参考


これまで修了したコース等

【YouTube_Django関係】
Pythonでウェブサービスを作ろう! 1
テンプレートをマスターしよう! 2
静的ファイルを配信しよう !3
本番公開しよう! 4
データベースと接続しよう! 5
ブログを作って学ぶモデル入門! 6
これが汎用ビューの力! 7
Djangoフォームを自由自在に操ろう! 8
djagoを最大限使って効率よくログインを作ろう! 9
ログイン完成!サインアップ & メール認証 10
データベースマイグレーション前編 15
データベースマイグレーション後編 16

【YouTube_Pandas関係】
3時間でマスター Pandas入門コース
Pandas20本ノック

【Paiza】
Aランクレベルアップメニュー 24/49問
データセット選択メニュー   12/17問
配列メニュー         64/64問
ループメニュー1      20/20問
ループメニュー2      12/20問
条件分岐メニュー       25/25問
二重ループメニュー      19/19問
配列活用メニュー       26/26問
文字列処理メニュー      30/30問
Bランクレベルアップメニュー 62/62問
Cランクレベルアップメニュー 30/30問
ランクB合格
ランクC合格
JavaScript体験篇       15/15講座
辞書(ディクショナリ)の基礎 8/8講座

【書籍/ブログ】
Django入門 | 初心者でも1時間でWebアプリ(Todoアプリ)を作成するコース
基礎からのMySQL
Web技術の基本
京大のPython教科書
Pythonデータベースプログラミング
Pythonエンジニアファーストブック

【Progate】
Python Ⅰ~Ⅴ
Python アプリ版 コースⅠ~Ⅴ
SQL Ⅰ~ Ⅳ
SQL アプリ版 コースⅢ
HTML&CSS 初級編

【環境構築】
Python, VSCode, MySQL(MAMP), Git / GitHub, HEROKU, anaconda, jupyter lab

いいなと思ったら応援しよう!