python — win32gui 特定程式窗口截圖(教學)

有天想說要用python來追蹤某視窗的畫面,並且使用圖像辨識opencv來辨識狀態,如果異狀則播放音效提醒。

上網google,首先找到這一篇:
https://hutdris.blogspot.com/2016/11/pythonopencv.html

該文作者最後仍未完成,因為功能也不是如想像的那般,可行是可行,不過侷限性太大了。作者也自行提出這個問題,想要找到解決方法。

「目前是抓取該程式的畫面相對位置,如果該程式縮到最小或被其他程式蓋過,還是會持續抓取該位置的畫面。 如果希望持續擷取該程式的畫面該怎麼作」
前提就到這裡了,本文將會「提供程式碼」與「編寫邏輯」,不過JN並非專科出身,可能會有講出錯的理解,希望能夠指正JN講錯的地方。


流程圖

 


關於如何對特定程式窗口截圖,首先要先瞭解到底截的對象是甚麼?

資料型態是寫程式的基本功,這個沒有紮實領會,寫程式就很容易碰壁。
JN透過搜尋另外一種關鍵字,找到了這篇問題。下方有人提供了大神解法與連結,也是本次JN主要參照的文章《Capturing Minimized Window》,運作流程也是照著這篇文章的流程。但是,這文章並不是用python作為程式語言,一開始雖然JN是放棄參照這篇大神邏輯,最後還是回過頭來仔細看這篇文章的概念。
本篇程式碼主要使用 win32gui 去完成,而 win32gui 剛好能找到那篇文章所使用的api,真的很巧。不過win32gui的document還是看得很頭痛,因為物件並不是用常見的python資料型態。
要如何將特定程式窗口截圖?首先要先抓區特定程式的「handle」,中國翻譯叫「句柄」,handle就是一個連結到程式窗口的路。在win32gui中,handle(PyHANDEL)通常是int型態,用int也能通大部分函式的PyHANDEL物件。
#以Name找出特定窗口的handle
win32gui.FindWindow(‘Name’)
Ex:win32gui.FindWindow(‘未命名 – 小畫家’)
用了 win32gui.FindWindow() 函式,就能找到我們目標窗口的handle,接下來要用此 handle 來找出其 handle device content (HDC)。HDC是窗口輸出的內容,各位想必開了瀏覽器來看這篇文章,你所看到的文章(畫面)都是瀏覽器的HDC,這個就是做特定程式窗口截圖的對象。
 
用電腦的時候,不一定只有開瀏覽器,可能還會打開檔案管理員。但是我們只看到瀏覽器的畫面,是因為瀏覽器的圖層比檔案管理員的還要更上方。HDC會輸出成圖層,變成圖層才會有上下之分,上方圖層會擋住下方的。如果我們直接抓取下方圖層的HDC,就不會被上方的圖層擋到的問題,也就能實踐本次的特定程式窗口截圖
 
透過handle,我們可以用 win32gui.GetWindowDC(handle) 來找出該程式窗口的HDC。接下來的工作就是要把HDC轉成常用的np.array型態,這樣就能儲存圖片與圖像辨識。HDC可以轉換成 bitmap 型態,再從 bitmap 又要轉換成 np.array。好像很複雜,你就當作是在 bitmap (畫布)輸出 HDC的內容,然後都是圖樣的東西,彼此(與np.array)互轉就不是難事了。
這邊,JN直接使用這篇範例的部分內容,主要就是將 bitmap 轉換成 np.array 。至於為什麼會這樣寫,JN並不是很瞭解,只是知道怎麼用而已。


”’
import win32con # 各種win32函式的參數
import win32gui
import win32ui
import numpy as np

hwnd = the handle of a window
x = the x coordinate of the window
y = the y coordinate of the window
width = the width of the window
height = the height of the window
”’
# 創造輸出圖層
hwindc = win32gui.GetWindowDC(hwnd)
srcdc = win32ui.CreateDCFromHandle(hwindc)
memdc = srcdc.CreateCompatibleDC()
bmp = win32ui.CreateBitmap()
# 如果視窗最小化,則移到Z軸最下方
win32gui.SetWindowPos(hwnd, win32con.HWND_BOTTOM, x, y, width, height, win32con.SWP_NOACTIVATE)
# 複製目標圖層,貼上到 bmp
bmp.CreateCompatibleBitmap(srcdc, width, height)
memdc.SelectObject(bmp)
memdc.BitBlt((0 , 0), (width, height), srcdc, (0, 0), win32con.SRCCOPY)
# 將 bitmap 轉換成 np
signedIntsArray = bmp.GetBitmapBits(True)
img = np.fromstring(signedIntsArray, dtype=’uint8′)
img.shape = (height,width,4)
# 釋放device content
srcdc.DeleteDC()
memdc.DeleteDC()
win32gui.ReleaseDC(hwnd, hwindc)
win32gui.DeleteObject(bmp.GetHandle())


如此,我們透過 win32gui 的函式搜尋找到「特定窗口的handle」,然後透過 「handle 找出 HDC」, 再把 「HDC」 轉到 「bitmap」 ,然後換成「 np.array」 。到了np.array型態,用cv2.imshow()就能秀出圖片。主要功能到這裡就完成了。
接下來要修飾主要功能

主要功能只是抓取特定窗口,但是在「最小化」的時候,卻只會抓到 icon。如果窗口不解除最小化,就沒有辦法抓到完整畫面。

依照JN主要參考的文法,它的做法是將圖層透明化、取消最大小化的動畫,最後再把窗口秀出來。因為透明化的窗口圖層不會被看見,就能有「偷偷地截取」的感覺,取消動畫是讓作動加速,而窗口秀出來是避免讓截圖只抓到icon。


到這裡還沒結束,如果我們在程式運作中,又想點開特定程式窗口,如果此時沒有將該圖層透明化給還原,那麼就會發現用滑鼠再怎麼點icon都沒有畫面。但是透明度一還原,就會發現其實程式圖層在上方,擋到其他程式窗口。按照主要參考文章,它的作法會將窗口最小化後,再把圖層透明度還原。但是,依照這樣的概念運作,就會發現工作列實在閃閃發亮很熱鬧,並沒有偷偷地截圖。

