サナララツイート自動収集の基礎的検討(2)

このドキュメントは、ぴょこりんクラスタ Advent Calendar 2015のために書いたものであり、 、これに引き続き、神のシステムに挑みます。言うまでもないですが、神のシステムに挑む者とは彼女のことを指します。

前回までのおさらい

通常のアレだと1週間分のツイートしか検索できないけどブラウザ上ではもっと古いツイートが見つかる。ってことは普通にスクレイピングすれば古いツイート取れるんじゃないの?

In [2]:
from requests_oauthlib import OAuth1Session

CKey,CSecret, AToken, ASecret=open('./TWOauth','rb').read().strip().split('\n')
knatsuno=OAuth1Session(CKey,CSecret, AToken, ASecret)
from bs4 import BeautifulSoup as bs
import bs4
results=[]
query=u'サナララ'
def strongstrip(s):
    if type(s)!=bs4.element.NavigableString and type(s)!=str:
        if len(s.contents)==0: s=''
        else: 
            s= s.contents[0]
            s=strongstrip(s)
    return s

req2 = knatsuno.get(u'https://twitter.com/search?f=tweets&vertical=default&q=%s&src=typd'%query)
t=bs(req2.content, "lxml").findAll("div", { "class" : "content" })
for i in range(10):
    for text in t[1:]:
        results.append([text.findAll('a')[0]['href'].strip('/'),text.findAll('strong')[0].contents[0], \
                        text.findAll('a')[1]['title'], ''.join([strongstrip(i) for i in text.findAll('p')[0]]), \
                        text.findAll('a')[1]['href'].split('/')[-1]])
    req3= knatsuno.get(u'https://twitter.com/search?f=tweets&q=%s max_id%%3A%s&src=typd'%(query,str(int(results[-1][-1])-1)))
    t=bs(req3.content, "lxml").findAll("div", { "class" : "content" })
    if len(t)==1:
        break
print '取得できたツイートは%d個' % len(results)
print '一番ふるいのは、'
for i in results[-1][:len(results[-1])]:
    print i
取得できたツイートは77個
一番ふるいのは、
flat_ps
flat.ps
3:32 - 2015年12月5日
サナララR たのむ
673102626423091200

だめでしたー。もっと古いのがとりたいんだよ僕は。というわけで次の一手を考えなきゃいけないんだ。と言うわけで次の一手、Seleniumをご紹介。これが石の名前とかだとsanararity高めで良かったんですが残念ながらミネラル的なヤツ。

Selenium最高

Seleniumはウェブブラウザの操作自動化のためのツールです。Web開発者向けのテスト用途に使われてるやつだそうです。と言うわけでpythonからウェブブラウザ起動、検索クエリを投げ、5回ほど下にスクロールさせて、そのあとそのページをスクレイピングしてみましょうか。うまくいけばそこそこ古めなツイートが取れてるはず。以下のスクリプトでは古いの5つをピックアップしてprintしています。

In [11]:
from selenium import webdriver
import time
fox =webdriver.Firefox()

fox.get(u'https://twitter.com/search?f=tweets&vertical=default&q=%s&src=typd'%query)
time.sleep(3)
for i in range(5):
    fox.execute_script('window.scrollTo(0, 10000000)') #下にスクロールしてるだけです。
    time.sleep(3)

data = fox.page_source.encode('utf-8')

#これ以降はスクレイピング+表示なので前野と一緒
from bs4 import BeautifulSoup as bs
import bs4
def strongstrip(s):
    if type(s)!=bs4.element.NavigableString and type(s)!=str:
        if len(s.contents)==0: s=''
        else: 
            s= s.contents[0]
            s=strongstrip(s)
    return s

t=bs(data, "lxml").findAll("div", { "class" : "content" })
for text in t[-1:-6:-1]:
    print text.findAll('a')[0]['href'].strip('/')
    print text.findAll('strong')[0].contents[0]
    print text.findAll('a')[1]['title']
    print ''.join([strongstrip(i) for i in text.findAll('p')[0]])
    print ''
ergserifu_bot
エロゲセリフbot
10:34 AM - 29 Nov 2015
折角,こんなチャンスだったのに,ここで雨に濡れて,絵とか描いてて…(サナララ/矢神由梨子)

yamato1428
やまと
5:25 PM - 29 Nov 2015
@
サナララときいて思わずコメント
うめ先生の親戚の絵はかわいいし、シナリオも無駄なものがない一種の最高傑作ですね

kinotanu
Cloverキノたぬ
7:18 PM - 29 Nov 2015
サナララRやるのは夜にするか

capp365
pyoriko.ex_
11:17 PM - 29 Nov 2015
サナララは更に学びが多かった。

HomuHoooomu
やはり俺の社会人生活は間違っているP
12:36 AM - 30 Nov 2015
@ サナララ初プレイはおいくつの時だったんですか!?(真剣

どう見てもNovemberです本当にありがとうございました!!!!!!!!

これをもっと下にスクロールしまくればガンガン古いツイートが漁れそうです。それにしてもサナララは本当に学びが多いですね。サナララがなければ僕がSeleniumと出会うことは無かったはず。あ、一生に一度のチャンスをそんなアレで消費しちゃうのはもちろんお断りです。

さて、上述のスクリプトについて問題点を挙げるとすれば、

  • 下にスクロールしすぎると表示してるページがめっちゃでかくなり、下手すると凍る(夏野こおり!!)
  • Firefox起動するのがうざい

 あたりでしょうか。1つ目に関しては、前回やったmax_idであるID以前を対象とした検索クエリを定期的に投げなおすことによって解決できそうです。2つ目に関してはブラウザ画面の無いブラウザを使えばええやんってことでPhantomJSを使うことにより解決出来そうです。・・・っと思ったのですがPhantomJSダメでした(こんなのがあった)。apt-getで入れたら1.9が入ったので、新しめなバージョン入れると何とかなりそうかも。気が向いたらこれもやってみようかなと。

まとめ

 と言うわけで神のシステムの限界(一週間)を超えることが出来ました。サナララっぽい記事が書けた気がして、すごい満足しています。それにしてもSelenium素晴らしいですね。クローリング、スクレイピングに使えるのはもちろんですが、社内システムのクソみたいなユーザインターフェースへの対策として、必要な情報を用意しておけばそのクソ冗長なフォームに全ての項目を自動入力してくれるスクリプトなんてのもかけるかもしれませんね!