2018年10月31日 星期三

購買旺德二代口譯雙向翻譯機

上周小舅媽從沖繩玩回來之後, 要我幫她找哪一種翻譯機比較好, 我以為是傳統翻譯機, 聽她解釋才知在沖繩等車時看到有人脖子上掛著一台小機器, 用說的就能雙向翻譯. 我上網搜尋赫然發現現在很多人出國都帶這種口譯機, 不懂韓文照樣暢遊首爾.

這麼多機種中, 我覺得台灣旺德的口譯機在實用性與價格方面最優, 知名的 486 團購網也是推這支, 參考 486 大哥的評測影片 :

486大哥 12支口譯翻譯機實測




還有一台 Ten 口譯機也不錯, 優點是具有 SIM 卡插槽與熱點分享功能, 到國外時可以購買當地 SIM 卡上網並分享給手機電腦等設備, 有 38 國語言對譯功能 :

https://www.youtube.com/watch?v=t_llnuJS5PI 




這台 MOMO 賣 3890 :

【TEN】4G翻譯機 AI智能口譯即時雙向翻譯 2吋螢幕 4G+WIFI 即時語音翻譯 同步顯示翻譯詞句 $3890

但考慮最後還是買旺德這款, PCHome 賣 6490 元, 但 486 團購價一支是 5490 元, 買 2 台 1 組總價 10198, 使用 200 元折價券後總價 9998, 平均一台 4994 元, 比 PCHome 便宜 1500 元左右.




註冊 486 團購會員時卡在住址欄, 打電話問客服才知中間不能有空格, 我建議他們找程式設計師改掉這個限制.

口譯機平時可用來學外語, 出國可派上用場, 所以我就答應小舅媽幫她一起買, 週五應可到貨帶回鄉下給她, 相信她已迫不急待這台機器.

參考 :

【實測片】出國免驚 4大雙向翻譯機旺德最直覺
【團購最低價】wonder旺德ai雙向語言翻譯機好用嗎?評價、開箱、同時跟翻譯APP實測
2018年語音翻譯機排行,第一名竟然是這台…

成敗之鑑

今天在 Youtube 看到下面這個影片 :

蔣介石為什麼敗退台灣?這位國民黨幕後大佬終於道出真因




裡面提到陳立夫晚年在其回憶錄中總結國民黨最後失去大陸政權的原因有兩個 :
  1. 軍事上 :
    其一, 何應欽沒有採納其意見, 要求日本投降軍先守住鐵路線, 讓國軍迅速鐵運至東北, 反而從上海海運貽誤軍機. 其二, 中央沒有接納敵後游擊武力, 導致其投共
  2. 財政上 :
    宋子文金融處置錯誤, 以 200:1 比率兌換淪陷區偽幣, 淪陷區人民資產縮水民怨滔滔. 
或許陳立夫歸咎於何應欽, 宋子文是出於其個人對他們的不對盤, 但身為國民黨高層身歷其境應該是第一手資料. 這本書市圖可借 :

成敗之鑑 :陳立夫回憶錄

2018年10月30日 星期二

南山投資型保單贖回與組合變更

最近因為在研究 Fintech, 想起呂先生 2009 年向我招攬的南山伴我一生投資型保單, 剛開始呂先生幫我操作組合, 但有一年因南山要求變更密碼, 之後他就無法登入, 結果多年來停留在兩檔美元基金 (賺), 以及一檔黃金基金 (對折), 都是因為我不懂如何操作, 也沒時間去操作之故.

由於明年經濟展望不佳, 因此決定將兩檔賺錢的美元基金贖回, 但還是持續扣款, 投資組合可能也需要調整. 但從來沒操作過, 這幾天研究了一下, 將南山網站的基金贖回與投資組合操作方式記錄如下, 首先登入南山客戶園地 :

https://www.nanshanlife.com.tw





先按上方 "保單變更/借款", 再按右下方 "我要申請保單變更", 點選投資型保單 :




點選 "部分提領 (贖回部分) 保單價值" 或 "變更投資標的組合" : 




點選欲贖回之保單 :




將富達與瑞銀兩檔美元基金全部贖回, 腰斬的貝萊德黃金保留不動, 讓帳戶上保有 1 萬元以上資金, 這好像是大部分投資型保單普遍的規定 :




按底下試算會估算此次所贖回金額 :




如果上面是按變更投資組合, 則會出現下列頁面, 可調整現有組合之配比或增加新的標的 :




可選擇之標的如下 :

A24-柏瑞印度股票基金A|風險等級RR5
AAC-柏瑞美國股票基金A|風險等級RR4
AAM-柏瑞環球重點股票基金 A|風險等級RR3
AAO-柏瑞環球債券基金A|風險等級RR2
AAP-柏瑞全球策略高收益債券基金B|風險等級RR3
AAQ-柏瑞新興亞太策略債券基金B|風險等級RR3
ABA-安本環球日本股票基金A累積日圓|風險等級RR4
ABB-安本環球日本股票基金A累積美元避險|風險等級RR4
ABC-安本環球東歐股票基金A累積歐元|風險等級RR5
AC2-聯博-國際醫療基金A(美元)|風險等級RR3
AC3-聯博-美國收益基金A2(美元)|風險等級RR3
AC6-聯博-新興市場成長基金A(美元)|風險等級RR5
AC8-聯博-全球高收益債券基金A2(美元)|風險等級RR3
AI1-柏瑞巨人基金|風險等級RR5
AI2-柏瑞巨輪貨幣市場基金|風險等級RR1
AI3-柏瑞旗艦全球平衡組合基金|風險等級RR4
AI4-柏瑞日本小型公司股票基金A3|風險等級RR5
AI5-柏瑞旗艦全球成長組合基金|風險等級RR4
AI6-柏瑞旗艦全球債券組合基金|風險等級RR3
AI7-柏瑞全球金牌組合基金|風險等級RR4
AI8-柏瑞日本新遠景股票基金A|風險等級RR4
AI9-柏瑞歐洲小型公司股票基金A1|風險等級RR5
CAA-瀚亞全球價值股票基金A(美元)|風險等級RR3
EAB-MFS 全盛全球股票基金 A1 美元|風險等級RR3
EAT-MFS全盛新興市場債券基金 A1 美元|風險等級RR3
EBB-MFS全盛通脹調整債券基金 A1 美元|風險等級RR2
EBC-MFS全盛全球資產配置基金A1美元|風險等級RR3
EBD-MFS全盛美國價值基金A1美元|風險等級RR4
F10-富達歐洲高收益基金|風險等級RR3
F11-富達世界基金|風險等級RR3
F14-富達印度聚焦基金|風險等級RR5
F15-富達東協基金(美元)|風險等級RR5
FAA-富達太平洋基金|風險等級RR5
FAN-富達國際基金|風險等級RR3
FAY-富達全球金融服務基金|風險等級RR4
FBQ-富達中國內需消費基金A股累計美元|風險等級RR5
FBR-富達新興市場基金A股累計美元|風險等級RR5
FBS-富達大中華基金|風險等級RR4
FBT-富達亞太入息基金A股F1穩定月配息美元|風險等級RR5
FBW-富達北歐基金A股累計美元避險|風險等級RR4
FBX-富達全球科技基金A股累計美元避險|風險等級RR4
FBY-富達全球消費行業基金A股累計美元|風險等級RR4
FF1-富達台灣成長基金|風險等級RR4
FF2-富達歐洲基金|風險等級RR4
FF3-富達日本基金|風險等級RR4
FF5-富達美國基金|風險等級RR4
FF6-富達全球債券基金|風險等級RR2
FF7-富達歐元債券基金|風險等級RR2
FF8-富達歐洲小型企業基金|風險等級RR4
G01-駿利亨德森遠見泛歐地產股票基金|風險等級RR4
GAE-駿利亨德森遠見日本小型公司基金|風險等級RR4
HAA-滙豐中國高收益債券基金(台幣不配息)|風險等級RR4
HAB-滙豐中國高收益債券基金(台幣配息)|風險等級RR4
HAC-滙豐資源豐富國家債券基金(B配息)|風險等級RR3
HC1-滙豐龍鳳基金-A類型|風險等級RR4
I20-景順天下地產證券基金A|風險等級RR4
IAO-景順開發中市場基金A|風險等級RR5
IAQ-景順中國基金A(美元)|風險等級RR5
IAT-景順大中華基金A|風險等級RR5
IAU-景順環球指標增值基金A-穩定月配息(美元)|風險等級RR3
IN1-景順潛力基金|風險等級RR4
IN2-景順主流基金|風險等級RR4
IN3-景順全球科技基金|風險等級RR4
IN4-景順全球康健基金|風險等級RR3
IN5-景順台灣科技基金|風險等級RR5
IN6-景順日本小型企業基金A|風險等級RR4
IT1-天達環球策略股票基金C收益股份|風險等級RR4
JAA-摩根中國A股基金|風險等級RR5
JAC-摩根美國企業成長基金|風險等級RR4
KAA-駿利亨德森美國40基金A acc(美元)|風險等級RR4
KAS-駿利亨德森環球生命科技基金A acc(美元)|風險等級RR3
KAW-駿利亨德森環球科技基金A acc(美元)|風險等級RR4
LAJ-聯博-全球高收益債券基金 AT (美元)|風險等級RR3
LAR-聯博-美國成長基金 A (美元)|風險等級RR4
LAS-聯博-美國收益基金 AT (美元)|風險等級RR3
LBI-聯博-新興市場債券基金AT(美元)|風險等級RR3
LBJ-聯博-新興市場多元收益基金A(美元)|風險等級RR5
M12-貝萊德世界礦業基金A2(美元)|風險等級RR4
M13-貝萊德新能源基金A2(美元)|風險等級RR4
MAC-貝萊德世界健康科學基金A2(美元)|風險等級RR3
MAN-貝萊德歐洲基金A2(歐元)|風險等級RR4
MAU-貝萊德環球特別時機基金A2(美元)|風險等級RR3
MAW-貝萊德新興市場基金A2(美元)|風險等級RR5
MAZ-貝萊德歐元優質債券基金A3(歐元)|風險等級RR2
MBA-貝萊德中國基金A2(美元)|風險等級RR5
MBB-貝萊德歐洲基金A2避險(美元)|風險等級RR4
MBC-貝萊德環球企業債券基金A6(美元)|風險等級RR2
ML1-貝萊德世界黃金基金A2(美元)|風險等級RR5
ML3-貝萊德世界科技基金A2(美元)|風險等級RR4
ML5-貝萊德環球資產配置基金A2(美元)|風險等級RR3
ML8-貝萊德新興歐洲基金A2(歐元)|風險等級RR5
ML9-貝萊德拉丁美洲基金A2(美元)|風險等級RR5
OAA-永豐滬深300紅利指數基金|風險等級RR5
PAA-霸菱大東協基金-A類|風險等級RR5
QAC-野村成長基金|風險等級RR4
QAD-野村全球高股息基金季配型|風險等級RR3
QAF-NN (L) 食品飲料基金|風險等級RR4
R03-Franklin Templeton-全球債券基金(美元)|風險等級RR2
R04-Franklin Templeton-全球基金|風險等級RR3
RAB-Franklin Templeton-互利歐洲基金(歐元)|風險等級RR4
RAE-Franklin Templeton-全球股票收益基金Mdis|風險等級RR3
RAS-Franklin Templeton-生技領航基金|風險等級RR4
RAW-Franklin Templeton-亞洲債券基金acc|風險等級RR2
RAX-Franklin Templeton-公司債基金Mdis|風險等級RR3
RAY-Franklin Templeton-全球債券總報酬基金Mdis|風險等級RR3
RAZ-Franklin Templeton-美國機會基金|風險等級RR4
RBA-Franklin Templeton-穩定月收益基金Mdis|風險等級RR3
RBB-Franklin Templeton-新興國家固定收益基金Mdis|風險等級RR3
SBQ-施羅德(環)環球企業債券A1累積USD|風險等級RR2
SBW-施羅德(環)環球債券A1累積USD|風險等級RR2
SC1-施羅德(環)亞洲債券A1累積USD|風險等級RR3
SC2-施羅德(環)新興亞洲A1累積USD|風險等級RR5
SC4-施羅德(環)美國小型公司A1累積USD|風險等級RR4
SC5-施羅德(環)美元債券A1累積USD|風險等級RR2
SC7-施羅德(環)拉丁美洲A1累積USD|風險等級RR5
SCE-施羅德樂活中小基金-A類型|風險等級RR5
SCF-施羅德(環)歐洲收益股票基金A1配息EUR|風險等級RR4
SCG-施羅德(環)環球氣候變化策略A1累積USD|風險等級RR3
TAQ-天達環球策略管理基金C收益-2股份(澳幣避險月配)|風險等級RR3
TAR-天達新興市場公司債券基金C收益-2股份|風險等級RR3
TAT-天達環球特許品牌基金C累積股份|風險等級RR4
U01-瑞銀新興市場債券基金|風險等級RR3
U02-瑞銀美元基金|風險等級RR1
U03-瑞銀歐元基金|風險等級RR1
U05-瑞銀澳幣基金|風險等級RR1
UAH-瑞銀生化股票基金|風險等級RR4
UAR-瑞銀歐元高收益債券基金(歐元)(美元避險)(累積)|風險等級RR3
WAA-復華復華基金|風險等級RR4
WAB-復華新興市場高收益債券基金B|風險等級RR4
WAC-復華人生目標基金|風險等級RR4
WAD-復華中小精選基金|風險等級RR5
WAE-復華中國新經濟A股基金|風險等級RR5
WAF-復華中國新經濟平衡基金|風險等級RR4
WAG-復華全球大趨勢基金|風險等級RR4
WAH-復華全球平衡基金|風險等級RR4
WAI-復華全球物聯網科技基金|風險等級RR4
WAJ-復華全球原物料基金|風險等級RR4
WAK-復華全球短期收益基金|風險等級RR2
WAL-復華全球債券基金|風險等級RR2
WAM-復華全球債券組合基金|風險等級RR3
WAN-復華全球資產證券化基金A|風險等級RR4
WAO-復華全球戰略配置強基金|風險等級RR3
WAP-復華亞太成長基金|風險等級RR5
WAQ-復華東協世紀基金|風險等級RR5
WAR-復華美國新星基金|風險等級RR4
WAS-復華高益策略組合基金|風險等級RR3
WAT-復華奧林匹克全球組合基金|風險等級RR3
WAU-復華奧林匹克全球優勢組合基金A|風險等級RR3
WAV-復華新興市場高收益債券基金A|風險等級RR4