所以JN這裡採取將窗口圖層移動到最下方,再把透明度還原,而沒有把窗口最小化。


”’
如果要用 win32gui.SetLayeredWindowAttributes()
hwnd (int) 要變成 WindowLong 的型態
”’

# 將 hwnd 的型態變成 WindowLong
s = win32gui.GetWindowLong(hwnd,win32con.GWL_EXSTYLE)
win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, s|win32con.WS_EX_LAYERED)

# 將窗口最大小化動畫給取消
win32gui.SystemParametersInfo(win32con.SPI_SETANIMATION, 0)

# 如果沒有把 hwnd 的型態轉換,代入此函式會報錯
# 第一個 0 是 顏色,第二個 0 是 不透明度

win32gui.SetLayeredWindowAttributes(hwnd, 0, 0, win32con.LWA_ALPHA)
# 讓指定窗口顯示出來,最後一項參數( win32con.SW_…… )可以改變顯示方式
win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
# 將圖層移到最下層
# 最後一項參數,JN使用過 SWP_NOMOVE 及 SWP_NOSIZE 都會導致圖層無法移到最下層
win32gui.SetWindowPos(hwnd, win32con.HWND_BOTTOM, x, y, width, height, win32con.SWP_NOACTIVATE)

”’
此部分程式碼,「不包含」還原圖層透明度。
”’


透過這樣的方式抓取特定窗口截圖,就能解決前提連結的問題,不僅不會被別的窗口蓋過,就連最小化的窗口也能抓取。此外,也改進主要文章的缺陷,JN的方式不會讓工作列不停閃爍。

這也是JN第一篇發在GitHub的程式碼,當了初心者那麼久了,總算有東西值得發布了。


2018.06.20 刪除了冗碼,並以win10校正,避免截圖抓到四周的透明部分。

後記

程式碼發布是JN第一次嘗試,看到別人的網站爾偶會有制式的表格,JN很好奇是怎麼樣產生的。如果有甚麼方法能夠快速產生展示程式碼的表格,也請拜託在下方留言教學。

現在寫程式能寫出以前沒有能力完成的,我想這就是經驗的累積吧。慢慢累積各種方法與技巧,融會貫通就又開啟了一扇門˙,而且是以前認為沒辦法寫出來的東西。

這篇文章也是讓JN第一次接觸win32系列,win32不只存在於python,在C#還是其他的程式語言都有。我想win32系列就是windows的架構,每天從螢幕看到的、鍵盤操作的,都能用win32系列來完成,而且能做到的更強大,但是,卻也更源頭的物件。這次還碰到二進位,之前在寫都沒有注意到位元,也花了一些時間學習如何處理。

最後還有一些詞彙釐清。會用「窗口」是因為這是中國用語,而台灣常講「視窗」,然而用窗口是比較容易找到教學。此次撰寫,也讓JN分清楚「window」、「handle」、「device content」、「layer」的差異,不同的詞都有不同呈現的方式,要瞭解自己操作的是哪一種,才不會混淆或作不出來。

金融職業道德 — 快問快答!

最近要考「金融市場常識與職業道德」,直接從官網下載題庫,想說就靠題庫準備了。

為了要有效率學習,JN認為必須加入一點「刺激」的要素,如時間限制,在適當的壓力之下,答題會更容易保持專注,專注也會讓人更容易記憶。

此外,也為了實踐JN自身對「效率學習」的想像。這套快問快答,會讓答錯的題目更容易出現。即使一回錯,二回錯,到了第三回,就算還不知道題目所述,但至少答案也該背起來了吧!


使用本程式的目的為「通過考試」,但是如果想要變得專業,一定要弄懂每題所要考的概念,不然就會淪為背答案的過度學習者。

以下是示範影片
要如何使用本程式呢?(過關)
(完美路線)
1. 不斷答題,答到兩種題庫的平均每題權重小於 0.5
(效率路線)
2. 最近5次成績的平均 – 最近5次成績的全距 > 70分

希望對於要考「金融市場常識與職業道德」的考生,能透過本程式給予在準備考試上的幫助。考試考60分鐘,但JN一次約在15分鐘內完成,只要玩一個下午的時間,相信每一個人,也能很有信心可以通過考試。

如果周圍親朋好友也有要考的,歡迎轉發本文章或本程式給他們。
如果題目有誤,或是有甚麼建議,請留言在下方,下次更新會一併改進。


更新2018.6.21

身為本程式的作者,JN將考試目標分數設定為拿「滿分」,這也是實際能支持使用這程式的好處。不然要是JN沒拿到滿分,實在很難拿出證據去說服別人,使用這個程式準備真的很有效果。上大學後,也很少拿滿分,手癢,剛好這個考試不難,拿個滿分,緬懷一百分的感覺。

昨天去應試,今天查詢,不意外就是「滿分」。在考試當下,感覺只花了七、八分鐘就寫完100題,而且,寫完就知道會拿100分了。

八分鐘寫完的境界是甚麼?就是看題目外形(不是看字),以及答案外形(不是看字),大概就知道答案是甚麼了。順利的話五秒鐘就能連寫三題。不過JN不會這麼冒險,實際上有刻意拉慢速度好好看仔細,因為考前在用快問快答的時候,發現只要作答速度一快就會犯眼殘的錯。如果犯錯失了一分,就等於任務失敗,不要犯錯比較神速寫完更重要。

100題寫完後,再從頭,穩定又細心地重新讀選項與關鍵字,這完成後,才劃卡,劃卡完又再對劃卡的答案與試卷的答案有沒有一致。總之,這100題被掃過四遍,還剩一點時間讓JN趴著闔眼休息,幸好考官宣布三十分鐘到了,JN急著搶第一名交卷,急著想出去晃一晃,犒賞自己達成考前所設的目標「拿滿分」。

用這套程式準備真的很容易,想考滿分只要多玩個幾十次就能輕鬆記住,不用特別買書還是列印題庫慢慢看,用這程式邊玩邊記。

認真玩個幾遍,你也能及格!認真玩遍題庫數次,你也能拿滿分!考生加油加油!



更新 2018.6.18

