Twitterの呟きビックデータをGolangで取り出して、形態素解析する

2021-05-07

Twitterの呟きビックデータをGolangで取り出して、形態素解析する

GWが終わりを迎え、悲しみにくれる中、「私、能力は平均値でって言ったよね!」と「ぐらんぶる」を1日中見ていました。 Amazon 星3のアニメは最高ですね。可愛い女の子がキャッキャしてるだけで良い。 GWの終わりに感情の起伏が激しいアニメを見るのは辛いのですよ。わかりますか?平坦が良いんですよ。平坦が…

ぐらんぶるは下ネタが大丈夫な人であれば見れると思います。 監獄学園が好きな人は好きなアニメだと思います。

癒やされたところで、寝る前にサクサクとツイートを取得するコードを書いていきましょう。

何をするのか

Twitterの呟きビックデータを取得し、感情コーパスに当てはめ感情の起伏を時系列に見ようと考えてます。

STEP1:Twitterの呟きデータをダウンロード STEP2:ダウンロードした呟きデータをプログラムから読み込み STEP3:呟きデータを形態素解析にかけて字句毎に分割する STEP4:字句毎の感情コーパスに当てはめ感情推定を実施する

STEP2とSTEP3を組み合わせて実行していきます。

STEP2:ダウンロードした呟きデータをプログラム読み取る

tweet.jsをそのまま取り込み、構造体にデータを詰め込んでいきます。 javascriptなので、Json形式で取り込むことができそうですが修正箇所が多そうなので手詰めしていきます。 Json読み込み版は「未確認コード(おまけ)」として置いておきます。

やっていることは、tweet.jsを一行ずつ読み込み ツイート用の構造体にデータを詰め込んで行きます。

どの要素に詰め込むかについては、読み取った行毎に含まれる単語から決めています。

全文

package main

import (
    "bufio"
    "fmt"
    "os"
    "os/exec"
    "strings"
)

type Tweet struct {
    retweeted          bool
    source             string
    entities           Entities
    urls               Urls
    display_text_range string
    favorite_count     string
    id_str             string
    truncated          bool
    retweet_count      string
    id                 string
    created_at         string
    favorited          bool
    full_text          string
    lang               string
}

type Entities struct {
    hashtags      string
    symbols       string
    user_mentions Usermentions
}

type Usermentions struct {
    name        string
    screen_name string
    indices     []int
    id_str      int
    id          int
}

type Urls struct {
    url string
}

func convertTweet(tweetString string, tweet *Tweet) bool {
    tweets := strings.TrimSpace(tweetString)
    arrtweet := strings.Split(tweets, ":")

    arrtweet[0] = strings.Replace(arrtweet[0], "\"", "", -1)
    arrtweet[0] = strings.TrimSpace(arrtweet[0])

    if len(arrtweet) > 1 {
        switch string(arrtweet[0]) {
        case "tweet":
            return true
        case "retweeted":
            tweet.retweeted = false

        case "source":
            tweet.source = arrtweet[1]

        case "hashtags":
            tweet.entities.hashtags = arrtweet[1]

        case "symbols":
            tweet.entities.symbols = arrtweet[1]

        // case "user_mentions":

        case "urls":
            tweet.urls.url = arrtweet[1]

        case "display_text_range":
            tweet.display_text_range = arrtweet[1]

        case "favorite_count":
            tweet.favorite_count = arrtweet[1]

        case "id_str":
            tweet.id_str = arrtweet[1]

        case "truncated":
            tweet.truncated = false

        case "retweet_count":
            tweet.retweet_count = arrtweet[1]

        case "id":
            tweet.id = arrtweet[1]

        case "created_at":
            tweet.created_at = arrtweet[1] + arrtweet[2] + arrtweet[3]

        case "favorited":
            tweet.favorited = false

        case "full_text":
            tweet.full_text = arrtweet[1]

        case "lang":
            tweet.lang = arrtweet[1]

        default:
            // fmt.Println("Nonm")
        }
    }
    return false
}

func main() {
    data, _ := os.Open("./tweet.js")
    defer data.Close()
    scanner := bufio.NewScanner(data)

    var alltweet []Tweet
    var tweet Tweet
    for scanner.Scan() {
        flag := convertTweet(scanner.Text(), &tweet)
        if flag == true { // tweetが来たら次のTweetに移行
            alltweet = append(alltweet, tweet)
            tweet = Tweet{}
        }
    }
    for _, tweet := range alltweet {
        fmt.Println(tweet.created_at, ":", tweet.full_text)
        tweetstr := tweet.full_text
        cmdstr := "echo " + tweetstr + "|jumanpp.exe --model=jumandic.jppmdl"
        stdout, err := exec.Command("sh", "-c", cmdstr).Output()
        if err != nil {
            fmt.Println(err)
        }

        fmt.Printf("ls result: \n%s", string(stdout))
    }
}

STEP3と組み合わせて使用するコード

各ツイートの内容を構造体から取り出して前の記事のコードと組み合わせます。 Windows10にJUMAN++をインストールし、Golangから実行結果を受け取る方法