參考 : 

變更投資標的操作小幫手

2018年10月29日 星期一

Python 學習筆記 : pexpect 模組測試

上周為了評估上級新交辦任務, 花了些時間研究 Python 網路通訊功能中關於 telnet 與 ssh 協定的相關模組, 在下面這篇文章看到關於 pexpect 模組的介紹, 覺得它功能很多, 且很容易實現 telnet 與 ssh 協定通訊 :

[ Python 文章收集 ] python Pexpect

我在下面這本書看到 Python 模組 pexpect 的介紹 :

# Mastering Python Networking 2nd Edition (Packt)

此模組常用函數如下 :

我在 Win10 上的 Python 3.6.1 版安裝 pexpect 過程如下 :

Python 3.6.1 (C:\Python36\python.exe)
C:\Users\user>pip3 install pexpect 
Collecting pexpect
  Downloading https://files.pythonhosted.org/packages/89/e6/b5a1de8b0cc4e07ca1b305a4fcc3f9806025c1b651ea302646341222f88b/pexpect-4.6.0-py2.py3-none-any.whl (57kB)
Collecting ptyprocess>=0.5 (from pexpect)
  Downloading https://files.pythonhosted.org/packages/d1/29/605c2cc68a9992d18dada28206eeada56ea4bd07a239669da41674648b6f/ptyprocess-0.6.0-py2.py3-none-any.whl
Installing collected packages: ptyprocess, pexpect 
Successfully installed pexpect-4.6.0 ptyprocess-0.6.0 

可見 pexpect 相依於 ptyprocess 模組, 離線安裝必須同時下載 ptyprogress 與 pexpect 的 whl 安裝檔, 而且要先安裝 ptyprogress 再安裝 pexpect. 但安裝完後呼叫 pexpect.spawn() 欲建立 telnet 連線卻出現錯誤訊息 :

>>> import pexpect 
>>> child=pexpect.spawn("telnet 127.0.0.1") 
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
AttributeError: module 'pexpect' has no attribute 'spawn'

用 Python 內建函數 dir() 檢視 pexpect 模組之內容, 發現裡面並無 spawn() 方法 :