1.依照勘誤表勘誤題庫
2.補上職業道德第2題、第579題的缺失文字
3.新增了音樂及音效開關。若要關閉音樂或音效,請從「setting.txt」將Music或Sound的參數改為0


後記

這其實是一隻很簡單的程式,不知道為什麼能變成224MB。裡面也沒有藏著甚麼病毒,因為我的功力還沒有到能寫出病毒呢。如果能寫出,也不會讓程式變成224MB的大小。就安心的玩吧!要是防毒軟體能偵測到有毒,再砍掉程式也沒關係。

怎麼寫出來的?因為這也是JN第一次用python寫出快問快答的遊戲,程式碼也超亂的,所以就不想放上來現醜。如果真有安全上的疑慮,JN也是可以放上整個程式碼,大概不到1MB,供有心學習的人觀摩。

用python,GUI使用tkinter,音樂音效使用pygame。題庫整理,是過去資料分析學到的經驗,用re快速清理乾淨,再用人眼快速確認沒有重大疏漏,不用一小時就弄好了。音樂從youtube無版權素材庫下載,cc是最free的那種,不用特別標註的自由使用,從檔案總管也能看到音樂的各種資訊。音效是從知識+找到的,不過這種廣泛流傳的音效版權很難有歸屬,大多也成了不用特別標註的自由使用。最後使用pyinstaller打包成224MB的exe。

個人使用過後,覺得是很棒的,比起無聊的一題一題看題庫,還是看參考書。因為設計了時間限制,所以逼得JN必須專心快速分析題目,順便動腦思考跟相類的題目差異點與共同點。另外,權重制度也不像一般的快問快答APP是隨機的(每題權重相同)。而將「權重」導入當然是為了補足弱點,也讓你本來就會的不要老是出現送你分。早點將弱點補起來,才是讓表現提升的快速路徑。

臺大學號轉系所 (網頁版)

程式連結如何使用?
1.複製學號:
文字不用整理整齊,亂成一團也沒關係,因為程式會自動找學號
2.貼上到輸入框內
 3.按下送出
4.可以將結果選取並複製到EXCEL或是其他地方





亂數產生的二十個臺大學號,並加入字打亂

