計算社会科学入門発売記念、twitterのサンプリングデータを、GCP BigQueryに入れるまでチュートリアル

# 概要

[計算社会科学入門](https://www.amazon.co.jp/%E8%A8%88%E7%AE%97%E7%A4%BE%E4%BC%9A%E7%A7%91%E5%AD%A6%E5%85%A5%E9%96%80-%E9%B3%A5%E6%B5%B7-%E4%B8%8D%E4%BA%8C%E5%A4%AB/dp/4621305964)　というこの分野の教科書的な書籍が発売された。しかしこの書籍ではどの様にデータ（特にtwitterなどの大規模データ）を集めるのかという記事は少なかった。そのため前書の内容を補完する意味で、twitterのサンプリングデータをもし、BigQueryに入れて活用するならどうするかと言ったHowToを書く。

# 計算社会科学

[計算社会学入門](https://www.amazon.co.jp/%E8%A8%88%E7%AE%97%E7%A4%BE%E4%BC%9A%E7%A7%91%E5%AD%A6%E5%85%A5%E9%96%80-%E9%B3%A5%E6%B5%B7-%E4%B8%8D%E4%BA%8C%E5%A4%AB/dp/4621305964)の冒頭には、同分野に関して、

> 計算社会学とは、人間の相互関係によって成り立つ社会をデータに基づいて解明していく学問である。

とある。
その中で、SNSは特に人間関係の相互関係（フォローフォロイー、返信）などの興味深いデータがある。しかし、前書ではではどの様にSNS上のデータを集めるのかに対して、その概要を述べるにとどまっている（第四章にその記述がある）。

この記事では、計算科学入門では触れられなかった、それでは例えばtwitterからデータを集めるとしたらどうするのか？という観点で詳細を書くことにする。

何故ならば計算社会科学はデータを使った分析なので、まずデータを集める必要がある。しかし後述するtwitterのサンプリングデータでも一日50万件程度あり、そのハンドリングにはある程度エンジニアリングスキルが必要だからだ（そのノウハウは余り表には出てこない）。

## データの収集と倫理

計算社会学入門で、データ収集やアルゴリズムに関する倫理についての一章を割いて説明している（第11章）。また、twitterの[開発者ポリシー](https://developer.twitter.com/ja/developer-terms/policy)に従う必要がある。

# 今回作るものアーキテクチャー

今回は、twitterの[sampling data](https://developer.twitter.com/en/docs/twitter-api/tweets/sampled-stream/introduction)を使ってtwitter社が公開している全tweetの1%サンプルを、[GCP](https://developer.twitter.com/en/docs/twitter-api/tweets/sampled-stream/introduction)(Google Cloud Platform) にあるBigQueryに保存するというシステムを開発する。

スケルトンプログラムは、こちらの[github](https://github.com/shibacow/twitter_data_sampling)に用意しました。

図にすると以下のようなデータの流れになる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26986/9697cf5a-01bb-3188-d54c-7a4e1f865e76.png)

1. twitterからのstreaming sampleはfluendで受ける。
2. そのデータは、GCPのPubSubに投げる。
3. dataflowを使って、tweet,引用tweet,ReTweet,投稿Userでテーブルを作る
4. そのデータをBigQueryに格納する

という手順でデータを収集する。技術的には **fluned** でBigQueryにtweetを保存までできるが、データの収集と、データの整形と保存に関してそれぞれ責任を分担したいので上記のような構造を選択した（単一責任の原則から着想を得ました）。

何故tweetにuserを入れずにuserテーブルを分けるのかと言うと、tweetのままでもBigQueryは検索できるのですが、ネストが深いと直感的なデータの把握がしづらく、クエリーも複雑なものになりがちなので、tweet,retweet,引用tweet,Userでテーブルを分けました。本当に使う際には、メディアツイート、位置ツイート、hashタグなども分離します（その方がテーブル構造がシンプルになるので）。

# twitterのSampled streamをflunetdで受けて、pubsubに格納するまで

## 用意するもの

- twitterの開発者アカウント
- Google PubSubのTopic

### twitterの開発者アカウント

twitterのSampled streamを取得する場合は、twitterの開発者アカウントが必要になります。開発者アカウントはtwitterの[開発者サイト](https://developer.twitter.com/ja)で作成してください。英語で用途などを入力する必要があるかもしれません。
詳しくはこちらのサイトが参考になるかもしれません(2019年8月時点の情報です)。

https://qiita.com/kngsym2018/items/2524d21455aac111cdee

### GCPのpubsub TOPIC

PUBSUBについての概要は[こちら](https://cloud.google.com/pubsub/docs/overview?hl=ja)。

[pubsub](https://console.cloud.google.com/cloudpubsub/topic/
)は、事前にご自身のプロジェクトで、トピックの作成をお願いします。

### fluentdの準備

fluentdは[こちら](https://www.fluentd.org/)で、イントールをお願いします。

### 必要なプラグインの準備

twitterからpubshubへのデータ保存には、flunetdに対して、いくつかプラグインを追加する必要があります。

- fluent-plugin-twitter twitterのプラグインhttps://github.com/y-ken/fluent-plugin-twitter
- fluent-plugin-gcloud-pubsub-custom pubsubへのプラグインになります。https://github.com/mia-0032/fluent-plugin-gcloud-pubsub-custom pubsubプラグインは、[こちら](https://github.com/mdoi/fluent-plugin-gcloud-pubsub) もありますが、pubsub-customの方が新しいようです。

### twitter-pluginで日本語の判定をする。

twitter-pluginでは、日本語がうまく判定できないかもしれません。その際はこちらのPRを参考にtwitter-pluginの修正が必要になります。 https://github.com/y-ken/fluent-plugin-twitter/pull/45/files

### flunetdのconfファイルの準備

fluentdのconfファイルは次のようになります。

github上は[こちら](https://github.com/shibacow/twitter_data_sampling/blob/main/fluentd.conf)

 consumer_key,consumer_secret,access_token, access_token_secret は、twitterの開発者サイトで取得できます。lang jaは上記にあるように、twitter-pluginの修正が必要かもしれません。

twitterからデータ取得

```xml
<source>
  @type twitter
  consumer_key xxxxxxxxxxxxxx
  consumer_secret  xxxxxxxxxxxxxxxxxxxxxxx
  access_token xxxxxxxxxxxxxxxxxxxxx
  access_token_secret xxxxxxxxxxxxxxxxxxx
  tag input.twitter.sampling
  timeline sampling
  lang ja
  output_format nest
</source>
```

pubsubへの格納

これは、メモリへbufferをためて一秒ごとに送信するというもの

```xml

<match input.twitter.sampling>
   @type gcloud_pubsub
   project xxxxxxxxxxxxxxxxx
   key xxxxxxxx.json
   topic xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
   autocreate_topic false
   max_messages 1000
   max_total_size 9800000
   max_message_size 4000000
   compression zip
   <buffer>
     @type memory
     flush_interval 1s
   </buffer>
   <format>
     @type json
   </format>
</match>
```

# pubsubからBigQueryへデータをロードする

pubsubへデータを格納した後、dataflowを利用して、BigQueryへデータを送る。これは、pythonで行う。

[github](https://github.com/shibacow/twitter_data_sampling/blob/main/strem_count.py)はこちら。

### python-apache-beamの準備

pythonでdataflowを使う場合は `apache-beam` ライブラリを使う。

```
pip install apache-beam
```
こうすれば apache-beamがインストールされる。

## コードの解説

```py
  with beam.Pipeline(options=pipeline_options) as p:
    # Read from PubSub into a PCollection.
    if known_args.input_subscription:
      messages = (
        p
        | beam.io.ReadFromPubSub(subscription=known_args.input_subscription).\
        with_output_types(bytes)) #<- pubsubのsubscriptionからデータをread
    
    lines = messages | 'decode' >> beam.Map(lambda x: x.decode('utf-8')) #バイナリなので、utf-8に変更する。
    devide_dict = (
      lines
      | 'devide_dict' >> (beam.ParDo(DevideDict())) #DevideDictでJSONをパースして、中身を取り出す。
      | "to BQ" >>  WriteToBigQuery(table_fn,schema=schema_fn,project="xxxxxxxxx") #table_fn,schema_fnで `table_name` を見て対象テーブル振り分け
      #| "print" >> (beam.Map(print))
    )
```

### DevideDict

elementはjsonなので、loadでdictにしたあと、変更する。

### table_fn

table_nameによってテーブル振り分けする。

### schema_fn

同様に、schemaも振り分ける。

# 一旦BigQueryにロードすることで他の分析ができるようになる。

sampleingのデータは、日本語tweetで一日50万件程度だ。一年で大体1億から2億ぐらいになる。mysqlで分析するのは困難になる。bigqueryに入れることでデータの分析がしやすくなる。こちらの[記事](https://qiita.com/shibacow/items/75a8adcc05be983765f6)にあるように、形態素解析もできるようになる。

データ分析のためにはデータの取得が必要になる。そのための一助になれば幸いだ。