>>> dir(pexpect) 
['EOF', 'ExceptionPexpect', 'Expecter', 'PY3', 'TIMEOUT', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__revision__', '__spec__', '__version__', 'exceptions', 'expect', 'is_executable_file', 'searcher_re', 'searcher_string', 'split_command_line', 'sys', 'utils', 'which']

搜尋上面之錯誤訊息才知 pexpect 目前僅支援 Linux/Unix 系列作業系統, 還沒有完全支援 Windows, 參考 :

Cannot import name 'spawn' for pexpect while using pxssh
pxssh import issue #339

我在樹莓派安裝 pexpect 後用 dir() 檢視發現在 Linux 上確實可用 :

pi@raspberrypi:~ $ sudo pip3 install pexpect   (務必要用 sudo)
Collecting pexpect
  Downloading https://files.pythonhosted.org/packages/89/e6/b5a1de8b0cc4e07ca1b305a4fcc3f9806025c1b651ea302646341222f88b/pexpect-4.6.0-py2.py3-none-any.whl (57kB)
Collecting ptyprocess>=0.5 (from pexpect)
  Downloading https://files.pythonhosted.org/packages/d1/29/605c2cc68a9992d18dada28206eeada56ea4bd07a239669da41674648b6f/ptyprocess-0.6.0-py2.py3-none-any.whl
Installing collected packages: ptyprocess, pexpect
Successfully installed pexpect-4.6.0 ptyprocess-0.6.0
pi@raspberrypi:~ $ python3 
Python 3.4.2 (default, Oct 19 2014, 13:31:11)
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pexpect 
>>> dir(pexpect) 
['EOF', 'ExceptionPexpect', 'Expecter', 'PY3', 'TIMEOUT', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__revision__', '__spec__', '__version__', 'exceptions', 'expect', 'is_executable_file', 'pty_spawn', 'run', 'runu', 'searcher_re', 'searcher_string', 'spawn', 'spawnbase', 'spawnu', 'split_command_line', 'sys', 'utils', 'which']

在 Portable Python v3.7 上線上安裝 pexpect :

D:\PortablePython37>python -m pip install pexpect 
Collecting pexpect
  Downloading https://files.pythonhosted.org/packages/89/e6/b5a1de8b0cc4e07ca1b305a4fcc3f9806025c1b651ea302646341222f88b/pexpect-4.6.0-py2.py3-none-any.whl (57kB)
Collecting ptyprocess>=0.5 (from pexpect)
  Downloading https://files.pythonhosted.org/packages/d1/29/605c2cc68a9992d18dada28206eeada56ea4bd07a239669da41674648b6f/ptyprocess-0.6.0-py2.py3-none-any.whl
Installing collected packages: ptyprocess, pexpect
Successfully installed pexpect-4.6.0 ptyprocess-0.6.0

但後來在 Portable Python 3.7.0 上離線安裝 pexpect (有相依模組 ptyprocess) :

Microsoft Windows [版本 6.3.9600]
 (c) 2013 Microsoft Corporation. All rights reserved.

 C:\Python37>python -m pip install ptyprocess-0.6.0-py2.py3-none-any.whl 
 Processing c:\python37\ptyprocess-0.6.0-py2.py3-none-any.whl
 Installing collected packages: ptyprocess
 Successfully installed ptyprocess-0.6.0
 C:\Python37>python -m pip install pexpect-4.6.0-py2.py3-none-any.whl 
 Processing c:\python37\pexpect-4.6.0-py2.py3-none-any.whl
 Requirement already satisfied: ptyprocess>=0.5 in c:\python37\app\python\lib\sit
 e-packages (from pexpect==4.6.0) (0.6.0)
 Installing collected packages: pexpect
 Successfully installed pexpect-4.6.0

卻發現它有與樹莓派上一樣完整的功能 :

>>> import pexpect 
>>> dir(pexpect) 
['EOF', 'ExceptionPexpect', 'Expecter', 'PY3', 'TIMEOUT', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__revision__', '__spec__', '__version__', 'exceptions', 'expect', 'is_executable_file', 'pty_spawn', 'run', 'runu', 'searcher_re', 'searcher_string', 'spawn', 'spawnbase', 'spawnu', 'split_command_line', 'sys', 'utils', 'which']

所以 Pythoin 3.7 似乎可用 pexpect 了.

參考 :

Python安装使用命令行交互模块pexpect的基础教程

2018年10月28日 星期日

2018 年第 43 周記事

最近入夜後開始轉涼, 換季穿長袖了. 玉兔颱風偏向菲律賓, 週三恐變天. 小舅與舅媽周三從沖繩回來, 昨日來訪送來沖繩土產蜂蜜蛋糕一盒, 蠻好吃的, 不像台灣的那麼甜. 不過小舅說自由行不好玩, 心思都花在下一站怎麼走, 擔心耽誤時間, 不如跟團讓導遊操心行程, 單純當自在的傻遊客就好. 而且做廉航其實沒便宜多少錢, 登機環境好像難民, 沒空橋, 走好遠, 商店三兩家, 行李秤重收費, ... 連一張面紙也沒有, 說以後出國玩絕不再自由行, 比跟團還累. 我也這麼覺得.

由於今天要去吃喜酒 (單位主管娶媳婦), 故早上 10:30 便從鄉下回高雄, 12:00 到福華竟然還是我們那桌最早到的. 爸本來這周想回鄉下住, 但瘡的傷口還剩一小洞還需換藥, 我說再住一周好了. 吃完喜酒去長明街找金手指插座, 沒找到 20 pin 的只好作罷, 順路去茂訊看筆電, 覺得 Acer Swift 5 還真是輕啊, 與我的 Inhon 筆電一樣都是 970g, 若沒買新 MacBook Air 就考慮 Swift 5, MSI 的 MSI PS42 8M-402TW. LG Gram 續航雖長, 但太貴了. 不過 Inhon 筆電還堪用, 若買新機這台不就變成閒置資產?

本周追劇進度, "九號房間" 中, 張華娑知道母親是被馬代表用酒瓶砸頭導致失憶一時憤怒也用酒瓶砸馬代表致死, 但被奇會長掩護未被揭發. 張華娑想利用為奇會長之子奇讚成撞死人一案辯護的機會讓奇會長失去現在的一切, 回到剛認識她時一無所有的樣子. "我身後的陶斯" 裡, 陶斯去 J 國際欲取回李容泰的沙漏來救高愛琳, 卻被權世英次長的人盯上追捕, 赫然發現魔法師 K 竟然也在權次長的行列中, 急忙從橋上躍下時手臂被 K 擊中一槍沉入河底, 心急如焚的高愛琳跳下河將陶斯救上來. 李容泰因 J 國際曝光而被神秘的上級被清除, 遭到 K 的追殺, 所幸陶斯及時趕到救回一命. "百日的郎君" 世子回宮後漸漸找回過去記憶, 從找回的小木箱中的髮帶想起洪心就是自己一直在尋找的尹瑞利而相認, 但這卻讓洪心陷入危機中 ...

2018年10月27日 星期六

大學多元入學說明會

今天早上去鳳中參加學校舉辦的大學多元入學說明會, 從 9 點到 12 點整整 3 小時, 主要介紹繁星, 申請入學, 指考等多元入學方案, 制度看似複雜, 但其實重點就是 :
  1. 校排很前面且心儀科系很明確就爭取繁星 (名額非常少).
  2. 參與活動或訓練, 競賽等經驗豐富就準備申請入學資料, 當然學測成績要好看
  3. 以上皆非就乖乖準備指考
姊姊以前是美術班, 比較特殊, 這一套制度我其實沒有很了解. 姊姊班排前五名所以參加了繁星推薦, 可惜學測國文沒考好, 繁星上台藝大古蹟修復系, 因不是第一志願高師大視覺設計, 且一直沒時間準備作品集, 所以繁星與申請入學都放棄, 直接準備指考, 反而考上台師大. 二哥念普通高中, 所以這回得仔細搞清楚遊戲規則了. 二哥班排中上在 6~15 名之間, 繁星應該沒機會, 所以必須拚申請入學了.

2018年10月26日 星期五

Python 學習筆記 : SSH 模組 paramiko 測試

前幾天上面交辦了一件自動化任務, 希望我寫個程式來解決. 此任務主要是透過 ssh 連線遠端主機後向其發送自動產生的指令. 我想用正在學習的 Python tkinter 來撰寫 UI, 而 SSH 連線部分原本想用 Putty, 但我覺得 Python 應該有自己的解決方案, 便上網找到 Paramiko 這個模組.

Paramiko 是以純 Python 實作 SSH v2 加密協定的第三方模組, 底層以 C 擴充函式庫 cryptography 達成低階加密驗證功能, 提供遠端安全連線服務. 最早是由 Jeff Forcier 所創建, 實作並高階封裝了 SSH 協定, 後來開放原始碼成為 Python 底下知名的 SSH 專案, 其首頁, API, 與 GitHub 網址如下 :

http://www.paramiko.org
http://docs.paramiko.org (API)
https://github.com/paramiko/paramiko

Paramiko 需先用 pip 安裝才能匯入使用, 安裝過程如下 :

D:\Python\pip3 install paramiko
Collecting paramiko
  Downloading https://files.pythonhosted.org/packages/cf/ae/94e70d49044ccc234bfdba20114fa947d7ba6eb68a2e452d89b920e62227/paramiko-2.4.2-py2.py3-none-any.whl (193kB)
Collecting pyasn1>=0.1.7 (from paramiko)
  Downloading https://files.pythonhosted.org/packages/d1/a1/7790cc85db38daa874f6a2e6308131b9953feb1367f2ae2d1123bb93a9f5/pyasn1-0.4.4-py2.py3-none-any.whl (72kB)
Collecting pynacl>=1.0.1 (from paramiko)
  Downloading https://files.pythonhosted.org/packages/b5/0f/5f21bd04c8f45685c7b72013fc8efaf0e25baf3b77e62b3b915b5b43a7b6/PyNaCl-1.3.0-cp36-cp36m-win_amd64.whl (188kB)
Requirement already satisfied: cryptography>=1.5 in c:\python36\lib\site-packages (from paramiko) (2.3.1)
Collecting bcrypt>=3.1.3 (from paramiko)
  Downloading https://files.pythonhosted.org/packages/4b/c0/c0550e4b98e0536d3a8b8753b68aa0d3c03af654c43a58328d0bf2c06747/bcrypt-3.1.4-cp36-cp36m-win_amd64.whl
Requirement already satisfied: cffi>=1.4.1 in c:\python36\lib\site-packages (from pynacl>=1.0.1->paramiko) (1.11.5)
Requirement already satisfied: six in c:\python36\lib\site-packages (from pynacl>=1.0.1->paramiko) (1.11.0)
Requirement already satisfied: asn1crypto>=0.21.0 in c:\python36\lib\site-packages (from cryptography>=1.5->paramiko) (0.24.0)
Requirement already satisfied: idna>=2.1 in c:\python36\lib\site-packages (from cryptography>=1.5->paramiko) (2.6)
Requirement already satisfied: pycparser in c:\python36\lib\site-packages (from cffi>=1.4.1->pynacl>=1.0.1->paramiko) (2.19)
Installing collected packages: pyasn1, pynacl, bcrypt, paramiko
Successfully installed bcrypt-3.1.4 paramiko-2.4.2 pyasn1-0.4.4 pynacl-1.3.0

可見 paramiko 有 pyasnl, PyNaCl, bcrypt, asn1crypto, pycparser 等相依模組, 如果是本地安裝, 需先下載安裝這些相依模組後才能順利安裝 paramiko. 我在 Python 3.6.0 (32 位元) 上離線安裝結果如下 :

 D:\>pip3 install pyasn1-0.4.4-py2.py3-none-any.whl
 Processing d:\pyasn1-0.4.4-py2.py3-none-any.whl
 Installing collected packages: pyasn1
 Successfully installed pyasn1-0.4.4

 D:\>pip3 install bcrypt-3.1.4-cp27-cp27m-win32.whl
 bcrypt-3.1.4-cp27-cp27m-win32.whl is not a supported wheel on this platform.

 D:\>pip3 install idna-2.7-py2.py3-none-any.whl
 Processing d:\idna-2.7-py2.py3-none-any.whl
 Installing collected packages: idna
 Successfully installed idna-2.7

 D:\>pip3 install pycparser-2.19.tar.gz
 Processing d:\pycparser-2.19.tar.gz
 Installing collected packages: pycparser
   Running setup.py install for pycparser ... done
 Successfully installed pycparser-2.19

D:\>pip3 install six-1.11.0-py2.py3-none-any.whl
 Processing d:\six-1.11.0-py2.py3-none-any.whl
 Installing collected packages: six
 Successfully installed six-1.11.0

 D:\>pip3 install bcrypt-3.1.4-cp36-cp36m-win32.whl
 Processing d:\bcrypt-3.1.4-cp36-cp36m-win32.whl
 Requirement already satisfied: cffi>=1.1 in c:\python3\lib\site-packages (from b
 crypt==3.1.4)
 Requirement already satisfied: six>=1.4.1 in c:\python3\lib\site-packages (from
 bcrypt==3.1.4)
 Requirement already satisfied: pycparser in c:\python3\lib\site-packages (from c
 ffi>=1.1->bcrypt==3.1.4)
 Installing collected packages: bcrypt
 Successfully installed bcrypt-3.1.4

 D:\>pip3 install PyNaCl-1.3.0-cp36-cp36m-win32.whl
 Processing d:\pynacl-1.3.0-cp36-cp36m-win32.whl
 Requirement already satisfied: six in c:\python3\lib\site-packages (from PyNaCl=
 =1.3.0)
 Requirement already satisfied: cffi>=1.4.1 in c:\python3\lib\site-packages (from
  PyNaCl==1.3.0)
 Requirement already satisfied: pycparser in c:\python3\lib\site-packages (from c
 ffi>=1.4.1->PyNaCl==1.3.0)
 Installing collected packages: PyNaCl
 Successfully installed PyNaCl-1.3.0

D:\>pip3 install asn1crypto-0.24.0-py2.py3-none-any.whl
 Processing d:\asn1crypto-0.24.0-py2.py3-none-any.whl
 Installing collected packages: asn1crypto
 Successfully installed asn1crypto-0.24.0

 D:\>pip3 install cryptography-2.3.1-cp36-cp36m-win32.whl
 Processing d:\cryptography-2.3.1-cp36-cp36m-win32.whl
 Requirement already satisfied: six>=1.4.1 in c:\python3\lib\site-packages (from
 cryptography==2.3.1)
 Requirement already satisfied: idna>=2.1 in c:\python3\lib\site-packages (from c
 ryptography==2.3.1)
 Requirement already satisfied: asn1crypto>=0.21.0 in c:\python3\lib\site-package
 s (from cryptography==2.3.1)
 Requirement already satisfied: cffi!=1.11.3,>=1.7 in c:\python3\lib\site-package
 s (from cryptography==2.3.1)
 Requirement already satisfied: pycparser in c:\python3\lib\site-packages (from c
 ffi!=1.11.3,>=1.7->cryptography==2.3.1)
 Installing collected packages: cryptography
 Successfully installed cryptography-2.3.1

 D:\>pip3 install paramiko-2.4.2-py2.py3-none-any.whl
 Processing d:\paramiko-2.4.2-py2.py3-none-any.whl
 Requirement already satisfied: pynacl>=1.0.1 in c:\python3\lib\site-packages (fr
 om paramiko==2.4.2)
 Requirement already satisfied: cryptography>=1.5 in c:\python3\lib\site-packages
  (from paramiko==2.4.2)
 Requirement already satisfied: bcrypt>=3.1.3 in c:\python3\lib\site-packages (fr
 om paramiko==2.4.2)
 Requirement already satisfied: pyasn1>=0.1.7 in c:\python3\lib\site-packages (fr
 om paramiko==2.4.2)
 Requirement already satisfied: cffi>=1.4.1 in c:\python3\lib\site-packages (from
  pynacl>=1.0.1->paramiko==2.4.2)
 Requirement already satisfied: six in c:\python3\lib\site-packages (from pynacl>
 =1.0.1->paramiko==2.4.2)
 Requirement already satisfied: asn1crypto>=0.21.0 in c:\python3\lib\site-package
 s (from cryptography>=1.5->paramiko==2.4.2)
 Requirement already satisfied: idna>=2.1 in c:\python3\lib\site-packages (from c
 ryptography>=1.5->paramiko==2.4.2)
 Requirement already satisfied: pycparser in c:\python3\lib\site-packages (from c
 ffi>=1.4.1->pynacl>=1.0.1->paramiko==2.4.2)
 Installing collected packages: paramiko
 Successfully installed paramiko-2.4.2

連線 ssh 主機首先要呼叫 SSHClient() 方法建立 SSHClient 物件, 然後呼叫 load_system_host_keys() 載入 ssh 系統主機鍵以便驗證, 再呼叫 connect() 傳入帳號密碼連線遠端主機, 最後呼叫 exec_command() 指令對主機下命令, 它會傳回 3 個元素的 tuple : (stdin, stdout, stderr), 前兩者都是 ChannelFile 物件; 而 stderr 則是 ChannelStderrFile 物件, 利用其 redlines() 方法可讀取命令之回應.

下面範例是以鄉下那台 Pi 3 為遠端主機, 本地則使用 paramiko 來建立 ssh 連線, 並向主機傳送 "ls -al" 指令, 例如 :

Python 3.6.4 (C:\Python36\python.exe)
>>> import paramiko 
>>> ssh=paramiko.SSHClient() 
>>> ssh.load_system_host_keys()   
>>> ssh.connect(hostname="114.27.111.273", username="pi", password="pi") 
>>> ssh_stdin, ssh_stdout, ssh_stderr=ssh.exec_command('ls -al') 
>>> print(''.join(ssh_stdout.readlines())) 
total 64040
drwxr-xr-x 31 pi   pi       4096 Oct 23 07:34 .
drwxr-xr-x  3 root root     4096 Mar  3  2017 ..
-rw-r--r--  1 pi   pi     741773 Dec 24  2017 2017-12-24-204625_1280x1024_scrot.png
-rw-r--r--  1 pi   pi    1139579 Jan 21  2018 2018-01-21-161652_1280x1024_scrot.png
-rw-r--r--  1 pi   pi    1126447 Jan 21  2018 2018-01-21-161711_1280x1024_scrot.png
-rw-r--r--  1 pi   pi     137558 Jan 21  2018 2018-01-21-162138_1280x1024_scrot.png
-rw-------  1 pi   pi       7796 Oct 16 15:59 .bash_history
-rw-r--r--  1 pi   pi        220 Mar  3  2017 .bash_logout
-rw-r--r--  1 pi   pi       3512 Mar  3  2017 .bashrc
drwxr-xr-x  8 pi   pi       4096 Jun 23 20:43 .cache
-rw-r--r--  1 pi   pi         70 Dec 31  2017 checkwifi.sh
drwx------  2 pi   pi       4096 Sep  5 16:17 .chewing
drwx------ 20 pi   pi       4096 Jun 30 23:33 .config
-rw-r--r--  1 pi   pi       1786 Feb 22  2017 crontab.txt
-rw-r--r--  1 pi   pi          0 Apr 29 07:34 db.sqlite
drwx------  3 pi   pi       4096 Mar  4  2017 .dbus
-rw-r--r--  1 pi   pi    4924423 Oct 24 22:40 dead.letter
drwxr-xr-x  2 pi   pi       4096 Sep 17  2017 Desktop
drwxr-xr-x  5 pi   pi       4096 Mar  3  2017 Documents
drwxr-xr-x  2 pi   pi       4096 Feb 16  2018 Downloads
drwx------  2 pi   pi       4096 Jul  1 14:56 .gconf
drwx------  3 pi   pi       4096 Sep 16  2017 .gnome
drwxr-xr-x  2 pi   pi       4096 Mar  4  2017 .gstreamer-0.10
-rw-r--r--  1 pi   pi         26 Jun 30 22:09 .gtkrc-2.0
drwxr-xr-x  2 pi   pi       4096 Apr  1  2017 .idlerc
-rw-r--r--  1 root root      523 Mar 31  2017 interfaces
-rw-r--r--  1 pi   pi        375 Sep 24  2017 interfaces_fix
-rw-r--r--  1 pi   pi        430 Jan 23  2017 interfaces_kao
-rw-r--r--  1 pi   pi        428 Sep 24  2017 interfaces_mei
-rw-r--r--  1 pi   pi        428 Jan 24  2017 interfaces_mob
drwxr-xr-x  3 pi   pi       4096 Apr 14  2018 .keras
-rw-r--r--  1 pi   pi         28 Oct 22 14:30 lastip.txt
drwxr-xr-x  3 pi   pi       4096 Mar  3  2017 .local
drwxr-xr-x  2 pi   pi       4096 Mar  4  2017 Music
-rw-------  1 pi   pi         34 Mar 24  2018 .node_repl_history
drwxr-xr-x  6 pi   pi       4096 Dec 12  2017 node-v9.3.0-linux-armv6l
-rw-r--r--  1 pi   pi   16930799 Dec 12  2017 node-v9.3.0-linux-armv6l.tar.gz
-rw-r--r--  1 pi   pi         21 Jan 26  2017 phpinfo.php
-rw-r--r--  1 pi   pi    8151264 Jan 24  2017 phpMyAdmin-4.6.6-all-languages.tar.bz2
drwxr-xr-x  2 pi   pi       4096 Mar  4  2017 Pictures
drwxr-xr-x  2 pi   pi       4096 Jan  7  2018 .pip
drwx------  3 pi   pi       4096 Aug  6  2017 .pki
-rw-r--r--  1 pi   pi        675 Mar  3  2017 .profile
drwxr-xr-x  2 pi   pi       4096 Mar  4  2017 Public
drwxr-xr-x  2 pi   pi       4096 Mar  3  2017 python_games
-rw-------  1 pi   pi          0 Oct 23 07:34 .python_history
-rw-------  1 pi   pi         17 Jun 23 20:42 .python_history-
-rw-r--r--  1 pi   pi       2960 Dec 10  2017 reportip2.py
-rw-r--r--  1 pi   pi       2866 Dec  8  2017 reportip3.py
-rw-r--r--  1 pi   pi       2928 Dec  3  2017 reportip.py
-rw-r--r--  1 pi   pi       2173 Apr 22  2017 rpi-benchmark.sh
drwx------  2 pi   pi       4096 Oct 22  2017 .scim
-rw-r--r--  1 pi   pi         66 Dec  9  2017 .selected_editor
-rw-r--r--  1 pi   pi        671 Feb 26  2017 ssmtp.conf
drwxr-xr-x  5 pi   pi       4096 May 13 16:03 ta-lib
-rw-r--r--  1 pi   pi    1330299 May 13 15:04 ta-lib-0.4.0-src.tar.gz
drwxr-xr-x  2 pi   pi       4096 Mar  4  2017 Templates
-rw-r--r--  1 pi   pi   28584139 May 26  2017 tensorflow-1.1.0-cp34-cp34m-linux_armv7l.whl
-rw-r--r--  1 pi   pi        281 Feb 25  2018 tensorflow.txt
drwxr-xr-x  4 pi   pi       4096 Feb 19  2018 test
drwxr-xr-x  3 pi   pi       4096 Mar  4  2017 .themes
drwx------  5 pi   pi       4096 Jun 30 23:38 .thonny
drwx------  4 pi   pi       4096 Aug  6  2017 .thumbnails
-rw-r--r--  1 pi   pi    2235652 Feb 22  2017 tony_20170222.zip
drwxr-xr-x  2 pi   pi       4096 Mar  4  2017 Videos
drwx------  4 pi   pi       4096 Dec 11  2017 .vnc
-rw-------  1 root root      153 Mar 31  2017 wpa_supplicant.conf
-rw-------  1 pi   pi        266 Sep  5 16:17 .Xauthority
-rw-------  1 pi   pi       1187 Sep  5 16:17 .xsession-errors
-rw-------  1 pi   pi       1187 Aug 12 17:17 .xsession-errors.old

exec_command() 的傳回值 ssh_stdout 與 ssh_stdin 均為 ChannelFile 物件; 而 ssh_stderr 則是 ChannelStderrFile 物件 :

>>> type(ssh_stdout) 
<class 'paramiko.channel.ChannelFile'>
>>> type(ssh_stdin) 
<class 'paramiko.channel.ChannelFile'>
>>> type(ssh_stderr) 
<class 'paramiko.channel.ChannelStderrFile'> 

參考 :

Python Paramiko基本使用
Paramiko在Python執行 SSH 小記
系統運維工程師的法寶:python paramiko

2018年10月25日 星期四

Python 學習筆記 : PortablePython 安裝模組的方法

Portable Python 是一個綠色的 Python 執行與開發環境, 適合用在沒有軟體安裝權限的電腦, 特別是如今各行各業都非常重視資安, 通常都會限制只有管理員才有安裝權限, 使得在這種電腦環境無法執行或開發 Python 軟體. 不過 Portable Python 原創者封裝到 v2.7.6.1 與 v3.2.5 後已停止繼續開發, 參考 ;

https://portablepython.com/
https://portablepython.com/wiki/Download/

還好有一位專家 Aman Ansari 繼續封裝 v3.7.0, 可從 Source Forge 下載 :

https://sourceforge.net/projects/portable-python/

此為 64 位元版, 下載的 zip 檔大約 32MB, 解開後約 110MB, 可放在隨身碟中執行, 其目錄結構如下 :




其中有四個開啟程式 (Launcher) :
  1. Python_Launcher.exe : 開啟命令提示字元視窗執行 Python.exe 
  2. IDLE-Launcher.exe : 開啟 IDLE 介面
  3. PyScripter-Launcher.exe : 開啟 PyScripter 編輯器
  4. Console-Launcher.exe : 開啟命令提示字元視窗
檔案 how to use PIP.txt 描述在 Portable Python 下使用 pip 相關指令是在前面加上 "python -m" :
  1. python -m pip install package_name (安裝套件模組)
  2. python -m pip install --upgrade pip (升版 pip)
  3. python -m pip list (檢視已安裝套件模組)
  4. python -m pip uninstall package_name (移除套件模組)
  5. python -m pip list --outdated (顯示過時套件模組)
  6. python -m pip search "package_name" (搜尋套件模組)
執行 Console-Launcher.exe 開啟命令提示字元視窗 (不要用 Windows 本身的), 下 python -m pip list 會列出目前已安裝模組 :

Microsoft Windows [版本 10.0.17134.345]
(c) 2018 Microsoft Corporation. 著作權所有,並保留一切權利。

D:\PortablePython37<python -m pip list
Package    Version
---------- -------
pip              10.0.1
setuptools   39.0.1 
You are using pip version 10.0.1, however version 18.1 is available.
You should consider upgrading via the 'python -m pip install --upgrade pip' command.

可見預設只安裝了 pip 與 setuptools 這兩個模組而已, 在網路暢通環境下可用 python -m pip install 指令安裝所需要的模組. 但在離線或有防火牆阻擋環境下, 則需要用到 App\Python\Scripts 下的 setup_tools.exe 安裝已下載的模組檔案 (whl 或 gz 檔) :

如何在 Portable Python 離線安裝模組

不過由於模組相依問題, 離線安裝通常要準備全部相依的模組才能順利安裝, 作者建議在網路暢通環境下安裝全部用得到的模組較省事. 我常用的模組安裝指令如下, 主要是機器學習與網路爬蟲等相關之模組 :

python -m pip install scipy
python -m pip install pandas
python -m pip install matplotlib
python -m pip install scikit-learn
python -m pip install --upgrade tensorflow
python -m pip install keras
python -m pip install requests
python -m pip install beautifulsoup4
python -m pip install lxml
python -m pip install html5lib
python -m pip install selenium
python -m pip install pymysql
python -m pip install mysqlclient
python -m pip install xlrd
python -m pip install fix_yahoo_finance
python -m pip install openpyxl
python -m pip install "django<1.9"
python -m pip install virtualenv
python -m pip install paramiko

注意 :

如果電腦中原本已安裝了 Python, 則必須到 "系統/進階設定" 中將 path 中的 Python 路徑 (例如 C:\Python36\Scripts\, C:\Python36\) 暫時移除, 否則 pip install 安裝時會安裝到原本的 Python 安裝目錄裡, 而不是 Portable Python 裡, 安裝完再恢復 path 原設定. 另外, Numpy 於安裝 Scipy 時會一併安裝, 不需單獨安裝. 

上面的模組除了 mysqlclient 外均順利安裝完成, mysqlclient 則出現如下錯誤訊息 :

D:\PortablePython37>python -m pip install mysqlclient
Collecting mysqlclient
  Using cached https://files.pythonhosted.org/packages/ec/fd/83329b9d3e14f7344d1cb31f128e6dbba70c5975c9e57896815dbb1988ad/mysqlclient-1.3.13.tar.gz
Installing collected packages: mysqlclient
  Running setup.py install for mysqlclient ... error
    Complete output from command D:\PortablePython37\App\Python\python.exe -u -c "import setuptools, tokenize;__file__='C:\\Users\\TONYHU~1\\AppData\\Local\\Temp\\pip-install-0cfu1z52\\mysqlclient\\setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record C:\Users\TONYHU~1\AppData\Local\Temp\pip-record-tzrl7s3z\install-record.txt --single-version-externally-managed --compile:
    D:\PortablePython37\App\Python\lib\distutils\dist.py:274: UserWarning: Unknown distribution option: 'long_description_content_type'
      warnings.warn(msg)
    running install
    running build
    running build_py
    creating build
    creating build\lib.win-amd64-3.7
    copying _mysql_exceptions.py -> build\lib.win-amd64-3.7
    creating build\lib.win-amd64-3.7\MySQLdb
    copying MySQLdb\__init__.py -> build\lib.win-amd64-3.7\MySQLdb
    copying MySQLdb\compat.py -> build\lib.win-amd64-3.7\MySQLdb
    copying MySQLdb\connections.py -> build\lib.win-amd64-3.7\MySQLdb
    copying MySQLdb\converters.py -> build\lib.win-amd64-3.7\MySQLdb
    copying MySQLdb\cursors.py -> build\lib.win-amd64-3.7\MySQLdb
    copying MySQLdb\release.py -> build\lib.win-amd64-3.7\MySQLdb
    copying MySQLdb\times.py -> build\lib.win-amd64-3.7\MySQLdb
    creating build\lib.win-amd64-3.7\MySQLdb\constants
    copying MySQLdb\constants\__init__.py -> build\lib.win-amd64-3.7\MySQLdb\constants
    copying MySQLdb\constants\CLIENT.py -> build\lib.win-amd64-3.7\MySQLdb\constants
    copying MySQLdb\constants\CR.py -> build\lib.win-amd64-3.7\MySQLdb\constants
    copying MySQLdb\constants\ER.py -> build\lib.win-amd64-3.7\MySQLdb\constants
    copying MySQLdb\constants\FIELD_TYPE.py -> build\lib.win-amd64-3.7\MySQLdb\constants
    copying MySQLdb\constants\FLAG.py -> build\lib.win-amd64-3.7\MySQLdb\constants
    copying MySQLdb\constants\REFRESH.py -> build\lib.win-amd64-3.7\MySQLdb\constants
    running build_ext
    building '_mysql' extension
    error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools

    ----------------------------------------
Command "D:\PortablePython37\App\Python\python.exe -u -c "import setuptools, tokenize;__file__='C:\\Users\\TONYHU~1\\AppData\\Local\\Temp\\pip-install-0cfu1z52\\mysqlclient\\setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record C:\Users\TONYHU~1\AppData\Local\Temp\pip-record-tzrl7s3z\install-record.txt --single-version-externally-managed --compile" failed with error code 1 in C:\Users\TONYHU~1\AppData\Local\Temp\pip-install-0cfu1z52\mysqlclient\

有看沒有懂, 這模組就算了, 反正有 PyMySQL 就可以了.

另外, 因 tensorflow 目前僅支援到 Python 3.6, 而此 Portable Python 為 3.7.0 版, 因此用 pip 安裝時會報 "No matching distribution found" 錯誤, 所以就先不安裝, 等以後支援了再說, 連帶 Keras 也是以後再安裝 :

D:\PortablePython37>python -m pip install tensorflow
Collecting tensorflow
  Could not find a version that satisfies the requirement tensorflow (from versions: )
No matching distribution found for tensorflow

最後用 pip list 查看已安裝模組如下 :

D:\PortablePython37>python -m pip list   
Package           Version
----------------- ----------
asn1crypto        0.24.0
bcrypt            3.1.4
beautifulsoup4    4.6.3
certifi           2018.10.15
cffi              1.11.5
chardet           3.0.4
cryptography      2.3.1
cycler            0.10.0
Django            1.8.19
et-xmlfile        1.0.1
fix-yahoo-finance 0.0.22
html5lib          1.0.1
idna              2.7
jdcal             1.4
kiwisolver        1.0.1
lxml              4.2.5
matplotlib        3.0.0
multitasking      0.0.7
numpy             1.15.3
openpyxl          2.5.9
pandas            0.23.4
paramiko          2.4.2
pip               18.1
pyasn1            0.4.4
pycparser         2.19
PyMySQL           0.9.2
PyNaCl            1.3.0
pyparsing         2.2.2
python-dateutil   2.7.3
pytz              2018.6
requests          2.20.0
scikit-learn      0.20.0
scipy             1.1.0
selenium          3.14.1
setuptools        39.0.1
six               1.11.0
urllib3           1.24
webencodings      0.5.1
xlrd              1.1.0

安裝完後約 350MB, 沒想到這些模組加起來竟然這麼大! 重新壓縮後的 ZIP 檔也膨脹到 150MB 左右, 這樣就可以移到無法線上安裝的電腦使用了.

2018-10-26 補充 :

安裝完所需模組移到網內使用時若仍發現缺了那些模組, 不用回網外電腦安裝後再搬檔案, 只要去 PyPi 網站下載該模組之 whl 檔 (以及所有相依模組) 安裝即可, 例如今天發現 pexpect 好用沒安裝到, 於是下載了 pexpect 及其相依模組 ptyprocess 以 pip 安裝m 例如 :


 C:\Python37>python -m pip install ptyprocess-0.6.0-py2.py3-none-any.whl
 Processing c:\python37\ptyprocess-0.6.0-py2.py3-none-any.whl
 Installing collected packages: ptyprocess
 Successfully installed ptyprocess-0.6.0

 C:\Python37>python -m pip install pexpect-4.6.0-py2.py3-none-any.whl
 Processing c:\python37\pexpect-4.6.0-py2.py3-none-any.whl
 Requirement already satisfied: ptyprocess>=0.5 in c:\python37\app\python\lib\sit
 e-packages (from pexpect==4.6.0) (0.6.0)
 Installing collected packages: pexpect
 Successfully installed pexpect-4.6.0

2018年10月23日 星期二

Python Fintech 學習筆記 : 用 twder 模組擷取台銀外匯報價

最近勤加練習 Python 應用, 發現 Python 應用實在既深且廣, 不斷的有高手將爬蟲寫成模組分享出來, 在 PyPi 網站隨時都有驚奇出現. 今天在 "行銷資料科學 (Medium)" 看到下面這拚文章, 介紹用 twder 模組抓取台銀即時外匯報價的方法, 省去自行撰寫網頁爬蟲的麻煩, 真是 Fintech 的好工具 :

一秒就上手!馬上幫您爬取匯率價格!掌控最新貿易匯損!(附python 程式碼)

此模組作者為 Jimms Hsieh, 並將其發布在 PyPi 網站 :

https://pypi.org/project/twder/

線上安裝過程 :

D:\Python\E_14_1>pip3 install twder 
Collecting twder
  Downloading https://files.pythonhosted.org/packages/03/3b/49ab585b6cd2734afee4227f5a344a110419e3eba298269eb87a943a980b/twder-0.1.1-py2.py3-none-any.whl
Requirement already satisfied: lxml in c:\python36\lib\site-packages (from twder) (4.2.1)
Requirement already satisfied: requests in c:\python36\lib\site-packages (from twder) (2.18.4)
Requirement already satisfied: idna<2 .7="">=2.5 in c:\python36\lib\site-packages (from requests->twder) (2.6)
Requirement already satisfied: chardet<3 .1.0="">=3.0.2 in c:\python36\lib\site-packages (from requests->twder) (3.0.4)
Requirement already satisfied: urllib3<1 .23="">=1.21.1 in c:\python36\lib\site-packages (from requests->twder) (1.22)
Requirement already satisfied: certifi>=2017.4.17 in c:\python36\lib\site-packages (from requests->twder) (2018.4.16)
Installing collected packages: twder
Successfully installed twder-0.1.1

從安裝過程可知, 此模組使用 requests 與 lxml 等等來爬台銀外匯報價網頁. 如果因為防火牆無法用 pip 安裝, 可到 Pypi 網站下載 whl 安裝檔 twder-0.1.1-py2.py3-none-any.whl :

https://pypi.org/project/twder/#files

本地安裝過程 :

D:\Python>pip3 install twder-0.1.1-py2.py3-none-any.whl 
Processing d:\python\twder-0.1.1-py2.py3-none-any.whl
Requirement already satisfied: requests in c:\python36\lib\site-packages (from twder==0.1.1) (2.13.0
)
Collecting lxml (from twder==0.1.1)

安裝好 twder 模組即可利用其 API 抓取台銀匯率報價, 使用方法見作者之 GitHub :

https://github.com/jimms/twder

從 twder 目錄裡面的 api.py 可窺知其爬蟲做法 (高手不吝分享的絕技非常值得學習), 擷取對象是台銀外匯報價網頁 :

http://rate.bot.com.tw/xrt?Lang=zh-TW (即時)

擷取歷史匯價的 URL 格式如下 :

http://rate.bot.com.tw/xrt/quote/{range}/{currency}

其中 range 為 "年-月", 例如 2018-10; 而 currency 為貨幣代號, 例如 USD (美金), JPY (日元), CNY (人民幣), EUR (歐元) 等等. 例如擷取 2018 年 10 月之美元歷史匯率報價, 其 URL 為 :

http://rate.bot.com.tw/xrt/quote/2018-10/USD


使用 twder 前須先匯入模組 :

import twder 

此模組提供如下函數 :

 twder 模組的函數 說明
 currencies() 傳回全部外幣代號 (list)
 currency_name_dict() 傳回全部外幣代號與其中文名稱  (dict)
 now(currency) 傳回指定外幣目前之報價 (tuple)
 now_all() 傳回全部外幣目前之報價  (dict)
 past_day(currency) 傳回指定外幣昨日全部報價 (list)
 past_six_month(currency) 傳回指定外幣過去半年每天之收盤價 (list)
 specify_month(currency, year, month) 傳回指定外幣在指定年月每天之收盤價 (list)


1 : 列出台銀可交易之外幣代號

currencies() 只傳回幣別代號串列, 而 currency_name_dict() 則附上外幣中文名稱字典.

>>> import twder
>>> twder.currencies() 
['USD', 'HKD', 'GBP', 'AUD', 'CAD', 'SGD', 'CHF', 'JPY', 'ZAR', 'SEK', 'NZD', 'THB', 'PHP', 'IDR', 'EUR', 'KRW', 'VND', 'MYR', 'CNY']
>>> twder.currency_name_dict() 
{'USD': '美金 (USD)', 'HKD': '港幣 (HKD)', 'GBP': '英鎊 (GBP)', 'AUD': '澳幣 (AUD)', 'CAD': '加拿大幣 (CAD)', 'SGD': '新加坡幣 (SGD)', 'CHF': '瑞士法郎 (CHF)', 'JPY': '日圓 (JPY)', 'ZAR': '南非幣 (ZAR)', 'SEK': '瑞典幣 (SEK)', 'NZD': '紐元 (NZD)', 'THB': '泰幣 (THB)', 'PHP': '菲國比索 (PHP)', 'IDR': '印尼幣 (IDR)', 'EUR': '歐元 (EUR)', 'KRW': '韓元 (KRW)', 'VND': '越南盾 (VND)', 'MYR': '馬來幣 (MYR)', 'CNY': '人民幣 (CNY)'}

可見總共有 19 種可交易外幣


2. 查詢指定貨幣目前的即時報價

>>> twder.now('USD') 
('2018/10/23 16:01', '30.54', '31.23', '30.91', '31.01')

傳回之 tuple 有五個元素 :

(時間, 現金買入, 現金賣出, 即期買入, 即期賣出)


3. 查詢全部貨幣目前的即時報價

>>> twder.now_all() 
{'USD': ('2018/10/23 16:01', '30.54', '31.23', '30.91', '31.01'), 'HKD': ('2018/10/23 16:01', '3.783', '3.999', '3.919', '3.979'), 'GBP': ('2018/10/23 16:01', '39.01', '41.13', '40.01', '40.43'), 'AUD': ('2018/10/23 16:01', '21.55', '22.33', '21.82', '22.05'), 'CAD': ('2018/10/23 16:01', '23.16', '24.07', '23.55', '23.77'), 'SGD': ('2018/10/23 16:01', '21.88', '22.79', '22.37', '22.55'), 'CHF': ('2018/10/23 16:01', '30.29', '31.49', '30.95', '31.24'), 'JPY': ('2018/10/23 16:01', '0.2663', '0.2791', '0.2736', '0.2776'), 'ZAR': ('2018/10/23 16:01', '-', '-', '2.11', '2.19'), 'SEK': ('2018/10/23 16:01', '3.05', '3.57', '3.39', '3.49'), 'NZD': ('2018/10/23 16:01', '19.85', '20.7', '20.23', '20.43'), 'THB': ('2018/10/23 16:01', '0.819', '1.007', '0.9276', '0.9676'), 'PHP': ('2018/10/23 16:01', '0.5018', '0.6348', '-', '-'), 'IDR': ('2018/10/23 16:01', '0.00168', '0.00238', '-', '-'), 'EUR': ('2018/10/23 16:01', '34.69', '36.03', '35.31', '35.71'), 'KRW': ('2018/10/23 16:01', '0.02551', '0.02941', '-', '-'), 'VND': ('2018/10/23 16:01', '0.00095', '0.00145', '-', '-'), 'MYR': ('2018/10/23 16:01', '6.344', '7.974', '-', '-'), 'CNY': ('2018/10/23 16:01', '4.36', '4.522', '4.432', '4.482')}


4. 查詢指定貨幣昨日全部報價

>>> twder.past_day('USD')
[('2018/10/23 09:01:01', '30.52', '31.21', '30.89', '30.99'), ('2018/10/23 09:02:55', '30.525', '31.215', '30.895', '30.995'), ('2018/10/23 09:04:30', '30.535', '31.225', '30.905', '31.005'), ('2018/10/23 09:05:43', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 09:07:02', '30.53', '31.22', '30.9', '31'), ('2018/10/23 09:12:28', '30.525', '31.215', '30.895', '30.995'), ('2018/10/23 09:15:49', '30.525', '31.215', '30.895', '30.995'), ('2018/10/23 09:17:17', '30.52', '31.21', '30.89', '30.99'), ('2018/10/23 09:21:57', '30.515', '31.205', '30.885', '30.985'), ('2018/10/23 09:24:32', '30.51', '31.2', '30.88', '30.98'), ('2018/10/23 09:28:09', '30.515', '31.205', '30.885', '30.985'), ('2018/10/23 09:35:59', '30.51', '31.2', '30.88', '30.98'), ('2018/10/23 09:38:46', '30.51', '31.2', '30.88', '30.98'), ('2018/10/23 09:40:44', '30.51', '31.2', '30.88', '30.98'), ('2018/10/23 09:48:51', '30.52', '31.21', '30.89', '30.99'), ('2018/10/23 09:50:10', '30.525', '31.215', '30.895', '30.995'), ('2018/10/23 09:52:31', '30.525', '31.215', '30.895', '30.995'), ('2018/10/23 09:58:44', '30.525', '31.215', '30.895', '30.995'), ('2018/10/23 10:05:43', '30.525', '31.215', '30.895', '30.995'), ('2018/10/23 10:15:33', '30.52', '31.21', '30.89', '30.99'), ('2018/10/23 10:21:54', '30.525', '31.215', '30.895', '30.995'), ('2018/10/23 10:26:31', '30.525', '31.215', '30.895', '30.995'), ('2018/10/23 10:28:29', '30.53', '31.22', '30.9', '31'), ('2018/10/23 10:30:59', '30.53', '31.22', '30.9', '31'), ('2018/10/23 10:45:50', '30.53', '31.22', '30.9', '31'), ('2018/10/23 10:59:00', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 10:59:46', '30.545', '31.235', '30.915', '31.015'), ('2018/10/23 11:01:47', '30.535', '31.225', '30.905', '31.005'), ('2018/10/23 11:20:48', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 11:36:05', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 11:44:24', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 11:54:21', '30.545', '31.235', '30.915', '31.015'), ('2018/10/23 11:57:05', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 12:21:57', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 12:30:55', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 12:47:50', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 13:06:57', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 13:21:23', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 13:41:52', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 13:52:33', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 13:59:38', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 14:02:10', '30.545', '31.235', '30.915', '31.015'), ('2018/10/23 14:03:06', '30.55', '31.24', '30.92', '31.02'), ('2018/10/23 14:11:39', '30.56', '31.25', '30.93', '31.03'), ('2018/10/23 14:19:02', '30.57', '31.26', '30.94', '31.04'), ('2018/10/23 14:23:07', '30.565', '31.255', '30.935', '31.035'), ('2018/10/23 14:24:54', '30.56', '31.25', '30.93', '31.03'), ('2018/10/23 14:26:10', '30.555', '31.245', '30.925', '31.025'), ('2018/10/23 14:33:29', '30.55', '31.24', '30.92', '31.02'), ('2018/10/23 14:34:03', '30.545', '31.235', '30.915', '31.015'), ('2018/10/23 14:42:06', '30.545', '31.235', '30.915', '31.015'), ('2018/10/23 14:44:05', '30.545', '31.235', '30.915', '31.015'), ('2018/10/23 14:46:08', '30.55', '31.24', '30.92', '31.02'), ('2018/10/23 14:54:23', '30.555', '31.245', '30.925', '31.025'), ('2018/10/23 14:55:39', '30.555', '31.245', '30.925', '31.025'), ('2018/10/23 14:56:56', '30.555', '31.245', '30.925', '31.025'), ('2018/10/23 14:58:30', '30.55', '31.24', '30.92', '31.02'), ('2018/10/23 14:59:08', '30.545', '31.235', '30.915', '31.015'), ('2018/10/23 14:59:53', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 14:59:56', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 15:02:22', '30.545', '31.235', '30.915', '31.015'), ('2018/10/23 15:04:53', '30.55', '31.24', '30.92', '31.02'), ('2018/10/23 15:20:15', '30.55', '31.24', '30.92', '31.02'), ('2018/10/23 15:21:26', '30.545', '31.235', '30.915', '31.015'), ('2018/10/23 15:33:32', '30.545', '31.235', '30.915', '31.015'), ('2018/10/23 15:37:56', '30.545', '31.235', '30.915', '31.015'), ('2018/10/23 15:38:09', '30.545', '31.235', '30.915', '31.015'), ('2018/10/23 15:42:42', '30.54', '31.23', '30.91', '31.01'), ('2018/10/23 16:01:16', '30.54', '31.23', '30.91', '31.01')]


5. 查詢指定貨幣過去半年每天收盤價

>>> twder.past_six_month('USD') 
[('2018/10/23', '30.54', '31.23', '30.91', '31.01'), ('2018/10/22', '30.495', '31.185', '30.865', '30.965'), ('2018/10/19', '30.53', '31.22', '30.9', '31'), ('2018/10/18', '30.53', '31.22', '30.9', '31'), ('2018/10/17', '30.41', '31.1', '30.78', '30.88'), ('2018/10/16', '30.485', '31.175', '30.855', '30.955'), ('2018/10/15', '30.5', '31.19', '30.87', '30.97'), ('2018/10/12', '30.44', '31.13', '30.81', '30.91'), ('2018/10/11', '30.68', '31.37', '31.05', '31.15'), ('2018/10/09', '30.54', '31.23', '30.91', '31.01'), ('2018/10/08', '30.505', '31.195', '30.875', '30.975'), ('2018/10/05', '30.405', '31.095', '30.775', '30.875'), ('2018/10/04', '30.36', '31.05', '30.73', '30.83'), ('2018/10/03', '30.24', '30.93', '30.61', '30.71'), ('2018/10/02', '30.245', '30.935', '30.615', '30.715'), ('2018/10/01', '30.11', '30.8', '30.48', '30.58'), ('2018/09/28', '30.105', '30.795', '30.475', '30.575'), ('2018/09/27', '30.17', '30.86', '30.54', '30.64'), ('2018/09/26', '30.27', '30.96', '30.64', '30.74'), ('2018/09/25', '30.24', '30.93', '30.61', '30.71'), ('2018/09/21', '30.28', '30.97', '30.65', '30.75'), ('2018/09/20', '30.37', '31.06', '30.74', '30.84'), ('2018/09/19', '30.36', '31.05', '30.73', '30.83'), ('2018/09/18', '30.385', '31.075', '30.755', '30.855'), ('2018/09/17', '30.38', '31.07', '30.75', '30.85'), ('2018/09/14', '30.32', '31.01', '30.69', '30.79'), ('2018/09/13', '30.38', '31.07', '30.75', '30.85'), ('2018/09/12', '30.385', '31.075', '30.755', '30.855'), ('2018/09/11', '30.385', '31.075', '30.755', '30.855'), ('2018/09/10', '30.39', '31.08', '30.76', '30.86'), ('2018/09/07', '30.345', '31.035', '30.715', '30.815'), ('2018/09/06', '30.37', '31.06', '30.74', '30.84'), ('2018/09/05', '30.375', '31.065', '30.745', '30.845'), ('2018/09/04', '30.33', '31.02', '30.7', '30.8'), ('2018/09/03', '30.28', '30.97', '30.65', '30.75'), ('2018/08/31', '30.295', '30.985', '30.665', '30.765'), ('2018/08/30', '30.255', '30.945', '30.625', '30.725'), ('2018/08/29', '30.275', '30.965', '30.645', '30.745'), ('2018/08/28', '30.3', '30.99', '30.67', '30.77'), ('2018/08/27', '30.345', '31.035', '30.715', '30.815'), ('2018/08/24', '30.355', '31.045', '30.725', '30.825'), ('2018/08/23', '30.36', '31.05', '30.73', '30.83'), ('2018/08/22', '30.305', '30.995', '30.675', '30.775'), ('2018/08/21', '30.3', '30.99', '30.67', '30.77'), ('2018/08/20', '30.34', '31.03', '30.71', '30.81'), ('2018/08/17', '30.375', '31.065', '30.745', '30.845'), ('2018/08/16', '30.425', '31.115', '30.795', '30.895'), ('2018/08/15', '30.43', '31.12', '30.8', '30.9'), ('2018/08/14', '30.385', '31.075', '30.755', '30.855'), ('2018/08/13', '30.39', '31.08', '30.76', '30.86'), ('2018/08/10', '30.28', '30.97', '30.65', '30.75'), ('2018/08/09', '30.2', '30.89', '30.57', '30.67'), ('2018/08/08', '30.195', '30.885', '30.565', '30.665'), ('2018/08/07', '30.19', '30.88', '30.56', '30.66'), ('2018/08/06', '30.205', '30.895', '30.575', '30.675'), ('2018/08/03', '30.28', '30.97', '30.65', '30.75'), ('2018/08/02', '30.25', '30.94', '30.62', '30.72'), ('2018/08/01', '30.18', '30.87', '30.55', '30.65'), ('2018/07/31', '30.185', '30.875', '30.555', '30.655'), ('2018/07/30', '30.18', '30.87', '30.55', '30.65'), ('2018/07/27', '30.165', '30.855', '30.535', '30.635'), ('2018/07/26', '30.14', '30.83', '30.51', '30.61'), ('2018/07/25', '30.2', '30.89', '30.57', '30.67'), ('2018/07/24', '30.28', '30.97', '30.65', '30.75'), ('2018/07/23', '30.22', '30.91', '30.59', '30.69'), ('2018/07/20', '30.285', '30.975', '30.655', '30.755'), ('2018/07/19', '30.205', '30.895', '30.575', '30.675'), ('2018/07/18', '30.155', '30.845', '30.525', '30.625'), ('2018/07/17', '30.08', '30.77', '30.45', '30.55'), ('2018/07/16', '30.15', '30.84', '30.52', '30.62'), ('2018/07/13', '30.13', '30.82', '30.5', '30.6'), ('2018/07/12', '30.11', '30.8', '30.48', '30.58'), ('2018/07/11', '30.045', '30.735', '30.415', '30.515'), ('2018/07/10', '29.965', '30.655', '30.335', '30.435'), ('2018/07/09', '29.95', '30.64', '30.32', '30.42'), ('2018/07/06', '30.07', '30.76', '30.44', '30.54'), ('2018/07/05', '30.1', '30.79', '30.47', '30.57'), ('2018/07/04', '30.065', '30.755', '30.435', '30.535'), ('2018/07/03', '30.16', '30.85', '30.53', '30.63'), ('2018/07/02', '30.09', '30.78', '30.46', '30.56'), ('2018/06/29', '30.11', '30.652', '30.41', '30.51'), ('2018/06/28', '30.225', '30.767', '30.525', '30.625'), ('2018/06/27', '30.095', '30.637', '30.395', '30.495'), ('2018/06/26', '30.05', '30.592', '30.35', '30.45'), ('2018/06/25', '30.035', '30.577', '30.335', '30.435'), ('2018/06/22', '29.95', '30.492', '30.25', '30.35'), ('2018/06/21', '29.935', '30.477', '30.235', '30.335'), ('2018/06/20', '29.79', '30.332', '30.09', '30.19'), ('2018/06/19', '29.83', '30.372', '30.13', '30.23'), ('2018/06/15', '29.635', '30.177', '29.935', '30.035'), ('2018/06/14', '29.58', '30.122', '29.88', '29.98'), ('2018/06/13', '29.53', '30.072', '29.83', '29.93'), ('2018/06/12', '29.495', '30.037', '29.795', '29.895'), ('2018/06/11', '29.46', '30.002', '29.76', '29.86'), ('2018/06/08', '29.455', '29.997', '29.755', '29.855'), ('2018/06/07', '29.365', '29.907', '29.665', '29.765'), ('2018/06/06', '29.375', '29.917', '29.675', '29.775'), ('2018/06/05', '29.455', '29.997', '29.755', '29.855'), ('2018/06/04', '29.465', '30.007', '29.765', '29.865'), ('2018/06/01', '29.49', '30.032', '29.79', '29.89'), ('2018/05/31', '29.6', '30.142', '29.9', '30'), ('2018/05/30', '29.68', '30.222', '29.98', '30.08'), ('2018/05/29', '29.63', '30.172', '29.93', '30.03'), ('2018/05/28', '29.58', '30.122', '29.88', '29.98'), ('2018/05/25', '29.595', '30.137', '29.895', '29.995'), ('2018/05/24', '29.57', '30.112', '29.87', '29.97'), ('2018/05/23', '29.61', '30.152', '29.91', '30.01'), ('2018/05/22', '29.55', '30.092', '29.85', '29.95'), ('2018/05/21', '29.635', '30.177', '29.935', '30.035'), ('2018/05/18', '29.555', '30.097', '29.855', '29.955'), ('2018/05/17', '29.545', '30.087', '29.845', '29.945'), ('2018/05/16', '29.525', '30.067', '29.825', '29.925'), ('2018/05/15', '29.5', '30.042', '29.8', '29.9'), ('2018/05/14', '29.38', '29.922', '29.68', '29.78'), ('2018/05/11', '29.41', '29.952', '29.71', '29.81'), ('2018/05/10', '29.51', '30.052', '29.81', '29.91'), ('2018/05/09', '29.575', '30.117', '29.875', '29.975'), ('2018/05/08', '29.43', '29.972', '29.73', '29.83'), ('2018/05/07', '29.405', '29.947', '29.705', '29.805'), ('2018/05/04', '29.355', '29.897', '29.655', '29.755'), ('2018/05/03', '29.375', '29.917', '29.675', '29.775'), ('2018/05/02', '29.385', '29.927', '29.685', '29.785'), ('2018/04/30', '29.255', '29.797', '29.555', '29.655'), ('2018/04/27', '29.27', '29.812', '29.57', '29.67'), ('2018/04/26', '29.355', '29.897', '29.655', '29.755'), ('2018/04/25', '29.3', '29.842', '29.6', '29.7'), ('2018/04/24', '29.26', '29.802', '29.56', '29.66')]


6. 查詢指定貨幣於指定月份每天收盤價

>>> twder.specify_month('USD', 2018, 10) 
[('2018/10/23', '30.54', '31.23', '30.91', '31.01'), ('2018/10/22', '30.495', '31.185', '30.865', '30.965'), ('2018/10/19', '30.53', '31.22', '30.9', '31'), ('2018/10/18', '30.53', '31.22', '30.9', '31'), ('2018/10/17', '30.41', '31.1', '30.78', '30.88'), ('2018/10/16', '30.485', '31.175', '30.855', '30.955'), ('2018/10/15', '30.5', '31.19', '30.87', '30.97'), ('2018/10/12', '30.44', '31.13', '30.81', '30.91'), ('2018/10/11', '30.68', '31.37', '31.05', '31.15'), ('2018/10/09', '30.54', '31.23', '30.91', '31.01'), ('2018/10/08', '30.505', '31.195', '30.875', '30.975'), ('2018/10/05', '30.405', '31.095', '30.775', '30.875'), ('2018/10/04', '30.36', '31.05', '30.73', '30.83'), ('2018/10/03', '30.24', '30.93', '30.61', '30.71'), ('2018/10/02', '30.245', '30.935', '30.615', '30.715'), ('2018/10/01', '30.11', '30.8', '30.48', '30.58')]

嗯, 這套件很棒.

購買創見 64GB SD 卡

因換 Note8 手機後移過去的 16GB 卡似乎有點小 (照相因解析度變高, 檔案大小也變大了), 所以上露天買了一張 64GB 的 SD 卡 :

SanDisk 64GB 64G microSDXC【Ultra 80MB/s】micro SD SDXC C10 $277

加運費 55 元共 332 元. 本來有考慮買 128GB, 但想到 INHON 筆電的 D 碟也才 64GB 用到現在還 OK, 手機若用 128GB 不是比筆電記憶體還多嗎? 實在很奇怪. 參考 :

# SanDisk 128GB 128G microSDXC【80MB/s Ultra】microSD SDXC 記憶卡 $568
SanDisk Ultra microSDXC UHS-I (A1) 64G 記憶卡 $890

清朝影像 China Qing Dynasty film

今天在 Youtube 看到名為 "清朝影像" 的一系列黑白紀錄片, 下方的留言說這是清朝時期法國電影公司 GAUMONT 拍攝, 又有人說是法國駐昆明領事方蘇雅所拍攝, 總之是難得的歷史紀錄片, 使吾人得以一窺清朝人的樣子.

清朝影像 China Qing Dynasty film 1
清朝影像 China Qing Dynasty film 2
清朝影像 China Qing Dynasty film 3
清朝影像 China Qing Dynasty film 4
清朝影像 China Qing Dynasty film 5
清朝影像 China Qing Dynasty film 6
清朝影像 China Qing Dynasty film 7
清朝影像 China Qing Dynasty film 8


1

2

3

4

5

6

7

8


默片只能看到動作, 如果當時有錄下聲音就好了. 當時的人交通工具只有走路, 騎馬, 坐轎, 還有人力車, 百餘年後的今天到處是風馳電掣的汽車, 如果真能穿越時空, 他們一定會嚇一跳吧?


2018年10月22日 星期一

Python Fintech 學習筆記 : 從 Yahoo Finance 擷取台股成交資料

上周利用 "Python 網頁程式交易 APP 實作" 第 14 章的程式去下載 Google Finance 台股歷史資料, 測試結果失敗, 因為 Google 已經關閉了歷史資料下載功能, 只提供線型瀏覽. 殘念! 不過另一個抓 Yahoo Finance 的範例程式倒是可用, 以下為測試紀錄.

Python Fintech 測試系列此前文章參考 :

Python Fintech 學習筆記 : 安裝技術指標套件 TA-Lib
Python Fintech 學習筆記 : Google Finance 無法下載歷史資料

"Python 網頁程式交易 APP 實作" 範例程式下載連結 :

https://github.com/letylin/pyptbook

我下載的是第二版範例 :

# 二版課本範例程式.zip

Yahoo 版抓取程式為其中的 E_14_1.py, 但在我的電腦直接執行時卻出現缺少 pymysql, xlrd, fix_yahoo_finance 與 openpyxl 等模組之錯誤訊息, 須用 pip3 安裝 :

D:\Python\E_14_1>pip3 install pymysql
Collecting pymysql
  Downloading https://files.pythonhosted.org/packages/a7/7d/682c4a7da195a678047c8f1c51bb7682aaedee1dca7547883c3993ca9282/PyMySQL-0.9.2-py2.py3-none-any.whl (47kB)
Collecting cryptography (from pymysql)
  Downloading https://files.pythonhosted.org/packages/f1/01/a144ec664d3f9ae5837bd72c4d11bdd2d8d403318898e4092457e8af9272/cryptography-2.3.1-cp36-cp36m-win_amd64.whl (1.3MB)
Requirement already satisfied: six>=1.4.1 in c:\python36\lib\site-packages (from cryptography->pymysql) (1.11.0)
Requirement already satisfied: idna>=2.1 in c:\python36\lib\site-packages (from cryptography->pymysql) (2.6)
Collecting asn1crypto>=0.21.0 (from cryptography->pymysql)
  Downloading https://files.pythonhosted.org/packages/ea/cd/35485615f45f30a510576f1a56d1e0a7ad7bd8ab5ed7cdc600ef7cd06222/asn1crypto-0.24.0-py2.py3-none-any.whl (101kB)
Collecting cffi!=1.11.3,>=1.7 (from cryptography->pymysql)
  Downloading https://files.pythonhosted.org/packages/2f/85/a9184548ad4261916d08a50d9e272bf6f93c54f3735878fbfc9335efd94b/cffi-1.11.5-cp36-cp36m-win_amd64.whl (166kB)
Collecting pycparser (from cffi!=1.11.3,>=1.7->cryptography->pymysql)
  Downloading https://files.pythonhosted.org/packages/68/9e/49196946aee219aead1290e00d1e7fdeab8567783e83e1b9ab5585e6206a/pycparser-2.19.tar.gz (158kB)
Building wheels for collected packages: pycparser
  Running setup.py bdist_wheel for pycparser: started
  Running setup.py bdist_wheel for pycparser: finished with status 'done'
  Stored in directory: C:\Users\Tony\AppData\Local\pip\Cache\wheels\f2\9a\90\de94f8556265ddc9d9c8b271b0f63e57b26fb1d67a45564511
Successfully built pycparser
Installing collected packages: asn1crypto, pycparser, cffi, cryptography, pymysql
Successfully installed asn1crypto-0.24.0 cffi-1.11.5 cryptography-2.3.1 pycparser-2.19 pymysql-0.9.2

D:\Python\E_14_1>pip3 install xlrd
Collecting xlrd
  Downloading https://files.pythonhosted.org/packages/07/e6/e95c4eec6221bfd8528bcc4ea252a850bffcc4be88ebc367e23a1a84b0bb/xlrd-1.1.0-py2.py3-none-any.whl (108kB)
Installing collected packages: xlrd
Successfully installed xlrd-1.1.0

D:\Python\E_14_1>pip3 install fix_yahoo_finance
Collecting fix_yahoo_finance
  Downloading https://files.pythonhosted.org/packages/0a/96/d44330e427f5368cb8abd25997b72956a31b52073d285c4d5cd56e5fdc17/fix-yahoo-finance-0.0.22.tar.gz
Requirement already satisfied: pandas in c:\python36\lib\site-packages (from fix_yahoo_finance) (0.22.0)
Requirement already satisfied: numpy in c:\python36\lib\site-packages (from fix_yahoo_finance) (1.14.1)
Requirement already satisfied: requests in c:\python36\lib\site-packages (from fix_yahoo_finance) (2.18.4)
Collecting multitasking (from fix_yahoo_finance)
  Downloading https://files.pythonhosted.org/packages/ac/1a/0750416c5e3683d170757e423f097fdf78ceb9ccdc65658b24341664e53e/multitasking-0.0.7.tar.gz
Requirement already satisfied: python-dateutil>=2 in c:\python36\lib\site-packages (from pandas->fix_yahoo_finance) (2.6.1)
Requirement already satisfied: pytz>=2011k in c:\python36\lib\site-packages (from pandas->fix_yahoo_finance) (2018.3)
Requirement already satisfied: chardet<3 .1.0="">=3.0.2 in c:\python36\lib\site-packages (from requests->fix_yahoo_finance) (3.0.4)
Requirement already satisfied: certifi>=2017.4.17 in c:\python36\lib\site-packages (from requests->fix_yahoo_finance) (2018.4.16)
Requirement already satisfied: urllib3<1 .23="">=1.21.1 in c:\python36\lib\site-packages (from requests->fix_yahoo_finance) (1.22)
Requirement already satisfied: idna<2 .7="">=2.5 in c:\python36\lib\site-packages (from requests->fix_yahoo_finance) (2.6)
Requirement already satisfied: six>=1.5 in c:\python36\lib\site-packages (from python-dateutil>=2->pandas->fix_yahoo_finance) (1.11.0)
Building wheels for collected packages: fix-yahoo-finance, multitasking
  Running setup.py bdist_wheel for fix-yahoo-finance: started
  Running setup.py bdist_wheel for fix-yahoo-finance: finished with status 'done'
  Stored in directory: C:\Users\Tony\AppData\Local\pip\Cache\wheels\2c\ca\ce\218a19aaecf63fd74c75d6a6772b1a799fa05826d8762bfd83
  Running setup.py bdist_wheel for multitasking: started
  Running setup.py bdist_wheel for multitasking: finished with status 'done'
  Stored in directory: C:\Users\Tony\AppData\Local\pip\Cache\wheels\41\e4\48\af808a1c57f43f104042abdaf80fa623ab213ca0268ba4189c
Successfully built fix-yahoo-finance multitasking
Installing collected packages: multitasking, fix-yahoo-finance
Successfully installed fix-yahoo-finance-0.0.22 multitasking-0.0.7

D:\Python\E_14_1>pip3 install openpyxl 
Collecting openpyxl
  Downloading https://files.pythonhosted.org/packages/e5/0a/e0a095149a23cedd9c8db6cdde2af7f82105e219e14edea0c31a19aeff9e/openpyxl-2.5.8.tar.gz (1.9MB)
Collecting jdcal (from openpyxl)
  Downloading https://files.pythonhosted.org/packages/a0/38/dcf83532480f25284f3ef13f8ed63e03c58a65c9d3ba2a6a894ed9497207/jdcal-1.4-py2.py3-none-any.whl
Collecting et_xmlfile (from openpyxl)
  Downloading https://files.pythonhosted.org/packages/22/28/a99c42aea746e18382ad9fb36f64c1c1f04216f41797f2f0fa567da11388/et_xmlfile-1.0.1.tar.gz
Building wheels for collected packages: openpyxl, et-xmlfile
  Running setup.py bdist_wheel for openpyxl ... done
  Stored in directory: C:\Users\Tony Huang\AppData\Local\pip\Cache\wheels\3f\37\28\5ab3dffb7ff261e6fa21455ec9d157f95958e818c6b89f024c
  Running setup.py bdist_wheel for et-xmlfile ... done
  Stored in directory: C:\Users\Tony Huang\AppData\Local\pip\Cache\wheels\2a\77\35\0da0965a057698121fc7d8c5a7a9955cdbfb3cc4e2423cad39
Successfully built openpyxl et-xmlfile
Installing collected packages: jdcal, et-xmlfile, openpyxl
Successfully installed et-xmlfile-1.0.1 jdcal-1.4 openpyxl-2.5.8

另外還需要 pandas 模組處理 DataFrame 物件以儲存所擷取之資料, 需用 pip3 安裝 pandas, 但我電腦之前已安裝過, 因此不會有找不到 pandas 問題.

書中範例 E_14_1.py 中我只改了要抓取的起迄日期 :

    start = datetime.datetime(2017,10,1)
    end = datetime.datetime(2018,10,19)

執行結果如下 :

D:\Python\E_14_1>python E_14_1.py 

[*********************100%***********************]  1 of 1 downloaded
           Date       Open       High        Low      Close    Volume
0    2007-10-01  45.558601  46.860199  45.233200  45.558601  16490774
1    2007-10-02  45.558601  46.372101  44.907700  45.151798  10906577
2    2007-10-03  45.151798  45.883999  43.524700  44.419601  17748233
3    2007-10-04  43.931499  43.931499  41.409500  41.979000  22471999
4    2007-10-05  41.816200  43.036598  41.490799  42.792500  18974962
5    2007-10-08  43.117901  43.768799  42.711201  43.524700   9772038
6    2007-10-09  43.117901  43.199299  41.653500  43.199299   8877189
7    2007-10-11  43.199299  46.209400  43.199299  46.209400  38372764
8    2007-10-12  46.209400  47.917900  45.151798  45.151798  25902660
9    2007-10-15  45.558601  46.778900  44.500900  45.151798  12476249
10   2007-10-16  44.419601  44.826401  43.850101  44.175499  10468987
11   2007-10-17  43.931499  44.338200  42.873901  43.850101  10294442
12   2007-10-18  43.850101  44.744999  43.117901  44.338200   7405852
13   2007-10-19  44.338200  44.744999  43.850101  44.012798   4603306
14   2007-10-22  42.304401  42.467098  41.490799  42.223000  11138894
15   2007-10-23  42.223000  42.711201  41.979000  42.385700   8129844
16   2007-10-24  42.711201  43.850101  41.897598  41.897598  10767679
17   2007-10-25  42.385700  43.036598  41.897598  41.897598   7598835
18   2007-10-26  42.304401  43.524700  42.223000  43.524700  11097101
19   2007-10-29  43.768799  44.744999  43.117901  44.744999  11185603
20   2007-10-30  44.582298  44.582298  43.605999  43.687401   5092522
21   2007-10-31  43.687401  43.768799  42.060299  42.629799   8389202
22   2007-11-01  43.280602  43.443298  40.514599  40.921299  12767567
23   2007-11-02  40.270500  40.270500  38.968800  39.050201  16188394
24   2007-11-05  38.684101  38.684101  38.236599  38.562099  15356235
25   2007-11-06  39.457001  39.863701  38.765499  39.457001  14665432
26   2007-11-07  40.270500  40.473900  39.619701  39.945099  11753487
27   2007-11-08  39.212898  39.212898  38.318001  38.846802   7270642
28   2007-11-09  38.846802  39.538300  37.870602  39.538300   9424178
29   2007-11-12  38.236599  38.562099  36.772301  37.016300  10525529
...         ...        ...        ...        ...        ...       ...
2442 2017-08-21  27.200001  27.350000  27.049999  27.250000   1832333
2443 2017-08-22  27.299999  27.400000  27.250000  27.299999   1246889
2444 2017-08-23  27.400000  27.400000  27.100000  27.250000   1961836
2445 2017-08-24  27.200001  27.450001  27.150000  27.450001   1893695
2446 2017-08-25  27.500000  27.549999  27.350000  27.350000   2735921
2447 2017-08-28  27.450001  27.450001  27.350000  27.400000   2451080
2448 2017-08-29  27.400000  27.400000  27.250000  27.400000   3203620
2449 2017-08-30  26.600000  27.000000  26.600000  26.900000   3838522
2450 2017-08-31  27.000000  27.350000  27.000000  27.350000   3695972
2451 2017-09-01  27.400000  27.600000  27.299999  27.450001   3451475
2452 2017-09-04  27.299999  27.450001  27.150000  27.450001   1711265
2453 2017-09-05  27.450001  27.600000  27.299999  27.450001   2508250
2454 2017-09-06  27.350000  27.350000  27.100000  27.200001   2100535
2455 2017-09-07  27.200001  27.200001  26.950001  27.100000   2700381
2456 2017-09-08  27.100000  27.400000  27.000000  27.299999   2028684
2457 2017-09-11  27.299999  27.350000  27.100000  27.200001   1863056
2458 2017-09-12  27.250000  27.250000  27.049999  27.150000   1988872
2459 2017-09-13  27.100000  27.100000  26.950001  26.950001   1732912
2460 2017-09-14  26.799999  27.250000  26.799999  27.250000   1714303
2461 2017-09-15  27.250000  27.500000  27.000000  27.500000   3562428
2462 2017-09-18  27.500000  27.650000  27.299999  27.650000   3323904
2463 2017-09-19  27.700001  27.700001  27.450001  27.600000   2012329
2464 2017-09-20  27.299999  27.600000  27.250000  27.549999   1858452
2465 2017-09-21  27.500000  27.500000  27.200001  27.250000   1967469
2466 2017-09-22  27.200001  27.200001  26.950001  26.950001   2382746
2467 2017-09-25  26.950001  27.450001  26.850000  27.350000   2749546
2468 2017-09-26  27.400000  27.400000  27.049999  27.250000   1039137
2469 2017-09-27  27.049999  27.200001  27.000000  27.049999   1616491
2470 2017-09-28  27.049999  27.100000  26.650000  26.700001   4079153
2471 2017-09-29  26.700001  26.950001  26.650000  26.750000   1903258

[2472 rows x 6 columns]
C:\Python36\lib\site-packages\pandas\io\excel.py:784: DeprecationWarning: Call to deprecated function remove_sheet (Use wb.remove(worksheet) or del wb[sheetname]).
  self.book.remove_sheet(self.book.worksheets[0])
程式執行時間 = 5秒

如果擷取失敗會出現下列訊息 :

D:\Python\test\E_14_1>python E_14_1.py
[*********************100%***********************]  1 of 1 downloaded
zero-size array to reduction operation maximum which has no identity
Traceback (most recent call last):
  File "E_14_1.py", line 33, in <module>
    df = main(stkno,start,end) #呼叫主程式
  File "E_14_1.py", line 21, in main
    RowData =gf.getstock(True)
  File "D:\Python\test\E_14_1\ProgramTrade\class_GetYahooFinance.py", line 54, in getstock
    if  len(Data)   != 0:        #有取得資料才進行轉換
UnboundLocalError: local variable 'Data' referenced before assignment


範例程式 E_14_1.py 將擷取網頁與儲存等功能寫成 GetYahooFinance.py 類別, 然後在
E_14_1.py 中建立物件來抓取網頁資料, 這使得程式閱讀起來很彆扭.  事實上此程式是利用第三方模組 fix_yahoo_finance 與 pandas_datareader 來抓資料, 垂回 Pandas 的 DataFrame 物件.

我將其中的核心部分改寫成函數 get_yahoo_twstock(), 如下列範例所示 :

import datetime
import pandas as pd
import fix_yahoo_finance as yf
from pandas_datareader import data as pdr

def get_yahoo_twstock(stock_no, start_date, end_date):
    start=datetime.datetime.strptime(start_date,'%Y%m%d')
    end=datetime.datetime.strptime(end_date,'%Y%m%d')
    yf.pdr_override()
    df=pdr.get_data_yahoo(stock_no + '.tw', start, end)
    del df['Adj Close']
    df=df.reset_index()
    return df

stock_no='0050'
start_date='20170101'
end_date='20181022'
data=get_yahoo_twstock(stock_no, start_date, end_date)
print(data)
print(type(data))

注意, get_yahoo_twstock() 的三個參數都是字串, 起訖日期在函數裡面要用 datetime.datetime.strptime() 將字串轉成 datetime.datetime 物件. 

執行結果如下 :

>>> %Run test.py

[                       0%                       ]
[*********************100%***********************]  1 of 1 downloaded
        Date       Open       High        Low      Close    Volume
0 2018-10-01  86.900002  87.400002  86.900002  87.349998   5912234
1 2018-10-02  87.250000  87.250000  86.000000  86.050003   5434332
2 2018-10-03  86.050003  86.300003  85.800003  85.949997   4369690
3 2018-10-04  85.699997  85.699997  84.800003  84.900002   9736968
4 2018-10-05  84.500000  84.500000  83.000000  83.449997  26744096
5 2018-10-08  82.949997  83.400002  82.500000  83.000000  46512204
6 2018-10-09  83.050003  83.400002  82.800003  83.250000  20530897
7 2018-10-11  80.150002  80.150002  77.349998  77.400002  68551624
<class 'pandas.core.frame.DataFrame'>

可見它沒辦法抓到今天為止的歷史資料, 只能到 11 天前左右, 而且有時候會執行失敗, 但這樣總比 Google Finance 完全不能下載歷史資料好吧! 就是沒辦法抓到今天為止的歷史資料有點可惜, 只能用在回測.

購買小米手環 3

因我的小米手環 2 螢幕變暗, 只有在黑暗時才能看清, 燈光亮的地方有帶跟沒帶是一樣的. 參考 :

小米手環 2 亮度變暗問題

剛好同事與清潔公司打掃的阿桑也要買,找到露天一個三件免運的賣家, 比官網多送一個錶帶 + 2 個保護貼 :

【送5大好禮 台灣公司貨】 小米手環3 繁體版本 運動 智慧型手錶 小米 米家 智慧手錶 智慧手環 生日 $839

839*3=2517 元算整數 2510 元. 

2018年10月21日 星期日

2018 年第 42 周記事

周四晚上接到來自台北師大夜市附近一家文具五金行店員的電話, 說我們家小朋友的手機遺失在他們店裡, 原先以為是詐騙電話, 詢問了緣由才知是真的, 姊姊可能去文具行買東西把手機掉在店裡了, 還好手機沒設密碼, 店員從通訊錄找到爸比就撥給我了. 但問題是我要怎麼通知姊姊呢? 忘了叫她留宿舍或室友連絡電話了. 水某說在 LINE 群組留言啊! 我說沒手機怎麼看 LINE 啊! 後來才知姐姐比較常用 PC 版的 LINE, 但我沒用過電腦版, 於是又打電話給文具行店員, 請他找看看手機 LINE 群組有沒有師大班級群組, 請他在上面留言, 這樣室友看到就會通知她了. 半小時後姊姊留言說手機已經拿回來了.

週五晚上姊姊回到高雄, 距離暑假期間上去籌備迎新事宜到現在已兩個月了, 這次坐台鐵要花四個多小時在車上, 實在太花時間, 她同學五點出發七點半就到家, 姊姊四點出發十點才到, 以後都訂高鐵好了, 時間比較值錢.

週五小舅與小舅媽去琉球玩, 晚上小舅用 LINE 電話告訴我說週六回鄉下時去菜園看看番茄園最後一排有無蟲害, 因週四他發現突然出現蟲蟲大軍, 不僅吃掉剛茁壯的高麗菜, 連小番茄也遭殃, 他已經噴了低劑量農藥防治. 昨日下午回到鄉下趕緊衝去菜園, 果然還是有蟲, 我用夾子一隻一隻地抓, 大概抓了 20 隻之多 :




本周 "百日的郎君" 進度已到 16 集, 左相大人知道世子還活著但失憶後趕去松州縣把他請回, 阻止了西元大君的冊封大典, 想利用他失憶重新建立與世子關係, 讓懷孕的世子嬪順利生下世孫, 使其權力能繼續延續下去. 看起來世子應該已經會恢復記憶, 他假裝還在失憶中是否在尋找機會扳倒左相呢? 少了鄉居的平民生活劇情感覺笑點變少了. 另外, "我身後的陶斯" 中, 由於 J 國際的陳容泰查到陶斯的假身分王正南竟然是竊盜犯, 他將資料交給高愛琳後導致陶斯失去保母的工作, 但聰明的高愛琳用了一些小技巧輕易揭開 King's Bag 其實是情報局掩護站的外衣, 陶斯竟然是情報員! 陳容泰的老闆發現 J 國際已經暴露身分, 決定清除他, 逃命中的陳容泰綁架高愛琳, 要求陶斯去他辦公室拿沙漏來換, 結果被金世英次長掌握行蹤追捕, 在大橋上被攔截, 陶斯意外發現魔法師 K 竟然也在金世英攔截隊伍中, 急忙跳下大橋, 但被 K 擊中手臂 ....

本周雖然仍在追劇, 但也認真的進行 Python 學習,  主要是測試透過 Gmail 傳送郵件之功能, 以及從 Google Finance 擷取台股收盤資料的方法, 可惜的是 Google 現在已經不讓人抓歷史資料了, 但 Yahoo 可以. 周末已開始了 Python 資料科學的學習, 從 Numpy 開始.