Sponsored Link

カテゴリー別アーカイブ: WEB

(89) Pythonで画像をHTTP出力する。

(89) Pythonで画像をHTTP出力する。

アクセスカウンタの画像表示などでよく使われる方法だ。
通常、HTMLの IMGタグの SRC属性には画像ファイルのURLを指定する。
SRC属性に Pythonプログラムを指定し、HTTPレスポンスとして画像を返させることができる。

用途

プログラムで動的に生成した画像をHTTPレスポンスとして返したい場合に使用できる。
また、画像ファイルのパスを秘密にしておきたい場合に使用できる。

プログラム

以下のプログラムでは、IMGタグの SRC属性に Pythonプログラムを指定している。
この Pythonプログラムでは、実行ごとにランダムに画像を選択し、HTTPレスポンスとして返している。

index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>サンプル</title>
</head>
<body>
<img src="load_img.cgi">
</body>
</html>

load_img.cgi

※cgiファイルはアップロード後に実行属性を付与しないと500エラーが出るので注意

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import os
import random

# 画像ファイルをランダム選択
img_src = "%d.jpg" % random.randint(1,2)

# 画像を出力
print "Content-Type: image/jpeg"
print "Content-Length: %d\n" % os.stat(img_src).st_size
print open(img_src,"rb").read()

実行サンプルはこちら
//python.dogrow.net/sample/0089

(88) ライブカメラ画像をホームページに貼る。

(88) ライブカメラ画像をホームページに貼る。

■やりたいこと

・実家の窓際にライブカメラを設置し、窓から見える景色を東京でも眺めたい。
・映像ではなく、1分に1回程度更新される静止画でよい。
・カメラはなるべく安価なものがよい。

■必要なもの

・ホームページ設置サーバー(FTP,Python,PHPが使えること)
・WEBカメラ(Wifi接続可能であること)

■手順

手順1:WEBカメラを購入する。

・今回はI-O DATA社製の TS-WRLCE を使用する。(その後、赤外線カメラ付きのTS-WLC2でも同じ手順でできた。)

手順2:WEBカメラの設定をする。

・画像を転送するFTPサーバーの情報を設定する。

・動作検知の検出範囲を画面全体に設定する。

・動作検知の検出間隔を60秒に設定し、動作検知機能を有効に設定する。

手順3:最新画像ファイル取得プログラムを作成(Python)

このライブカメラは、指定したディレクトリ配下に「snapshot_yyyymmddhhmm」なるディレクトリを複数作成する。さらに、個々のディレクトリ配下に「motion-yyyy-mm-dd-hh-mm-ss.jpg」なるJPEG画像ファイルを複数作成する。

したがって、ホームページ上に最新の1枚を表示したい場合は、最新ディレクトリ配下の最新画像ファイルを1個だけ取得し、表示すればよい。ここではPythonでこの機能を実装する。

get_img.py

import sys
import os

#---------------------------------
# argv[1] : directory path
if __name__ == '__main__':
    ary_argv = sys.argv
    img_path = ary_argv[1] + "/"

    # 指定ディレクトリ配下の最新ディレクトリを取得
    ary_entry = os.listdir(img_path)
    ary_entry = [x for x in ary_entry if os.path.isdir(img_path + x) ]
    ary_entry = sorted(ary_entry, reverse=True)
    target_dir = img_path + ary_entry[0] + "/"

    # 最新ディレクトリ配下の最新画像ファイルを取得
    ary_jpeg = os.listdir(target_dir)
    ary_jpeg = [x for x in ary_jpeg if os.path.isfile(target_dir + x) ]
    ary_jpeg = sorted(ary_jpeg, reverse=True)
    target_jpeg = ary_jpeg[0]

    # 実行者へ答えを提供
    print(target_dir + target_jpeg)

手順4:ライブ画像を閲覧するためのホームページを作成(HTML+PHP)

livecam.php

<!DOCTYPE html>
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=utf-8" />
<TITLE>サンプル</TITLE>
</HEAD>
</BODY>
<?php
  exec("python3 ./get_img.py '{FTP画像転送先ディレクトリ}'", $resp, $code);
  echo "<img src='{$resp[0]}'>";
?>
</BODY>
</HTML>

(87) NPBプロ野球選手の年別成績を取得する。(ver.2)

