{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# サナララツイート自動収集の基礎的検討(2)\n", "\n", "このドキュメントは、[ぴょこりんクラスタ Advent Calendar 2015](http://www.adventar.org/calendars/960)のために書いたものであり、\n", "、[これ](http://nbviewer.ipython.org/github/capp365/mynote/blob/master/advent2.ipynb)に引き続き、神のシステムに挑みます。言うまでもないですが、神のシステムに挑む者とは[彼女](https://www.google.com/search?q=%E5%8D%97%E6%96%B9%E3%81%B2%E3%81%8B%E3%82%8B)のことを指します。\n", "\n", "## 前回までのおさらい\n", "\n", " 通常のアレだと1週間分のツイートしか検索できないけどブラウザ上ではもっと古いツイートが見つかる。ってことは普通にスクレイピングすれば古いツイート取れるんじゃないの?" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "取得できたツイートは77個\n", "一番ふるいのは、\n", "flat_ps\n", "flat.ps\n", "3:32 - 2015年12月5日\n", "サナララR たのむ\n", "673102626423091200\n" ] } ], "source": [ "from requests_oauthlib import OAuth1Session\n", "\n", "CKey,CSecret, AToken, ASecret=open('./TWOauth','rb').read().strip().split('\\n')\n", "knatsuno=OAuth1Session(CKey,CSecret, AToken, ASecret)\n", "from bs4 import BeautifulSoup as bs\n", "import bs4\n", "results=[]\n", "query=u'サナララ'\n", "def strongstrip(s):\n", " if type(s)!=bs4.element.NavigableString and type(s)!=str:\n", " if len(s.contents)==0: s=''\n", " else: \n", " s= s.contents[0]\n", " s=strongstrip(s)\n", " return s\n", "\n", "req2 = knatsuno.get(u'https://twitter.com/search?f=tweets&vertical=default&q=%s&src=typd'%query)\n", "t=bs(req2.content, \"lxml\").findAll(\"div\", { \"class\" : \"content\" })\n", "for i in range(10):\n", " for text in t[1:]:\n", " results.append([text.findAll('a')[0]['href'].strip('/'),text.findAll('strong')[0].contents[0], \\\n", " text.findAll('a')[1]['title'], ''.join([strongstrip(i) for i in text.findAll('p')[0]]), \\\n", " text.findAll('a')[1]['href'].split('/')[-1]])\n", " 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)))\n", " t=bs(req3.content, \"lxml\").findAll(\"div\", { \"class\" : \"content\" })\n", " if len(t)==1:\n", " break\n", "print '取得できたツイートは%d個' % len(results)\n", "print '一番ふるいのは、'\n", "for i in results[-1][:len(results[-1])]:\n", " print i" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "だめでしたー。もっと古いのがとりたいんだよ僕は。というわけで次の一手を考えなきゃいけないんだ。と言うわけで次の一手、Seleniumをご紹介。これが石の名前とかだとsanararity高めで良かったんですが残念ながらミネラル的なヤツ。\n", "\n", "\n", "## Selenium最高\n", "[Selenium](http://www.seleniumhq.org/)はウェブブラウザの操作自動化のためのツールです。Web開発者向けのテスト用途に使われてるやつだそうです。と言うわけでpythonからウェブブラウザ起動、検索クエリを投げ、5回ほど下にスクロールさせて、そのあとそのページをスクレイピングしてみましょうか。うまくいけばそこそこ古めなツイートが取れてるはず。以下のスクリプトでは古いの5つをピックアップしてprintしています。" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ergserifu_bot\n", "エロゲセリフbot\n", "10:34 AM - 29 Nov 2015\n", "折角,こんなチャンスだったのに,ここで雨に濡れて,絵とか描いてて…(サナララ/矢神由梨子)\n", "\n", "yamato1428\n", "やまと\n", "5:25 PM - 29 Nov 2015\n", "@\n", "サナララときいて思わずコメント\n", "うめ先生の親戚の絵はかわいいし、シナリオも無駄なものがない一種の最高傑作ですね\n", "\n", "kinotanu\n", "Cloverキノたぬ\n", "7:18 PM - 29 Nov 2015\n", "サナララRやるのは夜にするか\n", "\n", "capp365\n", "pyoriko.ex_\n", "11:17 PM - 29 Nov 2015\n", "サナララは更に学びが多かった。\n", "\n", "HomuHoooomu\n", "やはり俺の社会人生活は間違っているP\n", "12:36 AM - 30 Nov 2015\n", "@ サナララ初プレイはおいくつの時だったんですか!?(真剣\n", "\n" ] } ], "source": [ "from selenium import webdriver\n", "import time\n", "fox =webdriver.Firefox()\n", "\n", "fox.get(u'https://twitter.com/search?f=tweets&vertical=default&q=%s&src=typd'%query)\n", "time.sleep(3)\n", "for i in range(5):\n", " fox.execute_script('window.scrollTo(0, 10000000)') #下にスクロールしてるだけです。\n", " time.sleep(3)\n", "\n", "data = fox.page_source.encode('utf-8')\n", "\n", "#これ以降はスクレイピング+表示なので前野と一緒\n", "from bs4 import BeautifulSoup as bs\n", "import bs4\n", "def strongstrip(s):\n", " if type(s)!=bs4.element.NavigableString and type(s)!=str:\n", " if len(s.contents)==0: s=''\n", " else: \n", " s= s.contents[0]\n", " s=strongstrip(s)\n", " return s\n", "\n", "t=bs(data, \"lxml\").findAll(\"div\", { \"class\" : \"content\" })\n", "for text in t[-1:-6:-1]:\n", " print text.findAll('a')[0]['href'].strip('/')\n", " print text.findAll('strong')[0].contents[0]\n", " print text.findAll('a')[1]['title']\n", " print ''.join([strongstrip(i) for i in text.findAll('p')[0]])\n", " print ''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "どう見てもNovemberです本当にありがとうございました!!!!!!!! \n", "\n", "\n", "これをもっと下にスクロールしまくればガンガン古いツイートが漁れそうです。それにしてもサナララは本当に学びが多いですね。サナララがなければ僕がSeleniumと出会うことは無かったはず。あ、一生に一度のチャンスをそんなアレで消費しちゃうのはもちろんお断りです。\n", "\n", "さて、上述のスクリプトについて問題点を挙げるとすれば、\n", "\n", " - 下にスクロールしすぎると表示してるページがめっちゃでかくなり、下手すると凍る(夏野こおり!!)\n", " - Firefox起動するのがうざい\n", "\n", "\n", " あたりでしょうか。1つ目に関しては、[前回](http://nbviewer.ipython.org/github/capp365/mynote/blob/master/advent2.ipynb)やったmax_idであるID以前を対象とした検索クエリを定期的に投げなおすことによって解決できそうです。2つ目に関してはブラウザ画面の無いブラウザを使えばええやんってことで[PhantomJS](http://phantomjs.org/)を使うことにより解決出来そうです。・・・っと思ったのですがPhantomJSダメでした([こんなのがあった](http://stackoverflow.com/questions/26991998/selenium-phantomjs-does-not-work-for-twitter-com))。apt-getで入れたら1.9が入ったので、新しめなバージョン入れると何とかなりそうかも。気が向いたらこれもやってみようかなと。\n", " \n", "## まとめ\n", "\n", " と言うわけで神のシステムの限界(一週間)を超えることが出来ました。サナララっぽい記事が書けた気がして、すごい満足しています。それにしてもSelenium素晴らしいですね。クローリング、スクレイピングに使えるのはもちろんですが、社内システムのクソみたいなユーザインターフェースへの対策として、必要な情報を用意しておけばそのクソ冗長なフォームに全ての項目を自動入力してくれるスクリプトなんてのもかけるかもしれませんね!" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.6" } }, "nbformat": 4, "nbformat_minor": 0 }