2011/12/18

れあつい ~Twitterフォローユーザーのレア発言を取得してみる~

(* この記事は PowerShell Advent Calendar 2011 18日目の参加記事です。)


raretwit.ps1というスクリプトを作りました。
コードは一番下に置いたので、すぐに使ってみたいひとはそちらをどぞ。



さて、ではまずヘルプを呼び出してみます。スクリプトのはじめのコメント文に書式に沿って書くことでget-helpを使用した場合に表示することができます。



とりあえず、ずらずら書いてみました。
順を追ってみてみましょう。


1.フォローユーザーを取得する
引数に g をつけることで、フォローユーザーを取得できます。

アカウント名の入力を促されるので、取得したいユーザーアカウントを入力すると、
webAPIからデータを取得し、カレントディレクトリのtuser.txtに書き出します。
別に自分のアカウントでなくても、他の人のフォローリストでも取得できます。

2.レアユーザーを取得する
引数に r をつけることで、レアユーザーを取得することができます。

昨日分のツイートから指定した回数以下の発言をしたユーザーを抽出し、
rareuser.txtに書き出します。
たとえば、5回以下の発言をしたユーザーを取得する場合を例とします。

「全 2 ページ 1 ページ目 9 回実施」と出たあと、「~回目完了」と出力してます。
これは、たとえばフォロー数が100人いたとして、そのままwebAPIに100人分のツイートを検索するよう投げても、検索過多でエラーを返してくるための処置です。
一度にできる検索上限数は25件前後のようだったので、余裕をもって20人ずつ分割して処理させてます。
100人フォローユーザーがいたとすれば、20人1組として5組にわけ、5回にわけてツイート検索する、、という感じです。

また、一度に取得できる結果は最大100件までです。
101件以降のデータを取りたい場合は、pageを指定してあげる必要があります。
ちょうど書き込みが多いWeb掲示板なんかを想像するとわかりやすいかもしれません。
今回は、あまり多くのデータを取得しても時間がかかるだけなので、
10組以上、つまり200人以上のフォローユーザーがいる場合はデータ取得を1ページ分(100件)だけ。
それ以下の組数の場合は2ページ分(200件) 取るようにしました。

それぞれの組ごとで取得したデータは一度すべて足し合わせます。
その結果をGroup-Objectを使用して名前ごとにグループ分けし、出力します。


さきほど指定した回数以上のユーザーを切り捨てた後、
残ったユーザーで再度20人1組にわけ、再び同じ処理を繰り返します。
これを指定した回数以上のツイートユーザーがいなくなるまで繰り返します。


最終的に残ったユーザーアカウントはカレントディレクトリのrareUser.txtに書き出されます。
これで、レアユーザーリストができました。


3.レアユーザーのツイートを表示する

引数にsをつけることで、前日分からのレアユーザーのツイートを表示します。

出力の仕方は、ストリームのように流れ、一文字ごとタイプで打つような表示がされます。
タイプのような表示がうざったい、という場合には、引数にnotypeをつけることで、やめることもできます。
正直、個人的にはタイプでの表示は文字を目で追うことが疲れるので、お遊び的な要素ですねw

次のツイートには3秒待ってから進みますが、引数に tltime [数字] で待ち時間を変えることもできます。ツイートの数が多い時には待ち時間を小さくして一気にみたり、逆にゆっくり見たい場合には秒数を増やしてください。

4.キーワード検索をする
派生した機能として、キーワード検索もつけました。
昔、Googleでリアルタイム検索がありましたが、あれと似たような感じです。
引数に word [キーワード] をつけることで呼び出せます。

キーワード検索の場合、レアユーザーツイートと違い、多くのデータをとってくるので
さしあたって実行した時点での最新25件を表示します。あまり多くあっても見きれないですしね。
25件ながし終えたら、再度検索しなおし、25件目の時刻以降のツイートを再び表示します。
こちらもデータが多い場合には最新の25件のみの表示となってます。


ちなみに取得準備待ちというのは、
twitter webAPIには1時間あたりの取得制限があり、取得頻度が多い場合、制限にひっかかってしまうため 次の検索を行うまで70秒待つよう設定してます。
ただし、ツイート表示の時間分は引いているので、実際には
70-(データ表示数)*1ツイートあたりの待ち時間(デフォルト3秒)待機してから検索を行います。
せっかくなのでその時間を write-progress に渡して表示させるようにしてみました。
引数に viewtime をつければ見ることができます。



