【Python3】データがstrではなくbytesで返ってくる問題【MySQL】
PyMySQL3を使ってDB内のデータ取得をしたところ、varchar型に設定されたカラムのデータがbytes型で返ってきてしまう問題に遭遇。
後々のためメモ。
環境
Python 3.4.3
MySQL 5.6
PyMySQL3
解決策
PyMySQL3への接続とクエリは以下のような平凡なものである。
import pymysql conn = pymysql.connect(params) cur = conn.cursor() cur.execute("SELECT id, name from some_table") result = cur.fetchone() print(result['name'])
ここでnameカラムがvarchar型だと、<class 'bytes'>
として値が返ってきてしまった。
この問題を解決するためには、DBで定義されているカラムのcollation(照合順序)を変更する必要がある。
現在のcollationを確認するにはmysqlに接続、DBを選択し、
show table status;
を実行すればよい。
Name: some_table (略) Collation: utf8_bin
上記のように、collationがutf8_bin
になっているのがbytes型でデータが返却される原因のようだ。
というわけで下記を実行。
ALTER TABLE some_table COLLATE utf8_general_ci; ALTER TABLE some_table MODIFY COLUMN name VARCHAR(255) CHARACTER SET `utf8` COLLATE `utf8_general_ci`;
これでカラムの設定と既存のデータがutf8_bin
からutf8_general_ci
に変更された。
pythonに戻り再度実行すると、データがstrで返って来ていることが確認出来るはずである。
参考
python - PyMySQL returns bytes instead of str - Stack Overflow
馬場誠Blog(東京都世田谷区経堂のWebクリエイター)» ブログアーカイブ » 照合順序をutf8_general_ciからutf8_unicode_ciへ変更する
【Python3】HTMLのエスケープされた記号を元に戻す
環境
Python 3.4.3
方法
xml.sax.saxutils
モジュールのunescape
メソッドを使えばOK。
from xml.sax.saxutils import unescape text = '& < >' print(unescape(text)) # => '& < >'
第二引数にdictを与えると、独自辞書の定義も可能とのこと。
from xml.sax.saxutils import unescape text = '<ルイズ貧乳>' unescape(text, {'貧乳': 'かわいい'}) # => '<ルイズかわいい>'
参考
19.11. xml.sax.saxutils — SAX ユーティリティ — Python 2.7ja1 documentation
236:HTMLをエスケープする
【Python3】sqlite3.ProgrammingError: Incorrect number of bindings supplied【SQLite3】
また引っかかりそうなのでメモ。
環境
Python 3.4.3
sqlite3
解決策
sqlite3.ProgrammingError: Incorrect number of bindings supplied.
と怒られてしまいました。
原因は単純で、executeメソッドの第二引数の形式です。
executeメソッドでは、第二引数はタプルで指定する必要が有るため、以下のようになります。
name = 'some_user_name' # 駄目な例 c.execute('select * from users where name=?', name) # 正しい例 c.execute('select * from users where name=?', (name,))
指定する変数がひとつの場合、最後に余計なカンマが付着するため若干気持ち悪いですが、Pythonの文法規則上やむを得ないですね。
【MySQL】データアップデート時に自動で時刻を更新する
フルスタックのWebアプリケーションフレームワークだと自動でやってくれますが、小規模フレームワークだと自力でやらねばならないためメモ。
CREATE TABLE some_tabel ( `id` INT(11) NOT NULL AUTO_INCREMENT, `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) );
これでcreated
には新規に作成された時の時刻が、modified
にはupdateされる度にその時刻が挿入されます。
【CentOS6】鍵認証によるssh接続
ローカルのMacからリモートのCentOSへ鍵認証でssh接続するための手順をメモっておきます。
公開鍵および秘密鍵は予め作成が済んでいるものとします。
環境
鍵の設定
まずはローカルMacからリモートに公開鍵の送付。
scp ~/.ssh/id_rsa.pub some_user@xxx.xxx.xxx.xxx:~/.
次にリモートのCentOSにログインし、公開鍵の設定を行う。
mkdir ~/.ssh # .sshディレクトリがない場合 mv ~/id_rsa.pub ~/.ssh/. cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys
次にsshd_config
の修正。
sudo emacs /etc/ssh/sshd_config
各行のコメントアウトを外し、AuthorizedKeysFileでは公開鍵の場所の指定をする。
RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile ~/.ssh/authorized_keys
またパスワード認証は不要なので、PasswordAuthentication no
に設定する。
以上終了後sshdの再起動。
sudo service sshd restart
ssh -i ~/.ssh/id_rsa some_user@xxx.xxx.xxx.xxx
無事鍵認証でssh接続成功!
ついでにローカルMacのconfigファイルにssh接続の設定を追記。
emacs ~/.ssh/config
Host some_host HostName xxx.xxx.xxx.xxx User some_user port 22 IdentityFile ~/.ssh/id_rsa
上記設定追記後、下記コマンドでssh可能。
ssh some_host
面倒な入力がなくなって便利!
参考
【CentOS6】新規ユーザーの追加とsudo権限の付与
環境
CentOS 6.6
ユーザー作成と権限付与
各コマンドはsudo権限のあるユーザーで実行する。
またsudoは、wheelグループに属しているユーザーのみ可能という方針で行う。
visudo
コマンド単体で/etc/sudoers
の編集は可能だが、/etc/sudoers
の下部に記載されている#includedir /etc/sudoers.d
によって、/etc/sudoers.d/
下のファイルが読み込まれるため、ここでは/etc/sudoers.d/
下に新規で設定ファイルを作成し、そのファイルに設定を記載する。
ちなみに#
はコメントアウト的な意味ではないので注意。
まずはユーザーの追加とパスワードの設定。
$ sudo useradd hoge $ sudo passwd hoge
次にsudo権限の付与。
$ visudo -f /etc/sudoers.d/wheel
当たり前だが、emacsではなくviで開くので注意。
下記設定を新規記載。
%wheel ALL=(ALL) ALL
wheelユーザーに権限付与。
文法ミス等してしまうと、sudoコマンドが実行不可になってしまう危険があるので注意。visudo
で開いており、警告を出してくれるはずなので大丈夫だとは思われる。
最後にユーザーをwheelグループに追加。
$ sudo usermod -G wheel hoge
新しく作ったユーザーに変更し、sudoが必要なコマンドを実行してみる。
$ su hoge
$ sudo visudo
[sudo] password for hoge
無事sudo権限を持ったユーザーの作成に成功!
参考
CentOS - sudoできるユーザを制限する。 - Qiita
CentOS - sudoers は編集せずに sudoers.d の中に設定を書こう - Qiita
【MeCab】Python3で解析時にbuiltins.UnicodeDecodeError【Flask】
Twitterから取得したデータをMeCabで処理し、Flaskを用いたWebアプリケーション上で表示しようとしたところ、下記のようなエラーが出現。
builtins.UnicodeDecodeError
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 0: invalid start byte
もしかしたらまたハマるかもしれないので念のためメモ。
環境
解決策
原因は不明だが、エラーメッセージを見るにエンコーディングがどうので死んでしまう模様。
ちなみにMeCabの実装コードは下記のような感じ。
import MeCab mecab = MeCab.Tagger() node = mecab.parseToNode("私はルイズが好きです。なぜなら、彼女はとてもキュートだからです。") while node: print(node.surface) node = node.next
中々解決策に辿りつけなかったのだが、一行追記することでエラーの回避が可能に。
原因は正直良くわからない。
修正したコードは以下。
import MeCab mecab = MeCab.Tagger() mecab.parse('') # これを追記! node = mecab.parseToNode("私はルイズが大好きです。なぜなら、彼女はとてもとてもキュートだからです。ルイズかわいい!") while node: print(node.surface) node = node.next
元々Python3でMeCabのparseToNode
メソッドを使用すると、最初に返されるnodeが空の値というバグがあり、この解決のためにmecab.parse('')
をするらしいのだが、UnicodeDecodeErrorへも対策として効果を発揮してくれた。
原因はよく分からないが、動くようになったので取り敢えず良しとする。
参考
【Bottle】@viewデコレータを自前で定義する【Jinja2】
通常Bottleを使用する場合は、使用するテンプレートに併せて必要なメソッドやデコレータをインポートして用いることで、viewテンプレートの描画を非常に簡単に行うことが出来ます。
例えば、Jinja2テンプレートを使用し、メソッドでviewテンプレートの描画を行う場合は以下のようになります。
from bottle import route, jinja2_template as template @route('/') def index(): return template('index.html', data = some_func())
また、同様の処理をデコレータで実装すると以下のようになります。
from bottle import route, jinja2_view as view @route('/') @view('index.html') def index(): return dict(data=some_func())
このメソッド群は非常に便利ですが、細かいセッティングをしようとすると、少なくない問題が発生します。
例えば、テンプレート側で何らかの関数を利用したい場合、jinja2.Environment
のglobals
に追加しますが、bottleに用意されているメソッドでそれを実装するのは少々難しいです(そこまでソースコードを読んでいないので可能かもしれません)。
というわけで、templateの展開を行うviewデコレータを自力で実装してしまえという結論に至ります。
環境
Bottle v0.12
Jinja2 v2.8
実装
新しいview用のファイルをdecoview.pyという名前で作成し、以下のようにします。
# decoview.py import functools import jinja2 env = jinja2.Environment( loader=jinja2.FileSystemLoader('./path/to/views'), autoescape = True ) env.globals.update({ 'some_func_key', some_func() }) def some_func(): return 'Called some_func!' def view(template_name): def decorator(view_func): @functools.wraps(view_func) def wrapper(*args, **kwargs): response = view_func(*args, **kwargs) if isinstance(response, dict): template = env.get_or_select_template(template_name) return template.render(**response) else: return response return wrapper return decorator
せっかくなのでautoescape
もTrue
にしてみました。
新しく作成したdecoviewをインポートして使用すれば、jinja2テンプレートファイルの中でsome_func()
が実行可能です。
# index.py from bottle import route from decoview import view @route('/') @view('index.html') def index(): return {}
例えば下記のようにjinja2テンプレートを呼び出します。
<!-- index.html -->
{{ some_func_key }}
きっとCalled some_func!
という文字列が表示されていることと思います。
参考
API — Jinja2 Documentation (2.8-dev)
Jinja2 templates and Bottle | Reliably Broken