Perl で Twitter API を叩いたあとに Excel へ csv を作るやつ

# Perl で Twitter API を叩いてデータを得る

　Twitterで行われるグラブルの募集についてまとめてみたかったので、ちょっと Perl + Twitter API でやってみて、さらにそれを csv ファイルとして出力をやってみました。出力されたものは Excel で見る想定です。

```perl
#!/usr/bin/perl

binmode(STDOUT, ":utf8"); #だるい

use warnings; #だるい
use strict; #だるい
use utf8; #だるい
use feature 'say'; #だるい

if (-t STDOUT) {
    # input attached to terminal and will probably ask user
    # ターミナルへ出てる
    # say "terminal";
} else {
    # input from other process
    # 多分パイプなのでBOMつけてあげる
    # say "pipe";
    print "\x{feff}";
}

package MyTweet; # 今更だけど $this 相当の物をいちいち shift するのだるいな……

sub parse {
    my $this = shift;
    my $text = shift;
    $text = $text . "\n";
    $this->{'org'} = $text;
    my $res = ($text =~ 
        m/.*?([0-9A-Z]*).*?:参戦ID[\n|\r\n|\r].*?募集.*?[\n|\r\n|\r]\s*(Lv[0-9]+)\s*(.*?)\s*[\n|\r\n|\r]\s*(https[^\s]*)\s*.*/s);
    if($res){
        # 1234ABCD :参戦ID
        # 参加者募集！
        # Lv100 セレスト・マグナ
        # https://t.co/abcd1234Zz
        # say $1; # 参戦ID
        # say $2; # 対象Lv
        # say $3; # 対象名
        # say $4; # URL
        $this->{'data'} = [$1, $2, $3, $4];
    }else{
        #say $text;
        #say "fail";
        $this->{'data'} = undef;
    }
    #say $text;
}

sub parsed { my $this = shift; return defined($this->{'data'}); }

sub simple_line { my $this = shift; my $data = $this->{'data'} ;
    return "\"$this->{'uid'}\",\"$data->[0]\",\"$data->[1]\",\"$data->[2]\",\"$data->[3]\",\"$this->{'tid'}\""; }

sub full_line { my $this = shift; my $data = $this->{'data'} ;
    return "\"$this->{'uid'}\",\"$data->[0]\",\"$data->[1]\",\"$data->[2]\",\"$data->[3]\",\"$data->[4]\",\"$data->[5]\",\"$this->{'tid'}\""; }

sub create { my $ret = bless {}, shift; $ret->parse(shift); 
    $ret->{'uid'} = shift;
    $ret->{'tid'} = shift;
    return $ret; }

package main;

use Net::Twitter;
use Data::Dumper; # デバッグ時に必死になって使った

my $nt = Net::Twitter->new(
    traits          => [ qw/AppAuth API::RESTv1_1/ ],
    consumer_key    => 'こんしゅまー', #だるい
    consumer_secret => 'しーくれっと', #だるい
);

$nt->request_access_token;
#say 'token: ', $nt->access_token;

# 多分これが含まれる(リプとリツイートは除外)
my $search_term = '"参戦ID" "参加者募集" lang:ja exclude:replies exclude:retweets'; 

# 次のページに行くために
my $lasttid = "";

while(1){

    my $r = $nt->search({
        q => $search_term, 
        lang => "ja", 
        max_id => $lasttid, 
        result_type=>"recent",
        count => 100,
        tweet_mode => "extended" # これを入れないと勝手に切り詰められる酷い仕様
        });

    # 雑な終了判定
    my $cnt = @{$r->{statuses}};
    if($cnt==0){
        last;
    }
    for my $status ( @{$r->{statuses}} ) {
        #print Dumper $status;
        #print $status->{'user'}->{'id'};
        my $tid = $status->{'id'};
        $lasttid = $tid;

        my $uid = $status->{'user'}->{'id'};
        my $text = $status->{full_text}; # textじゃなくてfull_text

        #say "begin"; say $text; say "end";
        my $line = MyTweet->create($text,$uid,$tid);

        # 面倒なんで quoted csv にした
        say $line->simple_line if $line->parsed;
        #say $text;
        #say $line->full_line if $line->parsed;
        #exit 1;
        #print "\"$uid\",\"$notlike_lang\",\"$best_lang\",\"$tid\"\n";
    }

    $lasttid = $lasttid - 1; # 重複せずに次を引くためには -1 してやる必要がある

    sleep 10; # 気遣い
}
```

## 改行をいい感じに切り出すための正規表現

突っ込みありがとうございます！ 

> @scivola 2019-10-16 21:31
>
> 正規表現の
> 
> `[\n|\r\n|\r]`
> はおそらく意図と違うでしょう。
> これだと， `\n` または `|` または `\r` という意味になってしまいます。
> 
> Windows 流，旧 Mac 流，Unix 流の改行どれでも，ということを表すなら，たとえば，
> 
> `(?:\r\n?|\n)`
> ですね。
> 
> また， `[\s]*` は単に `\s*` でいいですね。

## Excel は utf8 の csv を開けない(BOMがない場合)

　Excel は ShiftJIS の csv しか開けないと思い込んでいたのですが

> @Kosen-amai 2019-10-20 12:17
> bom付きutf8ならExcelで開けるはず

　情報ありがとうございました！

　(BOMなしのcsvは)そのままダブルクリックでやると Shift-JIS として読んでくれやがるので https://qiita.com/snaruse0608/items/cd2951e4c32455d7c480 こんなかんじでやる

　本エントリのプログラムは terminal への出力でない場合は BOM をつけるようにやってみた。

## COUNTIFS関数で集計する

　`=COUNTIFS(A:A,"*Perl*")` とかでやると、その文字列が含まれているセルの数を返してくれます。(両端のアスタリスクを外せばぴったりの一致にできます)

　ちょっと応用すると
`=COUNTIFS(A:A,"*" & A1 & "*")` とか `=COUNTIFS(A:A,"*" & $A1 & "*")` とかできそうですね。