顯示具有 [Python] 標籤的文章。 顯示所有文章
顯示具有 [Python] 標籤的文章。 顯示所有文章

[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

[Python] Python 2 & 3 在Windows下共存的官方解決方法

首先我參考的是下面這篇文章:
https://python.freelycode.com/contribution/detail/139

重點就是裝了Python 2 & 3之後 (3的版本必須>=3.3),就會在C:\Windows\下面產生py.exe,直接在cmd下執行py就可以被抓到,不用再去設全域的環境變數PATH,假如有設定PATH,直接執行python檔案會根據後安裝的那個python版本去執行,下面是同個py檔透過py.exe去下不同參數導到不同版本的python:

py -2 2_1_1_Sample-3.py
py -3 2_1_1_Sample-3.py

下面是執行的結果:
這個範例檔案語法在python 2可pass,但在python 3會有error,而這個範例是先裝python 3.4.3再裝python 2.7.9,所以當設定全域PATH的時候會以2.7.9的為主。