前書き
こんにちはのidatenです。今回はwebスクレイピングというものを紹介したいと思います。
そもそもwebスクレイピングって?
ウェブスクレイピング(英: Web scraping)とは、ウェブサイトから情報を抽出するコンピュータソフトウェア技術のこと。
(出典:Wikipedia)
ということで、webサイトにある情報を取ってきて情報解析を行いたいときに用いるものです。
webスクレイピングの技術を使えば、web上に公開されているデータなら基本的になんでも取ってくることができます。しかし、法律上の注意事項やそもそもそのサイトがwebスクレイピングを禁止している場合もあるので、自分でよく調べてから実践してください。
今回は、webページにあるhtmlを取得して解析することで必要なデータを取ってきてそれを処理したいと思います。
webサイトからデータを抽出して何が楽しいの?
公開されているデータを取得して、自分の好きなようにデータ処理をすることができます。例えば、ホームページ内の見出しの一覧をとってきたり、中には、自然言語処理のために文章をとってきたりというのをコンピュータに自動でやらせることができます。
実際にやってみる
前置きはこれぐらいにして、実際にwebスクレイピングをしてみたいと思います。今回使用する言語はpythonです。ここではpythonの詳しい使い方やwebスクレイピングで用いるライブラリの詳しい説明は行わないので自分でやってみたい方は、別のサイトや書籍を参考にしてください。
今回は、僕が最近ハマっている競技プログラミングを題材にして、AtCoderというオンラインで参加できるプログラミングコンテストのサイトから、レーティングとパフォーマンスという数値をとってきて、それをグラフ化するまでやってみたいと思います。
とりあえず、僕のユーザーページのコンテスト成績表というに行くと次のように表示されています。(年月日現在)
表で表示されて、その要素の一つに新Ratingとパフォーマンスという値がありますね。これらの値を取ってこようという算段です。ソースコードを表示させて見てみると、htmlでは表を表示するための<table>タグが使われています。新Ratingの方は、次のように、ratingに応じた色をつけるために、<span>タグで囲まれていますが、このように、これだけについているcssセレクタやタグがあるとデータの抽出処理をしやすいのですが、パフォーマンスの値が入っている部分は表の要素を入れるための<td>タグで囲まれているだけです。
<td>1458</td>
<td><span class='user-cyan'>1316</span></td>
htmlパーサでは、ページのhtmlを取得してそれらに処理を行うことで、求めているデータを抽出する必要がありますがこのままではパフォーマンスを抽出できません。ここで、pandasというpythonのデータ処理を行うライブラリを用いることで、<table>タグに用いらる表の要素名<th>タグを指定するとその中身である<td>の要素を返してくれる機能を使います。
これによってデータを抽出できそうですね。
では実際のコーディングに入っていきましょう。
先ほどのユーザーページを見ると、urlが
https://beta.atcoder.jp/users/(ユーザー名)/history
となっているので、urlは次のように指定できます。
usr = "idaten"
url = "https://beta.atcoder.jp/users/" + usr + "/history"
urlを指定してpandasをインポートした上でpd.read_html(url)を実行するとページ内の表をすべて取得しDataFrameのリストとして返してくれます。但し、pandasを次のように実行するには色々あらかじめpandasとlxml、html5lib、beautifulsoup4をインストールしている必要があります。それは各自で行っておいてください。
import pandas as pd
usr = "idaten"
url = "https://beta.atcoder.jp/users/" + usr + "/history"
dfs = pd.read_html(url)
print(dfs)
これを実行すると、次のような出力がされるので、ちゃんと動いていることが確かめられます。(長いので一部抜粋)
Rank Performance NewRating Diff
0 1739 -165 5 -
1 777 560 45 +40
2 1382 621 106 +61
3 911 434 144 +38
4 644 - - -
5 641 1122 302 +158
6 485 1370 547 +245
7 427 1026 630 +83
8 374 1050 698 +68
9 515 1541 857 +159
10 1159 1077 887 +30
11 674 1153 923 +36
12 562 - - -
13 491 1255 969 +46
14 188 1278 1011 +42
15 499 1308 1050 +39
16 663 1512 1114 +64
17 458 - - -
18 654 - - -
19 497 1585 1178 +64
20 144 1600 1234 +56
21 486 1707 1298 +64
22 198 - - -
23 457 1458 1316 +18
dataframeとして返してくれるので、簡単に表の値を取得できます。しかしよくみて見ると、所々表の中身が"-"となっているところがあります。これは別にhtmlの解析に失敗したとかではなくAtCoderのコンテストの中にはunratedな(rateのつかない)コンテストも開催されていることが原因です。ここで、とりあえず、perfomanceだけ取ってきて要素が"-"のものは取り除く処理を行ってみます。
次のようなコードを実行するとできます。
import pandas as pd
usr = "idaten"
url = "https://beta.atcoder.jp/users/" + usr + "/history"
dfs = pd.read_html(url)
data = "Performance"
pef = dfs[0][[data]]
l = len(pef.index)
for i in range(l):
if(pef.at[i,data]=='-'):
pef.drop(i,inplace=True)
pef = pef.reset_index(drop=True)
print(pef)
pef = pef.astype(int)
pef.rename(columns={usr:data},inplace=True)
次のような操作をしています。dfsリストから"Performance"の要素を取り出してpef変数に入れたうえで、要素の中身が"-"であった場合はdropという要素をリストから消去するメソッドを実行しています。dropの2番目の引数は消去した時、間のを詰めることを指定しています。また、printのあとの行は後にグラフに表示させるために要素を文字列から数字に変換しています。(""をにしている)そして、列の名前の変更もしています。
これを実行すると次のように出力されるので、正しく動いていることがわかります。
Performance
0 -165
1 560
2 621
3 434
4 1122
5 1370
6 1026
7 1050
8 1541
9 1077
10 1153
11 1255
12 1278
13 1308
14 1512
15 1585
16 1600
17 1707
同様の操作をratingの方もするのですが、同じコードかくのは面倒なので、先ほどのコードを関数化してperformanceとratingをつの関数で扱います。これで、グラフに表示するためのデータが揃いましたが、ただそれらを表示するだけだと面白くないので、ついでにperformanceの平均値の推移も作ってこれをmatplotlibというデータを可視化するためのライブラリを用いて表示してみます。
それをコーディングしたものが次のソースコードです。ratedコンテストの参加回数ごとのperformanceなどの値をグラフにしています。
import pandas as pd
import matplotlib as mlp
import matplotlib.pyplot as plt
def get_usr_data(usr,data):
url = "https://beta.atcoder.jp/users/" + usr + "/history"
dfs = pd.read_html(url)
pef = dfs[0][[data]]
l = len(pef.index)
for i in range(l):
if(pef.at[i,data]=='-'):
pef.drop(i,inplace=True)
pef = pef.reset_index(drop=True)
pef = pef.astype(int)
pef.rename(columns={usr:data},inplace=True)
return pef
data = ["Performance","NewRating"]
usrname = "idaten"
data_all = get_usr_data(usrname,data[0])
for i in range(1,len(data)):
data_all = pd.concat([data_all,get_usr_data(usrname,data[i])],axis=1)
perf_sum_list = [data_all["Performance"][0]]
perf_ave_list = []
for i in range(1,len(data_all["Performance"])):
perf_sum_list.append((perf_sum_list[i-1]+data_all["Performance"][i]))
for i in range(len(perf_sum_list)):
perf_ave_list.append(perf_sum_list[i]/(i+1))
perf_ave = pd.DataFrame(perf_ave_list)
perf_ave = perf_ave.astype(int)
perf_ave.rename(columns={0:"perf_ave"},inplace=True)
print(perf_ave)
data_all = pd.concat([data_all,perf_ave],axis=1)
data_all.plot()
plt.style.use('ggplot')
data_all.plot(marker="o")
plt.title("AtCoderのperf,rate推移")
plt.xlabel("回数")
plt.show()
#data_all.to_csv("atcoder_data.csv")
これを実行すると次のようなグラフが表示されてます。
これでwebスクレイピングを無事することができました。
ちなみに最後のコメントアウトを外すとcsv形式でデータを出力することもできます。
終わりに
いかがでしたでしょうか。今回はpythonをつかってwebスクレイピングをしてみました。これで好きなデータをwebページから取ってきた情報解析をすることができることがわかったと思います。
明日はonkyiの記事です、お楽しみに!