白旗製作所

suumo賃貸物件情報取得スクリプト

そのうち引っ越すつもりで家探しを始めたのですが、
物件の数が多すぎて、比較したり条件に合う物件を抽出するのがかなりめんどくさいです。
比較評価をexcelでちまちまやっていると、条件を自動で取得してきたくなります。

物件DBにアクセスさせてくれれば良いんですけど、どうもそういうDBは公開されていないようで、
suumo等の不動産会社のWebページから、Webスクレイピングで情報を取得することが行われているようです。

以下では、GoogleスプレッドシートのIMPORTXML()関数を用いて、HTMLからXPathを用いて
情報を取得する方法が紹介されています。
家探しのための Suumo スクレイピング用スプレッドシート
Google Spreadsheetで効率的に物件を探す

たとえば、
=REGEXREPLACE(IMPORTXML(A3,"//*[@id='contents']/div[1]/div[2]/div[1]/table/tbody/tr/td[1]/div/div[1]/span"), "万円", "")
をセルに入力すると、A3に記載したURLの物件ページから、家賃の情報を取得し、数字部分のみを表示することができます。


しかしながら、私の環境では、物件数を増やすとIMPORTXML()関数の数が多すぎるようで、
GoogleスプレッドシートがLoading...で止まってしまいました。

どうにかならないかトライしてみたのですが、あまり改善が見られなかったので、
Pythonでスクリプトを作成しました。


基本的にやっていることはGoogleスプレッドシートと一緒で、
lxml.htmlとrequestsモジュールをインポートし、指定したURLからXPathでテキストを取得しています。
また、pandas.DataFrameにデータを追加し、まとめてcsvで外部に保存します。

車を持っているので駐車場の情報も取得したかったのですが、
取得しに失敗したのでここは列だけ用意しています。



これで以下のように情報をまとめた表が作れるので、
Excelの機能を使って自由に処理ができます。
suumoのWebスクレイピング結果例


ちなみに、上のコードにない情報でもXPathさえわかれば同様に取得できます。
XPathの調べ方は、たとえばFireBug等ブラウザアドオンのデバッグツールを使う方法があります。
意外と知られていない、FirebugのX-pathコピー機能

ただし、Webページの状態によってXPathのタグ番号等が異なるので、注意が必要です。
たとえば、suumoの場合、ユーザーログイン中とそうでない状態でURLとXPathが微妙に異なるので、
URLから状態を判定して使用するXPathを分ける等しないといけません。

今回のコードでは、最初のdivタグまでを"//*[@id='contents']"で置き換えて
どちらの場合にも対応する方法を用いていますが、このタグ内に無い情報には使えません。


スポンサーサイト
  1. 2017/04/03(月) 00:38:59|
  2. Python
  3. | トラックバック:0
  4. | コメント:0

[Python]Anaconda3+spyder環境でpandasのエラーが生じてIPythonに括弧が入力できない。

タイトルのとおりです。

長時間Spyderを開いていると、たまにIPython上に以下のメッセージがでて、
括弧"("とかの記号を入力しようとすると、エラーメッセージで潰されて実行できなくなります。

C:\Anaconda3\lib\site-packages\pandas\core\format.py:2037: RuntimeWarning: invalid value encountered in greater has_large_values = (abs_vals > 1e8).any()
C:\Anaconda3\lib\site-packages\pandas\core\format.py:2038: RuntimeWarning: invalid value encountered in less has_small_values = ((abs_vals < 10 ** (-self.digits+1)) &
C:\Anaconda3\lib\site-packages\pandas\core\format.py:2039: RuntimeWarning: invalid value encountered in greater (abs_vals > 0)).any()

ググるとgithubにQAがあったのですが、
どうやらpandasのバグのようです。
解決策は以下の2つが示されてます。
・pandasを最新版にする
 conda remove pandas
 conda install pandas
・pandasのオプションでfloatのフォーマットを変更する
 pd.set_option('display.float_format', lambda x:'%f' %x)

https://github.com/pydata/pandas/issues/9950

毎回spyder再起動やらオプション変更するのも面倒ですし、
とっとと最新版にしてしまいましょう。


  1. 2015/11/07(土) 16:16:32|
  2. Python
  3. | トラックバック:0
  4. | コメント:0

Pythonの基本操作

忘備録的に書いておきます。
ちょっと雑多すぎるので、今度整理します。

環境:Anaconda + Spyder (Python3.4)
import pandas as pd
from pandas import Series, DataFrame
import numpy as np

【数列をつくる】
In [52]: np.arange(3)
Out[52]: array([0, 1, 2])