(87) NPBプロ野球選手の年別成績を取得する。(ver.2)

前回の 「(86) NPBプロ野球選手の年別成績を取得する。」 では、一人の選手の紹介ページのURLを入力し、一人の選手の年別成績を出力するだけの機能だった。数百人ものプロ野球選手についてこれを手作業で繰り返すのは大変だ。

今回は、NPB様サイトのチームメンバー一覧ページのURLを入力し、そこから個々の選手紹介ページのURLを抽出し、個々の選手の年別成績を出力するように改良する。すなわち、チームを指定すれば所属メンバー全員分のデータが自動で出力されるようにする。

■仕様

・NPB様ホームページの1チームの選手一覧ページのURLを入力する。
・そのページから選手個々の紹介ページのURLを自動抽出する。
・選手個々の紹介ページから年別成績を自動抽出する。
・打者成績、投手成績に分けてCSVファイルを自動出力する。

■プログラム

ちょっと力ずくなところもあるが後で改良しよう…

コマンドライン入力

以下の操作でホークスの全選手の年度別投手成績、年度別打者成績がファイル出力される。

$ mkdir ./da
$ mkdir ./pt
$ python3

>>> import lib_npb
>>> lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_h.html", "H")

他のチームのURLは以下の通り。

http://npb.jp/bis/teams/rst_c.html 広島
http://npb.jp/bis/teams/rst_g.html 巨人
http://npb.jp/bis/teams/rst_db.html 横浜
http://npb.jp/bis/teams/rst_t.html 阪神
http://npb.jp/bis/teams/rst_s.html ヤクルト
http://npb.jp/bis/teams/rst_d.html 中日

http://npb.jp/bis/teams/rst_h.html ソフトバンク
http://npb.jp/bis/teams/rst_f.html 日ハム
http://npb.jp/bis/teams/rst_m.html ロッテ
http://npb.jp/bis/teams/rst_l.html 西武
http://npb.jp/bis/teams/rst_e.html 楽天
http://npb.jp/bis/teams/rst_bs.html オリックス

lib_npb.py

import urllib.request
from bs4 import BeautifulSoup
import csv

#////////////////////////////////////////////////////////////////
class NpbPlayer:
    #------------------------------------------------------------
    def __init__(self, name, url):
        self.name = name
        self.url  = "http://npb.jp" + url
        self.ary_record_pitch = []    # 投手記録
        self.ary_record_bat = []      # 打者記録

    #------------------------------------------------------------
    # 1選手の投手記録をCSVファイルに出力
    def output_record_to_csv_file_pitch(self, fname):
        if len(self.ary_record_pitch) > 0 :
            with open(fname, "w") as fh:
                writer = csv.writer(fh, lineterminator="\n")
                writer.writerows(self.ary_record_pitch)

    #------------------------------------------------------------
    # 1選手の打者記録をCSVファイルに出力
    def output_record_to_csv_file_bat(self, fname):
        if len(self.ary_record_bat) > 0 :
            with open(fname, "w") as fh:
                writer = csv.writer(fh, lineterminator="\n")
                writer.writerows(self.ary_record_bat)

    #------------------------------------------------------------
    # 1選手の記録を収集
    def get_record(self):
        try:
            resp = urllib.request.urlopen(self.url)
        except urllib.error.HTTPError as e:
            print("HTTP-Error : ", e.code)
            return False
        except urllib.error.URLError as e:
            print("URL-Error : ", e.reason)
            return False
        else:
            self.ary_record_pitch = []
            self.ary_record_bat = []
            bs = BeautifulSoup(resp.read(), "html.parser")
            for div in bs.findAll("div",id="registermaintbl"):
                th = div.find(text="防")
                for tr in div.findAll("tr",class_="registerStats"):
                    ary_1_record = []
                    for td in tr.findAll("td"):
                        ary_1_record.append(td.get_text())
                    if th == None :
                        self.ary_record_bat.append(ary_1_record)
                    else:
                        self.ary_record_pitch.append(ary_1_record)
            return True