for _, tweet := range alltweet {
  fmt.Println(tweet.created_at, ":", tweet.full_text)
  tweetstr := tweet.full_text
  cmdstr := "echo " + tweetstr + "|jumanpp.exe --model=jumandic.jppmdl"
  stdout, err := exec.Command("sh", "-c", cmdstr).Output()
  if err != nil {
    fmt.Println(err)
  }

実行結果

各ツイートの内容を形態素解析した結果が得られることが確認できます。

未確認コード(おまけ)

tweet.jsの中身をJSONとして読み込めるように修正します。 冒頭の変数名と「[」末尾の「]」を消去すると良いようです。 ファイル名もtweet.jsonに変えておきましょう。

json.Unmarshalでtweet.jsonを読み取り、構造体に詰め込むコード例を書いておきます。 tweet.jsonに余計な「,」が含まれているので取り込むことはできていないです。

Golangのコードは、多分まぁこんな感じで書けば良いと思います。 ※こんなの乗せるなよって話ですよ。

取得したデータ(tweet.js)を修正せずに、そのまま取り込んで処理できる方が好みです。 もしこの方法で取り込むことができればコメントください。

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
)

type TwitterData struct {
    Tweet struct {
        Retweeted bool   `json:"retweeted"`
        Source    string `json:"source"`
        Entities  struct {
            Hashtags []struct {
                Text    string   `json:"text"`
                Indices []string `json:"indices"`
            } `json:"hashtags"`
            Symbols      []interface{} `json:"symbols"`
            UserMentions []struct {
                Name       string   `json:"name"`
                ScreenName string   `json:"screen_name"`
                Indices    []string `json:"indices"`
                IDStr      string   `json:"id_str"`
                ID         string   `json:"id"`
            } `json:"user_mentions"`
            Urls []struct {
                URL         string   `json:"url"`
                ExpandedURL string   `json:"expanded_url"`
                DisplayURL  string   `json:"display_url"`
                Indices     []string `json:"indices"`
            } `json:"urls"`
            Media []struct {
                ExpandedURL     string   `json:"expanded_url"`
                SourceStatusID  string   `json:"source_status_id"`
                Indices         []string `json:"indices"`
                URL             string   `json:"url"`
                MediaURL        string   `json:"media_url"`
                IDStr           string   `json:"id_str"`
                SourceUserID    string   `json:"source_user_id"`
                ID              string   `json:"id"`
                MediaURLHTTPS   string   `json:"media_url_https"`
                SourceUserIDStr string   `json:"source_user_id_str"`
                Sizes           struct {
                    Thumb struct {
                        W      string `json:"w"`
                        H      string `json:"h"`
                        Resize string `json:"resize"`
                    } `json:"thumb"`
                    Medium struct {
                        W      string `json:"w"`
                        H      string `json:"h"`
                        Resize string `json:"resize"`
                    } `json:"medium"`
                    Small struct {
                        W      string `json:"w"`
                        H      string `json:"h"`
                        Resize string `json:"resize"`
                    } `json:"small"`
                    Large struct {
                        W      string `json:"w"`
                        H      string `json:"h"`
                        Resize string `json:"resize"`
                    } `json:"large"`
                } `json:"sizes"`
                Type              string `json:"type"`
                SourceStatusIDStr string `json:"source_status_id_str"`
                DisplayURL        string `json:"display_url"`
            } `json:"media"`
        } `json:"entities"`
        PossiblySensitive    bool     `json:"possibly_sensitive"`
        DisplayTextRange     []string `json:"display_text_range"`
        FavoriteCount        string   `json:"favorite_count"`
        InReplyToStatusIDStr string   `json:"in_reply_to_status_id_str"`
        IDStr                string   `json:"id_str"`
        InReplyToUserID      string   `json:"in_reply_to_user_id"`
        Truncated            bool     `json:"truncated"`
        RetweetCount         string   `json:"retweet_count"`
        ID                   string   `json:"id"`
        InReplyToStatusID    string   `json:"in_reply_to_status_id"`
        CreatedAt            string   `json:"created_at"`
        Favorited            bool     `json:"favorited"`
        FullText             string   `json:"full_text"`
        Lang                 string   `json:"lang"`
        InReplyToScreenName  string   `json:"in_reply_to_screen_name"`
        InReplyToUserIDStr   string   `json:"in_reply_to_user_id_str"`
        ExtendedEntities     struct {
            Media []struct {
                ExpandedURL   string   `json:"expanded_url"`
                Indices       []string `json:"indices"`
                URL           string   `json:"url"`
                MediaURL      string   `json:"media_url"`
                IDStr         string   `json:"id_str"`
                ID            string   `json:"id"`
                MediaURLHTTPS string   `json:"media_url_https"`
                Sizes         struct {
                    Thumb struct {
                        W      string `json:"w"`
                        H      string `json:"h"`
                        Resize string `json:"resize"`
                    } `json:"thumb"`
                    Medium struct {
                        W      string `json:"w"`
                        H      string `json:"h"`
                        Resize string `json:"resize"`
                    } `json:"medium"`
                    Small struct {
                        W      string `json:"w"`
                        H      string `json:"h"`
                        Resize string `json:"resize"`
                    } `json:"small"`
                    Large struct {
                        W      string `json:"w"`
                        H      string `json:"h"`
                        Resize string `json:"resize"`
                    } `json:"large"`
                } `json:"sizes"`
                Type       string `json:"type"`
                DisplayURL string `json:"display_url"`
            } `json:"media"`
        } `json:"extended_entities"`
    } `json:"tweet"`
}

func main() {
    raw, err := ioutil.ReadFile("./tweet.json")
    if err != nil {
        fmt.Println(err.Error())
    }

    var timeline []TwitterData

    if err := json.Unmarshal(raw, &timeline); err != nil {
        log.Fatal(err)
    }

    for _, tweet := range timeline {
        fmt.Println(tweet.Tweet.FullText)
    }
}

まとめ

とりあえず動くものから始め徐々にレベルアップを図っていきたいですね。 巧遅拙速ですよ。