HyogoKurumi.Scribble

言葉は嘘をつきません

python歴一週間で、ずっとほしかったツールを自作することができた!


にほんブログ村 メンタルヘルスブログ 成人発達障害へ にほんブログ村 メンタルヘルスブログ 自閉症スペクトラムへ にほんブログ村 メンタルヘルスブログ 発達障害グレーゾーンへ

↓↓↓CLICK HERE↓↓↓
【図解】発達障害攻略マニュアル――生き辛い境遇からの脱出
↑↑↑緊急告知↑↑↑

 先週の土曜にpythonデビューしたばかりなのだけど、ずっと前からほしかったツールを作ることができた。ツールといってもコマンドプロンプト上で走ってファイルを出力する感じで、ツールなんて製品のものか無料でダウンロードできるものしか使ったことがない私から見れば、プログラムの中身がむき出しのファイルなんてまだ作りかけみたいなものなんだけど、自分だけで使う分にはこれで十分である。

 

 作ったのはスクレイピング系のツールだ。スクレイピングとは、ウェブページから必要な情報を取得することで、Pythonが得意とする処理である。スクレイピングPythonの専門書も多数出ているほどだ。アクセスする時間などを考慮しないと相手のサーバーにえらい迷惑をかけてしまう。

 というわけで、あんまり大きな声じゃ言い難いので、着手から完成までの流れをかいつまんで話したいと思う。

 

前回の記事