#////////////////////////////////////////////////////////////////
def get_player_list_detail( resp ):
    ary_cPlayer = []
    bs = BeautifulSoup(resp.read(), "html.parser")
    # find name
    for tr in bs.findAll("tr",{"class":"rosterPlayer"}):
        td = tr.find("td",{"class":"rosterRegister"})
        a = td.find("a")
        if a == None :
            continue
        url  = a.attrs["href"]
        name = a.get_text()
        cPlayer = NpbPlayer(name, url)
        ary_cPlayer.append(cPlayer)
    return ary_cPlayer

#////////////////////////////////////////////////////////////////
def get_player_list( team_url ):
    try:
        resp = urllib.request.urlopen(team_url)
    except urllib.error.HTTPError as e:
        print("HTTP-Error : ", e.code)
        return None
    except urllib.error.URLError as e:
        print("URL-Error : ", e.reason)
        return None
    else:
        ary_cPlayer = get_player_list_detail(resp)
        return ary_cPlayer

#////////////////////////////////////////////////////////////////
def generate_record_file_for_1team( team_url, prefix ):
    ary_cPlayer = get_player_list(team_url)
    for cPlayer in ary_cPlayer:
        if True == cPlayer.get_record():
            print("now generating %s" % cPlayer.name)
            cPlayer.output_record_to_csv_file_bat(  "./da/data_%s_%s_da.txt" % (prefix, cPlayer.name))
            cPlayer.output_record_to_csv_file_pitch("./pt/data_%s_%s_pt.txt" % (prefix, cPlayer.name))

■備忘録

python3では reload()が使えなくなった。
同じことを以下のように実現できる。

import importlib
importlib.reload(lib_npb)

シーズン中は日々成績が変化するので、ときどき以下を実行して最新情報を収集する。

import lib_npb
lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_c.html", "C")
lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_g.html", "G")
lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_db.html", "DN")
lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_t.html", "T")
lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_s.html", "Y")
lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_d.html", "D")
lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_h.html", "H")
lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_f.html", "F")
lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_m.html", "M")
lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_l.html", "L")
lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_e.html", "E")
lib_npb.generate_record_file_for_1team("http://npb.jp/bis/teams/rst_bs.html", "O")

(85) BeautifulSoupでなんちゃってGoogle検索

(85) BeautifulSoupでなんちゃってGoogle検索

先の「(79) なんちゃってGoogle検索」をBeautifulSoupを使って実装してみる。
だいぶ楽に実装できる。開発者様に感謝感謝 m(_ _)m

>>> import urllib.request
>>> from bs4 import BeautifulSoup
>>> 
>>> url="https://www.google.co.jp/search?q=応仁の乱"
>>> 
>>> opener = urllib.request.build_opener()
>>> opener.addheaders = [('User-agent', 'Mozilla/5.0')]
>>> html = opener.open(url)
>>> bs = BeautifulSoup(html.read())
>>> 
>>> for h3 in bs.findAll("h3",{"class":"r"}):
>>> 	print(h3.a.get_text())
>>> 	print(h3.a['href'])

実行結果は以下の通り。

