DAY4 : Python3.5のurllib.requestでユーザーエージェントを偽装してみる
今日はPython3.5のurllib.requestでユーザーエージェントを偽装する話をする。
HTTPプロトコルに従ってサーバーにリクエストを送るとき、こちらのIPアドレスをサーバーに伝えなければ情報はもちろん送られてこない。実は、その他にも色々な情報をサーバー側に伝えている。
例えばどのようなブラウザなのか(Chrome, Firefoxなど)、OSはなんなのか(Windows, iOSなど)、言語はなんなのか(日本語、英語など)等の情報がサーバーに送られる。これらの情報をまとめた文字列をユーザーエージェントと言い、それを元にサーバー側はスマホ用ページを送るのか、日本語版ページを送るのかなどを判断し、最適なページを送ってきてくれることとなる。
実際にどのような情報が送られているのかはこちらのページにアクセスすると教えてくれる。
www.cman.jp
さてPython3.5のurllib.requestを使用してサーバーにリクエストを送るときのユーザーエージェントはどのようになっているのだろう。
上のサイトを利用して確かめてみた。
import urllib.request url='http://www.cman.jp/network/support/go_access.cgi' req=urllib.request.Request(url) response=urllib.request.urlopen(req) html=response.read().decode('utf-8') open("response.html","w").write(html)
得られたresponse.htmlをブラウザで表示してみると、HTTP_USER_AGENTの欄が
Python-urllib/3.5
となっており、Pythonで機械的にアクセスしていることがサーバー側に分かってしまっている(別に現状隠す必要はないのだが)。
そこでユーザーエージェントを、firefoxとしてアクセスしたときの文字列に変更してアクセスするための関数、request_as_foxを次のように定義した。
def request_as_fox(url): headers={"User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0"} return urllib.request.Request(url,None,headers)
この関数をはさんでrequestを得て、先のサイトにアクセスしてみる。
req=request_as_fox(url) response=urllib.request.urlopen(req) html=response.read().decode('utf-8') open("response.html","w").write(html)
結果、HTTP_USER_AGENTの欄が
Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0
となりブラウザのfirefoxを使用してアクセスしてきたようにサーバー側は判断するようになった。
なおユーザーエージェントを偽装するのは違法ではないのでご安心を。
代表的なブラウザでも変更できる。
DAY3 : matplotlibの背景画像をPillowを用いて設定する
まずは前回得られたshotsデータをmatplotlibでシンプルに可視化してみよう。
今回は元サイトとの差別化のためshotの起点に時間を記載してみた。
import matplotlib.pyplot as plt fig=plt.figure() ax=fig.add_subplot(111) for i in range(len(shots)): if shots[i][5]=='blue': color='b' elif shots[i][5]=='red': color='r' elif shots[i][5]=='yellow': color='y' elif shots[i][5]=='darkgrey': color='k' ax.plot([float(shots[i][1]),float(shots[i][3])],[float(shots[i][2]),float(shots[i][4])],color) ax.text(float(shots[i][1]),float(shots[i][2]),shots[i][0]) ax.set_xlim(0,740) ax.set_ylim(0,529) ax.invert_yaxis() plt.shot()
ピッチが背景に描写されていないので分かりにくい。
matplotlibでも背景画像を設定することができるようなのでやってみることにした。
tokeigaku.blog.jp
画像の読み込み自体はOpenCVかPillowという外部ライブラリを使用するよう。
今回はPillow(PIL)をpipでインストールして使ってみることにした。
import numpy as np from PIL import Image if pitch: im=Image.open("pitch_shot.png") im=np.array(im) ax.imshow(im)
このコードを上に追加して、pitch=Trueで実行することで得られた画像がこちら。
ロングシュートの起点がはみだしているもののなかなか良い感じ。
DAY2 : 初めてのWebスクレイピング②
昨日の記事のコードを詳しく見てみよう。
まずはPythonの標準ライブラリurllib.requestを利用してwebページのソースコードを取得したシーン。
response=urllib.request.urlopen('http://www.fourfourtwo.com/statszone/8-2015/matches/803352/team-stats/56/0_SHOT_01') html=response.read() print(type(html))
ここで引っかかったのがhtmlの型はbyteオブジェクトであるということ。
<class 'bytes'>
21.6. urllib.request — URL を開くための拡張可能なライブラリ — Python 3.5.1 ドキュメント
urlopen は bytes オブジェクトを返すことに注意してください。これは urlopen が、HTTP サーバーから受信したバイトストリームのエンコーディングを自動的に決定できないためです。一般に、返された bytes オブジェクトを文字列にデコードするためのエンコーディングの決定あるいは推測はプログラム側が行います。
大人になってからまた考えよう。
何はともあれソースコードが得られたのでここから正規表現を使って情報を抜き出していく。
それぞれのshotオブジェクトはコードにおいて以下のように定義されていた。
<line class="pitch-object timer-1-12" marker-start="url(#bigblueend)" marker-end="url(#bigblue)" x1="421.6" y1="485.99" x2="387.28" y2="195.54" style="stroke:blue;stroke-width:3" />
timer-1-12...前半(1)の12分
x1,y1...shotの起点座標
x2,y2...shotの終点座標
stroke:blue...線の色
という風に規定されているようだ。
したがって以下のような正規表現を定義した。
.は本来任意の一文字を表現するメタ文字だが、[ ]の中ではメタ文字は普通の文字として認識されるので[\d.]+で428.88などを指定できる。
htmlを通常の文字列に変換してからfindallメソッドに渡していることに注意。
r=re.compile('<line class=[^\d]*\d-(\d+)[^\d]*x1="([\d.]+)" y1="([\d.]+)" x2="([\d.]+)" y2="([\d.]+)" style="stroke:([a-z]+);') shots=r.findall(str(html)) print(shots)
正規表現で()で囲んだ部分の5つのデータがそれぞれタプルとなり、長さ13のタプルのリストが得られた。前半(1)か後半(2)かという情報は情報量が少ないので取得していない。
[('2', '428.88', '457.73', '473.6', '37.996', 'red'), ('12', '421.6', '485.99', '387.28', '195.54', 'blue'), ('16', '390.4', '319.57', '385.2', '60.375', 'red'), ('29', '156.4', '573.91', '317.6', '350.97', 'red'), ('45', '554.72', '511.11', '470.48', '136.367', 'red'), ('53', '260.4', '338.41', '280.16', '316.43', 'darkgrey'), ('56', '334.24', '385.51', '394.56', '206.53', 'blue'), ('61', '225.04', '489.13', '306.16', '120.75', 'red'), ('66', '496.48', '336.84', '481.92', '314.86', 'darkgrey'), ('71', '170.96', '291.31', '335.28', '145.705', 'yellow'), ('84', '594.24', '451.45', '559.92', '401.21', 'darkgrey'), ('84', '349.84', '338.41', '349.84', '184.55', 'blue'), ('91', '395.6', '318', '420.56', '127.351', 'yellow')]
webスクレイピングは初めてだったので、標準ライブラリのみを使用してコードを書いている。
とはいえPythonにはwebアプリケーションに関する有名なライブラリが沢山あり、有用性が分かるようになったらまたチャレンジしていきたい。
orangain.hatenablog.com
次回はmatplotlibを用いたshotデータの可視化について述べる。
DAY1 : 初めてのWebスクレイピング
分析をはじめるにあたって、当然必要となるのがデータだ。
したがって本ブログの前半部はデータ収集の手法を確立する過程を記すこととなる。
今回はPython3.5を使って、必要な情報をサッカーの情報サイトからwebスクレイピングすることにした。
まず当面は特定の1試合についての情報収集を自動化することを目標にする。
今回対象としたのは2016/01/02に行われたSunderland 3-1 Aston Villaの試合。
新年初戦を久々の勝利で終えることができ、安堵したファンの方も多いだろう。
まずはこの試合のシュート情報を読み取ることとする。
次の画像はfoufourtwoが公開しているその試合のシュートに関してのデータである。
ここから情報を読み取っていく。
webのソースを見てみると、それぞれの線は
<line class="
から始まるタグで管理されていることが分かった。
それを踏まえて正規表現を用いてデータを抽出する以下の関数を定義した。
import urllib.request import re def scrape_stats_lines(url): response=urllib.request.urlopen(url) html=response.read() r=re.compile('<line class=[^\d]*\d-(\d+)[^\d]*x1="([\d.]+)" y1="([\d.]+)" x2="([\d.]+)" y2="([\d.]+)" style="stroke:([a-z]+);') lines=r.findall(str(html)) return lines
実行結果を可視化してみると以下。
正常に動作していることが確認できる。
Abstract
本ブログは、Bioinformaticsを専攻し、また同時にPremier LeagueのSunderlandのファンでもある筆者が、Bioinformaticsの講義で学んだデータサイエンスの手法をサッカーの試合データの分析に適用していこうという目標のもと、開始したものである。
HelloHatena!
HelloHatena.py
print('Hello Hatena!')