マロングラッセ Mk.II

赤西真論のブログらしきもの

TwitterのTLから二次元画像だけ収集しよう! (5)データベースに情報を保存する

前回までで画像収集部分は完成したので、次は収集したツイートの情報を保存していきたいと思います。

別に画像さえ見れたらいいと思うこともあるのですが、せっかくなのでツイートの情報も収集してしまいましょう。

いつものスクリプト配布から行きます。

GitHub Gist - TL_capture_lbp_all.py

今回はGistに1つしかファイルがありません。DBに書き込むところだけなので、あまり分ける必要が無いかと思ったのでこうなりました。

スクリプトの名前からも分かる通り、これですべての機能が実装出来ました。

ダウンロード出来たら、実行してみましょう。もうやり方はいいですよね?

フォルダーに突っ込む→start.bat実行→python TL_capture_lbp_all.py です。

まあ、実行した見た目に変化はありません。

 

ということでいつものdiffを見てみます。

今回は左が前のTL_capture_lbp_overlap.py、右がTL_capture_lbp_all.pyとなっています。また入れ替わりました...

では、上から見ていきましょう。

まず、import文の追加があります(8行目)。

今回はデータベースとしてSQLiteを使用したので、それを操作するためのモジュールが入りました。O/Rマッパーなんてリッチなものは使いません。SQL文を直に叩きます。

次にメソッド mkdirです。on_statusより先に説明します。

今回はデータベース(以降DB)を日付ごと、つまりフォルダーごとに持つため、フォルダー作成時にDBファイルを作成するようにしました。

まず、DBファイルのパスを作成します(132行目)。ただの文字列連結です。

次にすでにDBファイルが存在するかを確認します(133行目)。

実は今までの状態だと一度終了して、同じ日にもう一度起動すると前回の画像ファイルを上書きしてしまう状態になっていました。

それを今回回避するためにDBファイルの有無で以前に実行されたかどうかを確認しています。

DBファイルが存在した場合は、そのDBに接続し、ファイル数をカウントします(134~139行目)。

まず、ファイルを発見したということでコンソールに「DB file exist」と出力します(134行目)。

次にそのDBファイルに接続します(135~136行目)。

次にSQL文を実行します。ここではlistテーブル内のfilenameカラムの行数を数えています(137行目)。DBの構造は後ほど説明します。

最後にカウント数を取り出します(138~139行目)。この数からファイル名がスタートするようにします。

DBファイルが存在しなかった場合は新たにテーブルを作成します(141~143行目)。

まず、DBファイルに接続します(141行目)。この時点でファイルが作成されます。

次にテーブルを作成します。今回はlistというテーブルを作成しています。カラムの内容は以下の通りです。

filename ファイル名(拡張子無し)
image 画像URL
username ツイートしたユーザーのID
url ツイートURL
fav 収集時のいいね数
retweet 収集時のRT数
tags ハッシュタグ
time 収集時刻
facex 顔のX座標群
facey 顔のY座標群
facew 顔のWidth群
faceh 顔のHeight群

こんな感じのテーブルを作成してみました。普通、型も指定するのですが今回は省略しました。別に厳密に管理する必要も無いので...

その後、ファイル名を0で初期化します(143行目)。これは前までと同じですね。

では、本命のメソッド on_statusに移ります。

まず、日付確認の部分で処理が増えています(45~46行目)。

ここでは、DBにまだ書き込まれていない情報(キューに溜まっている情報)をDBに反映した後に接続を切っています。

次にハッシュタグを取得する処理が増えています(109~113行目)。

statusオブジェクトを見て、ハッシュタグに関する情報があれば、それをリストに追加しています。

最後にDBにデータを反映する部分です(115~120行目)。

まず、statusオブジェクトにはツイートのURLは存在しないため、形式に沿ってURLを生成しています(115行目)。汚い文字列連結です。

次に実行するSQL文のテンプレートを用意しています(116行目)。

SQL文は文字列として実行するため、何か当てはめるなら文字列連結やメソッド formatを使用した文字列フォーマットを使用する方法があります。

ただ、それを行うとSQLインジェクションの温床になってしまいます。これは非常に危険です。