応仁の乱 - Wikipedia
/url?q=https://ja.wikipedia.org/wiki/%25E5%25BF%259C%25E4%25BB%2581%25E3%2581%25AE%25E4%25B9%25B1&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFggUMAA&usg=AFQjCNFx2YRTF34Q90Pc-ZVBNiK2mHmjVQ
応仁の乱とは (オウニンノランとは) [単語記事] - ニコニコ大百科
/url?q=http://dic.nicovideo.jp/a/%25E5%25BF%259C%25E4%25BB%2581%25E3%2581%25AE%25E4%25B9%25B1&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFggeMAE&usg=AFQjCNEEgMYcO6EozSu66kBx92gM4eXlkg
応仁の乱について
/url?q=http://www12.plala.or.jp/rekisi/ounin.html&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFggjMAI&usg=AFQjCNG72yMzIbhr1SXfTdNzdDSx11AQcw
応仁の乱 | ニューワイド学習百科事典 | 学研キッズネット
/url?q=https://kids.gakken.co.jp/jiten/1/10020780.html&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFggpMAM&usg=AFQjCNG-egrj_Tu5iFSB_x2msSdsYRkH7Q
第32回 応仁の乱と衰退する室町幕府 - 歴史研究所 - 裏辺研究所
/url?q=http://www.uraken.net/rekishi/reki-jp32.html&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFgguMAQ&usg=AFQjCNGXknEN9pNpL94HdINbriS9AceYvA
応仁の乱(おうにんのらん)とは - コトバンク
/url?q=https://kotobank.jp/word/%25E5%25BF%259C%25E4%25BB%2581%25E3%2581%25AE%25E4%25B9%25B1-38826&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFgg0MAU&usg=AFQjCNFq5UqUG4vYc1n8DGBmyzqRKc6U9w
菊池寛 応仁の乱 - 青空文庫
/url?q=http://www.aozora.gr.jp/cards/000083/files/1368_37258.html&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFgg5MAY&usg=AFQjCNEVNvDd9tsTtAHOzs-t_VtCyU25bQ
応仁の乱について|社会の部屋|学習教材の部屋 - Biglobe
/url?q=http://www7a.biglobe.ne.jp/~gakusyuu/rekisi/ouninnoran.htm&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFgg_MAc&usg=AFQjCNH9aKPuRrhptgrhIPVqxLW8ZEUTDw
わかる歴史【室町時代】応仁の乱 前編 - YouTube
/url?q=https://www.youtube.com/watch%3Fv%3DObDWAbDMBEo&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQtwIIRjAI&usg=AFQjCNG60Ikro_cShe86uEEQEYv1VXKcyA
応仁の乱が起こった理由 / 中学社会 歴史 by 早稲男 |マナペディア|
/url?q=http://manapedia.jp/text/798&sa=U&ved=0ahUKEwjOi_Hd47bQAhUDFpQKHaJBAsYQFghJMAk&usg=AFQjCNEB8cZKBua4-Gs8tYx7cfwZkDiz7A

(84) BeautifulSoupでスクレイピングを効率化

(84) BeautifulSoupでスクレイピングを効率化

先の「(81) 野球選手の成績を主成分分析 (続編1/2)」ではNPB様のホームページに記載されている個人打撃成績を取得させていただいた。前回は lxmlと XPathを使って個人成績データを抽出していたが、今回はこの処理を BeautifulSoup モジュールを使って行う。


(1) BeautifulSoupをインストール

$ pip3 install beautifulsoup4

(2) 2015年セリーグ打撃成績を取得してみる

① まずは情報を取得する。

>>> from urllib.request import urlopen
>>> from bs4 import BeautifulSoup
>>> html = urlopen("http://npb.jp/bis/2015/stats/bat_c.html")
>>> bs = BeautifulSoup(html.read())

② 次に、取得した情報から必要な情報だけを抽出する。

>>> ary_players = []
>>> for tr in bs.findAll("tr",{"class":"ststats"}):
>>>     ary_score = []
>>>     for td in tr.findAll("td"):
>>>         ary_score.append(td.get_text())
>>>     ary_players.append(ary_score)

or next_siblingsを使って順位列以降の必要なデータのみを抽出してもよい。

>>> ary_players = []
>>> for td in bs.findAll("tr",{"class":"ststats"}):
>>>     ary_score = []
>>>     for td in tr.td.next_siblings:
>>>         ary_score.append(td.get_text())
>>>     ary_players.append(ary_score)

③ 最後に結果を表示する。ここでCSVファイルなどに保存すればよい。

>>> for score in ary_players:
>>>     print(score)

取得した結果は以下の通り。

