[Python] 下載證交所影音檔

由於家裡網路真的很慢,看了總是卡得要死根本看不下去,

所以想要把影片抓下來看,但又沒辦法直接下載,

所以拖了很久都沒有看,不過這些天突然想到爬蟲應該會有幫助,

所以就找了些教學,雖然跟證交所的方法不同,

但這個教學還是對我來說非常有用:大數學堂

我透過 影片 -> 右鍵 檢查 -> Network 去看封包,

才發現現在根本直接這樣就可以看封包,好像不需要Wireshark呢,

由於他的影片被切成非常非常多的小片段,

我要這樣一個一個下載也真的太麻煩

http://webprovod.twse.com.tw/vod/_definst_/A3/E1/93EBE39C8605-5ECDEA97-CDE7-11E4-A3E1/93EBE39C8605-5ECDEA97-CDE7-11E4-A3E1_ATM.mp4/media_n.ts?memberId=

這是從 Network 看到的實際影音檔的URL,切成 n 個小檔,

先試著透過 python 抓回檔案,就參考這篇

但我不知道怎麼把 blob 的網址用 python 轉成直接可以下載的位置,

只好退而求其次找出總共切成多少個檔案,

透過迴圈抓再把全部都 append 在一起,

看到很多個 list 檔案就都下載回來看看,

發現 chunklist 就是所有 .ts 檔的 list,

於是就先抓 list 再從中找到最後的 .ts 數字,

再全部接在一起變成一個檔案,

只是檔名我是用檔案的順序數字當檔名,

還要再手動把影片改成相對應的檔名,

而 list.txt 是所有我要下載的檔案的 chunklist URL,

list.txt:

http://webprovod.twse.com.tw/vod/_definst_/83/84/EDD960F18933-B65C93CC-87CA-11E2-8384/EDD960F18933-B65C93CC-87CA-11E2-8384_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/83/84/EDD960F18933-0E727C6D-87CF-11E2-8384/EDD960F18933-0E727C6D-87CF-11E2-8384_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/83/84/EDD960F18933-A18D5F0E-87D0-11E2-8384/EDD960F18933-A18D5F0E-87D0-11E2-8384_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/83/84/EDD960F18933-4674F5BF-87D5-11E2-8384/EDD960F18933-4674F5BF-87D5-11E2-8384_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/97/A8/793F3CEAC1CA-3654196E-8B7D-11E2-97A8/793F3CEAC1CA-3654196E-8B7D-11E2-97A8_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/83/84/EDD960F18933-4EC1FE51-8A27-11E2-8384/EDD960F18933-4EC1FE51-8A27-11E2-8384_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/83/84/EDD960F18933-89FF6612-8A27-11E2-8384/EDD960F18933-89FF6612-8A27-11E2-8384_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/86/68/697D81F4E645-B3B54F04-B4B9-11E3-8668/697D81F4E645-B3B54F04-B4B9-11E3-8668_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/86/68/697D81F4E645-8E3C1AA5-B4C4-11E3-8668/697D81F4E645-8E3C1AA5-B4C4-11E3-8668_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/86/68/697D81F4E645-CB0E8367-B54A-11E3-8668/697D81F4E645-CB0E8367-B54A-11E3-8668_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/86/68/697D81F4E645-13B56099-B558-11E3-8668/697D81F4E645-13B56099-B558-11E3-8668_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/86/68/697D81F4E645-5EE1795C-B576-11E3-8668/697D81F4E645-5EE1795C-B576-11E3-8668_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/A9/54/D357CDC6DA76-0E31956A-16CB-11E4-A954/D357CDC6DA76-0E31956A-16CB-11E4-A954_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/A9/54/D357CDC6DA76-0F51D27B-16CB-11E4-A954/D357CDC6DA76-0F51D27B-16CB-11E4-A954_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/A9/54/D357CDC6DA76-157C09ED-16CB-11E4-A954/D357CDC6DA76-157C09ED-16CB-11E4-A954_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/A9/54/D357CDC6DA76-16F144CE-16CB-11E4-A954/D357CDC6DA76-16F144CE-16CB-11E4-A954_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/A3/E1/93EBE39C8605-5ECDEA97-CDE7-11E4-A3E1/93EBE39C8605-5ECDEA97-CDE7-11E4-A3E1_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/BD/99/195AEC8E3B81-132F3247-2A92-11E5-BD99/195AEC8E3B81-132F3247-2A92-11E5-BD99_ATM.mp4/chunklist.m3u8?memberId=
http://webprovod.twse.com.tw/vod/_definst_/BD/99/195AEC8E3B81-30F62EA9-2A92-11E5-BD99/195AEC8E3B81-30F62EA9-2A92-11E5-BD99_ATM.mp4/chunklist.m3u8?memberId=

Get_Video.py:

