Sponsored Link

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

(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

(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>
"""

(74) WEBブラウザ経由でPythonスクリプトを実行#2

(74) WEBブラウザ経由でPythonスクリプトを実行#2

(73) WEBブラウザ経由でPythonスクリプトを実行 を改造し、PHP経由ではなく CGIとしてPythonを実行してみる。機能自体は(73)とまったく同じ。

index.cgi

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

print 'Content-type: text/html'

import cgi
import csv_calc

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('upfile'):
	filename = form['upfile'].filename
	cmd      = int(form['cmd'].value)
	ret = csv_calc.sum_csv( filename, cmd )
	print ret

print """
<hr />
<form enctype="multipart/form-data" action="./" method=post>
<input type=file name=upfile><br />
<select name=cmd>
  <option value=1>合計値
  <option value=2>平均値
  <option value=3>最大値
  <option value=4>最小値
</select>
<input type=submit value="計算実行">
</form>
</body>
</html>
"""

csv_calc.py

import numpy as np

def sum_csv( fpath_csv, cmd ):
    data = np.loadtxt( fpath_csv, delimiter=',' )
    if cmd == 1:
        ret = data.sum()
    elif cmd == 2:
        ret = data.mean()
    elif cmd == 3:
        ret = data.max()
    elif cmd == 4:
        ret = data.min()
    else:
        print "invalid command"
        ret = 0
    return ret

(73) WEBブラウザ経由でPythonスクリプトを実行

(73) WEBブラウザ経由でPythonスクリプトを実行

Pythonの高機能且つ高速な処理をWEBアプリで使用できればとっても心強い。
今回はWEBブラウザ経由(PHPページ経由)でPythonを使用してみる。

仕様

[Client側] パソコン上でCSVファイルを作成し、n行m列の数値データを書いておく。
[Client側] WEBブラウザで計算種別を合計値、平均値、最大値、最小値の中から選択する。
[Client側] WEBブラウザでCSVファイルをアップロードする。
[Server側] CSVファイルと計算種別を受け取る。
[Server側] PythonスクリプトにCSVファイルと計算種別を渡して実行する。
[Server側] Pythonから計算結果を受け取りHTMLにして出力する。
[Client側] 受け取ったHTMLを表示する。

index.php

<!DOCTYPE html>
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=utf-8" />
<TITLE>サンプル</TITLE>
</HEAD>
</BODY>

<?php
  $cmd = (isset($_POST['cmd']))? $_POST['cmd'] : 0;
  if($cmd > 0){
    if(is_uploaded_file($_FILES['upfile']['tmp_name'])){
      move_uploaded_file($_FILES['upfile']['tmp_name'], 'tmp.csv');
      exec("python csv_calc.py tmp.csv $cmd", $resp, $code);
      echo $resp[0];
      unlink('tmp.csv');
    }
  }
?>

<hr />
<form enctype="multipart/form-data" action="./" method=post>
<input type=file name=upfile><br />
<select name=cmd>
  <option value=1>合計値
  <option value=2>平均値
  <option value=3>最大値
  <option value=4>最小値
</select>
<input type=submit value="計算実行">
</form>

</BODY>
</HTML>

csv_calc.py (Pythonスクリプトファイル)

import sys
import numpy as np

#---------------------------------
def calc_csv( fpath_csv, cmd ):
    data = np.loadtxt( fpath_csv, delimiter=',' )
    if cmd == 1:
        ret = data.sum()
    elif cmd == 2:
        ret = data.mean()
    elif cmd == 3:
        ret = data.max()
    elif cmd == 4:
        ret = data.min()
    else:
        print "invalid command"
        ret = 0
    return ret

#---------------------------------
# argv[1] : file path
# argv[2] : command 1:sum, 2:average, 3:max, 4:min
if __name__ == '__main__':
    ary_argv = sys.argv
    fpath = ary_argv[1]
    cmd   = int(ary_argv[2])
    print calc_csv( fpath, cmd )