['1', '川端\u3000慎吾', '(ヤ)', '.336', '143', '632', '581', '87', '195', '34', '1', '8', '255', '57', '4', '3', '2', '2', '43', '0', '3', '72', '15', '.439', '.383']
['2', '山田\u3000哲人', '(ヤ)', '.329', '143', '646', '557', '119', '183', '39', '2', '38', '340', '100', '34', '4', '0', '3', '81', '1', '5', '111', '11', '.610', '.416']
['3', '筒香\u3000嘉智', '(デ)', '.317', '138', '568', '496', '79', '157', '28', '1', '24', '259', '93', '0', '0', '0', '2', '68', '0', '2', '98', '5', '.522', '.400']
['4', 'ルナ', '(中)', '.292', '134', '564', '496', '61', '145', '26', '1', '8', '197', '60', '11', '0', '0', '6', '57', '2', '5', '77', '13', '.397', '.367']
['5', 'ロペス', '(デ)', '.291', '140', '565', '516', '63', '150', '29', '1', '25', '256', '73', '1', '1', '0', '3', '44', '3', '2', '82', '14', '.496', '.347']
['6', '平田\u3000良介', '(中)', '.283', '130', '559', '491', '76', '139', '27', '3', '13', '211', '53', '11', '7', '1', '0', '64', '1', '3', '86', '5', '.430', '.369']
['7', '鳥谷\u3000敬', '(神)', '.281', '143', '646', '551', '69', '155', '21', '4', '6', '202', '42', '9', '6', '2', '3', '89', '2', '1', '77', '8', '.367', '.380']
['8', '福留\u3000孝介', '(神)', '.281', '140', '569', '495', '53', '139', '24', '3', '20', '229', '76', '1', '2', '1', '7', '65', '1', '1', '75', '15', '.463', '.361']
['9', 'マートン', '(神)', '.276', '140', '583', '544', '46', '150', '27', '0', '9', '204', '59', '0', '1', '0', '5', '31', '0', '3', '77', '21', '.375', '.316']
['10', '梶谷\u3000隆幸', '(デ)', '.275', '134', '578', '520', '70', '143', '35', '2', '13', '221', '66', '28', '13', '2', '2', '54', '0', '0', '132', '4', '.425', '.342']
['11', '新井\u3000貴浩', '(広)', '.275', '125', '480', '426', '52', '117', '22', '2', '7', '164', '57', '3', '0', '0', '4', '48', '1', '2', '73', '15', '.385', '.348']
['12', '田中\u3000広輔', '(広)', '.274', '141', '590', '543', '61', '149', '33', '9', '8', '224', '45', '6', '7', '5', '1', '34', '2', '7', '105', '8', '.413', '.325']
['13', 'ゴメス', '(神)', '.271', '143', '601', '520', '49', '141', '28', '0', '17', '220', '72', '0', '1', '0', '3', '72', '1', '6', '134', '15', '.423', '.364']
['14', 'エルナンデス', '(中)', '.271', '138', '548', '498', '54', '135', '27', '2', '11', '199', '58', '5', '3', '10', '4', '35', '1', '0', '106', '13', '.400', '.317']
['15', '雄平', '(ヤ)', '.270', '141', '585', '551', '57', '149', '33', '4', '8', '214', '60', '7', '4', '1', '3', '27', '1', '3', '82', '7', '.388', '.307']
['16', '坂本\u3000勇人', '(巨)', '.269', '130', '558', '479', '50', '129', '21', '3', '12', '192', '68', '10', '4', '9', '5', '65', '1', '0', '79', '5', '.401', '.353']
['17', '畠山\u3000和洋', '(ヤ)', '.268', '137', '584', '512', '64', '137', '26', '0', '26', '241', '105', '0', '0', '0', '8', '62', '0', '2', '92', '10', '.471', '.344']
['18', '大島\u3000洋平', '(中)', '.260', '142', '620', '565', '70', '147', '20', '4', '6', '193', '27', '22', '8', '10', '1', '39', '1', '5', '65', '5', '.342', '.313']
['19', 'バルディリス', '(デ)', '.258', '139', '525', '465', '38', '120', '23', '0', '13', '182', '56', '0', '0', '0', '5', '43', '2', '12', '62', '12', '.391', '.333']
['20', '菊池\u3000涼介', '(広)', '.254', '143', '644', '562', '62', '143', '20', '3', '8', '193', '32', '19', '9', '49', '2', '29', '2', '2', '92', '7', '.343', '.292']
['21', '上本\u3000博紀', '(神)', '.253', '108', '452', '375', '44', '95', '18', '1', '4', '127', '31', '19', '11', '29', '0', '44', '1', '4', '69', '1', '.339', '.338']
['22', '長野\u3000久義', '(巨)', '.251', '130', '479', '434', '49', '109', '20', '3', '15', '180', '52', '3', '2', '5', '2', '34', '0', '4', '81', '12', '.415', '.310']
['23', '丸\u3000佳浩', '(広)', '.249', '143', '633', '530', '81', '132', '28', '1', '19', '219', '63', '15', '7', '4', '4', '94', '2', '1', '143', '4', '.413', '.361']
['24', '中村\u3000悠平', '(ヤ)', '.231', '136', '502', '442', '36', '102', '14', '0', '2', '122', '33', '3', '2', '14', '2', '40', '1', '4', '80', '9', '.276', '.299']