pythonはじめました。みんな応援してくれよな。 - HyogoKurumi.Scribble

 

 前回の記事でも書いたが、前々からios開発に興味があった私は、入院と手術で得た保険金を少し使って、Macを買おうとした。でもあまりお金を使いたくないというところからなんとなく調べたところ、アプリ開発ができるiosアプリをみつけた。それがpythonistaというアプリだった。

 このアプリについて調べていたら、スクレイピングという処理の記事がたくさんヒットした。それは私が副業の関係で前々から欲しいと思っていたツールの動作だった。そして私はios開発の為にMacを買う前に、まずは今の環境でスクレイピングツールを作成してみることにした、というわけだ。

 

 あるサイト内のあるページのある部分のデータがほしくて、これまでは手作業でアクセスしていた。その苦労がこれで解消できたらいいなぁなんて思いつつ、「スクレイピング」というワードに絞って、pythonの導入とコードを調べながら打っていったら、取得するところまであっさりできてしまった。まぁ、手本のコードのURLの部分を打ち換えるだけでよかったから、ここまでならオラウータンくらいにもできるだろう。

 

 ここからが本題だ。URLのパラメータの部分は後半が番号になっていて、番号だけ打ち換えれば別のページにアクセスすることができる。だから、番号の部分は別ファイルから読み込んで、URLと番号を組み合わせてアドレスを完成させて、一つ一つにアクセスしながら必要な情報を取得するコードを作ろうとした。

 で、最初に躓いたのがそこだった。ファイルを読み込むところまでは自力できた。でも番号を1つずつ読み込んで処理をする方法まで、どれだけ調べても理解が追い付かなかった。数時間調べてもらわからなかったのでお助けサイトを頼った。そして完成させることができた。

 

 1つ目のサイトで使うコードができたので、2つ目のサイトにも挑戦してみた。ここはURL+番号のやり方が通用しなかった。目当てのページと番号は規則的に無関係だったのだ。でも、TOPページの検索ボックスに番号を入れて、検索後に表示されたページなら必要な情報が取得できることがわかった。つまり直リンではなく、検索という工程を含むコードを書くことで解決できた。

 これも検索処理を含むコードまでは自力できたのだが、その処理と番号のリストファイルを組み合わせる方法がわからなかった。そこだけお助けサイトを頼った。この頃には関数や引数などが感覚的にわかるようになっていた。

 

 2つ目のサイトで使うコードができたので、3つ目のサイトで使うコードに挑戦した。これが本当に手強かった。

 まず直リンができない。目当てのぺージのURLの、番号の部分がわかないからである。でもリストファイルの番号をTOPページの検索ボックスに入れれば、そのページに辿り着くことはできた。

 しかし、その検索結果のページへの直リンができなかった。503エラーで弾かれてしまうのだ。1つ目のサイトと、2つ目のサイトの取得方法が通用しない、ということだ。念の為モバイルサイト用のURLでも同じことを試したが、結果は同じだった。

 厳重なスクレイピング対策だったので、念の為robots.txtを設定を解析してみたが、特に規制はかけられていなかったので作業を続行した。

 目当てのページの直アドはURLがわからない。番号を使って検索すれば辿り着けるが、検索結果のページは弾かれる。単純で厄介な壁だった。

 これが先週の日曜日の夜まで進んだところだ。

 それから平日はいつもの日常に戻り、3つ目のサイトについては諦めていたのだが、あれこれ調べている内に知識が少しずつついてきた。仕事の休憩時間を利用してPythonistaとdropboxを連動させたりしていた。

 そして昨日、先週の土日は理解できなかったseleniumuというツールに手を付けることにした。

 

 seleniumuとはブラウザの自動操作の為のプログラムだ。3つ目のサイトでは、検索処理を含むURLへのアクセスは弾かれてしまうのだが、TOPから手打ちで検索すれば検索結果のページを表示させることはできる。違いがわからない人の為に言うと、「検索処理のデーターを含めた検索結果のURLへの直リン」はダメだが、検索してアクセスする分には問題なく表示できる、ということだ。

 seleniumuを使えばその検索操作を自動化できる。解説サイトの内容は、先週の土日はさっぱりわからなかったが、昨日の土曜はなんとかついていくことができた。そして1時間ほど試行錯誤して、無事seleniumuのプログラムからテスト用のchromeブラウザを立ち上げて、検索結果のページを自動で表示させるところまで進むことができた。

 

 やった、これでなんとかなる、と思ったのが、その動作自体が課題となった。ブラウザを開いて検索してページを開く、必要なデータを取得するには、その処理を数千回繰り返す必要がある。その間PCは使えない。相手サーバーへの負荷を考えると、三日かそれ以上はかかるアクセス頻度で構築する必要がある。

 現実的ではない、と考えてせっかく導入したseleniumuを放棄することにした。

 

 次に思いついたのがGoogleの検索結果から目当てのページのURLをスクレイピングする方法だった。これはいい方法だと思ったのだが、なぜか取得結果を別ファイルに出力することができなかった。処理は行われているが空白のデーターが出来上がった。文字コードが原因か、何かのプロテクトか、検索をしながら思いつく限りの可能性に対処したが、解決できなかった上に、あれこれやってる内に、さっきまでデーターを取得していたコードで503エラーが返されるようになった。コードを考えながらテストでアクセスしていただけなので負荷をかけたつもりはなかったが、いずれにせよこの方法もダメだと思った。

 

 手詰まりだった。最後の手段として、Chrome拡張機能を解析するツールを探してみた。3つ目のサイトは、検索結果のページはダメ、目当てのページなら直接アクセスしても弾かれないが、そのURLはわからない。番号とURLのパラメータの生成部分に、規則性がないからだ。

 でも同じ処理をしてくれる拡張機能の中には、その目当てのページを表示してくれるものがあるのだ。

 拡張機能ソースコードを調べればヒントがつかめるかもしれない、と思い、祈る気持ちで検索をしたところ、マジであった!

 

 この拡張機能のお陰で自分にできないことをやってのけている拡張機能ソースコードを読むことができた。

 そして中を読んでいく数十分、アドレスを生成する処理の部分と思しきコードを特定した。そこで用いられていたURLが答えを示してくれた。URLのパラメーターの一部に「feed」という文字列があった。つまりその拡張機能RSS用のURLを利用してURLを生成していたということだ。

 なるほど!と思った。モバイルサイト用URLでは試したが、RSS用URLの方は盲点だった。

 

 早速、自分のコードと差し替えてみた。しかし、同じエラーが返されてしまった。でもブラウザのアドレスバーに貼り付ければ開くことができた。コマンドプロンプトからの直リンはダメで、ブラウザからの直リンはOKなのか?というよくわからない状態だった。

 この現象について調べてみるとUser-Agentという情報が関係していることがわかった。WEBサイトの中にはブラウザからのアクセスでないと受け付けないセキュリティ対策を設けているところがあるらしい。コマンドプロンプトからだとその情報がないので、弾かれてしまうというわけだ。

 これも調べまくって自分のコードに組み込んでみた。すると、テスト用のコードだと上手く行くのだが、本番用のコードだとエラーが返されてしまった。原因がわからなかった。しかも試しにUser-Agentのコードを消してみると、テスト用のコードでもアクセスすることができてしまった。User-Agentは関係なかったのだ。

 この現象に10時間近く費やしたと思う。原因は「res.raise_for_status()」という、アクセスエラーが起きた時は処理を停止させる命令のコードだった。スクレイピング処理の基本コードなので、やり始めの頃から組み込んでいたコードだったせいもあり、全く気にしていなかった。

 さらに、テストで使っていたURLと、本番用で使っていたURLでアクセスの結果が異なることもわかった。アクセス成功とエラーの2種ではなく、アクセス成功、404エラー(ペー時が存在しない)、530エラー(アクセスを弾く)と、3種類の結果があったのだ。

 私はその404と503の結果の違いを気にしておらず、だからコマんとプロンプト上のアクセス結果と、ブラウザでのアクセス結果の違いが不統一だと思えていたわけである。

 

 しかし本当の問題はここからだった。「res.raise_for_status()」を消せばエラーが起きても処理は止まらずない。存在しないURL(404)にアクセスしても、次のURLにアクセスしてくれる。機械的に総当たりをし、ページが存在する場合のみ情報を取得してくれればよい。これが求めている動作だ。

 しかし万一503(アクセス制限)がかかった時は処理を止めてほしい。しかし、res.raise_for_status()を消すということは、その停止動作をとらないコードにすることを意味する。このまま実装はできない。困ってしまった。

 

 必要な答えはわかっていた。エラーの種別で処理を分けるコードを書けばいいのだ。しかしこれが全く手掛かりがなかった。1時間ほど関係のありそうなワードで調べ続けたが、手掛かりさえつかめなかった。

 でもこれは後に私の調べかたが悪かったことが判明する。エラーコードのワードを基準に検索をしていたが、try exceptという例外処理に関する文章がちゃんとあったのである。

 ここからはお助けサイトの力も借りて、エラーの結果で処理を分けるコードを書き上げることができた。コードは無事完成したし、最後に教えてもらったコードの中に間違いの記述があって、それも自分で気が付いて直せたことが嬉しかった。

 

 今そのコードたちは昔使っていたIphone5sの中で静かに動作している。相手サーバーに迷惑をかけちゃいけないので、1アクセス10秒~20秒の間を開けて取得するようにしている。特に急ぎでやっているわけではないので、15秒以上を基準にしてもいいかな、と検討中だ。

 

 でもスクレイピングのことで調べていてわかったのは、これだけアクセスの間隔をあけていれば大丈夫、というのは自分の思い込みでしかない、ということだ。

 2010年に起きた岡崎図書館事件、これは有名になった事件なので知っている人も多いだろう。

