【python】unittestを使用して、関数などのテストを行う
標準モジュールのunittestを使うことで、クラスや関数が正しく機能しているかどうかをテストする事ができる。
準備
unittestのインポートをする。
import unittest
テストを行う関数の作成
URLからドメイン部分だけを抜き出す簡単な関数を作成。
引数がURLではない場合(httpで始まらない文字列の場合)は、UrlErrorにする。
class UrlError(Exception): """入力がURLじゃない場合のエラー""" def get_domain(url): if not url.startswith('http'): raise UrlError domain = url.replace('http://','').replace('https://','') if '/' in domain: domain = domain.split('/')[0] return domain
上記の関数のユニットテスト
引数にunittest.TestCaseを指定して、テスト用のclassを作成する。
class TestGetDomain(unittest.TestCase): #引数がURLの場合 def test__get_domain(self): test_domain = get_domain('http://example.com/') self.assertEqual(test_domain, 'example.com') #引数がURL以外の文字列の場合 def test__not_url(self): with self.assertRaises(UrlError): test_domain = get_domain('tp://example.com/')
テストの実行
テストはunittest.main()で実行する。
if __name__ == '__main__': unittest.main() #テスト結果 .. ---------------------------------------------------------------------- Ran 1 tests in 0.005s OK
ただし、IPythonやJupyter notebookでunittestを行おうとすると下記のエラーが発生する。
AttributeError: module '__main__' has no attribute ~~~~~~~~~~~
jupyter notebookでunittestを行う場合は、下記の記述に変更が必要。
if __name__ == '__main__': unittest.main(argv=['first-arg-is-ignored'], exit=False)
まとめ
今までテストをする時は適当な引数を渡して、ちまちまとテストしてたが
unittestなんていう便利なものがあるらしい・・。
本番環境で動かしてみると予想外のエラーが起こるということは頻繁にあるので、
日頃からテストを書く習慣をつけておくようにしたい。
【python】OAuth経由でSearch ConsoleのAPIから検索アナリティクスデータをCSVに保存する
Search ConsoleのAPIから、OAuth経由で検索アナリティクスのデータを抜き出してくる方法。
検索アナリティクスは90日分のデータしか保存されないので、定期的にこのプログラムを動かして、検索アナリティクスのデータを蓄積していきたい。
準備
諸々の必要なモジュールをインポートする。
今回は事前にSQlite3に保存しておいた、OAuth認証のトークンを使用する。
CSVの生成用にpandasもインポート。
#OAuth認証のデータを扱う時に使う import json import sqlite3 #API経由でデータ取得する時に使う import httplib2 from apiclient.discovery import build from oauth2client.client import OAuth2WebServerFlow from oauth2client import client #CSVに保存する時に使う import pandas as pd from pandas import DataFrame, Series
検索アナリティクスのデータ取得
サイトURLや取得対象日の入力
複数サイトを管理してる場合でも扱いやすくするために、今回はインプットでサイトURLを指定する方式にする。
#取得対象のURL、開始日、終了日をインプットで入力する。 property_uri = input('取得対象のURL: ') s_date = input('開始日の入力(YYYY-MM-DD):') e_date = input('終了日の入力(YYYY-MM-DD):') #CSVのファイル名用にURLの変換 domain = property_uri.replace('://','_').replace('/','_')
OAuthトークンを、SQlite3から抜き出すための関数
SQlite3で作成したテーブルから、取得対象URLに一致するサイトのOAuthトークンを取り出す。
※前もってOAuth認証を済ませておき、OAuthトークンをSQlite3に保存しておく必要がある。
def get_oauth_json(): #OAuth認証情報のデータベースに接続 #oauth_database.dbは同じディレクトリに保存しておく connecter = sqlite3.connect('oauth_database.db') c = connecter.cursor() #データベースから、取得対象URLに一致するデータを抜き出し c.execute("select * from oauth_list where site_name = '" + property_uri +"' ") for row in c: oauth_json = row[1] #データベースを閉じる connecter.commit() connecter.close() #取得対象URLのOAuth認証データを返す return oauth_json
リクエストを処理するための関数
これは参考にした記事の内容をそのまま使用させて頂いている。
def execute_request(webmasters_service, property_uri, request): return webmasters_service.searchanalytics().query( siteUrl=property_uri, body=request).execute()
取得したデータをCSVを保存するための関数
今回は取得したデータをCSVにして保存する。
def print_table(response): if 'rows' not in response: print ('Empty response') return rows = response['rows'] df = DataFrame(rows) #取得したデータのままだと検索クエリがリストになっているので、キーワードを抜き出す。 keys = Series(df['keys'].apply(lambda x : x[0])) df['keys'] = keys #各カラムの名前を日本語に変換。 shaped_df = df.rename(columns={'keys': 'キーワード', 'clicks': 'クリック数', 'impressions':'表示回数','position':'平均掲載順位','ctr':'CTR'}) #カラムの並び替え。Search Consoleの画面と同じ並びにする。 shaped_df = shaped_df.loc[:,['キーワード','クリック数','表示回数','CTR','平均掲載順位']] output_name ='【' + domain +'】SearchAnalytics_' +s_date + '_ ' + e_date + '.csv' #CSV形式でアウトプット df2.to_csv(output_name)
メインの処理
sqliteからOAtuhトークンを取り出し、API経由で取り出した検索アナリティクスのデータをCSVに保存する。
def main(): oauth_json_data = get_oauth_json() credentials = client.OAuth2Credentials.from_json(oauth_json_data) http_auth = credentials.authorize(httplib2.Http()) webmasters_service = build('webmasters', 'v3', http=http_auth) # Get the queries for the date range, sorted by click count, descending. request = { 'startDate': s_date, 'endDate': e_date, 'dimensions': ['query'], 'rowLimit': 2000 } response = execute_request(webmasters_service, property_uri, request) print_table(response)
まとめ
OAuth認証の仕方すら知らない状態からだったので、すごく苦戦してしまった…。
でも何とか形に出来たので、あとはスケジュール通りに実行できる仕組みさえ整えてしまえば、かなり便利になりそう!
今回のプログラムの作成にあたって下記の記事を参考にさせて頂きました。
xxbxxqxx.com
【python】複数サイト分のGRC順位データを比較表に整形する
GRCからダウンロードした順位データは上記の形式になっているが、このままではサイト毎の順位比較がしづらいので、下記の形式に整形したい。
準備
pandasを使用して整形する。
import pandas as pd from pandas import Series, DataFrame
GRCの順位データの整形
GRCデータの読み込み
pandasのread_csv()を使用して、GRCの順位データをDataFrameに読み込む。
GRCからダウンロードできるCSVは、1~2行目に必要ないデータが入っているので、skiprows=2を指定する。
dframe = pd.read_csv('grc_data.csv', encoding='cp932', skiprows=2)
DataFrameのデータを、ピボットに変換
下記の例では、'検索後'を行名、'サイト名'を列名、'Google順位'を値として埋める。
grc_pivot = dframe.pivot('検索語', 'サイト名' , 'Google順位') grc_pivot #出力結果 サイト名 サイト1 サイト2 サイト3 サイト4 サイト5 サイト6 検索語 キーワードA 1 6 11 11 11 11 キーワードB 2 7 12 12 12 12 キーワードC 3 8 13 14 15 16 キーワードD 4 9 14 14 14 14 キーワードE 5 10 15 15 15 15
整形後のデータをCSVで保存
grc_pivot.to_csv('grc_shaped.csv')
【python】HTMLからテーブルを読み込む(pd.io.html.read_html)
準備
pandasのインポート
import pandas as pd
pd.io.html.read_html()で、HTMLのからテーブルを読み込む
pd.io.html.read_html()を使用すると、指定したURLからテーブルのデータだけを抜き出してくれる。
url = 'https://stocks.finance.yahoo.co.jp/stocks/history/?code=2282.T' dframe = pd.io.html.read_html(url) dframe[1][0:5] #出力結果 0 1 2 3 4 5 6 0 日付 始値 高値 安値 終値 出来高 調整後終値* 1 2017年11月2日 2944 2949 2880 2910 7045000 2910 2 2017年11月1日 3245 3280 3230 3270 915000 3270 3 2017年10月31日 3250 3260 3230 3260 1082000 3260 4 2017年10月30日 3315 3315 3240 3275 1956000 3275
まとめ
いちいちDOMを指定しなくても、テーブルだけを取得してくれるのですごく便利そう。
スクレイピングする時に役立つ気がする。
【python】DataFrame, Seriesの欠損値の補完,削除
pandasのDataFrameやSeriesで、欠損値が含まれていると色々と面倒な事もある。
事前に欠損値の補完や、欠損データの削除をしておく方法。
準備
欠損値のデータを作るために、numpyのnanを使う。
import pandas as pd from pandas import Series , DataFrame import numpy as np from numpy import nan
Seriesの場合
#テスト用のシリーズの作成。 data = Series([1,2,nan,4]) data #出力結果 0 1.0 1 2.0 2 NaN 3 4.0 dtype: float64
欠損値が含まれるかを調べる
シリーズに欠損値が含まれているかどうかは、isnull()を使用する。
データがNaNの場合は、Trueが返ってくる。
data.isnull() #出力結果 0 False 1 False 2 True 3 False dtype: bool
欠損データの削除
欠損値を削除するには、dropna()を使用する。
0 1.0 1 2.0 3 4.0 dtype: float64
欠損データの補完
欠損データを補完するには、fillna()を使用する
data.fillna(10) #出力結果 0 1.0 1 2.0 2 10.0 3 4.0 dtype: float64
DataFrameの場合
#テスト用のDataFrameの作成 dframe = DataFrame([[1,2,3,5],[4,nan,5,6],[nan,7,nan,8],[nan,nan,nan,nan]]) dframe #出力結果 0 1 2 3 0 1.0 2.0 3.0 5.0 1 4.0 NaN 5.0 6.0 2 NaN 7.0 NaN 8.0 3 NaN NaN NaN NaN
欠損データの削除
Series同様に、dropna()で欠損データを削除する事ができる。
行の中に1つでもNaNが含まれている場合は、行単位で削除される
dframe.dropna() #出力結果 0 1 2 3 0 1.0 2.0 3.0 5.0
行の全てがNaNの場合に削除するには、how='all'を指定する
dframe.dropna(how='all') #出力結果 0 1 2 3 0 1.0 2.0 3.0 5.0 1 4.0 NaN 5.0 6.0 2 NaN 7.0 NaN 8.0
欠損値がある列を削除するには、axis=1を指定する。
dframe.dropna(axis=1) #出力結果 0 1 2 3
NaNじゃない数位がn個以上のある行だけを抜き出すには、thresh=nを指定する。
dframe.dropna(thresh=3) #出力結果 0 1 2 3 0 1.0 2.0 3.0 5.0 1 4.0 NaN 5.0 6.0
欠損データの補完
欠損データを補完するには、fillna()を使用する
dframe.fillna(100) #出力結果 0 1 2 3 0 1.0 2.0 3.0 5.0 1 4.0 100.0 5.0 6.0 2 100.0 7.0 100.0 8.0 3 100.0 100.0 100.0 100.0
fillna()に辞書型の値を渡すと、特定の列だけにfillna()を適用する事ができる。
◯列目は△~の様に、列毎に補完する値の指定も出来る。
dframe.fillna({3:3}) #出力結果 0 1 2 3 0 1.0 2.0 3.0 5.0 1 4.0 NaN 5.0 6.0 2 NaN 7.0 NaN 8.0 3 NaN NaN NaN 3.0
まとめ
dropna(), fillna()を使う事で、欠損データを補完・削除する事が出来る。
データを処理する前に、前もって欠損データを精査する時に役立ちそう。
【python】キーワードプランナーのデータから重複を削除して、CSVで出力
AdWordsのキーワードプランナーからダウンロードしたデータは、スペース前後の入れ替えの違いだけなどが多く含まれているデータになっている。
キーワード調査をする際に1つずつ目視していくのは大変なので、ある程度のノイズデータを一括で除去したい。
準備
#pandasのインポート import pandas as pd from pandas import Series , DataFrame #キーワードを整形するための関数 def shape_kw(keyword): split_kw = keyword.split(' ') #スペースなしのキーワード作成 join_kw = ''.join(split_kw) #並び替えをして、スペースなしのキーワード作成 sorted_kw = sorted(split_kw) shape_kw = ''.join(sorted_kw) #配列にして値を返す return [shape_kw, join_kw]
キーワードデータを整形して、重複行を削除
#CSVの読み込み。落としてきたままのCSVだとエラーになるので、一回保存し直す必要がある? dframe = pd.read_csv('keyword.csv',encoding="cp932") #検索ボリュームで降順に並び替え sorted_dframe = dframe.sort_values(by='Avg. Monthly Searches (exact match only)', ascending=False) #整形したキーワードを代入 shaped_kws = list(map(lambda keyword:shape_kw(keyword),sorted_dframe['Keyword'])) #整形したキーワードをDataFrameに変換 dframe2 = DataFrame(shaped_kws, columns=['shaped_kw', 'join_kw']) #整形したキーワードを、元のDataFrameに追加 sorted_dframe['shaped_kw'] = dframe2['shaped_kw'] sorted_dframe['join_kw'] = dframe2['join_kw'] #重複データのある行を削除 droped = sorted_dframe.drop_duplicates(['shaped_kw']) droped = droped.drop_duplicates(['join_kw'])
CSVで出力
#アウトプットのために列名を変換 renamed_dframe = droped.rename(columns = {'Avg. Monthly Searches (exact match only)':'検索vol.', 'Keyword':'キーワード'}) #アウトプットされる形式 キーワード 検索vol. 0 ハローワーク 1220000 2 バイト 246000 1 転職 246000 3 ハローワーク 求人 201000 4 求人 135000 #検索vol.が空欄のデータを補完 filled_dframe = renamed_dframe['検索vol.'].fillna(0) #キーワードと検索volのみをCSVで出力 filled_dframe[['キーワード','検索vol.']].to_csv('results_shaping_kw.csv')
【python】pandas_datareaderでYahooファイナンスの株価を取得する
pandas_datareaderを使うことで、Yahooファイナンスから過去の株価データを取得する事ができる。
pandas_datareaderを使用するには別でインストールが必要。
準備
Anaconda Promptから、pandas-datareaderをインストール。
#_ではなく、-になっている事に注意する
pip install pandas-datareader
データの取得~表示
#pandas_datareader.data のインポート import pandas_datareader.data as pdd import datetime start = datetime.datetime(2013,1,1) end = datetime.datetime(2013,1,31) #Yahooファイナンスから、SONY, SEGASammy, Nintendoの株価の取得 #https://finance.yahoo.com/lookup のSymbolから指定する data = pdd.get_data_yahoo(['SNE','SGAMY','NTDOY'], start, end)['Adj Close'] data [0:5] #出力結果 NTDOY SGAMY SNE Date 2013-01-31 11.644242 4.213631 14.94 2013-01-30 11.701698 4.270572 14.99 2013-01-29 12.640132 4.223121 15.14 2013-01-28 12.822073 4.232611 15.12 2013-01-25 12.333705 4.014338 14.41
グラフ化
data.plot()
変化量の計算
変化量の計算にはpct_change()を使用する
pct = data.pct_change() pct[0:5] #出力結果 NTDOY SGAMY SNE Date 2013-01-31 NaN NaN NaN 2013-01-30 0.004934 0.013514 0.003347 2013-01-29 0.080196 -0.011111 0.010007 2013-01-28 0.014394 0.002247 -0.001321 2013-01-25 -0.038088 -0.051569 -0.046958
変化量の相関関係を調べる
pct.corr() #出力結果 NTDOY SGAMY SNE NTDOY 1.000000 0.275519 0.345960 SGAMY 0.275519 1.000000 0.144161 SNE 0.345960 0.144161 1.000000
各社の株価の相関関係はあまりなさそう…。
相関関係の可視化
#seaborn , matplotlibのインポート import seaborn as sb import matplotlib.pyplot as plt #ヒートマップの表示 %matplotlib inline sb.heatmap(pct.corr())
#出力結果
まとめ
データ量が増えてきた時に、相関関係を調べたり、変化量を出せるのはすごく役立ちそう。
自然検索の順位変動の計算にも使えるかも。