5.キーワードリストから検索する
カレントディレクトリにtword.txtを用意して、キーワードを記入。
引数にwordlをつけることでtword.txtを読み込み、複数キーワード検索が可能です。
ちなみに、wordとwordl 両方引数つけた場合は、wordl の方が優先されます。

6.フォローユーザーを検索する
フォローユーザーを検索する場合には引数に f をつけることで検索可能です。
レアユーザーがダークマゼンダ、通常ユーザーは青色でアカウントに色付きで表示されます。

こちらもキーワード検索と同じように初回の検索を終えたら再び検索しなおし、表示し続けます。
初回が最新30件の表示、2回目以降は特に数に制限なく表示します。

こちらもキーワード検索と同じく、70秒待機して検索しますが、
あまりにもフォローユーザーが多い場合(1000人単位くらい)には検索に少し時間がかかってしまうこともあるかもしれません。
そういった場合には 手動でtuser.txtを編集して、ユーザー数を減らすことで改善されるかと思います。


7.全部検索する
引数なしで実行すると、 wordl、f の引数をつけた状態、つまりキーワード検索とフォロー検索両方実行して表示されます。

レアユーザーがダークマゼンダ、通常ユーザーは青色、キーワード検索は黄色でそれぞれ表示されます。
初回は最新30件表示します。キーワード検索は最新25件まで取得、フォローユーザーは制限なしで時系列にソートされて表示されていきます。

8.コード
ながながと説明してきましたが、結構まだエラーやら抜けがあったりコードも汚いです(;´∀`)。
まぁ、こんなやり方もあるよというサンプル的な感じで掲載

raretwit.ps1
<# 
.SYNOPSIS 
    レアツイ。 twitterで各発言検索スクリプト
.DESCRIPTION 
    -word [キーワード] キーワード検索、ツイート表示
    -wordl tword.txt に書かれたワード検索、ツイート表示
    -f follower、レアユーザーのツイート表示
    -s レアユーザーのツイート表示(1回のみ)
    無地 tword.txtとユーザーリストのツイート表示

    -r レアユーザーを取得、rareUser.txtに書きこむ
    -g followユーザーを取得、tuser.txtに書きこむ
    -tltime [整数] 次のTL表示の秒数指定。指定しなければは3秒
    -notype タイプ形式の表示をやめる
    -viewtime 次のデータ取得の待ち時間を表示する
    
    
.LINK 
    Twitter Search API の使い方 http://www.ibm.com/developerworks/jp/xml/library/x-twitsrchapi/
    Twitter API                 http://watcher.moe-nifty.com/memo/docs/twitterAPI.txt
    おえかきWindows             http://win-enikki.blogspot.com/ 
    
.NOTES 
    from%3Aユーザー名で特定ユーザー発言
    to%3Aユーザー名で特定ユーザーにむけての発言
    %23[検索名]でハッシュの検索
    langをつけて国の設定
    API制限は60分間に100回で発生
    最大検索数は25前後。検索数を20個に分割して検索実施

.EXAMPLE 
    
#>
param(`
[switch]$g,[switch]$r,[switch]$s,[switch]$f,`
$word,[switch]$wordl,`
[switch]$notype,$tltime=3,[switch]$viewtime`
)

#検索リストをTwitterAPI形式に変換
function join-search {
    param ($tuser)
    $txtUSerfrom=$tUser | %{ "from%3A"+"$_" }
    $txtUserfrom -join '+OR+'    
}

#検索リストを分割する
function join-search2 {
    param ($userlist)

    $countP=[math]::Truncate($userlist.length /20)
    $countD=$userlist.length % 20

    for ( $i = 1; $i -le $countP; $i++ )
    {
        $s=20*($i-1)
        $e=20*$i-1

        join-search $userlist[$s..$e]
        if ($i -eq $countP){
            join-search $userlist[$s..$($s+$countD)]
        }
    }
}


#TwitterAPI形式の検索語句から検索結果を返す
Function Get-TwitterSearch {
    Param($searchTerm)
    if ($WebClient -eq $null) {
        $WebClient=new-object System.Net.WebClient
    }
    $results=[xml]($webClient.DownloadString(`
    "http://search.twitter.com/search.atom?lang=ja&q=$SearchTerm"))
    $Searchitems=$results.feed.entry
    $SearchItems
}


