プログラミング初心者のため、あまり詳しくないので、易しく解説していただけると嬉しいです。研究室で、下図のような環境を構築したいと考えています。つまり、元サーバのそれぞれのtree構造の一番下にあるtxtデータのうち、取得日と同じ日付のファイルを取得し、研究室のサーバのmysqlのテーブルに入れる環境を作りたいです。そもそも、可能でしょうか。また、将来的に、統計的な解析やグラフの記述をしていくことも踏まえて、言語は、pythonで統一しようと思っています。参考となるコードを教えていただけると嬉しいです。ちなみに学内のパソコンから外部のサーバにアクセスする際は、proxy環境をくぐらないといけませんので、そこもどう対処すればいいか教えていただけると嬉しいです。
![![イメージ説明]WIDTH:600]WIDTH:600
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/06/20 01:51
回答1件
0
お邪魔します。
ご質問の内容は噛み砕くと以下の4点であっていますか?
0. pythonを使ってファイル名に特定の文字列を含むテキストファイルを取得
0. ファイルの内容を読み込む
0. データベースにファイルの内容を書き出す
0. データベースはプロキシ経由で別サーバにあるため、これとのコネクションの確立
まず、1からですが、pythonのosモジュールにwalkというディレクトリの内容を再帰的に辿って調べてくれる関数があるので、これを使います。
また、環境がよくわからないのでこれは経験とか感の領域ですが、対象のファイルを一旦収集して手元にコピーすることにします。
lang
1import re 2from tempfile import mkdtemp 3from shutil import copyfile 4from os import walk 5 6FILENAME_PATTERN = '20150601' 7ROOT_PATH = r'C:\\Documents and Settings\\' 8 9r = re.compile("[a-zA-Z]+-(?P<date>\d{8})") 10 11def collect(filename): 12 tempdir = mkdtemp() 13 copyfile(filename, tempdir) 14 return os.path.join(tempdir, os.path.basename(filename)) 15 16def gen_filelist(path): 17 for root, dirs, files in os.walk(path): 18 for file in files: 19 m = r.match(file) 20 if(m or FILENAME_PATTERN in m.groupdict().values()): 21 # ファイルを何かが開いていたら厄介なので、一旦テンポラリにコピーする 22 yield collect(os.path.join(root, file)) 23
2.のファイルの内容を読み込む、ですが、open関数というのが標準ライブラリにあるのでこれでファイルを開いて内容を読み込みます。
lang
1def read_line(data): 2 #データを読み出す。例えばcsvファイルをカンマで分けるとか。今回は何もせずそのまま返します。 3 return data 4 5def read(filename): 6 with open(filename, "r") as f: 7 lines = [] 8 for line in f.readlines(): 9 lines.append(read_line(line)) 10 return lines
3.のデータベースにファイルの内容を書き出す、ですが、単純にやるよりは将来のデータ構造の変化を考慮してORMを使うのが良いと思います。SQLAlchemyを使ってみます。(データの取得時にも、ここで定義したテーブルアクセサは使うことができますしね。)
ORMをセットアップしてテーブルにデータを登録するには以下のようにします。
テーブルの構造とか適当ですので読み替えてください。
(既存のテーブルにORMをアタッチさせる場合、他に少し工夫がいるかもしれません。)
lang
1from sqlalchemy import create_engine 2from sqlalchemy.orm import sessionmaker 3from sqlalchemy.ext.declarative import declarative_base 4from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey, Sequence 5 6Base = declarative_base() 7 8class Data(Base): 9 __tablename__ = 'tablename' 10 id = Column('id', Integer, Sequence('id_seq'), primary_key=True) 11 somedata = Column('somedata', String(512)) 12 13 def __init__(self, somedata): 14 self.somedata = somedata 15 def __repr__(self): 16 return "<Data('%s',...)>" % (self.somedata) 17 18def main(): 19 #ORMのセットアップ 20 engine = create_engine('mysql://<ユーザ名>:<パスワード>@<ip>/<db名>', encoding='utf-8', echo=True) 21 Session = sessionmaker(bind=engine) 22 session = Session() 23 data = Data("データの内容。。。。。") 24 #DBに対して書き込みを行う。 25 session.add(data) 26 session.commit()
4.のプロキシ越えの件ですが、基本的にはDBのクライアントソフトウェアで接続できるなら問題ないはずです。問題がある場合は、クライアントソフトウェアでも接続できないはずです。
(セッションを維持できない可能性があります。そういった問題が発生するのであれば、プロキシの設定を適時変更するなどしてください。プロキシの設定を変更できない場合、ポートフォワーディングを行うとか、何らかのプロトコルによるトンネリングを試みたりする余地はあるとは思いますが、大変そうです。どちらにしてもDBサーバと双方に何か仕込む必要がありますね。)
まとめ
提示したソースをつながるように一部改変したものを載せておきます。
一切テストをしていないので、問題があったら教えてください。w
なお、提示したソースの動作にはSQLAlchemyとMySQLに接続するためのpython用ドライバが必要です。
[MySQL用のドライバのうち一般的なもの]
・MySQL-python
・PyMySQL
・mysql-connector-python
適当に入れてください。
lang
1# -*- coding: utf-8 -*- 2 3import re 4import os 5from tempfile import mkdtemp 6from shutil import copyfile 7from os import walk 8 9from sqlalchemy import create_engine 10from sqlalchemy.orm import sessionmaker 11from sqlalchemy.ext.declarative import declarative_base 12from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey, Sequence 13 14 15FILENAME_PATTERN = '20150601' #収集対象とするファイル名のパターン 16ROOT_PATH = r'C:\\Documents and Settings\\' #収集するファイルが存在するトップディレクトリ 17CONNECT_STRING = 'mysql://<ユーザ名>:<パスワード>@<ip>/<db名>' #DB接続文字列 18 19r = re.compile("[a-zA-Z]+-(?P<date>\d{8})\.txt") #対象ファイル名を示す正規表現を定義。 20 21Base = declarative_base() 22 23class Data(Base): 24 __tablename__ = 'tablename' 25 id = Column('id', Integer, Sequence('id_seq'), primary_key=True) 26 somedata = Column('somedata', String(512)) 27 28 def __init__(self, somedata): 29 self.somedata = somedata 30 def __repr__(self): 31 return "<Data('%s',...)>" % (self.somedata) 32 33 34 35def collect(filename): 36 tempdir = mkdtemp() 37 copyfile(filename, tempdir) 38 return os.path.join(tempdir, os.path.basename(filename)) 39 40def gen_filelist(path): 41 for root, dirs, files in os.walk(path): 42 for file in files: 43 m = r.match(file) 44 if(m and FILENAME_PATTERN in m.groupdict().values()): 45 # ファイルを何かが開いていたら厄介なので、一旦テンポラリにコピーする 46 yield collect(os.path.join(root, file)) 47 48def read_line(data): 49 #データを読み出す。例えばcsvファイルをカンマで分けるとか。 50 #今回は、ORMのクラスにデータを格納してそのまま返します。 51 return Data(data) 52 53def read(filename): 54 with open(filename, "r") as f: 55 lines = [] 56 for line in f.readlines(): 57 lines.append(read_line(line)) 58 return lines 59 60 61def main(): 62 #SQLAlchemyの準備 63 engine = create_engine(CONNECT_STRING, encoding='utf-8', echo=True) 64 Session = sessionmaker(bind=engine) 65 session = Session() 66 67 #ファイルの一覧を取得 68 files = gen_filelist(ROOT_PATH) 69 70 #ファイルの内容を取得 71 for file in files: 72 data = read(file) 73 session.add(data) 74 75 #データベースへ内容を登録 76 session.commit() 77 78if __name__ == '__main__': 79 main()
(2015/06/28追記)
ssh(sftp)経由でリモートのパスを舐め、ファイルを取得してくるように改変します。
その前に、サードパーティのモジュールをインストールします。
lang
1pip install paramiko
※paramikoのインストールには色々と厄介なことがあるようです。linuxでは起こりにくいとは思いますが、ハッシュ化や暗号化モジュールがうまく入らない場合、また情報をください。
sshでファイルを収集するプログラムは次のような感じでできます。
ssh経由でファイルを探す部分は手作りですね。walkを適当に拾ったのを参考に自作しました。(黒)
1件だけ懸念点があり、SHHで接続に失敗する場合、直接鍵ファイルを指定する必要があるかもしれません。
コメントにしている、以下のコードを有効にし、鍵ファイルへのパスを直接指定してください。
conn.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
以下、対象ホストSSH接続し、ファイルを収集、内容をデータベースに書き出すサンプルです。
データベースのテーブル定義などは考慮していません。適時修正ください。
lang
1# -*- coding: utf-8 -*- 2 3import re 4import os 5from tempfile import mkdtemp 6 7import paramiko 8from stat import S_ISDIR 9 10from sqlalchemy import create_engine 11from sqlalchemy.orm import sessionmaker 12from sqlalchemy.ext.declarative import declarative_base 13from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey, Sequence 14 15HOST_ADDRESS = "XX.XX.XX.XX" #データ収集時の接続先ホストIPアドレス 16SSH_PORT = 22 #SSH接続ポート 17SSH_PASSWORD = "XXXXXXXX" #SSH接続パスワード 18SSH_USERNAME = "wheeluser" #SHH接続ユーザ名 19 20FILENAME_PATTERN = '20150601' #収集対象とするファイル名のパターン 21ROOT_PATH = r'/home/username/' #収集するファイルが存在するトップディレクトリ 22CONNECT_STRING = 'mysql://<ユーザ名>:<パスワード>@<ip>/<db名>' #DB接続文字列 23 24# ローカルがwindows機である場合、os.path.joinのパスのデリミタで問題がでるのでその対処 25RELATIONSHIP_TO_HOST = "posix_to_posix" # "posix_to_posix" / "nt_to_posix" / "posix_to_nt" 26 27if RELATIONSHIP_TO_HOST == "posix_to_posix": 28 from os import path as remote_path 29elif RELATIONSHIP_TO_HOST == "nt_to_posix": 30 import posixpath as remote_path 31elif RELATIONSHIP_TO_HOST == "posix_to_nt": 32 import ntpath as remote_path 33 34import logging 35from datetime import datetime 36logging.basicConfig(filename=datetime.today().strftime(r"%Y%m%d_%H%M%S")+'.log', level=logging.DEBUG) 37 38r = re.compile("[a-zA-Z]+-(?P<date>\d{8})\.txt") #対象ファイル名を示す正規表現を定義。 39 40Base = declarative_base() 41 42class Data(Base): 43 __tablename__ = 'tablename' 44 id = Column('id', Integer, Sequence('id_seq'), primary_key=True) 45 somedata = Column('somedata', String(512)) 46 47 def __init__(self, somedata): 48 self.somedata = somedata 49 def __repr__(self): 50 return "<Data('%s',...)>" % (self.somedata) 51 52 53def walk(sftp, path): 54 current = path 55 files = [] 56 folders = [] 57 for item in sftp.listdir_attr(current): 58 if S_ISDIR(item.st_mode): 59 folders.append(item.filename) 60 else: 61 files.append(item.filename) 62 yield current, folders, files 63 for folder in folders: 64 for x in walk(sftp, remote_path.join(path, folder)): 65 yield x 66 67def collect(sftp, filename): 68 tempdir = mkdtemp() 69 dist = os.path.join(tempdir, os.path.basename(filename)) 70 logging.info("copy file from " + filename + " to " + dist) 71 sftp.get(filename, dist, lambda x, y: (logging.warning("File tranceport fail !!!") if x!=y else logging.info("File tranceport success."))) 72 return dist 73 74def gen_filelist(sftp, path): 75 for root, dirs, files in walk(sftp, path): 76 for file in files: 77 m = r.match(file) 78 if(m and FILENAME_PATTERN in m.groupdict().values()): 79 # ファイルを何かが開いていたら厄介なので、一旦テンポラリにコピーする 80 yield collect(sftp, remote_path.join(root, file)) 81 82def read_line(data): 83 #データを読み出す。例えばcsvファイルをカンマで分けるとか。 84 #今回は、ORMのクラスにデータを格納してそのまま返します。 85 return Data(data) 86 87def read(filename): 88 with open(filename, "r") as f: 89 lines = [] 90 for line in f.readlines(): 91 if(line.strip() != ""): 92 lines.append(read_line(line)) 93 return lines 94 95 96def main(): 97 #SQLAlchemyの準備 98 engine = create_engine(CONNECT_STRING, encoding='utf-8', echo=True) 99 Session = sessionmaker(bind=engine) 100 session = Session() 101 102 #SFTP接続の準備 103 conn = paramiko.SSHClient() 104 conn.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 105 #conn.load_host_keys(os.path.expanduser('~/.ssh/known_hosts')) #鍵ファイルを直接指定する場合 106 conn.connect(HOST_ADDRESS, port=SSH_PORT, password=SSH_PASSWORD, username=SSH_USERNAME) 107 sftp = conn.open_sftp() 108 109 #ファイルの一覧を取得 110 files = gen_filelist(sftp, ROOT_PATH) 111 112 #ファイルの内容を取得 113 for file in files: 114 data = read(file) 115 session.add(data) 116 117 #データベースへ内容を登録 118 session.commit() 119 120if __name__ == '__main__': 121 main()
投稿2015/06/20 20:27
編集2015/07/01 08:11総合スコア540
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/06/22 07:09
2015/06/22 07:22
2015/06/22 07:33
2015/06/24 19:56 編集
2015/06/28 12:05
2015/06/28 17:00
2015/06/29 07:52
2015/07/01 08:09 編集
2015/07/06 05:20
2015/07/19 03:00 編集
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。