d57127062@#$%^
b87850012臺大好棒棒d96409093醉月湖b32B22081YJYGHYUGHJNr03550054TGHBJBJIb02402038$&^JHJIU*(
d11921037NJI02645^&*&^*b89610052,r66454045,r25302083
r36E03069,b57207065,
“b93E04071″,’d25844094”r70612005、r95456020
d43403060FYTFGUYG
d12401000只是展示用
b14141003整理資料好用
d67408000希望派得上用場
r29455047^*(&*(&*(

為什麼會寫這個程式?

因為最近在做統計,有些時候會需要找出樣本的學院或系所,因為我們會關心這場活動的人,是來自於甚麼學院或是甚麼系所比較多。

為了要能快速找出系所,所以最快的想法是紀錄時就是有「系所」一欄,但有人填寫的是簡稱,有人填寫全名,造就同義不同詞的困擾,想做統計前,還得一個個挑選並更改成統一用詞。還有別的方法能知道其就讀系所,如果拿到了「學號」,也幾乎等同於系所。這是因為臺大學號是由「學位 + 入學年度 + 學系代碼 + (學群、組別) + 序號」組成。

從學號判讀出其就讀學系,不僅能去除簡稱全名的困擾,此外,學號也是容易取得之資料,所以大部分都很適合用此方法找到樣本的系所。

唯一缺點,轉系的學生其學號照原就讀系所,用此程式只會找到原就讀系所。


本程式適用甚麼情形呢?

1. 舉辦活動,想知道參加活動的人來自甚麼系所。
2. 拿到一串神祕學號,很想知道那個學號究竟是怎樣的人。
3. 資料格式很亂,想快速整理出統一格式

本程式參考資料來源 臺大各系所必修科目及應修學分資料查詢
整理出來的系所代碼 臺灣大學學號的系所代碼※截至2018.06.02

Pandas.DataFrame之入門九道題目–處理從html讀取的表格

JN想標題很久,想不對甚麼會很貼切,於是勉強這樣下。
 
關於Python的Pandas教學,從Google搜尋就能找到許多中國的解說。不過每次JN想要做的處理,就要再次尋找相關教學,因為分散在各地,實在很麻煩又不容易學習。而且內容詳盡的教學網站都類似教科書一樣死板呈現,然而想學的地方就只是千段中的一段文字,並不能很有效就挖掘出來。JN僅推薦從「pandas 0.22.0 documentation」去尋找如何做你想要的操作。
這篇文章是以網路爬蟲的會用到「Pandas.DataFrame」的一些處理為編寫依據,也是從實戰中有做的處理。JN相信,只要能完成這九道題目,就能整理大多數從網頁讀取的表格。歡迎分享解題過程在下方留言,而JN有天也會補上自己的寫法(可能吧)。

規則:使用「pandas.read_html()」讀取html檔案,找出左欄的表格,並且左欄表格進行整理,最後整理成右欄表格的樣式。下載html表格(dropbox)

表格來源

目標

A. 整齊表格
小明 小華 小美 小倩
鉛筆 1 2 3 4
橡皮擦 5 6 7 8
直尺 9 10 11 12
A1. 各列為一串列(list)
銷售員 = [“小明”, “小華”, “小美”, “小倩”]
鉛筆 = [1, 2, 3, 4]
橡皮擦 = [5, 6, 7, 8]
直尺 = [9, 10, 11, 12]
A2. 轉置表格(DataFrame)
鉛筆 橡皮擦 直尺
小明 1 5 9
小華 2 6 10
小美 3 7 11
小倩 4 8 12
B. 項目歪曲的表格
小明 小華 小美 小倩
鉛筆 1 2 3 4
橡皮擦 5 6 7 8
直尺 9 10 11 12
B. 修復表格(DataFrame)
小明 小華 小美 小倩
鉛筆 1 2 3 4
橡皮擦 5 6 7 8
直尺 9 10 11 12
C. 有非NaN類缺值的表格
小明 小華 小美 小倩
鉛筆 2 3
橡皮擦 5 6 7 8
直尺 9 10 11 12
C.去掉缺值列(DataFrame)
小明 小華 小美 小倩
橡皮擦 5 6 7 8
直尺 9 10 11 12
D. 表格鑲表格
a
b
j
D 小明 小華 小美 小倩
鉛筆 1 2 3 4
橡皮擦 5 6 7 8
直尺 9 10 11 12
D. 小美小倩的橡皮擦與直尺的銷售狀況(DataFrame)
小美 小倩
橡皮擦 7 8
直尺 11 12
E. 有合併欄位的表格
銷售數據
男生
女生
小明 小華 小美 小倩
鉛筆 1 2 3 4
橡皮擦 5 6 7 8
直尺 9 10 11 12
E. 簡化表格(DataFrame)
小明 小華 小美 小倩
鉛筆 1 2 3 4
橡皮擦 5 6 7 8
直尺 9 10 11 12
F. 同類的兩個表格
鉛筆 橡皮擦 直尺
小明 1 5 9
小華 2 6 10
鉛筆 橡皮擦 直尺
小美 3 7 11
小倩 4 8 12
F1. 分別加入”性別”欄位(DataFrame)
性別 鉛筆 橡皮擦 直尺
小明 1 5 9
小華 2 6 10
性別 鉛筆 橡皮擦 直尺
小美 3 7 11
小倩 4 8 12
F2. 合併F1的兩表格(DataFrame) ※性別”女”的表格在上方
性別 鉛筆 橡皮擦 直尺
小美 3 7 11
小倩 4 8 12
小明 1 5 9
小華 2 6 10
F3. 從A2表開始,分離後再進行F1、F2(全程DataFrame)
A2 鉛筆 橡皮擦 直尺
小明 1 5 9
小華 2 6 10
小美 3 7 11
小倩 4 8 12

JN的小訣竅:

A1:使用「pandas.DataFrame.to_csv()」把DF(DataFrame)轉換成csv格式(str),並逐列整理成「list」。
B:創立新的DF,逐欄(df.loc())轉移到新DF。

C:用「.replace(“-“, pandas.np.NaN).dropna(axis=0,how=’any’) 」,先把「”-“」替換成「NaN」,再用「df.dropna()」即可丟掉缺失值。
D:找出內層

,再用「df.loc()」框出所需範圍並複製(.copy())成新的DF。

E:如B。
F1:使用「df.insert()」,在索引1的欄位先插入性別,即「df.insert(1,”性別”, “”)」,之後再統一該欄位的值,即「df[‘性別’] = “男” or “女”」。
F2:用「df.drop()」丟棄男表格的第一列(為:性別, 鉛筆……),再用「df.concat()」合併女表格與男表格。
F3:如D,先切割成男、女表格,即兩新DF。但因為來源DF的index也一同被複製,所以用「df.reset_index()」重置列的編號,再用「del df[‘index’]」刪除舊的index欄。之後如F1、F2步驟。

Excel物品清單原型,掌握所有物品蹤跡

JN最近幾天突然有感而發,腦海不時浮現一種物品清單。這物品清單要能詳盡列出物品以及其所在位置(收納位置),還有各個收納位置裡面會有甚麼物品。物品跟收納位置是雙向箭頭關係,不論從哪一個開頭,最後都能到另外一端,白話來說,從收納位置可以找到有甚麼物品在裡面,而從物品列表也可以找到是在甚麼收納位置。

這份清單的最大優點:任何人只需要搜尋這份清單,就能夠準確找到物品。(前提是做好清點)

在辦公室人來來去去,未必每一個人都清楚辦公室裡面到底有甚麼東西,以及東西在哪裡。有些時候以為沒有的東西,買了新的,才發現其實藏在小角落。或是新來的人對於想要找A4紙補充印表機,卻不知道A4紙放哪裡。最後就是有空間塞甚麼就塞,沒有做好分類,以至於各種類型的物品混雜在一塊,每次要找特定物品時,各種翻箱倒櫃好像要掀開整個辦公室,實在很傷神費工。

年關將近,這時候也是大掃除的時候。趁這時候完成物品清點並且照著填上物品清單,就能享受控管各種物品的狀況。

這份JN自製的範本,用到了函數MATCH、INDEX以及巨集。平面圖與3D圖使用到「Sweet Home 3D」繪製。

範例Excel(dropbox)※轉載須註明出處

「想學習的不是公式的使用,只是Idea」JN這樣說。
在幾天前,JN用這句話把一直真正想學的說出來。函數的使用雖然重要,但是要表達的意思更重要。最重要的就是一個厲害的idea,那能把函數跟所想表達的意思串起,而不會用了一堆函數,卻沒有人看得懂。

在製作本範例的過程中,JN找到一個excel教學部落格,裡面有蠻詳盡的excel教學。不過JN沒有參考裡面的寫法。

本次範例有三個部分:1.位置圖、2.收納位置、3.物品列表。

位置圖

位置圖

使用Sweet Home 3D,盡量照著實際空間與各櫃子位置繪製。不一定要全部的櫃子都要畫出來,用分區概括表示就能簡單化圖示。最低的要求就是不論是誰看到這張圖,就能找到甚麼櫃子位於辦公室哪裡。

旁邊的標示是用Excel插入圖案做的。小訣竅:對標示按右鍵,上超連結,連結到對應「收納位置」資料表。很直覺地就能從位置圖上找到該位置有甚麼物品。

收納位置

收納位置

各收納位置一樣設超連結,連到位置圖該標示的位置,能方便從位置圖上找到該空間。

物品種類其實是過程產物,也代表雜亂程度(?)。物品名稱會對應「物品列表」的物品,只要是在入口鐵櫃A的物品,都會在入口鐵櫃A下方表列。

這部分函數稍微複雜: 以入口鐵櫃A為例
1.用MATCH找出「物品列表」中,物品位於「入口鐵櫃A」,第一個符合的相對座標。
2.用INDEX以及(1)式所得的相對座標,找到第一個符合物品的名稱。
※恭喜找到第一個了,但函數也就只能找到第一個。
3.用COUNTIF,計算「物品列表」中,物品位於「入口鐵櫃A」共有幾項。(物品種類)
如果有六項,則下面還有五項在入口鐵櫃A的物品沒有找到。
4.剩下的物品,仿(2)步驟,在相對座標再+1、+2…找出,但+值不超過(3)式的值。
※這前提是物品列表都要照「位置」排序,沒有排好就會出錯。

接下來,要如何乖乖讓物品列表照位置自動排序,即時排序,是完成這份範例的重點。將使用巨集去實現「即時排序」。

物品列表

巨集是最好用的功能,JN這麼認為。只要程式碼想得到寫得出來,甚麼功能都能實踐。但是!如果前人已經造好輪子(函數),就不要再閉門造車,直接使用前人寫好的就省時省力。

這部分巨集寫法,JN參考這一篇。此外還設了一個核取方塊控制即時排序的開關。

物品列表別忘了財產編號,真的在辦公室以財產編號去翻找東西時,會覺得很困難,因為編號又是許多數字,容易看歪就差肩而過了。

別忘了這份清單是要清點的,清點也是很費工費神。只要有使用到其中的物品就上個異動註記,下次清點如何想偷懶,就去點有異動註記的東西就好了。

最後,在設計時別沉迷於技術而忽略了原意。目的是甚麼,其他簡單好懂易操作就好。寫完此篇之後,JN也許有天會改進這份範例的一些弱點,以及增進功能。
1.收納空間‧物品名稱的函數,並非直接實踐,而是拐了幾個彎。要改成巨集直接找。
2.用巨集自動幫物品位置上超連結。

標記為濫用,心得紀錄(純感想與流程)

這是一件不太容易遇到的事情:「標記為濫用」。在別人家的大本營做不好的事情,又用別人家的搜尋引擎搜尋如何處理,又在別人家的部落格發這篇心得,也算久違之後難得有動力寫篇文章。

前情提要:JN編修了程式,這個程式是個報名系統。之前的報名系統,使用者有提及一個狀況:在尖峰時段,有部分人會遺漏掉回信或缺少資料。這個情況算是很常見的問題,當圖時有大量報名者送出,而大量回覆平行運算寫入,以至於同時讀寫到同一行(正常情況下是新送出的回覆會到新的最後一行),而最後只留下最新的一筆。

要解決這個問題,就要延遲寫入,避開多數回覆同時執行寫入。就JN所知,在有「佇列 queue」或「鎖 lock」兩種方式能處理這問題。然而沒有找到佇列指令,但是有發現鎖指令。於是在程式碼中加了鎖,就能達到非同步讀取。因為只有得到鎖的線程才能讀取,其他沒有鎖的就會等待直到拿到鎖。

然而,為了測試大量回覆送出是否會出現異常,JN一共用了五十筆送出,每個送出都上傳3MB大小的檔案。這次測試之後,就發現報名系統其實就是平行運算的問題。關鍵字就是「回覆缺失、大量短時間內」。之後JN把「鎖」寫進程式內,再以三十筆回覆測試之。
(JN所參考的寫法)

這次的結果,回覆都有被寫入資料表裡而沒有任何缺失,只是收到的順序不一定是照送出的順序。大功告成。就不再需要測試這種大量送出的狀況。

卻也釀成帳號「標記為濫用」。


「標記為濫用」是怎麼樣子呢?就是上傳的檔案會被標上一隻小旗子,這些有小旗子的檔案都是「標記為濫用」,被標記的檔案就無法給其他人查看,更無法建立副本。對於一般團隊,這個限制真的很麻煩,自己上傳的檔案都無法分享給別人看。更何況是非自然人的帳戶,大家共用的帳號被標記起來,豈不是許多資料就無法像平常般傳給同事們了。

雖然很麻煩,但另一方面,這一塊也是JN一直所擔心的地方。JN檢查之前的報名系統,便發現也一直提及「安全」的問題,但是請JN編修程式的人一點都不在意這一塊。「安全有比方便重要嗎?」「應該不會有人這樣做吧?」JN聽到的當下是很傻眼的,「被大量傳送攻擊」這個議題就輕易被忽視過去。只要被一次這樣攻擊,就會導致整個系統從此無法順利運作。更何況操作系統的又不是JN,出了甚麼狀況JN也不會有甚麼損失。

想一想就算了,反正寫多少也就多賺工錢,拿錢做事不要想太多,也不要做太多。

但是!對於異常流量的警示,那家公司真的十分厲害。JN欽佩這種會對於「安全」上做了很棒措施的公司,不像委託JN修改程式的人一點都不在乎「安全」這個問題。之前JN也測試過用程式去抓取一些資料,一下子就會被擋下來,對於想收集資料的人,這真的令人厭煩,但不過也就鎖IP,換IP就能繼續抓。但「標記為濫用」應該是鎖帳號,而且快一週了都沒有解鎖,也不知道多久後才不會有事,或是從此就背負一輩子。

也不知道甚麼時候才能解開「標記為濫用」。但是對於不太行的電腦操作者,又會給JN下怎麼樣的任性要求,一想到就會頭痛啊。

問卷的圖像辨識,opencv – python

有天,在中心辦完活動後,回收了一大堆的回饋單,目測有上百張左右。回饋單以人工辨識,費時費力,實在覺得很麻煩。填入回饋單的結果是個重複性高且技術性低的工作,以人力執行,時間都大多花在辨識以及騰入回答,若是交給電腦執行,這將會是秒殺的工作,而且十分準確不會誤看。

圖像辨識跟機器學習一直是JN很想嘗試的一塊,而且這兩塊正是未來的大趨勢,雖然以後人類的工作多半會被機器取代,不過人類就可以有比較多時間思考與享受生活,到時一個全新的社會型態會挑戰人們習慣的認知,你我都被這個時代潮流推著走。

問卷圖像辨識,是一個很好的應用。若為了讀問卷而買讀卡機有點太過於浪費讀卡機的效益,而且讀卡機也限制了問卷的形式,出問卷的人就不能隨心所欲的出問題。JN想要寫出一個,問卷格式能相當自由,同時讀取精度也相當準確的問卷圖像辨識程式。

關於圖像辨識,JN第一個就想到opencv這個處理影像的套件。在之前爬youtube的python教學影片,也有教人使用opencv去做辨識碼的破解。但是,JN很快就發現那教學影片對想要做的功能是勾不著邊的,於是只好進去opencv的官網教程慢慢修練功夫。

JN是用visual studio 2017,並且python環境選用anacoda。然後在「python環境」→「封裝」,搜尋「opencv」,找到「opencv – python」安裝,之後等待流程跑完要很久,這時可以去看個電視節目再回來!

終於等到流程跑完了,這時候就能引用cv2這個套件。JN很快就碰到了大問題,就是系統相容的問題。JN的桌電系統是win7,每當執行cv2裡面的一些函式,程式就會停止運作。接下來使用偵錯程式去找原因,幾次原因都指向「ntdll.dll」。ntdll.dll是一個系統檔案,JN檢查桌電的ntdll.dll檔案,並沒有問題或遺失。然而JN換成筆電,筆電系統是win10,在win10下跑python – opencv就沒有任何異常了。中心的電腦也是win10,一樣沒有問題。所以JN推論,

opencv – python,不適用windows 7作業系統執行。而window 10作業系統可以執行。
老實說,這個問題差點讓JN放棄了,現在也在心想是不是該把桌電換上win10,最後還是決定win7再戰10年!
每次看官網的教程,JN都覺得很簡潔,很多細節沒有寫出來,令JN我總要想很久。這次的opencv更是困難,好幾次JN完全複製官網的程式碼,結果執行時各種錯誤,真的很賭爛。除了官網之外,JN也看了很多中國論壇寫的教學,也推薦看官網不得領悟的人去查看看。
關於問卷辨識系統,其實就是要讓程式以圖像判斷每一選項的填寫情形,並且把判斷的結果輸出。圖像判斷是用官網教程的「Template matching」的功能,這功能用來一個模版圖樣(template)去搜尋另一圖片上相同的模版圖樣。其中有個耐人尋味的閥值(Threshold),閥值高會抓不到對的,太低又會抓到錯的,倒底該設多少才會有最高的精準度呢?這就讓機器學習去告訴你答案吧!
實際上,JN走的路程比理論事先想的的路還多好幾倍。核心運作的功能相當好寫,但是「精準度」及「效率」,就是個永無止境的課題。為了達到JN心中所想的精準度,這套問卷圖像辨識系統被重頭寫兩次。要怎麼讓Template matching能順利抓出所有圖案,機器學習要怎麼安排及應用才能輔助精準度提高,此外如果圖像處理太複雜,整個執行下來會太冗,效率很差(雖然還是會比人工快)。
JN心中所想的精準度是99%。雖然在寫出三次後,測試後,精準度達到99%(事實上跟人工辨識相當了),但是JN仍然覺得不夠,不夠的地方在於給某些嚴苛又機掰的圖像測試會失敗,此外機器學習並沒有完美容入進入整個運作中。
JN寫的問卷圖像辨識還有許多地方要改善與編寫,JN期望某一天,能在Android系統上推出,讓大學生還是XX中心都能普及使用,省下時間來去為了更美好的事物。


後續記事(2018.6.18)
結果不到一年,桌電也換成了win10。JN後來就沒有再改良問卷圖像辨識,因為實體問卷數據化的好壞,決定了圖像辨識的成功度,並不是機械學習、也不是參數設定能讓圖像辨識度成功。
輸入資料的品質好壞,才是最應該先把關的,花時間注意的。圖像辨識,相對只是件較不重要的事。如果不會圖像的概念,處理圖像的技巧,矯正圖像,規格化,之後做的圖像辨識就會有很大的侷限性。
一個程式到底要不要完成,要看它後續到底能換來多少好處。對於一百多份問卷,一個細心的人可以一次全對地填寫,但對程式來說,這樣樣本過少,會學習不到最佳狀況,即使讓這次的問卷學到全對(過度學習),接下來面對下一次的問卷,就會判斷得很失敗,這是可以預期的。
大概就是這樣的感悟,JN也就放棄了改良圖像辨識。

嘗試用Python爬蟲技巧抓混音音樂

這天JN在耍廢休息時瀏覽ptt時,又見到過去頗想收藏的音樂。比較可惜的是,音樂網站只有提供網路播放,並未提供下載按鈕。然而,JN堅信一點「只要能被人聽閱的影音,其檔案一定被下載進自己的電腦裡。」所以,JN相信,只要我能在網路上以正常方式聽閱的任何影音,絕對有方法給它給存下來。
根據之前學過的爬蟲技巧,第一步,先用chrome的Web Developer Tool(F12)並重新整理網頁後觀察。JN很快就發現了影片格式的檔案:mp4以及m4s。接著,打開其影音檔案來源網址,使用另存新檔的方式存下。
存下來的mp4具有正確的影音時間長度卻沒有任何音訊。m4s則為分割的許多片段,直接用影音播放器並不能播放,於是JN試著把副檔名改成mp4、mp3,亦得到同樣的結果:無法播放。
隨手求助google,打上「m4s 合併檔案」搜尋,不過搜尋的結果卻不是很滿意,前幾個結果內容所說的程式,從程式的官方網站看,卻得到程式碼,而不是直接能用並帶有介面的程式。不然就是被防毒軟體掃出有疑慮,最後找到的程式碼也並非JN所學過的程式語言。用英文搜尋也是差不多的答案。
不過雖然程式碼並非JN所學過的程式語言,但JN觀其架構是十分簡單的,其運作過程只是把mp4(開頭)以byte方式合併其他的m4s(片段),最後就能得到所想要目標影音檔案。如果是正常能播放的影音檔案,就能用剪接軟體。不過碰到byte合併的方式,寫程式碼就是最簡單的方法。
byte合併可以直接以字元提示命令去執行,但JN不是很熟這個,JN比較熟悉python,於是用python去寫。
當使用Web Developer Tool去看時,JN就發現影音片段(m4s)會隨時間下載後面的片段,並不會一開始就把全部的片段都下載完,這段於提升網頁瀏覽速度是很有幫助,不過卻苦了想直接下載一個完整檔案的人。觀察其片段的網址,各片段的網址取決於編號的不同,所以只需要改編號,就能抓到想要的片段。JN直接嘗試1000號片段,跑出結果404,只要超過片段數,就會抓到404網頁,然後看檔案大小就知道從哪一編號後的就是抓到404網頁。
要抓所有片段很簡單,只需要用迴圈產生各片段網址,然後用python的request套件去get各片段網址,得到「get的結果」,並且用python的shutil套件把「get的結果」存成m4s影音。這些技巧都是從網路教學的爬蟲影片學到的,JN在第一篇的python網誌有寫到從哪裡學到的。最後,JN當時所抓的片段有七百多個,每個平均90kb。
所要的檔案都齊了:開頭的mp4、所有的m4s片段,接下來就是要合起來了。
別忘了要以byte方式合併,JN用python內建的open函式,以wb的方式寫一個輸出檔案。程式碼如下。
AllFragments = open(output.mp4,’wb’)
接著其他的片段,也是以open函式以’rb’來讀取,然後以read得到其byte數據。接下來,照順序以mp4的byte數據、以編號順序片段的byte數據,寫進輸出檔案裡。這樣就完成以byte方式合併所有片段了。
最後,用播放器打開輸出檔案,能夠播放,而且也與網路上所聽到的相同。直接跳到最後十秒測試,也沒有缺少音訊。這就是JN所想要的影音檔了。
後記:
JN猜測,其實合併各片段的程式碼,應該在第一步的時候就已經抓到了。猜測是程式語言javascript去做這次JN用python做的事情。但只是JN的功力不夠深厚,挖掘經驗太淺,沒辦法第一時間撈出來。希望有一天記得要再好好地把這個程式碼給揪出來,而不靠自己閉門造車。
會講混音音樂是個提示,並不是指各網站的混音音樂。
這個程式碼可以繼續寫成音樂爬蟲,能夠爬下所有的音樂並且合併輸出。不過JN觀察,該網站應該有下載流量限制,想快速抓完音樂需要有點技巧。

臺鐵大誤點以及捲簾式快門

今天JN去考全民英檢中高級,考完後,跟朋友去棒壘球打擊場打球。那時看了一下火車時刻表,有班車晚了一百多分鐘。因為出門後就沒有機會看到新聞,所以並不知道臺鐵發生電線脫落的意外,於是就選擇到火車站等車,達成人生史上等最久的車。
看到大誤點,延誤一百多、兩百多分鐘就好像在叫賣一樣,隨便喊價。難得碰到,拍下來做個記錄好了。正當拿著手機去照的時候,發現手機螢幕的跑馬燈,竟然跟以下影片一樣。

實際上肉眼看到的不是這個樣子,肉眼看到的是全部都亮的,如(圖一)的照片。JN看到這個現象就知道為甚麼。之後月臺又來了一對母子,那個媽媽拿起手機想拍下來,也同樣看到這個現象,然後困惑地說為什麼會這樣子。再後面又有一對情侶檔,也個那個媽媽一樣,並不了解為什麼拍起來會這樣。

(圖一)

JN直接講答案,因為手機是捲簾式快門,而且跑馬燈是超過人眼能捕捉的速度閃爍。捲簾式快門,顧名思義,就是像捲簾那樣,按下快門後,一條條曝光,最後形成一張完整的照片。一條條掃過的瞬間很快,於是我們拿手機拍照時,通常不會察覺到。人眼看到,相當於全局快門,也就是所有畫面一次曝光成像,同時閃爍過快導致看起來就像恆亮,因此看跑馬燈就不有像相機這樣一條條的問題。

這是JN認為為什麼會這樣子的答案,不知道大家有沒有自己的答案呢?

(圖二)
對了,照我的推論來看,手機拍起來應該要像(圖二)才對,為甚麼我能拍出(圖一)呢?厲害一點的人應該已經看出來是怎麼拍的了,就是調長快門時間。只要增加曝光的時間,比閃爍的間隔久,就能拍出(圖一)跟人眼看到的差不多。

這個現象有沒有很有趣呢?下次在臺鐵月台等車時,也不仿讓同行的朋友看看這個現象,讓他們動腦筋吧!

2017.9.23 學習了一個月的python歷程

在八月中,參加歡送朋友的聚會,會後歸程,我向友人提出想要學習大數據的想法。之後處理完宿舍後,回到家,JN就開始尋找大數據的相關資訊。透過Google第一個找到的,「Weka」(官方網站)。

在簡單使用Weka過後,JN明白Weka是一套分析數據的軟體。這套軟體能夠找出各數據間的相關性,近而找出影響結果成敗的因素。但是!如果手上沒有資料,那就無法進行數據分析了。為了要取得一套可供分析的資料,於是JN找到了Python,想藉Python抓取到一份資料,裡面至少有上千筆的記錄,完成一輪無法透過人力逐一蒐集資料(如果人力允許,就用excel即可應付),必須得透過程式才能完成的數據分析。

在大四的時候,JN跟系上導師聊天,導師那時就跟我提過Python,雖然系上都用R跟SAS作數據分析,不過系上教的R實在是有夠無趣而不令人想學習。Python也有一套數據分析的套件。根據這一個月體驗過後,JN覺得Python是很棒的語言,當然也有很多使用Python進行開發,像是Google、FB等。

Python的優點是甚麼?更簡單更人性化的編寫方式。JN接觸過這其他幾種程式語言:C++、C#、Java、Javascript,JN發現越新的語言,在函式跟變數的定義上越簡單。JN接觸到了Python,變數不用先宣告,函式也不用指定輸入的變數類型,每句結束不用;(分號),而是用縮排,這也使得程式碼看起來整齊多。當然還有很多不一樣的地方,不一一敘述。

JN的開發環境是Visual Studio 2017,並安裝了Anaconda,然後在Visual Studio的python環境使用Anaconda去編寫。JN認為,程式語言得按教科書按部就班學習是很無聊的,光是一個變數加減就能寫三頁,然後一本書有上千頁,也不知道讀了那麼多指令能用在哪裡。

任務導向式的學習,對於解決問題有很大的幫助。因為要完成任務,所以必須要學會相關的指令,不然無法達成目標。於是JN先不急著抓下一份上千筆的資料,而是先依序慢慢學習抓資料。以下是JN的Python任務:

抓ptt表特版→抓ptt西斯版→抓dcard西斯版→抓食譜網的食譜→抓股市即時資料

這流程的任務的困難度是逐漸增加的。不過一開始,JN也不熟悉Python真正強大的地方,沒有看過任何Python書籍,對於完成任務要怎麼寫是毫無頭緒的。其實啟發JN能寫出網路爬蟲的,是看了這一個Youtube頻道(大數軟體有限公司),裡面有很多關於python爬蟲的影片,影片不長,內容精要,照著做就能順利抓下資料,是一個很好的範例去參考。看這影片教學,你不會學到其他沒有用到的東西,從影片學到的東西都是直取目的會用到的。

很快地,就能碰到爬蟲所要使用的指令與套件。對於JN這種沒有資歷的人,花上了好幾天的學習,才弄懂程式碼的運作方式,像是BeautifulSoup(BS)套件是能夠剖析HTML(HyperText Markup Language)。HTML編寫是用標籤為架構,瞭解HTML的架構的特性,用BS就能取出要的部分。

這篇網誌也不是Python教學,所以想學Python還是去看看影片或是書會比較能瞭解Python要如何使用。

到了現在,JN只有看了幾本書,不過都不是python的。對於初學者來說,以JN這種任務導向型的人,蠻推薦o’reilly的head first系列去看,因為書中每一章節的例子都是要解決問題,而且書中的程式碼難度不高,是很平易近人的。初學者不會一開始就被教材難度的門檻撞死,才能學到東西並繼續延伸,之後若有興趣就再去讀其他講說更深入的書籍。

我的Python學習歷程(補充)

1.在「抓ptt表特版」時,學到了BeautifulSoup套件、Request套件、Shutil套件(儲存圖片)、re套件(過濾字串)。

2.在「抓ptt西斯版」時,學到了Request中的session、post,os套件(創立資料夾與切換工作目錄)。

3.在「抓dcard西斯版」時,把之前所學到的精良化,節省程式碼,更瞭解網頁的運作模式,回傳資料的類型。

4.在「抓食譜網的食譜」時,學到了Pyodbc(把資料寫進資料庫)、SQL指令、以及資料類型。

5.精良化「抓ptt表特版」,學到了多線抓取,python不再按部就班的一篇篇文章進去抓取、退出再換下一篇。提高的抓取效率。

6.在「抓股市即時資料」時,學到了Selenium,它能模擬瀏覽器做動作。GTTS套件(使用google語音)、Pygame套件(播放聲音)。

忘記行李後的心情低落

在2017年8月11日晚上六點二十分到達台北車站,發現台鐵南下列車均發生了誤點,緣由是因為號誌故障,導致大部分的車都延遲了二十分鐘發車。
JN我看到月台上擁擠的人,就知道不妙了,原本要搭的車延遲了二十多分鐘,導致我還要站半小時才能搭到車。剛好有一班列車進站,只行駛到新竹,想說我要是搭這班到新竹,再轉到我本來要搭的車,這段空檔時間,我可以出站買杯手搖飲後爽快回家。
上車後,真的很擠,我知道會站很久,於是把行李袋放到置物架上。大約到了中壢有了位置坐,就坐了。很無聊地滑手機,查看待會要去新竹買甚麼手搖飲。終於到了這班車的終點站,踏出輕快的腳步下車,一種很久違的感覺,先看看我有多少空檔可以溜出新竹火車站。只有十幾分鐘,我覺得不行。
就繞了新竹火車站外圍一圈,然後想到了充電器,就想到充電器在行李袋,一個快步進站,車早已離去。我想起那班車到了新竹之後,就換成往北上行駛。只是進了車站後,就不想回到大廳去說明遺失物。想說到了竹南再說,順便買線材。
說到線材,去年換了手機,原本手機傳輸線就不適用,得換成Type-C的線材。原本有多買一條,但是接觸不良,我幹你老師咧,不到幾下就退役了,絕對是錢坑。原廠的Type-C線真的很耐用,因為續航力夠而一天就充一次飽飽就夠了,線一條就足夠應付了。但是,還是遲早得再買一條Type-C傳輸線備用,這我遲早就知道的。
充電器已隨著行李袋遠去,Type-C的線在手機界中,並不算普遍的。我總算不得不再買條Type-C的線,不然手機明天就沒有電了。查了一下手機店,不過出了火車站一下就看到了,進門直接說要買Type-C傳輸線,這個錢花得心疼卻是不得不花的。店員拿了雙條線一組的,報價說490。算了,就算知道買貴又怎樣,但極需的人是不得說不的,不多說掏錢買了。
終於等到家人來接我了,比預期晚了快一個小時才到家。手搖飲沒喝到、行李遺失了、又得花錢買了線材好維持我手機電力。真是禍不單行,JN替自己都覺得好難過。加上昨天的手機原廠耳機也壞一聲道,立刻再買一條來維持我平常用耳機的感覺,但這個壞是自然又加上不小心摧殘多次,JN覺得原廠耳機很耐用。這兩天,通訊周邊就噴掉一千。
在下竹南車站時,立刻去反應我的行李袋掉到哪個車次的車上。但是站務人員對於我的搭車模式無法理解,我知道車就在那班車上,但是他們還是按他們步調,重頭按部就班的推論出是哪班車,這沒啥關係,至少有推論到正確的結果。在到竹南站前,我有回想起行李內容物是甚麼,但是沒有想得完全,在他們詢問的時候,竟然忘記一樣很重要的東西沒說到,不過說到的都有中,應該不會覺得要把內容物全部說到才是失主吧,哈哈。之後就留下資料等通知了,之後的當晚都沒有台鐵的電話,先預想是直接協尋是沒有下文了,我看我要跑到基隆去照正常流程領了吧,唉唉唉。
雖然自己是不容易犯忘記東西的錯,偶爾難免會犯,不過犯的當下好像是命運逼得你一定要犯。讓人記憶完全回想不起來在哪裡,推論也找不出來,非常的奇妙。我的一本書就是這樣找不到了,那本書是從圖書館借來的,這樣的話,八成可能性要賠錢了事了。不過我對我的行李袋印象深刻,相信過幾天就能拿回手裡。但是那本遺失的書,只好認輸而賠吧,但這就又要花錢了。
心情低落,唉唉唉,覺得自己像一般人的時刻。QQ