In [53]: np.arange(10,100,9)
Out[55]: array([10, 19, 28, 37, 46, 55, 64, 73, 82, 91])

In [65]: [x for x in range(10,100,9)]
Out[95]: [10, 19, 28, 37, 46, 55, 64, 73, 82, 91]

In [100]: np.ones(4)
Out[100]: array([ 1., 1., 1., 1.])

In [101]: np.ones([3,4])
Out[101]:
array([[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.],
[ 1., 1., 1., 1.]])

【乱数行列の生成】
In [63]: np.random.rand(3,3)*3
Out[63]:
array([[ 2.1996651 , 0.29671819, 2.15422178],
[ 0.28882665, 0.61283333, 0.38610587],
[ 2.64869479, 1.76624717, 2.03628832]])

【配列の長さ】
len()で求められる。size()に相当するものは無い様なので、
len(a[0])とかするしか無い。

In [74]: b = np.random.rand(3,5)
In [75]: len(b[0])
Out[75]: 5
In [76]: len(b)
Out[76]: 3

【平均・標準偏差・相関係数】
np.average(data)
np.std(data)
data.corr()

【重回帰分析】
予測したいデータをyに、説明変数をxに入れる。
interceptをTrueにすると切片ありで予測する。

model = pd.ols(x=xdata, y=ydata, intercept=True)
print(model) #結果表示
print(model.y_fitted) #説明変数から予測したデータ

【エクセルファイルの読み込み】
book = 'sample.xlsx'
sheet = 'sheet1'
xlsx = pd.ExcelFile(book)
data = xlsx.parse(sheet)

【csvファイルの読み込み】
filename = 'sample.csv'
data = pd.read_csv(filename, skiprows=0, encodin='SHIFT-JIS')

【csvファイルへの書き込み】
data.to_csv(filename)

●pandas.DataFrame関係

【NaNの行を削除】
nonandata = data.dropna()

【Pandas DataFrameの行インデックスと列名の取得】
index = data.index
columns = data.columns

【インデックスの振り直し】
data.reset_index(drop=True)

【インデックスの重複を排除】
In [116]: data
Out[116]:
0 1 2 3
0 1 1 1 1
1 1 1 1 1
2 1 1 1 1
0 0 0 0 0

In [119]: data[~data.index.duplicated()]
Out[119]:
0 1 2 3
0 1 1 1 1
1 1 1 1 1
2 1 1 1 1

【インデックスでソート】
In [127]: data
Out[127]:
0 1 2 3
0 1 1 1 1
2 1 1 1 1
3 1 1 1 1
1 0 0 0 0

In [128]: data.sort_index()
Out[128]:
0 1 2 3
0 1 1 1 1
1 0 0 0 0
2 1 1 1 1
3 1 1 1 1

【行列の転置】
data2 = data.T


【行・列を指定した参照】
In [105]: sample['data1'] #列名を指定
Out[105]: 50
In [106]: sample[['data1','data2']] #列名を複数指定する場合はlistを代入
Out[106]: 50
In [107]: sample.ix[1,2]
Out[107]: 50

【条件指定した参照】
data.query('data1==42')


data['column1'!=10]
data[data['data1']==42]]


【行列の連結】
pd.concat()で行います。
axis=1をつけると左右方向、付けないと上下方向です。
In [106]: base = pd.DataFrame(np.ones([3,4]) )

In [107]: right = pd.DataFrame(np.zeros([3,2]))

In [108]: bottom = pd.DataFrame(np.zeros([1,4]))

In [109]: pd.concat([base, bottom])
Out[109]:
0 1 2 3
0 1 1 1 1
1 1 1 1 1
2 1 1 1 1
0 0 0 0 0

In [110]: pd.concat([base, right], axis=1)
Out[110]:
0 1 2 3 0 1
0 1 1 1 1 0 0
1 1 1 1 1 0 0
2 1 1 1 1 0 0

●時系列関係
【文字列と時刻(datetime)の相互変換】
In [132]: import datetime
In [134]: today = datetime.datetime.strptime('2015/11/02 10:11:12','%Y/%m/%d %H:%M:%S')
In [134]: today = pd.to_datetime('2015/11/02 10:11:12', format='%Y/%m/%d %H:%M:%S')
In [135]: today.strftime('%Y-%m-%d %H%M%S')
Out[135]: '2015-11-02 101112'

【時刻列の作成】
date_range()関数を用います。freqは日:D, 時間:H, 分:T, 秒:S, などが使えます。
In [182]: pd.date_range('2014-12-02 14:00:00',periods=17,freq='10S')

In [183]: Out[182]:
DatetimeIndex(['2014-12-02 14:00:00', '2014-12-02 14:00:10',
'2014-12-02 14:00:20', '2014-12-02 14:00:30',
'2014-12-02 14:00:40', '2014-12-02 14:00:50',
'2014-12-02 14:01:00', '2014-12-02 14:01:10',
'2014-12-02 14:01:20', '2014-12-02 14:01:30',
'2014-12-02 14:01:40', '2014-12-02 14:01:50',
'2014-12-02 14:02:00', '2014-12-02 14:02:10',
'2014-12-02 14:02:20', '2014-12-02 14:02:30',
'2014-12-02 14:02:40'],
dtype='datetime64[ns]', freq='10S', tz=None)


【時刻の加減算】
In [142]: today
Out[142]: datetime.datetime(2015, 11, 2, 10, 11, 12)

In [140]: delta = datetime.timedelta(days=365, seconds=22, minutes=4, hours=1, weeks=0)
In [141]: today-delta
Out[141]: datetime.datetime(2014, 11, 2, 9, 6, 50)


In [151]: today
Out[151]: datetime.datetime(2015, 11, 2, 10, 11, 12)
In [152]: today+pd.DateOffset(year=2015, month=9, day=8)
Out[152]: Timestamp('2015-09-08 10:11:12')

【リサンプル】
data.resample('5s', how='mean')
data.resample('10T', how='sum')

【移動平均】
rolling_mean()関数で行います。windowパラメータが移動の窓サイズです。
data = pd.rolling_mean(data, window=31)

【時刻を指定したデータの抽出】
In [166]: data
Out[166]:
data1 data2
2014-12-02 14:00:00 56 5
2014-12-02 14:00:10 50 11
2014-12-02 14:00:13 47 14
2014-12-02 14:00:14 46 15
2014-12-02 14:00:18 42 19
2014-12-02 14:00:23 37 24
2014-12-02 14:00:27 33 28
2014-12-02 14:00:30 30 31
2014-12-02 14:00:36 24 37
2014-12-02 14:00:37 23 38
2014-12-02 14:00:41 19 42
2014-12-02 14:00:46 14 47
2014-12-02 14:00:49 11 50
2014-12-02 14:00:53 7 54
2014-12-02 14:00:58 2 59
2014-12-02 23:59:59 1 60
2014-12-03 14:52:29 0 100

In [167]: temp = data[data.index>=pd.to_datetime('2014-12-02 14:00:14')] #14時0分14秒以降を抽出
In [168]: newdata = temp[temp.index<=pd.to_datetime('2014-12-02 14:00:46')] #14時0分46秒以前を抽出

In [169]: newdata
Out[169]:
data1 data2
2014-12-02 14:00:14 46 15
2014-12-02 14:00:18 42 19
2014-12-02 14:00:23 37 24
2014-12-02 14:00:27 33 28
2014-12-02 14:00:30 30 31
2014-12-02 14:00:36 24 37
2014-12-02 14:00:37 23 38
2014-12-02 14:00:41 19 42
2014-12-02 14:00:46 14 47


●グラフ描画
import matplotlib.pyplot as plt

【データ描画】
plt.plot()でデータ描画する。デフォルトは折れ線グラフで、'o'などで線やマーカーの仕様を指定する。
figsizeはグラフサイズで、デフォルトは(6,4)くらい。

fig=plt.figure(figsize=(10,4))
fig.subplots_adjust(bottom=0.3) #グラフの下部を上に上げる
ax=fig.add_subplot(111) #1×1の範囲のうち1つ目にグラフ描画するの意
plt.plot(data, 'o' )
plt.grid(True)
plt.ylabel('length[m]') #縦軸の名前
plt.ylim([15,45]) #縦軸の上下限指定
plt.xtics(np.arrange(10), horizonallabel, rotation=90) #横軸の補助線位置とラベル指定。rotationでラベルを回転する
plt.yticks(np.arrange(-10,10,1)) #縦軸の補助線の位置とラベル指定
plt.show()

エラーバーグラフはplt.errorbar()
plt.errorbar(horizonalaxis, average, yerr=standard, fmt='o')

凡例はlegend()でつける。locを指定することで位置を変更できる。
plt.legend(loc='upper right')
もしどうしても凡例が邪魔な場合、以下のようにして凡例を図の外に追いやることも出来る。
plt.legend(bbox_to_anchor=(1.3,1))
#凡例の消し方がわからん。
 誰かおしえてください。

デフォルトのフォントだとグラフの文字に日本語が使えないので
以下を実行する。
import matplotlib as mpl
mpl.rcParams['font.family']='IPAexGothic'