そこでプレースホルダと呼ばれる機能が存在します。この機能を使用すると当てはめたい部分を?で書いておき、後からタプルを渡すことによって自動的に当てはめてくれます。この際にエスケープ処理などの処理も行ってくれます。非常に便利です。

次にプレースホルダに当てはめるためのタプルを作成しています(117~118行目)。

データが多いというか、文字列化処理が山ほど入っているのですごく長いです。

タプルの内容はテーブル内のカラムと同じ並びになっているので、そちらと照らし合わせながら読んでください。

最後にSQL文を実行して、コミットしています(119~120行目)。

executeの引数を見てもらえば分かりますが、SQL文とタプルを渡しています。

これによってタプルの内容がプレースホルダに当てはめられます。

ちなみに、この部分はTPTSの初期の頃はヤバいことになってました。

そのコードを載せておきます。

# データベースに保存
url = "https://twitter.com/" + status.user.screen_name + "/status/" + status.id_str
self.dbfile.execute("insert into list(filename) values('" + filename + ext + "')")
self.dbfile.execute("update list set image = '" + media_url + "' where filename = '" + filename + ext + "'")
self.dbfile.execute("update list set username = '" + status.user.screen_name + "' where filename = '" + filename + ext + "'")
self.dbfile.execute("update list set url = '" + url + "' where filename = '" + filename + ext + "'")
self.dbfile.execute("update list set fav = " + str(status.favorite_count) + " where filename = '" + filename + ext + "'")
self.dbfile.execute("update list set retweet = " + str(status.retweet_count) + " where filename = '" + filename + ext + "'")
self.dbfile.execute("update list set tags = '" + str(tags).replace("'","") + "' where filename = '" + filename + ext + "'")
self.dbfile.execute("update list set time = '" + str(datetime.datetime.now()) + "' where filename = '" + filename + ext + "'")
self.dbfile.execute("update list set facex = '" + str(facex) + "' where filename = '" + filename + ext + "'")
self.dbfile.execute("update list set facey = '" + str(facey) + "' where filename = '" + filename + ext + "'")
self.dbfile.execute("update list set facew = '" + str(facew) + "' where filename = '" + filename + ext + "'")
self.dbfile.execute("update list set faceh = '" + str(faceh) + "' where filename = '" + filename + ext + "'")
self.dbfile.commit()

ヤバいですね。何をやってるのでしょうか...

これはファイル名のみを入れた後にそのファイル名の行に対してデータをどんどん入れていくスタイルになっています。アホですね。

今回のスクリプトの変更点は以上になります。

 

さて、DBが出来たのはいいのですが閲覧方法がありません。

DBと画像を関連付けるソフトが必要になります。

そこで登場するのがTPTSViewerと呼ばれるソフトです。自作です。

このソフトはC#で書いてあり、画像とDBの内容を閲覧するためのものとなっています。

また、簡易的な検索機能も搭載しています。

で、ここで気づきましたかね。C#で書いてあるんですよ。

イントロダクションの時にWindows以外の人には悲しいことが起こると書きましたね?

これです。.Net Framework 4.5が動かないPCでは動作しません。Wineも多分無理です。

まあ、Windowsを持ってない人なんていないでしょうから大丈夫だとは思います。

では、とりあえずソフトをダウンロードしてもらいましょうか。

Release ブログ公開用 - marron-akanishi/TPTSViewer

ここにあるViewer.zipをダウンロードしてください。ちなみにこのReleaseページがあるGitHubリポジトリソースコードが上がってるので、興味がある人はどうぞ。

ダウンロードが出来たら展開して、中にあるヘンテコなアイコンの実行ファイルを実行します。するとこんな画面が最大化された状態で起動するはずです。

フォームアプリケーションです。WPF版も作ろうとしたのですが、心が折れました。

このソフトは最初にデータベースファイルを指定しないと何もできません。

ということで ファイル→開く をクリックしてください。ちなみにD&Dで指定なんてリッチな機能は付いてないです。

画像回収が行われている日付フォルダーを指定すれば、DBファイルのみ表示されるはずです。

そのファイルを選択して、開くをクリックしましょう。

するとこんな感じで画像が表示されるはずです。(通常だと00000から開始するのですが、ここでは00003の画像を表示しています)