(83) 「年ロボくん」を作る ver.1

(83) 「年ロボくん」を作る ver.1

東大合格を目指す「東ロボくん」が東大合格をいったん諦めた、とのニュースが流れていた。
現在の技術では自然言語処理が難しく、国語・英語の文章題を解けないのだとか。

そこまで本格的な物は作れないけれども、
・問題を与える
・WEB検索して回答を決める
というモノを作ってみたい。


テーマは年号当てロボット

いきなりハードルを上げず、まずはできそうなモノから作ってみる。
・[人] イベントに関するキーワードを与える。
・[COM] イベントの年号を回答する。(WEBスクレイピングにより)


動作仕様 ver.1

1)「(79) なんちゃってGoogle検索」の方法を使い、与えられたキーワードを Google様で検索させていただく。
2)検索結果として表示されたページを1個ずつ訪問し、「(81) 野球選手の成績を主成分分析 (続編1/2)」の方法を使って年号らしき表示物を抽出する。
3)抽出した年号でヒストグラムを作り、最頻値を回答として採用する。
※2)を改良し続けるのが大変な仕事なんだろうなぁ


実行結果

実行サンプルはこちら。
http://python.dogrow.net/sample/0083/
・プログラムが汚いので整理してからUPする。
・処理が重くてときどきタイムアウトしてしまう…







改善点

いろいろいろいろいろいろいろいろ…
Ver.2ではもうちょっと賢くしたい。

(82) 野球選手の成績を主成分分析 (続編2/2)

(82) 野球選手の成績を主成分分析 (続編2/2)

(81) 野球選手の成績を主成分分析 (続編1/2)」で作ったデータを使って、
(80) 野球選手を測ってみる 2016年版」と同じ解析をやってみる。
目的はオリジナルの物差しを作ること。


複数年度×複数リーグのデータを直列に繋ぐ

(81) 野球選手の成績を主成分分析 (続編1/2)」で作ったデータを直列に繋ぎ、一つの入力データにする。その際に、各行(=各選手)の先頭列に年度値を埋め込んでおく。後で「○○○○年の和田はすごかった!」みたいな喜び方をしたいから。

import csv
def load_data(basepath):
    files = glob.glob( basepath + '/*.csv')
    ary=[]
    for fpath in files:
        year = fpath.split('/')[-1].split('_')[0]
        f = open(fpath, 'r')
        dr = csv.reader(f)
        for row in dr:
            row.insert(0, year)
            ary.append(row)
    return np.array(ary)

12年間で延べ701人分の打撃結果を解析

12年間連続で規定打席に到達している人は1人で12人分カウントされる。

def calc_pca(X):
    Xm = np.broadcast_to(X.mean(axis=0), X.shape)
    Xs = np.broadcast_to(X.std(axis=0),  X.shape)
    Xv = (X - Xm) / Xs
    R1  = np.corrcoef(Xv.T)
    l,v = np.linalg.eigh(R1)
    r = l / l.sum()
    return v,r,Xv

def ctrl_proc():
    basepath = '../0081/data'
    ary_joined = load_data(basepath)
    # 不要項目を削除
    in_data = ary_joined[0::,6:24]
    # PCA
    X = in_data.astype(np.float64)
    v,r,Xv = calc_pca(X)
    return ary_joined,v,r,X,Xv

結果は?

(80) 野球選手を測ってみる 2016年版」と同じような物差しができた…
あんまりおもしろくないなぁ

第1主成分

よく試合に出場し、よくヒットを打ち、よくホームに帰り、長打も多く、打点も稼ぐ、スーパースターを見つけ出す物差し!

第2主成分

よく欠場するし、あんまりヒット打たないし、足遅くて併殺多いし、でもときどき一撃をくらわすパワーを持つ、ちょっとビミョーな準1流選手を見つけ出す物差し

12年間延べ701人の選手を物差しで測った結果は以下の通り。

(81) 野球選手の成績を主成分分析 (続編1/2)

(81) 野球選手の成績を主成分分析 (続編1/2)