事件の経緯

岡崎市立中央図書館
2010年3月頃、市民から岡崎市立図書館のウェブサイトの蔵書検索システムに対し接続が出来ないと苦情があり、その後もウェブサイトの閲覧が困難になる事態が相次いだ。同年4月15日、同図書館が迷惑なアクセスを受けていると愛知県警岡崎署に被害届を提出し、5月25日にアクセスを行っていた男性が蔵書検索システムに高頻度のリクエストを故意に送りつけたとして偽計業務妨害容疑で逮捕された。

男性が実際に行っていたのは、蔵書検索システムの使い勝手に満足しなかったため自身で作成したクローラを実行し、蔵書検索システムから図書情報を取得することであった。クローラとは、自動的に情報を引き出しデータベースにまとめるプログラムであり、GoogleYahoo!等の検索エンジンなどでも利用されている。また国立国会図書館でもインターネット資料収集のためクローラを用いている[1]。

20日間の勾留と取り調べの後、6月14日には男性の業務妨害の強い意図が認められないとして起訴猶予処分[2]となったが、専門家や技術者からは長期にわたる勾留の正当性[3]およびそれ以前に逮捕が必要であったのかが疑問視されている[4]。

この事件はインターネットを中心に議論を呼び、日経コンピュータ2010.08.04号で京都大学学術情報メディアセンター准教授の上原哲太郎は捜査手法に懸念を表明し、JPCERT/CC広報担当者はこのような場合はJPCERT/CCに相談して欲しかったと述べた。2010年8月21日付の朝日新聞名古屋本社版朝刊で図書館のシステムに問題があったことが報道された。

 

