2016年5月23日 星期一

[Django] 設定 Django 使用 MySQL 做為資料庫

首先是版本

MySQL 我使用目前最新 社群版(免費版) 5.6.28
安裝時 附帶一起安裝 mysql-connector-python 2.1.3 模組
這是MySQL官方自己出的 connector 但與 Django 最新版 (1.9.x) 不完全相容
因此,我們必須捨棄最新版本的 Django,pip 安裝時加上:


python3 -m pip install "Django<1.9"


這樣就可以安裝 1.8.x 的最新版本 ,需注意後面的 "Django<1.9" 雙引號不可省略,
完成安裝後,在 Django 的 project 目錄下的 setting.py,資料庫設定的部分:


DATABASES = {
    'default': {
        'ENGINE': 'mysql.connector.django',
        'NAME': 'datebase_name',
        'USER': 'datebase_user_name',
        'PASSWORD': 'datebase_user_password',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'OPTIONS': {
          'autocommit': True,
        },
    }
}
ENGINE 的部分是設定使用 MySQL 官方的 connector
完成後,執行 Django migrate 命令:

python3 manage.py migrate

若成功就會把 Django 的資料庫移轉到 MySQL 下了。

[python] regex 群組命名表示法

原本我只會

text = "123 is num"
re.match("^(\d+) is num$", text).group(1)

結果為 "123"

學了一下 Django 意外學到


text = "123 is num"
re.match("^(?P<num>\d+) is num$", text).group("num")
可以透過 ?P<variable_name>  為群組命名,程式碼可以更容易懂,還不錯。

2016年5月18日 星期三

[python] OAuth 取得 facebook 使用者的 id name email

1. 到 facebook developers頁面 建立一個 WWW APP ,可以得到一組 client id / client secret
下方填入自己的 WebService 網址。ex:


2.在系統的登入頁加入一個超連結,填入 上一步的 client id ,要向使用者索取的資源 scope, 以及使用者同意授權後,facebook 要將授權碼 redirect 的位址,我們填入自己的 WebService 位址。


href="https://www.facebook.com/dialog/oauth?
&client_id=1730077280596052
&scope=public_profile,email
&redirect_uri=http://bennu-aws.ddns.net:5000/fb_oauth"


3. 設計一個 WebService 接收 授權碼 code,使用 python Flask 框架。

@app.route("/fb_oauth", methods=["GET"])
def fbOauth():
    #接收授權碼
    strAuthCode = request.args.get("code", None, type=str)

4.將授權碼回傳給 facebook 換取 token,需要 client_id、client_secret、第3步得到的授權碼、以及第2步使用的 redircet_uri,facebook 就會回傳 token 回來

strAccessTokenUrlTemplate = "https://graph.facebook.com/oauth/access_token?
                                          &client_id=%s
                                          &redirect_uri=%s
                                          &client_secret=%s
                                          &code=%s"
strAccessTokenUrl = strAccessTokenUrlTemplate%
                                      (strFbClientId,
                                       "http://bennu-aws.ddns.net:5000/fb_oauth",
                                        strFbClientSecret,
                                        strAuthCode)
responseToken = urllib.request.urlopen(strAccessTokenUrl)
strToken =  responseToken.read().decode(responseToken.headers.get_content_charset())

5.使用 token 取得使用者的資料