(80) 野球選手を測ってみる 2016年版」では、日本プロ野球2016年シーズンの打者結果を使って遊んだ。どうせやるのならば、もっと大きなデータで同じことをやってみたい、プロ野球の過去の結果も使って同じ解析をしてみたい、と思う。

しかし…
前回はYahooスポーツの表示情報を手動でコピペして入力用CSVファイルを作成したが、この手作業を過去何年分もやると量が多すぎて途中で気持ちが切れちゃいそうだ。

そこで…
(75) 指定URLのWEBページのHTMLソースコードを表示」や
(79) なんちゃってGoogle検索
で使ったテクニックを利用して、過去データをプログラムに自動収集させてみたい。


過去データをスクレイピング

前述の(79)では、取得したHTMLの構文解析に HTMLParser モジュールを使用していた。
今回は楽をするために lxml モジュールを使う。C,C++での開発で libxmlを長いこと使っているが、これを使うと XPathでちょちょいと各タグにアクセスできるので便利だ。

まずは Pythonに lxmlをインストールしておく。

$ easy_install --user lxml
$ easy_install --user cssselect

データ収集&保存

NPB(日本野球機構)様のホームページでは、2005年~2016年の打者成績が掲載されているので、これをデータ解析の入力データとして使わせていただく。2005年~2016年の12年分×2リーグ分、全24ページのデータを使わせていただく。

index.cgi

#!/export/virtual/neuralnets/usr/local/bin/python
# -*- coding: utf-8 -*-

print 'Content-type: text/html'

import npbparser
import numpy as np

print """
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>0081</title>
</head>
<body>
"""

def collect_data(year, league_cp):
	url = "http://npb.jp/bis/%d/stats/bat_%s.html" % (year, league_cp)
	fname = "%d_%s.csv" % (year, league_cp)
	res = npbparser.get_npb_score(url);
	np.savetxt( fname, res, delimiter=',',fmt='%s')

for year in range(2005,2016):
	collect_data(year,'c')
	collect_data(year,'p')

print """
<form enctype="multipart/form-data" action="./" method=post>
<input type=submit value="実行">
</form>
</body>
</html>
"""

npbparser.py

#!~/usr/local/bin/python
# -*- coding: utf-8 -*-
import numpy as np
import urllib2
import lxml.html
import chardet

def get_npb_score(url):
    #------------------------------------------------
    # load html
    opener = urllib2.build_opener()
    opener.addheaders = [('User-agent', 'Mozilla/5.0')]
    res = opener.open(url)
    html = res.read()
    res.close()
    ccode = chardet.detect(html)          # 文字コードを判定
    html = html.decode(ccode['encoding']) # unicodeに変換

    #------------------------------------------------
    # parse HTML
    dom = lxml.html.fromstring(html)      # DOMツリー生成
    ary_tr = dom.xpath("//div[@id='stdivmaintbl']//table/tr")
    ary_1player = []
    for tr in ary_tr:
        if len(tr) < 25:
            continue
        da_items = []
        for td in tr:
            da_items.append(td.text.encode("utf-8"))
        ary_1player.append(da_items)
    ary_np = np.array(ary_1player)
    return ary_np

実行結果

過去12年分のCSVファイルデータができた。
※UTF-8なのでExcelで開くと文字化けするかも
2005年セ , 2005年パ
2006年セ , 2006年パ
2007年セ , 2007年パ
2008年セ , 2008年パ
2009年セ , 2009年パ
2010年セ , 2010年パ
2011年セ , 2011年パ
2012年セ , 2012年パ
2013年セ , 2013年パ
2014年セ , 2014年パ
2015年セ , 2015年パ
2016年セ , 2016年パ

次回はこのデータを使ってPCAしてみる。

(79) なんちゃってGoogle検索

(79) なんちゃってGoogle検索

Google検索&結果表示をPython経由で行う。
ウェブスクレイピングの基礎になるのかも(?)

実行サンプルはこちら。
http://python.dogrow.net/sample/0078/

(1/2) index.cgi

1ページ目しか表示できていない…
内部で start=10,20,30 などと切り替えて情報収集を繰り返せば複数ページ分を表示できるけど、まずは基本形のみを作ってみる。

#!/export/virtual/neuralnets/usr/local/bin/python
# -*- coding: utf-8 -*-

