SQLAlchemyでAUTO_INCREMENTされた値を取得したい
結論
- addした後にflushすると、addしたオブジェクトに設定されている。
- 【未確認】exuecuteで実行する場合には返り値から取得できる?(参考)
環境
コード
from sqlalchemy import Column from sqlalchemy.dialects.mysql import INTEGER,TEXT from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker Base = declarative_base() class tablea(Base): """テーブルの定義""" __tablename__ = 'tablea' id = Column(INTEGER, primary_key=True) id2 = Column(INTEGER, primary_key=True, autoincrement=True) name = Column(TEXT) if __name__ == "__main__": # MySQLに接続。 url = 'mysql+pymysql://user:password@hostname/dbname?charset=utf8' engine = create_engine(url, echo=True) engine.execute('DROP TABLE IF EXISTS {}'.format("tablea")) # テーブル作成 Base.metadata.create_all(engine) # セッションの作成 Session = sessionmaker(bind=engine) session = Session() tablea1 = tablea(id=100, name='太郎') tablea2 = tablea(id=200, name='次郎') session.add(tablea1) session.add(tablea2) # addしただけではDBに反映されない # autoincrementされるid2についても値はNone print('add後のid2の値') print(tablea1.id2) # None print(tablea2.id2) # None session.flush() print('flush後のid2の値') print(tablea1.id2) # 1 print(tablea2.id2) # 2 # 全件出力 print("全件出力") for row in session.query(tablea).all(): print(row.id, row.id2, row.name) # session.commit() # commitしなければrollbackされる
SQLAlchemyでAttributeError
経緯とか
- 調査依頼を受けて調べてみた結果を記載。
- どこかの誰かは助かるかも的な奴。
調査依頼の内容
- SQLAlchemyでselectしようとしたら変な例外で落ちた。
- SQLを実行する前に落ちている。
- よくわからないオブジェクトでよくわからない例外が発生している。
概要
- 変なオブジェクトを挿入しようとしたので例外が発生。
- なんで変なオブジェクト挿入しようとしたのかというと、autoincrementされた値を取得したいため。
詳細
- insertしようとして、intのカラムにオブジェクトを設定する。
- オブジェクトを設定した際には型チェックしてくれないのでそのまま設定される。
- addしただけではDBに挿入されず、次のフラッシュでデータベースに反映されるため、例外は発生しない。
- selectを行う際にフラッシュの処理が走る。
- そこでaddを実際にinsertを行うための前処理?が走る。
- 設定された値の文字コードを変換する処理が走る。
- 「設定された値 = オブジェクト」なので、変換できない。
- 例外発生。
環境
例外
Traceback (most recent call last): File "test.py", line 55, in <module> for row in session.query(tablea).all(): File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\orm\query.py", line 2726, in all return list(self) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\orm\query.py", line 2877, in __iter__ self.session._autoflush() File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\orm\session.py", line 1428, in _autoflush self.flush() File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\orm\session.py", line 2237, in flush self._flush(objects) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\orm\session.py", line 2363, in _flush transaction.rollback(_capture_exception=True) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\util\langhelpers.py", line 66, in __exit__ compat.reraise(exc_type, exc_value, exc_tb) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\util\compat.py", line 187, in reraise raise value File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\orm\session.py", line 2327, in _flush flush_context.execute() File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\orm\unitofwork.py", line 391, in execute rec.execute(self) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\orm\unitofwork.py", line 556, in execute uow File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\orm\persistence.py", line 181, in save_obj mapper, table, insert) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\orm\persistence.py", line 866, in _emit_insert_statements execute(statement, params) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\engine\base.py", line 948, in execute return meth(self, multiparams, params) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\sql\elements.py", line 269, in _execute_on_connection return connection._execute_clauseelement(self, multiparams, params) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\engine\base.py", line 1060, in _execute_clauseelement compiled_sql, distilled_params File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\engine\base.py", line 1200, in _execute_context context) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\engine\base.py", line 1416, in _handle_dbapi_exception util.reraise(*exc_info) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\util\compat.py", line 187, in reraise raise value File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\engine\base.py", line 1193, in _execute_context context) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\sqlalchemy\engine\default.py", line 507, in do_execute cursor.execute(statement, parameters) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\pymysql\cursors.py", line 168, in execute query = self.mogrify(query, args) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\pymysql\cursors.py", line 147, in mogrify query = query % self._escape_args(args, conn) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\pymysql\cursors.py", line 127, in _escape_args return dict((key, conn.literal(val)) for (key, val) in args.items()) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\pymysql\cursors.py", line 127, in <genexpr> return dict((key, conn.literal(val)) for (key, val) in args.items()) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\pymysql\connections.py", line 846, in literal return self.escape(obj, self.encoders) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\pymysql\connections.py", line 839, in escape return converters.escape_item(obj, self.charset, mapping=mapping) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\pymysql\converters.py", line 27, in escape_item val = encoder(val, mapping) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\pymysql\converters.py", line 118, in escape_unicode return u"'%s'" % _escape_unicode(value) File "C:\Users\username\AppData\Local\Programs\Python\Python36\lib\site-packages\pymysql\converters.py", line 73, in _escape_unicode return value.translate(_escape_table) AttributeError: 'ResultProxy' object has no attribute 'translate'
再現コード
from sqlalchemy import Column from sqlalchemy.dialects.mysql import INTEGER,TEXT from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker Base = declarative_base() class tablea(Base): """テーブルの定義""" __tablename__ = 'tablea' id = Column(INTEGER, primary_key=True, autoincrement=True) name = Column(TEXT) class tableb(Base): """テーブルの定義""" __tablename__ = 'tableb' id = Column(INTEGER, primary_key=True, autoincrement=True) tablea_id = Column(INTEGER) if __name__ == "__main__": # MySQLに接続。 url = 'mysql+pymysql://user:password@hostname/dbname?charset=utf8' engine = create_engine(url, echo=True) engine.execute('DROP TABLE IF EXISTS {}'.format("tablea")) engine.execute('DROP TABLE IF EXISTS {}'.format("tableb")) # テーブル作成 Base.metadata.create_all(engine) # セッションの作成 Session = sessionmaker(bind=engine) session = Session() # autoincrementのidカラムには何も指定せずに挿入 tablea1 = tablea(name='太郎') session.add(tablea1) # autoincrementされたidを取得するために、MySQLのlast_insert_idを使用する lastindex = session.execute("select last_insert_id() as id") # 取得したIDを別テーブルに挿入 # ※取得したオブジェクトをそのまま設定しているが、設定時、add時共にエラーとならない tablea2 = tableb(tablea_id=lastindex) # tablea2 = tableb(tablea_id=10) session.add(tablea2) # 全件出力 print("全件出力") # 全然関係ないselect投げる際に例外発生 for row in session.query(tablea).all(): print(row.id, row.name) # session.commit() # commitしなければrollbackされる
学んだこととか
- 型が恋しい。
- intのカラムにオブジェクト突っ込んだ時に例外として欲しい。
- session.query()はflushするが、session.execute()はflushしない?
- 上記のコードを正しく動かすために、「
session.execute("select last_insert_id() as id").fetchone().id
」とすると動作はするが、意図した値にならない。- 0が返ってくる。(本当は1が返ってきて欲しい)
- もし、このコードのまま使うのであればflush()してからlast_inset_id()を実行する必要がある。
- ただ、flush()すると、挿入時に使用したオブジェクトに値を設定してくれるので、わざわざlast_insertで取得する必要はない。
- 上記のコードを正しく動かすために、「
- スタックトレースをよく見る。
WSLでgoogle-images-downloadを使用する
概要
用語
- WSL
- google-images-download
- Google画像検索を使用して画像を取得してくれる。
- Seleniumを使用してスクレイピングで取得する。
- Google画像検索は普通に取ろうとすると100枚しか取れないらしいので、画像集める際にはかなり有用。
- https://github.com/hardikvasa/google-images-download
前提
- WSL上にPythonがインストールされていること
- Python2でも3でも良いが3が推奨との事(私は3.5.2を利用)
- Windows上にChrome最新版がインストールされていること
- 最新版でない場合にはChromeDriver取得の際に、適切なバージョンを選択してください。
手順
- WSL上にgoogle-images-downloadをclone
git clone https://github.com/hardikvasa/google-images-download.git
- setup
cd google-images-download && sudo python setup.py install
- 私の場合は
python3 setup.py install
で実行
- 試す
googleimagesdownload --keywords "りんご"
- 確認
downloads/りんご
配下に100枚弱のファイルがあればOK
- webdriverを取得
- 適当な所に解凍
- Chromeを呼び出すための設定
- 実行
- ChromeDriverのパスは適切なものに変更してください↓
googleimagesdownload --keywords "apple" --limit 1000 --chromedriver /mnt/c/work/github/google-images-download/chromedriver_win32/chromedriver.exe
- 確認
downloads/apple
配下に1000枚弱のファイルがあればOK
感想
- 最初、自力でスクレイピングするつもりだったので、簡単に取得できて凄い。
- が、多分Google側も構成変更したりと対応するだろうから、定期的に実行するのであればリポジトリは常に最新にしておく必要はあると思われる。
- Chromedriverの手動ダウンロードが面倒。
- Chromeがバージョンアップするたび?にChromeDriverの更新が必要。
- Javaでもあったけど、seleneで使用してたwebdriver_managerを組み込んだら喜ばれるのだろうか?
- Mac環境で
setup.py install
したらうまく行かなかった。。。わからないままやっている部分が多いので、Pythonの環境設定は嫌な感じ。
Atomのmarkdown-tocでタイトルが日本語の場合に動かない場合の対処
はじめに
Markdown書いていると見出し(toc)が欲しくなる ↓ Atomのプラグイン探す ↓ あった ↓ 日本語で正しく動かない
っといった感じでAtomのプラグイン「markdown-toc」を使ってみたものの、日本語で書いた際に動かなくて困る人がまあまあいるらしい。
ので対応方法を書いてみる。
結論
markdown-tocをforkして対応してくれてる人がいるので↓のコマンドでインストールしてAtom再起動。
apm install https://github.com/Sorix/markdown-toc/
以下は蛇足です。
リンクが正しくされない詳細
- 「
# hoge
」 は 「[hoge](#hoge)
」 と変換される(正常) - 日本語の場合 「
# ほげ
」 は[ほげ](#)
」 と変換される(異常)
原因
- リンクのhash生成時にドカンと一発除去しているため
- https://github.com/nok/markdown-toc/blob/master/lib/Toc.coffee#L205
リポジトリ見てみると。
- この問題に対するissueはいっぱい!pull requestもいくつか
- 各国の人が各言語対応してくれと言っていて割とカオス
- そんな中Sorixという人が、各言語まとめて指定してしたpull request作成してくれている
- けど、、マージしてくれないっぽい。
- 多分、作者がテストできないし、困ってないからから。
- なので、SorixさんがForkして、修正している奴をインストールすればOK。
どんな修正?
- 一発ドカンではなく不正な文字?(記号か?)だけを除去している
- この文字リストがどこからきたのかさっぱりわからないが、日本語、中国語、韓国語、ドイツ語?などは除去されなくなるっぽい。
- よって、 「
# ほげ
」 は 「[ほげ](#ほげ)
」 と変換されて正しく動作するようになる。
疑問
アンカー名が日本語
- 調べてる途中で、「#ほげ」というリンクは本来は正しくない(ブラウザがいい感じに対応してくれているだけ)というのを見た。
- 正しく修正するならば「
encodeURIComponent(hash)
」とするっぽい。
実を言うと、
- [インストール][#インストール]
のようにアンカー名を日本語で指定するのは、仕様上は正しくありません。正しく動くのはブラウザが賢く振る舞っているからです。
- [インストール][#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB]
のように URL Encode した文字列を指定するのが正しい書き方 です。
連続する空白の扱い
- このプラグインだとタイトルに連続する空白が含まれている場合には1つにまとめて「-」と置換する処理が入っているが、GitHubのwikiだと連続した「--」として扱うらしいので、正しくリンクされない。
- これはビューワの仕様による気がする。(Markdown → HTML の正式な仕様ってあるのかしら?)
- レアケースなので特に問題はないか?
- 具体例 : 「
# hoge hoge
」 は 「hoge-hoge
」 と変換されるが、GitHubのwikiだと 「hoge--hoge
」 となる。
参考
Python3とAWS LambdaでTwitterの名前に天気を表示する
はじめに
数日前に話題になっていた「Twitterの名前を5分毎に東京の天気☼☂☃と連動させるサーバレスプログラムを書いたら色々知らないことが出てきた話」を読んで、Pythonのいい練習になりそう!って事で自分でやってみた話です。
数日経って元記事見てみたら、既に何人かやっている人いるみたいです。(同じくPython3の人もいる) が、まぁ、他より割りと詳しく書いているので、試してみたい人はどうぞ。
楽しいお題?をくれた山本一成さんに感謝します。
前提
OpenWeatherMapAPIのAPIキーを取得
- OpenWeatherMapにアクセス → Sign Up
- 既に登録済みの人はSign In
- API keysでKeyを確認
- 検索したい都市名を取得
- ここのJSONから調べる
- 面倒なら「tokyo」、「osaka」、「nagoya-shi」、「london」とか
- ブラウザで試す
- ↓のURLに都市名とAPI Keyを入れこんで確認
http://api.openweathermap.org/data/2.5/weather?q=${都市名}&appid=${API Key}
Python3からOpenWeatherMapAPIを叩く
requests
というモジュールを使う例が多かったのでpipで入れる- 後でLambdaに乗せる時ために、「-t」オプションをつけてスクリプトと同じ場所にインストール
pip install requests -t .
- コードを書いて実行
import requests import json SEARCH_CITY = "tokyo" OPEN_WEATHER_MAP_API_KEY = "取得したAPI Key" API_URL = "http://api.openweathermap.org/data/2.5/weather?q={city}&APPID={key}" # URIスキーム url = API_URL.format(city=SEARCH_CITY, key=OPEN_WEATHER_MAP_API_KEY) r = requests.get(url) # 結果はJSON形式なのでデコードする data = json.loads(r.text) print(data)
TwitterのAPI Tokenを取得
- Twitter Appsを登録
- Tokenの作成
- 下記の4つをメモ
Twitterで名前更新
twitter
というモジュールを使う例が多かったのでpipで入れる- 後でLambdaに乗せる時ために、「-t」オプションをつけてスクリプトと同じ場所にインストール
pip install twitter -t .
- コードを書いて実行
import twitter # メモしたキーとアクセストークンを設定 auth = twitter.OAuth(consumer_key="Consumer Key (API Key)", consumer_secret="Consumer Secret (API Secret)", token="Access Token", token_secret="Access Token Secret") t = twitter.Twitter(auth=auth) t.account.update_profile(name="山pです。")
ちなみに
- Tweetする場合
t.statuses.update(status="ついーとするないよう")
天気を含めてTwitterの名前更新
import requests import json import twitter SEARCH_CITY = "tokyo" OPEN_WEATHER_MAP_API_KEY = "取得したAPI Key" API_URL = "http://api.openweathermap.org/data/2.5/weather?q={city}&APPID={key}" # https://openweathermap.org/weather-conditions WEATHER_EMOJI_MAP = { "01d": "☀", # clear sky "02d": "🌤", # few clouds "03d": "☁", # scattered clouds "04d": "⛅", # broken clouds "09d": "🌧", # shower rain "10d": "🌦", # rain "11d": "🌩", # thunderstorm "13d": "🌨", # snow "50d": "🌁" # mist } def get_weather_icon(): # URIスキーム url = API_URL.format(city=SEARCH_CITY, key=OPEN_WEATHER_MAP_API_KEY) r = requests.get(url) # 結果はJSON形式なのでデコードする data = json.loads(r.text) return data["weather"][0]["icon"].replace("n", "d") def get_weather_emoji(): return WEATHER_EMOJI_MAP.get(get_weather_icon()) def update_twitter_screen_name(name): # メモしたキーとアクセストークンを設定 auth = twitter.OAuth(consumer_key="Consumer Key (API Key)", consumer_secret="Consumer Secret (API Secret)", token="Access Token", token_secret="Access Token Secret") t = twitter.Twitter(auth=auth) t.account.update_profile(name=name) def main(): emoji = get_weather_emoji() twitter_name = "山p" + emoji update_twitter_screen_name(twitter_name) if __name__ == '__main__': main()
- 天気を絵文字に変換するのがかなり面倒なので、ここでは種類が少ないiconを使って変換してます。
- 正確に天気が欲しい場合には↓を見て変換すれば良いと思います。
Lambadaに乗せるために準備
コードを変更
- mainではなく、eventとcontextを受け取るhandler関数を作成
- 名前はなんでもいいけどとりあえず。
- Lambdaで環境変数を設定できるので、コードからKeyなどを除去して環境変数から取得するようにする。
import requests import json import twitter import os SEARCH_CITY = os.environ["SEARCH_CITY"] OPEN_WEATHER_MAP_API_KEY = os.environ["OPEN_WEATHER_MAP_API_KEY"] TWITTER_CONSUMER_KEY = os.environ["TWITTER_CONSUMER_KEY"] TWITTER_CONSUMER_SECRET = os.environ["TWITTER_CONSUMER_SECRET"] TWITTER_TOKEN = os.environ["TWITTER_TOKEN"] TWITTER_TOKEN_SECRET = os.environ["TWITTER_TOKEN_SECRET"] API_URL = "http://api.openweathermap.org/data/2.5/weather?q={city}&APPID={key}" # https://openweathermap.org/weather-conditions WEATHER_EMOJI_MAP = { "01d": "☀", # clear sky "02d": "🌤", # few clouds "03d": "☁", # scattered clouds "04d": "⛅", # broken clouds "09d": "🌧", # shower rain "10d": "🌦", # rain "11d": "🌩", # thunderstorm "13d": "🌨", # snow "50d": "🌁" # mist } def get_weather_icon(): # URIスキーム url = API_URL.format(city=SEARCH_CITY, key=OPEN_WEATHER_MAP_API_KEY) r = requests.get(url) # 結果はJSON形式なのでデコードする data = json.loads(r.text) return data["weather"][0]["icon"].replace("n", "d") def get_weather_emoji(): return WEATHER_EMOJI_MAP.get(get_weather_icon()) def update_twitter_screen_name(name): # 取得したキーとアクセストークンを設定する auth = twitter.OAuth(consumer_key=TWITTER_CONSUMER_KEY, consumer_secret=TWITTER_CONSUMER_SECRET, token=TWITTER_TOKEN, token_secret=TWITTER_TOKEN_SECRET) t = twitter.Twitter(auth=auth) t.account.update_profile(name=name) def handler(event, context): emoji = get_weather_emoji() twitter_name = "山p" + emoji update_twitter_screen_name(twitter_name)
Zip圧縮
- ここでわざわざライブラリをスクリプトと同じ場所に置いていることが生きてくる
- フォルダ毎ではなくて、ファイルを直で圧縮
- コマンド例 :
zip -r upload.zip *
- コマンド例 :
Lambadaに乗せる
- AWS Lambdaを開く
- 関数の作成
- 名前 : 関数の名前
- ランタイム : Python 3.6
- ロール : 新しいロールを作成して選択
- とりあえず「Designer」は後回し
- 関数コード
- 環境変数
- テストイベントの選択
- 今回はイベントの中身を何も使わないので「Hello World」でも何でも適当に作成
- 保存
- 忘れがち😉
- テスト!!
- 正しく実行されたことを確認
- Twitterの名前が変更されていることを確認
- Designer
- CloudWatch Events
- 新規ルール作成
- ルールタイプ : スケジュール式
- スケジュール式 : cron(1 * * * ? *)
- 保存
参考
Python3でZipの圧縮解凍操作
前記事のPython3でファイル操作に続いて、ZIPの圧縮解凍。 自分が業務内でどうでもいいスクリプト作る際に見るためのメモです。 Python勉強中なので間違っていたり、もっとPythonらしく書ける部分があればコメント欄やTwitterでツッコミお願いします。
コード
Githubにもあげてます。(使用しているファイルも。)
結局、Windows10環境でpyminizipを使用することはできなかった。。。別のモジュール使えばいいのかな?
import os import zipfile import pyminizip from datetime import datetime d = datetime.now().strftime("%Y%m%d%H%M%S") outputDir = "./output/" + d + "/" os.mkdir(outputDir) # -------------- # ZIP解凍 # -------------- # 基本形(とりあえず全部解凍) with zipfile.ZipFile("./data/simple.zip", "r") as zf: zf.extractall(outputDir) # 引数を指定しないとカレントに出力 # パスワード付きzipを全部解凍 with zipfile.ZipFile("./data/password.zip", "r") as zf: zf.extractall(outputDir, pwd="password".encode("ascii")) # encodeしないとエラー # ファイル指定して解凍する with zipfile.ZipFile("./data/somefile.zip", "r") as zf: print(zf.namelist()) # namelistでファイル名確認 # バイトで書き込むので「b」で開く with open(outputDir + "somefile.txt", "wb") as writeFile: writeFile.write(zf.read("somefile/file2.txt")) # readはバイトが返る # テキストで取得したい場合はopenで開いてデコード? # zipfile.open は文字コード指定できない with zf.open("somefile/file2.txt") as targetFile: for line in targetFile: print(line.decode("UTF-8").rstrip()) # -------------- # ZIP圧縮 # -------------- # 基本的な圧縮 with zipfile.ZipFile(outputDir + "compress1.zip", "w") as zf: # 第2引数がない場合は第1引数の通りに格納される。(この場合だと「/data/text1.txt」となる。) zf.write("./data/text1.txt", "text1.txt") zf.write("./data/text2.txt","nest/text2.txt") # パスワード付きは標準ライブラリではできない # pyminizipモジュールを使用する # https://github.com/smihica/pyminizip # ↓で入ればよいが入らない場合には下記参照 # pip install pyminizip # Windows10の場合↓のエラーが出たので、下記の通り「Microsoft Visual C++ Build Tools」をインストール # Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools # その後zlibが必要だと言われたが解決できず。↓が参考になるかも # https://stackoverflow.com/questions/42529239/ # Ubuntuの場合 # sudo apt-get install zlib1g-dev pyminizip.compress("./data/text1.txt", outputDir + "compress2.zip", "password", 5)
Python3でファイル操作
何番煎じだ的な感じですが、自分が業務内でどうでもいいスクリプト作る際に見るためのメモです。 Python勉強中なので間違っていたり、もっとPythonらしく書ける部分があればコメント欄やTwitterでツッコミお願いします。
コード
Githubにもあげてます。(使用しているファイルも。)
import os from datetime import datetime defaultTextPath = "./data/shift_jis-crlf.txt" #defaultTextPath = "./data/utf8-lf.txt" # 基本形(OSのデフォルト?文字コードで読む) with open(defaultTextPath) as f: print(f.read()) # 一括で読み込む # 行毎に読み込む with open(defaultTextPath) as f: for line in f: print(line) # 行毎に読み込む # メモリに全部乗る事、改行が含まれる事に注意 with open(defaultTextPath) as f: for line in f.readlines(): print(line.rstrip()) # 最後の改行も含まれるので注意。「rstrip()」で除去 # 違いはよくわからないが、f.read().splitlines() がオススメされてた # https://stackoverflow.com/questions/15233340/getting-rid-of-n-when-using-readlines # 行毎に読み込む # 使うことあるのか? with open(defaultTextPath) as f: line2 = f.readline() # 1行づつ読み込む(改行文字も含まれる) while line2: print(line2.rstrip()) line2 = f.readline() # 文字コードを指定する場合にはencoding # https://docs.python.jp/3.5/library/functions.html#open # 検索するとcodecsモジュールを使うなどと出てくるが「codecs」は古い! # python 2でもio.openでpython 3と同じ動きとのこと) with open("./data/euc-crlf.txt", "r", encoding="euc_jp") as f: print(f.read()) # ---------------------- # 書き込み # ---------------------- d = datetime.now().strftime("%Y%m%d%H%M%S") outputDir = "./output/" + d + "/" os.mkdir(outputDir) # 基本形(OSのデフォルト?文字コードで書き込み) with open(outputDir + "fisrt.txt", "w") as writeFile: writeFile.write("あいうえお\nかきくけこ\nさしすせそ\n") # Windowsの場合改行コードも何故か\r\nで出力 # 1行ごとに書き込み(OSのデフォルト?文字コードで書き込み) with open(outputDir + "second.txt", "w") as writeFile: # Windowsの場合改行コードも何故か\r\nで出力 writeFile.write("あいうえお\n") writeFile.write("かきくけこ\n") writeFile.write("さしすせそ\n") # 文字コードを指定して書き込み with open(outputDir + "third.txt", "w", encoding="UTF-8") as writeFile: writeFile.write("あいうえお\nかきくけこ\nさしすせそ\n") # Windowsの場合でも改行コードは\nで出力 # -------------------- # 文字コード変換して出力 # -------------------- # 文字コード変換 with open("./data/euc-crlf.txt", "r", encoding="euc_jp") as f: with open(outputDir + "euc-utf8.txt", "w", encoding="UTF-8") as writeFile: writeFile.write(f.read()) # 文字コードと改行コード変換 with open("./data/utf8-crlf.txt", "r", encoding="UTF-8") as f: with open(outputDir + "utf8-shift_jis-lf.txt", "w", encoding="shift_jis") as writeFile: writeFile.write(f.read().replace("\r\n","\n"))
追記(2018/02/28) Twitterでツッコミを頂いたのでコードを修正しております。ありがとうございます!
https://t.co/YWVLSClJgQ() は古くてもういらないです。
— INADA Naoki (@methane) 2018年2月27日
Python 3 では普通の open で open(filename, mode, encoding) で指定できます。
Python 2 なら import io; して https://t.co/Nq0LrUPgLQ() を使えば Python 3 とおなじになります。 https://t.co/zQIGQy8gNL