responseUserData = urllib.request.urlopen("https://graph.facebook.com/me?
                                                                     &fields=id,name,email
                                                                     &" + strToken)
    strUserData = responseUserData.read().decode(responseToken.headers.get_content_charset())
    dicUserData = json.loads(strUserData)
    return (dicUserData["id"], dicUserData["name"], dicUserData["email"])

2016年5月15日 星期日

[python] unicode decode encode 的秘密

python 2.7
u"abc中文" == unicode 物件
"abc中文" == str 物件

python 3.x
u"abc中文" == unicode 物件
"abc中文" == unicode 物件

所以大多數問題出現在 python 2.7
以下使用 2.7 的觀點來看:

encode == 把 unicode 物件轉為 str 物件
strUTF8 = u"abc中文".encode("utf-8")  == str 物件
strBIG5 = u"abc中文".encode("big5")  == str 物件
strCP950 = u"abc中文".encode("cp950")  == str 物件

decode == 把 str 物件轉為 unicode 物件
strUTF8 .decode("utf-8") == unicode 物件
strBIG5.decode("big5") == unicode 物件
strCP950.decode("cp950") == unicode 物件

秘密:
如果 str 物件 執行 encode 相當於 "先 decode 為 unicode 再 encode", ex:

strBIG5.encode("cp950") == strBIG5.decode("ascii").encode("cp950")

重點在於 decode 過程會使用 python 的預設系統編碼 ascii 而不是使用 big5
造成 明明是呼叫 encode 卻報錯 "無法使用 ascii 進行 decode " 之類的奇怪結果


[python] 使用 pkg_resources 模組存取 package 裡面的檔案

import pkg_resources

strPkgFilePath = pkg_resources.resource_filename("package_name", "resource_file")

當資源存放在 package (含有 __init__.py 的資料夾)裡面時,
一但經過封裝上傳  pip install  之後,檔案路徑會與 os.path 取得的路徑不同,
os.path 是根據 "當前執行的目錄 (cwd)" 去做尋找,也就是說:

在 c:\ 下執行 會尋找 c:\package_name\resource_file
在 c:\dir\ 下執行會尋找 c:\dir\package_name\resource_file
不可能因為執行的 cwd 不同而到處搬移 resource_file 
於是會將 resource_file 存放在 package 下,pip install 後
resourc_file 會固定放在 C:\Python34\Lib\site-packages\package_name\resource_file
而要取得該位置,只要使用 pkg_resources.resource_filename 即可。

2016年5月14日 星期六

python 設定 setup.py 及 MANIFEST.in 封裝整個專案 並上傳至 pypi 的設定

setup.py

from setuptools import setup,find_packages

with open("README.txt") as file:
    long_description = file.read()

setup(
    name = "bennu",
    version = "0.3.2.dev2",
    keywords = ["bennu", "utility", "muchu"],
    description = "muchu's utility module",
    author = "MuChu Hsu",
    author_email = "muchu1983@gmail.com",
    license = "BSD 3-Clause License",
    url="https://github.com/muchu1983/bennu",
    long_description=long_description,
    packages = find_packages(),
    include_package_data = True,
    install_requires = ["Pillow>=3.0.0"],
    platforms = "python 3.3",
    entry_points = {"console_scripts":["bennu=bennu.launcher:entry_point"]},
    classifiers = [
        "Programming Language :: Python :: 3.3",
        ],
)

重點:
name 之後使用 pip install 要用到的名稱
version 每次上傳不得重覆,否則上傳會失敗,數字越大版本越新。
find_packages() 可以自動找出含有 __init__.py 的資料夾,省去一一指定。
install_requires 指定本專案相依的其他 pypi 專案,用 pip 安裝時也會被加入安裝。
entry_points 指定 專案的執行進入點 並建立 可執行檔 ["execuatable_name=package.package:function"]

MANIFEST.in

include LICENSE
recursive-include bennu_res *
recursive-exclude test *

搭配 MANIFEST.in 可以指定其他檔案是否要一起加入封裝

上傳

1. 登入,首次會問你 pypi 上的帳密,之後可以記錄
python setup.py register 
2. 包裝,將你的專案包裝成 zip (windows) 或 tar.gz (linux)
python setup.py sdist
3. 上傳至 pypi
python setup.py upload

成功的話,要先移除專案資料夾內生成的 dist/ 還有 .egg-info/
否則 pip 會以為你已經有最新版本了,若是其他電腦就沒差
之後就可以用
pip install name
來安裝專案
安裝後也可以直接執行 executable_name 來執行

ubuntu 下使用 headless browser PhantomJS

PhantomJS 是一個 無畫面 的 browser
非常適合用來與 selenium 配合進行 自動化測試或是爬蟲程式
例如:

#取得 selenium driver 物件
def getDriver(self):
    driver = None
    if os.name == "nt":
        #chromeDriverExeFilePath = os.sep.join(("bennu_res", "chromedriver.exe"))
        #driver = webdriver.Chrome(chromeDriverExeFilePath)
        phantomjsDriverExeFilePath = os.sep.join(("bennu_res", "phantomjs.exe"))
        driver = webdriver.PhantomJS(phantomjsDriverExeFilePath)
    if os.name == "posix":
        phantomjsDriverExeFilePath = os.sep.join(("bennu_res", "phantomjs"))
        driver = webdriver.PhantomJS(phantomjsDriverExeFilePath)
    return driver

selenium 用法 本篇暫不介紹

PhantomJS 下載之後只有一個執行檔,將他放在 selenium 可以讀取到的位置即可使用
在 ubuntu 環境下,有幾點要注意的是:
1. 必須 chmod 755 phantomjs 加入執行權限
2. 必須安裝 apt-get install fontconfig 套件

2016年5月11日 星期三

windows 下安裝 python scrapy 模組

目前 scrapy 僅支援 2.7 版本 python

安裝前準備:

0. 升級 pip
python2 -m pip install pip --upgrade
1. Visual C++ compiler for python 2.7
https://www.microsoft.com/en-us/download/details.aspx?id=44266
2. lxml 用 exe 安裝 (用 pip 直接裝有點問題)
https://pypi.python.org/pypi/lxml/3.6.0
3. pypiwin32 (用 pip 直接裝就可以了)
python2 -m pip install pypiwin32

準備完成:

python2 -m pip install scrapy

python 使用 global 時機

當 local 區域 要 assign 資料給 外部區域 的變數時,
就必須先在 local 區域 使用 global  宣告一個相同名稱的 區域變數:

g = 1

def  func():
global g
g = 2
若是單純取值,沒有 assign 的動作,則可以不宣告:
g = 1
def func():
print(g)

 

關閉 windows 休眠,釋放 C 槽大量容量

命令列
powercfg -h off

ssh key 備忘

ssh key 分為 公錀 與 私錀,成雙成對。
擁有私錀可以產生對應的公錀,但擁有公錀無法反推產生私錀。
公錀放在 server
私錀放在 client
linux 上 ssh-keygen  產生的 公錀 檔名為 id_rsa.pub
linux 上 ssh-keygen  產生的 私錀 檔名為 id_rsa
windows server 不支援 ssh 認証,故 公錀 一般只有 linux 格式
windows 由 puttygen 可將 id_rsa 轉為可在 windows 下 putty 使用的 .ppk 檔
也就是說 .ppk 檔 等於 windows 下使用的 私錀,與原本的 id_rsa 格式有些不同,
但功能相同,若要將 .ppk 檔 轉回 linux 下使用的 id_rsa

sudo apt-get install putty-tools
puttygen key.ppk -O private-openssh -o id_rsa

linux 公錀存放位置  ~/.ssh/authorized_keys (可同時放多組公錀)
linux 私錀存放位置 ~/.ssh/id_rsa
存取權限必須設定為
chmod 700 ~/.ssh
chmod 777 ~/.ssh/authorized_keys (可公開)
chmod 600 ~/.ssh/id_rsa (不可公開)
原因是....文章一開頭有說了:擁有.....吧拉吧拉吧拉。