print 'Content-type: text/html'

import cgi
import myparser
import re

print """
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>サンプル</title>
</head>
<body>
"""

form = cgi.FieldStorage()
if form.has_key('word'):
	word = re.sub("[ | ]+","+",form['word'].value)
	url = 'https://www.google.co.jp/search?q=' + word
	linkItem = myparser.parse(url)
	for item in linkItem:
		title = item.get_title()
		site  = item.get_ref()
		if len(title) <= 0:
			title = '***'
		if re.match('http', site):
			href = site
		else:
			href = 'https://www.google.com' + site
		print '<a href=\"%s\">%s</a><hr />' % (href, title)

print """
<form enctype="multipart/form-data" action="./" method=post>
Google検索:<input type=text name=word style="width:400px">
<input type=submit value="実行">
</form>
</body>
</html>
"""

(2/2) myparser.py

強引なところもあるので、後で勉強し直して修正するかも…
指定WEBページのデータ取得には urllib2 モジュールのHTTPラッパー機能を使用する。
HTMLの構文解析には HTMLParser モジュールを使用する。(今回は楽できる lxml を使わない)
Pythonプログラムで文字列を処理する間は unicode で扱う。

#!~/usr/local/bin/python
# -*- coding: utf-8 -*-
import chardet
import urllib2
from HTMLParser import HTMLParser

class MyLinkItem:
    def __init__(self):
        self.m_ref = ''
        self.m_title = ''
    def set_ref(self, val):
        self.m_ref = val
    def set_title(self, val):
        self.m_title = val
    def get_ref(self):
        return self.m_ref.encode('utf-8')
    def get_title(self):
        return self.m_title.encode('utf-8')

class MyParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.m_linkItem = []
        self.proc_atag = False
        self.title = ''
    def handle_starttag(self, tagname, attribute):
        if tagname.lower() == 'a':
            for i in attribute:
                if i[0].lower() == 'href':
                    if re.match('\/url\?q=http', i[1]):
                        if not re.search('webcache\.googleusercontent', i[1]):
                            self.proc_atag = True
                            self.title = ''
                            linkItem = MyLinkItem()
                            linkItem.set_ref(i[1])
                            self.m_linkItem.append(linkItem)
    def handle_endtag(self, tagname):
        if tagname.lower() == 'a':
            if self.proc_atag == True:
                self.m_linkItem[-1].set_title(self.title)
                self.proc_atag = False
    def handle_data(self, data):
        if self.proc_atag == True:
            self.title = self.title + data
    def get_linkItem(self):
        return self.m_linkItem

def parse(url):
    # load html
    opener = urllib2.build_opener()
    opener.addheaders = [('User-agent', 'Mozilla/5.0')]
    html = opener.open(url)
    data = html.read()
    html.close()
    rr = chardet.detect(data)           # 文字コードを判定
    data = data.decode(rr['encoding'])  # unicodeに変換
    # parse
    parser = MyParser()
    parser.feed(data)
    linkItem = parser.get_linkItem()
    parser.close()
    return linkItem

(75) 指定URLのWEBページのHTMLソースコードを表示

(75) 指定URLのWEBページのHTMLソースコードを表示

Pythonの urllib2 を使用し、指定したWEBページのHTMLソースコードを取得してみます。

サンプルはこちらです。
http://python.dogrow.net/sample/0075

HTTPのリクエスト送信&レスポンス受信を隠蔽してくれています。
いろいろとネット上の情報収集&解析に使えそうです。

index.cgi

#!/usr/local/bin/python
# -*- coding: utf-8 -*-

print 'Content-type: text/html'

import cgi
import urllib2

def get_webpage( url ):
	file = urllib2.urlopen(url)
	return file.read();

print """
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>サンプル</title>
</head>
<body>
"""

form = cgi.FieldStorage()

if form.has_key('url'):
	url = form['url'].value
	text = get_webpage(url)
	fname = 'temp.txt'
	fh = open(fname, 'w')
	fh.write(text)
	fh.close()
	print "<a href=\"" + fname + "\" target=_blank>" + fname + "</a>"

print """
<hr />
<form action="./" method=post>
URLを入力 <input type=text name=url style="width:200px;"><br />
<input type=submit value="実行">
</form>
</body>
</html>
"""