#引数ユーザーのFollowerチェック
function Check-follower {
 param($tname,$tpage=-1)
 if ($WebClient -eq $null) {
  $WebClient=new-object System.Net.WebClient
 }
 
    while ($tpage -ne 0)
    {
     $results=[xml]($webClient.DownloadString(`
        "http://api.twitter.com/1/statuses/friends/"+ $tname + '.xml?cursor=' + $tpage))
     $results.users_list.users.user | % { $_.screen_name }
        $tpage=$results.users_list.next_cursor
    }    
}

#検索ユーザーテキスト
if ($g){
 $tname=read-host アカウント名入力
 write-host データ取得中...
 $a=Check-follower $tname
 $a
 $a | Out-File .\tuser.txt
 write-host tuser.txtに書き込みました。
 break
}

if ( test-path .\tuser.txt) {
    $txtUser=Get-Content .\tuser.txt | sort -Unique
} else {
    write-host tuser.txtがみつかりません。
    write-host -g オプションでfollowerを読み込んでください
    break
}

$searchUser= join-search2 $txtuser


#Search Twitterから読み込んだデータを編集、表示する
function See-tweet {
    param($obj, $rare, $txtuser)
    $($obj.length-1)..0 | ? { $obj[$_] -ne $null } | % {
        
        $twitname=$obj[$_].author.name
        $twittitle=$obj[$_].title

       #それぞれの取得データに応じて名前部分の色を変化        
        if (Select-String -InputObject $twitname $rare) {
            $colorname="DarkMagenta"
        }elseif (Select-String -InputObject $twitname $txtUser) {
            $colorname="blue" 
        }else {
            $colorname="darkgreen"
        }

        if ($txtTword){
            if (Select-String -InputObject $twittitle $txtTword){
                $colorname="yellow" 
            }
        }        


       #出力内容
        write-host $twitname -ForegroundColor $colorname -NoNewline 
        write-host " ---- " -NoNewline
        write-host $([datetime]($obj[$_].published)) $_
        
        if (-not $notype){
            for ($i=0;$i -le $(($obj[$_].title).length-1); $i++){
            
                write-host $($obj[$_].title)[$i] -nonewline
                Start-Sleep -Milliseconds 20
            }            
            Write-Host ""
        } else {
            write-host $obj[$_].title
        }
        
        Write-Host ""
        Start-Sleep -s $tltime
    }
   
    $colorname=$null
   
}

$yday=[string]$(Get-Date).adddays(-1).tostring("yyyy-MM-dd")
$tday=[string]$(Get-Date).tostring("yyyy-MM-dd")

#レアユーザーを取得する
function Check-RareUser {
    param($searchrareuser, $txtuser)

    
    [int]$twitcount=Read-host 何回発言以下を取得しますか?
    $rare=$txtuser
    do { 
        $twitresult=$null
        $txtmanyUser=$null
        $gruser=$null
        
        if (($searchrareUser.length) -le 10) {
            $t=2
        }else { 
            $t=1
        }

        for ($i=1; $i -le $t; $i++){
           write-host "全" $t "ページ" $i "ページ目"`
            ($searchrareUser.length) "回実施"
           $twitresult += 0..($searchrareUser.length-1) |`
           % { 

                get-twittersearch $($searchrareuser[$_] + `
                "+since%3A$yday+until%3A$tday&rpp=100&page=$i")
                write-host $($_+1) 回目完了
             }
           

        }
        write-host 回数ソート中..
        $twitresult.count
       
        $gruser = $twitresult | %{ $_.author.name } | Group-Object |`
        sort -Property count -Descending
        $gruser
        $txtmanyUser = $gruser | ? { $_.count -gt $twitcount } |`
        % {$_.name -replace " .*",""  }
        Start-Sleep $tltime
        
        if ($txtmanyuser) {
            $rare=Compare-Object $txtmanyUser $rare -SyncWindow $rare.count |`
            % { $_.inputobject }
            $searchrareUser = join-search2 $rare
        }
            
                
    } while ($txtmanyUser)   
     $rare > .\rareUser.txt
}


if ($r){
    Write-Host "前日分のレアチェックユーザー発言"
    Check-RareUser $searchuser $txtUser
    write-host 完了
    break
}    

if ( test-path .\rareuser.txt) {
    $txtrareUser=Get-Content .\rareuser.txt | sort -Unique
} else {
    write-host rareuser.txtがみつかりません。
    write-host -r オプションでレアユーザーを生成してください
    break
}
$searchrareUser=join-search2 $txtrareUser


#レアユーザーのツイート表示
if ($s){
    $twitresult=$null
    for ($i=1; $i -le 2; $i++){
        $twitresult += 0..($searchrareUser.length-1) |`
        % { get-twittersearch $($searchrareuser[$_] + `
        "+since%3A$yday+until%3A$tday&rpp=100&page=$i") }

    }
    $twitresult = $twitresult | sort -Property published -Descending 
    write-host "総発言数:" $twitresult.count
    see-tweet $twitresult $txtrareUser $txtUser
    break
}  


#キーワード検索フラグ
if ( test-path .\tword.txt) {
    $txtTword= Get-Content .\tword.txt | sort -Unique
    if ($txtTword){
        $searchWord=$txtTword -join '+OR+'
    }
}

if ($word) {
    $searchWord=$null
    $searchWord=$word -join '+OR+'
    $w="true"
}

if($wordl) {
    if ($txtTword) {
        $w="True"
    }else{
            write-host tword.txtに検索文字をいれてください。
            break
    }
}    


#残り時間表示
function Count-Time {
    param($time)
    for ($i = 1; $i -le $time; $i++ )
    {
            $status = " {0} 秒" -F $i,$($i*100/$time)
            $currentOperation = "{0} 秒待ちです" -F $time
            Write-Progress -activity "待機中" -status $status -PercentComplete $($i*100/$time)`
            -CurrentOperation $currentOperation  
            Start-Sleep -s 1  
    }
    Write-Progress -Activity "Working..." -Completed -Status "All done."
}    


#ツイートを調べる
function Check-List {
    param($first)
    $twitresult = $null
    if ($w) {
        #wordは25発言まで取得
        $twitresult += get-twittersearch $("$searchWord&rpp=25")
    } 
    if ($f) {
     
        $twitresult += 0..($searchUser.length-1) |`
        % { get-twittersearch $($searchUser[$_] + "&rpp=10") }
     
    }
    if (-not($w -or $f)){

        $twitresult += 0..($searchUser.length-1) |`
        % { get-twittersearch $($searchUser[$_] + "&rpp=10") }
        if ($searchWord) {
            $twitresult += get-twittersearch $("$searchWord&rpp=25")
        }

    }
    
    
    
    if (-not $first){
        $twitresult = $twitresult | ? { $([datetime]($_.published)) -gt $script:getlasttime}
    }
    
    if ($($twitresult -eq $null) -or $($twitresult[0] -eq $null)) {
        Write-Host "##########################################"       
        Write-Output "データはありませんでした"
        $script:getlasttime=$(get-date)
        
        if ($viewtime){
            Count-Time 70
        }else{
            Start-Sleep -s 70
        }
        
        Write-Host "##########################################"
                
   
    }else{
    
        Write-Host "############## コメント数 $($twitresult.length)"
        $twitresult = $twitresult | sort -Property published -Descending 
        #30ツイートまで出力する
        if ($first) {
            $twitresult = $twitresult[0..30]
        }
            
        see-tweet $twitresult $txtrareUser $txtUser
     
        $script:getlasttime=$([datetime]($twitresult[0].published))
    
        Write-Host "##########################################"
        Write-Host "取得準備待ち"
        if (($twitresult.length-1)*$tltime -le 70) {

            if ($viewtime){
                Count-Time $(70-($twitresult.length-1)*$tltime)
            } else {
                Start-Sleep -s (70-($twitresult.length-1)*$tltime)
            }                
                
        }else{

        }
        
    }
    
}

Write-Host "##########################################"
Write-Host "ストリーム開始!"
check-list first
$a = 0
do  
    { 

    check-list        
 
    }
while ($a -ne 1)


今回は、おもいついたままに作ったスクリプトでしたが、
PowerShellはデータの編集や 動作処理など色々なことができるので、是非使ってみてください( ´∀`)

スポンサーリンク

スポンサーリンク