男性の作成したクローラの動作
産業技術総合研究所情報セキュリティ研究センター、情報セキュリティー会社など3カ所による解析では男性の作成したクローラに違法性はなく、図書館の蔵書検索システムに不具合が存在していたと回答した[5]。

このクローラは、同時には一回しかリクエストを送らず、受信後に間隔をおいてから次のリクエストを送信していた(1秒に1アクセス程度に調整)。これはクローラの動作としては「常識的」「礼儀正しい」[6]程度のものであり、応答を待たずに過大なアクセスを行うことで高負荷にさせる攻撃用のプログラムと異なる動作であった。サーバの能力について言えば、この程度の「常識的な」アクセス頻度ではまだ十分な余裕があった[6]のだが、システムが特殊な接続方式をとっていたことが原因でアクセス障害が発生した[6]。岡崎市立中央図書館のウェブサイトは、自治体のサイトとしては専門家でも想像できないほどに脆弱[6]なものであった。

 

引用

岡崎市立中央図書館事件 - Wikipedia

 

 調べれば調べるほど図書館側のシステムがおかしいことがわかるのだが、Librahack氏は自分のサイトでこのように述べている。

 

むすび | Librahack : 容疑者から見た岡崎図書館事件

 

逮捕されたことについて不満はございません。

限られた予算(税金)で運営されている公共施設のサービスに、大手企業のサービスと同じような感覚でアクセスしてしまった、
相手サーバのパフォーマンスを自分勝手に予測し、レスポンスに気を配らなかった、
相手サーバからのレスポンスHTTP500を細かくハンドリングせず単にスキップだけしていた、
固定IPのレンタルサーバ(3月14日から3月31日、cronで起動)と、プロバイダより割り振られるIPの自宅や実家(4月2日から4月15日、Thinkpadを持ち運んで自宅と実家から手動で、日毎にどちらか一方から)、合計3カ所からアクセスしていた、
などなど私に配慮が足りないことが多かったからです。

岡崎市立中央図書館ご利用者の皆様、並びに関係各位の皆様に大変なご迷惑をお掛けしました事、深くお詫び申し上げます。

 

  Pythonは人気上昇中だから、このブログをみてる人の中にも、これからPythonとお友達になる人も出てくるだろう。スクレイピングに手を付ける時はこの事件のことを思い出してほしい。

 

Pythonista 3

Pythonista 3

  • omz:software
  • 仕事効率化
  • ¥1,200
スラスラ読める Pythonふりがなプログラミング (ふりがなプログラミングシリーズ)

スラスラ読める Pythonふりがなプログラミング (ふりがなプログラミングシリーズ)