【グラフ保存】
savefig()関数で保存する。拡張子によって保存形式を判定。
texとかで埋め込む時に使用するepsでも保存可能。
ベクターデータであるemfも保存できるが、
Microsoft Officeで読み込むと文字が消えたりする。
pngでdpiを上げて保存し後で解像度落とすのが使い勝手が良い。

plt.savefig('sample.png', dpi=300)


●フォルダやファイル操作
import shutil
import os

【フォルダの中身の一覧表示】
folder = '\\test\\'
files = os.listdir(folder)

【ファイル・フォルダの削除】
os.remove(folder+files[0])

【ファイルコピー】
folder2 = '\\test2\\'
shutil.copyfile(folder+files[1], folder2+files[1])

【拡張子】
splitextで拡張子とそれ以外を分ける。
In [201]: os.path.splitext(filename)[1]
Out[201]: '.csv'
  1. 2015/11/03(火) 02:05:34|
  2. Python
  3. | トラックバック:0
  4. | コメント:0

Python開発環境(Anaconda3+spyder)の操作の小技

忘備録的に書いておきます。
環境:Anaconda2.3.0 + Spyder2.3.5.2 (Python 3.4.3)

【タブ補完】
IPythonウインドウで途中まで書いてタブを押すと保管してくれる。
モジュールの場合ドット'.'まで打ってからタブを押すとメソッドも表示可。

【オブジェクトの情報】
変数やオブジェクトの後に'?'をつけると
その情報を見れる。関数でも可。
関数の場合'??'で関数の中身も見れる。

【部分実行】
エディタでF9を押すと、いまカーソルがある行だけ実行する。
テキスト選択すると選択部分だけ実行。
F5を押すとその.pyファイルを保存して実行してくれる。

【変数のクリア】
Variable explorerに、今まで定義した変数が一覧表示される。
%resetと入力すると
Once deleted, variables cannot be recovered. Proceed (y/[n])? y
と聞かれて、yを押すと変数がクリアされる。

【時刻計測】
%timeit コマンド
と入力すると、コマンドの実行時間を測定できる。
pyファイルの実行時間なら、
%timeit runfile('temp.py')
と入力すれば良い。

【コマンド履歴の保存】
%logstartと入力すると、
ipython_log.pyに今までの入力コマンド履歴が保存・記録されるようになる。



  1. 2015/11/03(火) 01:17:18|
  2. Python
  3. | トラックバック:0
  4. | コメント:0

Python3.4+pandas+pylabによる重回帰分析と結果の描画

Pythonが、統計処理等のライブラリが充実しているとのことで、
少し触ってみました。

①Python環境構築
Anacondaをインストール
https://www.continuum.io/downloads
スタートメニュー→すべてのプログラム→Anaconda→Spyderを起動

②必要なモジュールのインストール
Spyderのメニューバー→Tools→Open Command Promptを開き、
コマンドプロンプトから
conda install モジュール名
と入力する。
(pandas等の主要モジュールはあらかじめインストールされています。)


# -*- coding: utf-8 -*-

import pandas
import pylab

#ファイル名
filename = 'sample.csv'

#ファイルの読み込み
#header=-1はヘッダを付けない、skiprows=1で1行読み飛ばし
#sample = pandas.read_csv(filename, skiprows=1, names=['time','serial','temperature','humidity'])
sample = pandas.read_csv(filename, skiprows=1,header=-1)
print(sample)

#関係ない列の削除
del(sample[0])

#基本統計量を算出する
desc = sample.describe()
print(desc)

#相関行列を計算する
cor = sample.corr()
print(cor)

#重回帰分析する
y = sample[1]
x = sample[[2,3]]
model = pandas.ols(x=x, y=y, intercept=True)
print(model)

pylab.plot(model.y)
pylab.plot(model.y_fitted)
pylab.show()

#結果を書き出し
sample.to_csv(filename+'_model.csv',index=False)

正直なところ、MATLABとjavaに慣れてしまったこの体では、
全然直感的と感じられないPythonのライブラリの扱い方に
四苦八苦させられました。

仕組みがいまいちわかってない言語のさらにわからんライブラリは、
なかなか触るのが疲れます。
逆にプログラム初めて触る人で、ちゃんとデータ構造から勉強すれば、
自然にできるようになるのかも。

この辺参考にしました。
http://www.s12600.net/psy/python/22-3.html
http://openbook4.me/projects/183/sections/1367
http://hennohito.cocolog-nifty.com/blog/2014/03/pythonspyder-bf.html

  1. 2015/10/08(木) 07:50:35|
  2. Python
  3. | トラックバック:0
  4. | コメント:0