これで全機能が使えるようになりました。メニューバーから説明していきます。

まず、ファイルメニューです。

「開く」は先ほどのDB選択画面が出ます。この選択画面では、最後に指定したフォルダーパスが自動的に保存されます。ソフト側が保存しているわけではなく、Windows側が勝手に覚えていてくれます。

「リロード」はDBをリロードします。これは裏で収集中にDBを閲覧した際、ファイル選択時から後に更新された情報を取得するために使用します。

「終了」はそのままです。このソフトを終了します。

次の「フォルダーを開く」はDBファイルが存在するフォルダーをエクスプローラーで開きます。

その次の番号入力欄とジャンプはファイル名を指定するために使用します。ここに数字を入力することで任意のファイルに飛ぶことができます。

ただ、ちょっと作り方が悪かったため、非アクティブの状態からアクティブにした際に結構な頻度でこの入力欄にカーソルが移動します。こうなると方向キー等が使えなくなるので、Escキーを押して抜けるようにしてください。

その次の「<<」「<」「>」「>>」は移動用ボタンです。

「<<」は一番最初のファイルへ、「<」は1つ前のファイルへ、「>」は1つ後のファイルへ、「>>」は一番最後のファイルへ移動します。

1つ前と後は方向キーの左右とJ、Kキーでも移動可能です。ファイル移動はループするようになっているので、一番最後の1つ後ろは一番最初になります。

次に「検索」です。これは別ウィンドウが開きます。

このウィンドウを使用するとユーザーIDとハッシュタグによって画像を検索することができます。初期状態で表示されている画像のユーザーIDが入力されています。

検索をかけるとこのようにリストが表示されます。リストの番号をクリックすると後ろのメインウィンドウの画像が切り替わります。

次は顔枠メニューです。ここが一番重要な機能となります。

まず、「顔枠表示」と「情報表示」をクリックしてみましょう。

なにやらいろいろ出てきましたね。

まず、「顔枠表示」をクリックすると画像の上に顔検出をした位置が四角い枠によって囲まれるようになります。

次に「情報表示」をクリックすると顔の座標情報が表示されます。ちなみにこのリストはクリックしても何も起きません。色を変えたりとかできたら良かったんですけどね。

あと、顔枠が表示されていない状態だと情報一覧にも何も表示されません。

どちらも非表示にする際は、もう一度顔枠メニューを見てもらうと「顔枠非表示」と「情報非表示」に変わってるはずなのでそれをクリックしてください。

「枠色設定」は顔枠の色を変えるためのカラーピッカーが表示されます。

この設定はソフトを一旦終了すると元に戻ります。

メニューバーの説明は以上です。

次に画像が表示されているエリアですが、ここをダブルクリックすると関連付けられている画像ビューアーで表示されている画像を開きます。

画像の下の情報表示エリアに移動します。

まず、URLと書かれた部分をダブルクリックするとそのツイートURLをブラウザで開きます。

次にIDと書かれた部分をダブルクリックするとそのユーザーのプロフィールページをブラウザで開きます。

最後にハッシュタグの部分を右クリックするとハッシュタグの一覧がメニューとして表示されます。

このメニューのハッシュタグをクリックするとそのハッシュタグをキーワードとして入力された状態で検索ウィンドウが開きます。ちなみにクリックするたびに開くので注意してくださいw

最後にステータスバーですが、ここにはたまにメッセージが表示されます。

エラーとかも表示されるので、動かないと思ったときは見てください。

また、右端には画像の解像度も表示してあります。

以上で全機能の説明です。結構使いやすい感じに作ってあります。

ただ、自分が使うためだけに開発していたということもあってバグも残っています。

Web版を作ってからはあまり使っていないので放置してますw

  以上でTPTSの全機能が実装できました。お疲れ様です。

ところで、ビューアーを見ておかしいと思ったことはありませんか?

そうです、顔以外のところに顔枠が表示されてしまっています。

全体的に見ればそこまで悪くないのですが、少し気になってしまいます。

次回はこの精度を少し改善した実際のTPTSで使用しているモデルに入れ替えてみます。

次回が本編の最後です。頑張りましょう!