# -*- coding:utf-8 -*-
# file: Get_Video.py
#

import urllib.request as request

name = 0
# 透過播放影片 -> 右鍵 檢查 -> Network -> 找到 chunklist
# 把要抓的影片的 chunklist URL 全部加入 list.txt 中
for line in open("list.txt",'rU'): # 一次讀一行
    # fake user agent of Safari
    fake_useragent = 'Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25'
    req_list = request.Request(line, headers={'User-Agent': fake_useragent})
    list_file = request.urlopen(req_list)
    # 從 chunk list 裡面抓出切割的影音檔個數
    end_num = int(str(list_file.read()).split("media_")[-1].split(".")[0])
    for index in range(0, end_num + 1):
        # 從 chunk list 的 Request URL 轉成要下載的多個 .ts 檔案 URL
        url = "/".join(line.split("/")[:-1]) + "/media_" + str(index) + ".ts?memberId="
        print(url)
        req = request.Request(url, headers={'User-Agent': fake_useragent})
        video_file = request.urlopen(req)
        # 把多個 .ts 最終存成一個 .mp4
        with open(str(name) + ".mp4",'ab') as file:
            file.write(video_file.read())
    # 檔名用數字替代 下載完需手動改
    name += 1

而最後會下載成 0.mp4, 1.mp4.....等等,

就是照 list.txt 的順序,還要手動改檔名,

應該可以更簡單的自動列出 list.txt,

只是我只有要看這麼幾部 mp4,

只要可以自動下載回 local 端就已經解決我的需求了,

所以就沒有再細究下去了

[Python] multiprocessing Pool() 用法

由於有想把sqlite資料庫切割開來,同時存取多個資料庫的想法,
所以就開始來研究multiprocessing的用法

我主要參考的是這篇,裡面提到的非常多,搜尋multiprocessing就好,
即便是multiprocessing也有很多用法,我有試過Pool()跟Process(),

但由於我需要抓回傳值,我也沒有需要晚點執行process的需求,
所以Pool()的方法比較適合我,所以下面都只談Pool(),

首先是Pool().map()的用法,我這邊只是單純轉一手,
用法就是把參數變成一個list,就可以同時執行同個function多個參數,
function我都擺在taiwan_xbrl_common.py裡面,

function的內容:

def pool_map(function_name, arguments_list, processes = multiprocessing.cpu_count()):
    #
    # Description:
    #   透過 multiprocessing.Pool().map 去同時跑多個 process
    #   Pool(processes = cpu_count())
    #   沒有給初始值預設是給 cpu 數量的執行緒個數
    #
    #   平常使用上要用 [] 框起來 return_values 才能抓到所有的值
    #   不然會只有存到最後一個值 但函數的回傳值本來就是 list 形式
    #   return_values = [Pool().map(function_name, arguments_list)]
    #
    # Arguments:
    #   function_name: 要同時執行多個執行緒的 function 名稱
    #                  只接收只有一個參數的 function
    #   arguments_list: 要丟進 function 裡面的唯一參數組合而成的 list
    #
    # Returns:
    #   回傳每個執行緒的 return value list 集合
    #
    return multiprocessing.Pool(processes).map(function_name, arguments_list)
使用function的方式:

import taiwan_xbrl_common as common

arguments_list1 = ['2009Q1', '2009Q2', '2009Q3', '2009Q4', \
                  '2010Q1', '2010Q2', '2010Q3', '2010Q4', \
                  '2011Q1', '2011Q2', '2011Q3', '2011Q4', \
                  '2012Q1', '2012Q2', '2012Q3', '2012Q4', \
                  '2013Q1', '2013Q2', '2013Q3', '2013Q4', \
                  '2014Q1', '2014Q2', '2014Q3', '2014Q4', \
                  '2015Q1', '2015Q2', '2015Q3', '2015Q4', \
                  '2016Q1', '2016Q2', '2016Q3', '2016Q4',]

def test1(arg1):
    print(current_process().name)
    return arg1 + "test"

result1 = common.pool_map(test1, arguments_list1)
print(result1)
執行結果:

SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-2
SpawnPoolWorker-3
SpawnPoolWorker-2
SpawnPoolWorker-3
['2009Q1test', '2009Q2test', '2009Q3test', '2009Q4test', '2010Q1test', '2010Q2te
st', '2010Q3test', '2010Q4test', '2011Q1test', '2011Q2test', '2011Q3test', '2011
Q4test', '2012Q1test', '2012Q2test', '2012Q3test', '2012Q4test', '2013Q1test', '
2013Q2test', '2013Q3test', '2013Q4test', '2014Q1test', '2014Q2test', '2014Q3test
', '2014Q4test', '2015Q1test', '2015Q2test', '2015Q3test', '2015Q4test', '2016Q1
test', '2016Q2test', '2016Q3test', '2016Q4test']

由於程式碼很簡單,所以也不太會用到不同的process去執行,
接下來就看看Pool().apply_async()怎麼實現不定數量參數去執行多process,
而且還要把所有resault全接回來,Pool(),map()只能傳一個參數,
Process()又還要透過Pipe()傳接參數,實在不是很單純,
我也不想改要跑的function還要在裡面傳接,
所以似乎就只有Pool().apply_async()比較實用了,

下面就直接先來看看function內容:

def pool_apply_async(function_name, *arguments_lists, processes = multiprocessing.cpu_count()):
    #
    # Description:
    #   透過 multiprocessing.Pool().apply_async 去同時跑多個 process
    #   Pool() 沒有給初始值預設是給 cpu 數量的 process 個數
    #   參數透過迴圈加進 Tuple 傳給 args
    #   process 回傳的 Queue list 個別提取出來再轉成 list 回傳
    #
    # Arguments:
    #   function_name: 要同時執行多個執行緒的 function 名稱
    #   *arguments_lists: 不定個數的參數 可同時丟入多個參數 list
    #                     每個參數 list 的內容數量要相同
    #
    # Returns:
    #   回傳每個執行緒的 return value list 集合
    #   假如有產生錯誤就回傳 None
    #
    argument_count = len(arguments_lists) # 參數個數
    each_argument_count = len(arguments_lists[0]) # 每個參數 list 的數量
    queue_list = [] # 建立一個空白參數 list
    for argument_number in range(argument_count):
        if each_argument_count != len(arguments_lists[argument_number]):# 每個參數的數量應該都要相同
            print("Every argument list length not sycn!")
            return None
        queue_list += 'None' # 先丟個隨意的字串進 list 佔位置 因為 Queue元素不能直接串接產生
        queue_list[argument_number] = multiprocessing.Queue() # 把 Queue 元件丟進 list
        for argument in arguments_lists[argument_number]: # 把所有參數丟進 Queue 裡面
            queue_list[argument_number].put(argument)
    # 產生一個 Pool() 物件 並指定產生的 process 個數 預設為 CPU 個數
    pool = multiprocessing.Pool(processes)
    # 同一個 Pool() 物件在一個時間同時處理設定個數的 process 其餘在 Queue 中等待
    # 假如宣告多個 Pool() 物件會造成同一時間執行過多 process 反而造成效率低落
    # 參數是透過迴圈來把多個參數 Queue 個別 get 出來變成 Tuple 傳進 args
    # 回傳值則是透過 [] 把所有回傳的 Queue list 起來
    values = [pool.apply_async(function_name, args = ([queue_list[argument_number].get() for argument_number in range(argument_count)])) for index in range(each_argument_count)]
    # 回傳的是 Queue list 需要個別 get 出再用 [] list 起來
    return [value.get() for value in values]
使用function的方式:

import taiwan_xbrl_common as common

arguments_list1 = ['2009Q1', '2009Q2', '2009Q3', '2009Q4', \
                  '2010Q1', '2010Q2', '2010Q3', '2010Q4', \
                  '2011Q1', '2011Q2', '2011Q3', '2011Q4', \
                  '2012Q1', '2012Q2', '2012Q3', '2012Q4', \
                  '2013Q1', '2013Q2', '2013Q3', '2013Q4', \
                  '2014Q1', '2014Q2', '2014Q3', '2014Q4', \
                  '2015Q1', '2015Q2', '2015Q3', '2015Q4', \
                  '2016Q1', '2016Q2', '2016Q3', '2016Q4',]

arguments_list2 = ['2009Q1', '2009Q2', '2009Q3', '2009Q4', \
                  '2010Q1', '2010Q2', '2010Q3', '2010Q4', \
                  '2011Q1', '2011Q2', '2011Q3', '2011Q4', \
                  '2012Q1', '2012Q2', '2012Q3', '2012Q4', \
                  '2013Q1', '2013Q2', '2013Q3', '2013Q4', \
                  '2014Q1', '2014Q2', '2014Q3', '2014Q4', \
                  '2015Q1', '2015Q2', '2015Q3', '2015Q4', \
                  '2016Q1', '2016Q2', '2016Q3', '2016Q4',]

arguments_list3 = ['2009Q1', '2009Q2', '2009Q3', '2009Q4', \
                  '2010Q1', '2010Q2', '2010Q3', '2010Q4', \
                  '2011Q1', '2011Q2', '2011Q3', '2011Q4', \
                  '2012Q1', '2012Q2', '2012Q3', '2012Q4', \
                  '2013Q1', '2013Q2', '2013Q3', '2013Q4', \
                  '2014Q1', '2014Q2', '2014Q3', '2014Q4', \
                  '2015Q1', '2015Q2', '2015Q3', '2015Q4', \
                  '2016Q1', '2016Q2', '2016Q3', '2016Q4',]

arguments_list4 = ['2009Q1', '2009Q2', '2009Q3', '2009Q4', \
                  '2010Q1', '2010Q2', '2010Q3', '2010Q4', \
                  '2011Q1', '2011Q2', '2011Q3', '2011Q4', \
                  '2012Q1', '2012Q2', '2012Q3', '2012Q4', \
                  '2013Q1', '2013Q2', '2013Q3', '2013Q4', \
                  '2014Q1', '2014Q2', '2014Q3', '2014Q4', \
                  '2015Q1', '2015Q2', '2015Q3', '2015Q4', \
                  '2016Q1', '2016Q2', '2016Q3', '2016Q4',]

def test2(arg1, arg2):
    print(current_process().name)
    return arg1 + arg2

def test3(arg1, arg2, arg3):
    print(current_process().name)
    return arg1 + arg2 + arg3

def test4(arg1, arg2, arg3, arg4):
    print(current_process().name)
    return arg1 + arg2 + arg3 + arg4

result2 = common.pool_apply_async(test2, arguments_list1, arguments_list2)
print(result2)
result3 = common.pool_apply_async(test3, arguments_list1, arguments_list2, arguments_list3)
print(result3)
result4 = common.pool_apply_async(test4, arguments_list1, arguments_list2, arguments_list3, arguments_list4)
print(result4)
執行結果:

SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
SpawnPoolWorker-1
['2009Q12009Q1', '2009Q22009Q2', '2009Q32009Q3', '2009Q42009Q4', '2010Q12010Q1'
 '2010Q22010Q2', '2010Q32010Q3', '2010Q42010Q4', '2011Q12011Q1', '2011Q22011Q2'
 '2011Q32011Q3', '2011Q42011Q4', '2012Q12012Q1', '2012Q22012Q2', '2012Q32012Q3'
 '2012Q42012Q4', '2013Q12013Q1', '2013Q22013Q2', '2013Q32013Q3', '2013Q42013Q4'
 '2014Q12014Q1', '2014Q22014Q2', '2014Q32014Q3', '2014Q42014Q4', '2015Q12015Q1'
 '2015Q22015Q2', '2015Q32015Q3', '2015Q42015Q4', '2016Q12016Q1', '2016Q22016Q2'
 '2016Q32016Q3', '2016Q42016Q4']
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
SpawnPoolWorker-5
['2009Q12009Q12009Q1', '2009Q22009Q22009Q2', '2009Q32009Q32009Q3', '2009Q42009Q
2009Q4', '2010Q12010Q12010Q1', '2010Q22010Q22010Q2', '2010Q32010Q32010Q3', '201
Q42010Q42010Q4', '2011Q12011Q12011Q1', '2011Q22011Q22011Q2', '2011Q32011Q32011Q
', '2011Q42011Q42011Q4', '2012Q12012Q12012Q1', '2012Q22012Q22012Q2', '2012Q3201
Q32012Q3', '2012Q42012Q42012Q4', '2013Q12013Q12013Q1', '2013Q22013Q22013Q2', '2
13Q32013Q32013Q3', '2013Q42013Q42013Q4', '2014Q12014Q12014Q1', '2014Q22014Q2201
Q2', '2014Q32014Q32014Q3', '2014Q42014Q42014Q4', '2015Q12015Q12015Q1', '2015Q22
15Q22015Q2', '2015Q32015Q32015Q3', '2015Q42015Q42015Q4', '2016Q12016Q12016Q1',
2016Q22016Q22016Q2', '2016Q32016Q32016Q3', '2016Q42016Q42016Q4']
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
SpawnPoolWorker-12
['2009Q12009Q12009Q12009Q1', '2009Q22009Q22009Q22009Q2', '2009Q32009Q32009Q3200
Q3', '2009Q42009Q42009Q42009Q4', '2010Q12010Q12010Q12010Q1', '2010Q22010Q22010Q
2010Q2', '2010Q32010Q32010Q32010Q3', '2010Q42010Q42010Q42010Q4', '2011Q12011Q12
11Q12011Q1', '2011Q22011Q22011Q22011Q2', '2011Q32011Q32011Q32011Q3', '2011Q4201
Q42011Q42011Q4', '2012Q12012Q12012Q12012Q1', '2012Q22012Q22012Q22012Q2', '2012Q
2012Q32012Q32012Q3', '2012Q42012Q42012Q42012Q4', '2013Q12013Q12013Q12013Q1', '2
13Q22013Q22013Q22013Q2', '2013Q32013Q32013Q32013Q3', '2013Q42013Q42013Q42013Q4'
 '2014Q12014Q12014Q12014Q1', '2014Q22014Q22014Q22014Q2', '2014Q32014Q32014Q3201
Q3', '2014Q42014Q42014Q42014Q4', '2015Q12015Q12015Q12015Q1', '2015Q22015Q22015Q
2015Q2', '2015Q32015Q32015Q32015Q3', '2015Q42015Q42015Q42015Q4', '2016Q12016Q12
16Q12016Q1', '2016Q22016Q22016Q22016Q2', '2016Q32016Q32016Q32016Q3', '2016Q4201
Q42016Q42016Q4']

看起來似乎好像沒有同時跑到太多的process,但其實是因為這執行太快,
根本不需要用到多個process處理,假如是同時處理多個資料庫,
因為時間比較久,可以看得出來同一個時間是處理cpu數量的process,
數量是可以指定的,只是太多process真的反而比較慢,

implement過程中遇到幾個問題,第一個是不固定數量參數,可參考這篇
再來就是每個參數都是list,偏偏apply_async需要透過Queue處理參數,
本來想用Queue存Queue,但是Queue沒辦法[index]處理,
所以才想說用List來存,但Queue又沒辦法直接串接,
所以只好先隨便存個str進去,然後再用Queue的位置丟進list,
就有了Queue list的功能,

再來就是apply_async的return值沒辦法全抓到,就可參考這篇
但其實裡面範例跟我要的功能還有差距,所以只好再研究,
重點就是用[]在裡面跑迴圈去apply_async,最後就可以把所有return值list起來
而最終的return list是Queue,還要透過get()去把所有值再list起來

而Pool()的宣告要在[]迴圈之外,
因為在迴圈之內就會產生跟迴圈數量一樣多的process,
同時執行太多process,光是彼此sync就受不了了,
最終會產生跟list內容數量一樣多的process,
會慢很多,假如需要同時有非常多process,
應該只需要在Pool()中設定數量就好,也不需要那麼多個物件,

最終就可以實現同時執行多個多參數function的功能了

[Python] 計算股票最低不虧損賣價

這是我的第一隻python程式,就想寫些有點用的小工具,
主要分成selling_price.py(主程式)跟selling_price_gui.py(GUI)兩隻,
Mac執行:
python3 selling_price_gui.py
python3 selling_price.py 買入價格
Windows執行:
py -3 selling_price_gui.py
py -3 selling_price.py 買入價格

主要遇到的問題應該是GUI輸入浮點數跟主程式處理浮點數的問題,
主程式用round函式取近似值抓精度來解決,GUI則一輸入就丟去檢查

GUI的code是參考程式語言教學誌跟良葛格的python專欄:
http://www.kaiching.org/2014/07/python-guide.html
https://openhome.cc/Gossip/CodeData/PythonTutorial/index.html


selling_price.py 程式碼&註解:


# -*- coding:utf-8 -*-
# file: selling_price.py
#

class SellingPrice():

    # 常數定義
    # TAX: 證券交易稅 3/1000
    # CHARGE: 手續費 1.425/1000
    TAX = 0.003
    CHARGE = 0.001425
    # ticks_table 欄位定義
    # MINIMUM: tick range 最小邊界
    # MAXIMUM: tick range 最大邊界
    # TICK: 這個 range 的 tick 大小
    MINIMUM = 0
    MAXIMUM = 1
    TICK = 2
    # 使用 round 函式解決浮點數精確度的問題
    # 取到小數點後兩位數
    ACCURATE = 2

    # 不同的股價區間有不同的 tick 數值
    ticks_table = [
        [0, 10, 0.01],
        [10, 50, 0.05],
        [50, 100, 0.10],
        [100, 500, 0.50],
        [500, 1000, 1],
        [1000, 0, 5]
    ]

    # 藉由輸入的數值 cost (購買一張1000股的價格)
    # 去 ticks_table 裡面找到對應的 tick 值
    def get_tick(self, cost):
        for ticks in self.ticks_table:
            if cost < ticks[self.MAXIMUM]:
                return ticks[self.TICK]
        return ticks[self.TICK]

    # 藉由輸入的數值 cost (購買一張1000股的價格)
    # 算出所需的購入金額 ($NT dollars)
    def calculate_cost_nt(self, cost):
        return int(cost * 1000 * (1 + self.CHARGE))

    # 藉由輸入的數值 sell_price (賣出一張1000股的價格)
    # 算出最終賣出的金額 ($NT dollars)
    def calculate_sell_nt(self, sell_price):
        return int(sell_price * 1000) - int(sell_price * 1000 * (self.CHARGE + self.TAX))

    # 檢查買入賣出的金額不能為 0
    # 另外也根據買入賣出價格所處的 range 來抓出 tick
    # 在那個 range 中的最小移動單位應該要為 tick
    # 假如沒辦法被 tick 除盡,代表是不正確的輸入值
    def input_check(self, cost, tick):
        if cost <= 0:
            return False
        if round(cost * 100, self.ACCURATE) % round(tick * 100, self.ACCURATE) != 0:
            return False
        return True

    # 先檢查輸入值是否合法,再算出損益
    # (賣出價格 - 購入成本) = 實際損益
    def calculate_gain_loss(self, cost, sell):
        if (self.input_check(cost, self.get_tick(cost)) == False) | \
           (self.input_check(sell, self.get_tick(sell)) == False):
            return 0
        return (self.calculate_sell_nt(sell) - self.calculate_cost_nt(cost))

    # 先檢查輸入值,算出購入成本
    # 再找出滿足條件的 n: "買入價格 + (n * tick)" >= "購入成本"
    # 根據 n 算出 "至少不虧本的賣價 = 買入價格 + (n * tick)"
    def calculate_least_price(self, cost):
        tick = self.get_tick(cost)
        if self.input_check(cost, tick) == False:
            return 0
        cost_nt = self.calculate_cost_nt(cost)
        sell_price = round(cost + tick, self.ACCURATE)
        while True:
            sell_nt = self.calculate_sell_nt(sell_price)
            if sell_nt >= cost_nt:
                return sell_price
            sell_price = round(sell_price + tick, self.ACCURATE)

import sys
if __name__ == '__main__':
    Selling = SellingPrice()
    sell_price = Selling.calculate_least_price(float(sys.argv[1]))
    if sell_price == 0:
        print("Input valure error!")
    else:
        print(sell_price)

selling_price_gui.py 程式碼&註解:


# -*- coding:utf-8 -*-
# file: selling_price_gui.py
#

import tkinter
from selling_price import SellingPrice

class SellingPriceGUI(tkinter.Frame):

    ACTION_INSERT = '1'
    # input 最大輸入的長度
    INPUT_MAX_LENGTH = 10
    # 設定函式 check_imput 檢查的類型,可以是 int 或 float
    check_type = float
    Selling = SellingPrice()

    def __init__(self, master=None):
        tkinter.Frame.__init__(self, master)
        # tkinter 有 pack、grid、place三種佈局管理
        # grid(row=列, column=行, rowspan=佔用列數, columnspan=佔用行數)
        self.grid()
        self.create_widgets()

    def create_widgets(self):
        # (action)'%d', (index)'%i', (value_if_allowed)'%P'
        # 代表傳入 check_imput 的三個參數的型態
        validate_command = (self.register(self.check_imput), '%d', '%i', '%P')

        self.buy_lable = tkinter.Label(self)
        self.buy_lable["text"] = "買入價格:"
        self.buy_lable.grid(row=0, column=0)

        self.buy_entry = tkinter.Entry(self, validate = 'key', validatecommand = validate_command)
        self.buy_entry["width"] = 20
        self.buy_entry.grid(row=0, column=1)

        self.sell_lable = tkinter.Label(self)
        self.sell_lable["text"] = "賣出價格:"
        self.sell_lable.grid(row=1, column=0)

        self.sell_entry = tkinter.Entry(self, validate = 'key', validatecommand = validate_command)
        self.sell_entry["width"] = 20
        self.sell_entry.grid(row=1, column=1)

        self.least_lable = tkinter.Label(self)
        self.least_lable["text"] = "不虧損價:"
        self.least_lable.grid(row=2, column=0)

        self.least_sell_result = tkinter.Label(self)
        self.least_sell_result["text"] = ""
        self.least_sell_result.grid(row=2, column=1)

        self.profit_lable = tkinter.Label(self)
        self.profit_lable["text"] = "實際盈虧:"
        self.profit_lable.grid(row=3, column=0)

        self.profit_loss_result = tkinter.Label(self)
        self.profit_loss_result["text"] = ""
        self.profit_loss_result.grid(row=3, column=1)

        self.clear_button = tkinter.Button(self)
        self.clear_button["text"] = "清除"
        self.clear_button.grid(row=4, column=0)
        self.clear_button["command"] = self.clear

        self.count_button = tkinter.Button(self)
        self.count_button["text"] = "計算"
        self.count_button["width"] = 20
        self.count_button.grid(row=4, column=1)
        self.count_button["command"] = self.calculate

    # 限制輸入的長度,以及內容必須是整數或是浮點數
    # 檢查的類型則是由 self.check_type 決定
    def check_imput(self, action, index, value_if_allowed):
        if action != self.ACTION_INSERT:
            return True
        if int(index) > self.INPUT_MAX_LENGTH:
            return False
        try:
            return self.check_type(value_if_allowed)
        except ValueError:
            return False

    def calculate(self):
        # 計算最低不虧損價格
        cost_input = self.buy_entry.get()
        if cost_input != "":
            least_sell_price = self.Selling.calculate_least_price(float(cost_input))
            if least_sell_price == 0:
                self.least_sell_result["text"] = "輸入錯誤!"
            else:
                self.least_sell_result["text"] = least_sell_price
        # 計算實際損益
        sell_input = self.sell_entry.get()
        if sell_input != "":
            gain_loss = self.Selling.calculate_gain_loss(float(cost_input), float(sell_input))
            if gain_loss == 0:
                self.profit_loss_result["text"] = "輸入錯誤!"
            else:
                self.profit_loss_result["text"] = str(gain_loss) + "元"

    # 清除輸入輸出值
    def clear(self):
        self.buy_entry.delete(0, self.buy_entry["width"])
        self.sell_entry.delete(0, self.sell_entry["width"])
        self.least_sell_result["text"] = ""
        self.profit_loss_result["text"] = ""

if __name__ == '__main__':
    root = tkinter.Tk()
    root.title("賣價計算")
    app = SellingPriceGUI(master=root)
    app.mainloop()

[Python] 用Batch file修改Windows環境變數Path for Python

因為工作機上build code會用到2.7.x的python,偏偏我又在學3.4.x的語法,本來不想設全域變數,想說寫batch每次都設PATH就好,但偏偏Sublime Text的SublimeLinter套件又一定要有Python跟底下Scripts的全域變數,但每次都手動去設也很麻煩,就打算寫個batch來幫忙save/restore,執行下面的batch file,因為會修改到註冊檔,需要按右鍵用Administrator來執行。

[Cmd] Add Python Path.cmd

@set SAVE_RESTORE_FILE_NAME="c:\Non-Python.sav"
@set PATH_ENV_REG_PATH="HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment"

@if exist %SAVE_RESTORE_FILE_NAME% echo The backup file already exists! & @goto end

reg save %PATH_ENV_REG_PATH% %SAVE_RESTORE_FILE_NAME% /y

reg add %PATH_ENV_REG_PATH% /v path /d "%Path%;C:\Python34;C:\Python34\Scripts" /f

:end
@pause

[Cmd] Remove Python Path.cmd

@set SAVE_RESTORE_FILE_NAME="c:\Non-Python.sav"
@set PATH_ENV_REG_PATH="HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment"

@if not exist %SAVE_RESTORE_FILE_NAME% echo The backup file not exists! & goto end

reg restore %PATH_ENV_REG_PATH% %SAVE_RESTORE_FILE_NAME%

del /f %SAVE_RESTORE_FILE_NAME%

:end
@pause

[Sublime Text] Sublime Text 3 套件

Sublime Text 3快捷鍵:
1.      打開命令列「Ctrl + Shift + P
2.      打開控制台列「Ctrl + `
3.      內建全域搜尋「Ctrl + Shift + F
4.      檔案切換「Ctrl + <Tab>
5.      以檔名搜尋開啟檔案「Ctrl + P
6.      列出目前檔案函式列表「Ctrl + R
7.      Ctrl + F」搜尋後,用「F3」向後、「Shift + F3」向前找
8.      開啟剛才關閉的設定頁面「Ctrl + Shift + T
9.      游標點兩下圈選住所在單字,點三下圈選游標所在行。
10.  全螢幕編輯「Shift + F11
11.  選取項目後按下「Alt + F3」,可同時編輯所有選取項目
12.  游標移動至括號內開始或結束的位置「Ctrl + M
13.  圈選括號內開始到結束的位置「Ctrl + Shift + M
14.  放大縮小「Ctrl + <+>」「Ctrl + <->
Reference:

名稱:Package Control
功能:套件管理程式
說明:
1.      打開控制台列: 從選單的View -> Show Console,或是按快捷鍵「Ctrl + `
2.      貼上這個網站提供的安裝碼: https://packagecontrol.io/installation
3.      試著打開命令列: 按快捷鍵 Ctrl + Shift + P」,或是從選單的Tools -> Command Palette,找到"Package Control: Install Package",然後按下Enter鍵。接著輸入要安裝的套件名稱,再按下Enter鍵就會進行安裝。
Reference:

名稱:Alignment
功能:對齊程式碼
說明:
1.      選取好要對齊的區域,按下「Ctrl + Alt + A」就可以對齊了,會對齊到圈選的多行中,開頭起始字元(不算空白鍵)最靠右的那行,它的字元起始位置。
2.      按著Ctrl點游標,可以選擇很多輸入點,再按「Ctrl + Alt + A」就可以把所有游標點對齊到最靠右的那個游標點。
3.      需要注意的是,他對齊的方法是使用 <Tab>,而不是用空白取代,仍須在(Preferences -> Package Settings -> Alignment -> Settings - User中設定:
{
    "mid_line_tabs": true,
}
4.      在(Preferences -> Settings把基本<Tab>相關設定如下:
    "tab_size": 4,
    "translate_tabs_to_spaces": true,
    "detect_indentation": false,
    "use_tab_stops": false,
Reference:

名稱:Bracket​Highlighter
功能:把各種括號符號、標記特別高亮度顯示出來。
說明:
1.      游標移過去應該會把前後的符號特別高亮度呈現出來,假如感受不到效果,需要把"bracket_styles""style": 特別改成"hightlight"
2.      在(Preferences -> Package Settings -> Bracket​Highlighter -> Bracket​ Settings - User當中加入下面的設定,才會真正被enable起來,可以改在default就改在default,不能改才加在User設定當中。
{
    "bracket_styles": {
        "default": {
            "style": "hightlight"
        }
    }
}
Reference:

名稱:ChineseLocalizations
功能:選單中文介面
說明:
Help -> Language)有日語、簡體中文、繁體中文三種語言。
Reference:

名稱:Color Highlighter
功能:安裝完成後在檢視色碼的部分點選候可以即時瀏覽顏色
說明:
1.      輸入任一色碼,如:「#59ffeb」,指標指到就會顯示色碼顏色,比較適合在網頁相關程式上使用。
Reference:

名稱:ConvertToUTF8
功能:專換檔案編碼
說明:
1.      Preferences -> Package Settings -> ConvertToUTF8 -> Settings - Default)在設定檔中把BIG5移到GBK前,把繁體移到判斷的順序的最前面,避免轉換錯誤。
Reference:

名稱:CTags
功能:程式追蹤
說明:
1.      先從Package Control安裝完套件後,還需上官網下載Ctags主程式http://ctags.sourceforge.net/
2.      解壓縮到特定路徑,此處以Sublime安裝位置為例,在設定檔(Preferences -> Package Settings -> CTags -> Settings - User)中,加入設定,主要是指定Ctags主程式目錄。
{
    "command": "C:/Program Files/Sublime Text 3/ctags58/ctags.exe",
    "autocomplete": true
}
3.      可以在Function上面按右鍵選擇”Navigate to Definition” orCtrl + Shift + .」跳到定義處,再按”Jump Back” orCtrl + Shift + ,」跳回來。ST3本來就有類似功能,但CTags的比較準確。
4.      ”Find -> CTags -> Rebuild Tags”可以測試是否可以產生索引檔。
Reference:
使用說明:

名稱:DocBlockr
功能:程式註解輔助
說明:
1.      Python還需要另外安裝DocBlockr_Python
2.      ‘’’ + <Tab> or “”” + <Tab> 」自動產生註解(for Python
3.      # 」按 <enter>,自動產生下一行「# 」號註解
4.      /** + <Tab> 」自動產生註解 for C code),在Function前使用會把參數也都列出來。
Reference:

名稱:HighlightWords
功能:常駐(持續性)的標記某個單詞
說明:
1.      安裝完後,按「Ctrl + Alt + H」來常駐標記單字。
Reference:

名稱:Material Theme
功能:背景主題套件
說明:
1.      安裝完後,按(Preferences -> Package Settings -> Material Theme -> Activate)來選擇要啟用的主題。
Reference:

名稱:Side​Bar​Enhancements
功能:加強側邊列(檔案與資料夾)能的套件
Reference:

名稱:Sublime​Linter
功能:語法檢查
說明:
1.      Sublime​Linter安裝完後,還要安裝Sublime​Linter-pep8Sublime​Linter-pyflakes的補充套件,這邊只是安裝ST3跟檢查套件的介面,另外還需要在command line再透過pip去安裝pep8pyflakespylint的主程式。需要確定”C:\Python34””C:\Python34\Scripts”有加入全域”PATH中,才能有作用。
set PATH=C:\Python34;C:\Python34\Scripts;%PATH%
pip3 install pep8 pyflakes pylint
2.      設定(Preferences -> Package Settings -> Sublime​Linter -> Settings - User),可以根據喜好開關不同的檢查功能,@disable設成true可以把那項功能關掉,下面例子中把pylint關掉,pylint較少人用,是因為可能會抓出太多警告跟錯誤。
"linters": {
    "pep8": {
        "@disable": false,
    },
    "pyflakes": {
        "@disable": false,
    },
    "pylint": {
        "@disable": true,
    }
}
Reference:

名稱:SublimeCodeIntel
功能:智慧型語法、函式自動完成的套件
Reference:
名稱:TrailingSpaces
功能:自動清除每行程式碼後面的 <space>
說明:
1.      欲手動則設定快捷鍵設定檔,寫入JSON參數後存檔即可。
Preferences -> Key Bindings -> User
{ "keys": ["ctrl+shift+t"], "command": "delete_trailing_spaces" }
]
2.      在設定檔內加入下面設定,就可在每次儲存時自動清除空白。
Preferences -> Package Settings -> Trailing Spaces -> Settings - User
{
    "trailing_spaces_trim_on_save": true
}
Reference:

名稱:x86 and x86_64 assembly
名稱:NASM x​86 Assembly
功能:AssemblySublime上的解析
說明:
Reference: