iT邦幫忙

2021 iThome 鐵人賽

DAY 30
0
Software Development

妄想對自己的Windows優化兼Debug的工程師很正常吧系列 第 30

是工程師就自己簡化流程,這種熱血你我都有吧!?Windows鐵人無腦自動發文帶出30天最終大結局

終於,這天還是來了,是時候該做個了結,基於純粹的好奇跟想炫技,想要認識Windows把她改造成自己喜歡的樣子,這樣的工程師到底正不正常,是第30天終究要解釋的!(文末有結局,精華必看)


前情提要

第1天我們簡單說了30天的目標,順便示範了Windows備份和磁碟切割。
第2天到第14天我們介紹了所謂的登錄檔,知道他是如何運作跟操作,他的意義和清理,REG檔的撰寫我們也一並探討。
第15天說了一些軟體安裝造成的系統影響。
第16,17天研究了一下Windows特殊資料夾的意義,批次檔路徑,系統權限等等。
第18天到24天我們學習事件檢視器,討論怎麼認識了解一個事件,甚至對日誌檔進行手動改寫的操作。
第25-27天分別介紹了元件服務,工作排程器,效能監視器。
第28,29天提了命令提示字元和PowerShell的用法和指令稿的寫法。

最後!來到了這一天,筆者想帶給各位一個想法,Windows不再只是一個作業系統,我們不需要凡事都靠第三方軟體才能達成,有時候用的習慣就忘了其實還有很多功能可以讓我們來尋寶,為了證實這點,我就親自挑戰用PowerShell寫個腳本來讓鐵人文章可以定時自動發文,全程使用Windows內建的功能,不用另外寫Python,用最天真的想法去實現他。


確認目標

照著你最真實的需求去做!通常要自動發文,就是文章大致都完成了,不想為了每天發一篇文章而特地去記得然後去發文,而且為了看出預覽狀況,你一定先在鐵文發文區打完文章的草稿了。
再來我們會使用自己平常的筆電,用著自己習慣的瀏覽器,在這裡已經有大大幫我們看出,IT邦幫忙的cookie有效期限是30天,可以確保登入狀態不會中途消失。

所以我們的現狀大概會是,設定一個排程指向一個.ps1腳本,執行任務是把當天的草稿按下發表文章送出去。
(先偷偷透露成品給大家看)
Imgur


Coding前的思路

想像執行任務應該是先用瀏覽器開啟文章列表,網址應該是:

https://ithelp.ithome.com.tw/users/<個人代碼>/articles?page=<頁碼>

(其中page如果是第一頁也不用加上問號後的page參數)

接著是我們如何判斷今天要發的文章是哪一篇,在這裡有個剛剛好的技巧,我們照順序打完所有文章的草稿後,最後一篇草稿就是當天要發的,而發出去的新文章會往上疊,最後一篇草稿剛剛好還是下一篇要的發的,簡單來說就是個佇列結構,FIFO原則,以下面這個例子來看,下一篇要發的草稿會一直維持在由上往下數來第7篇文章,我稱這個數為todayArticleNumber
Imgur

筆者想到最簡單方法就是我們設計指令一直去按Tab鍵去選中要發的文,但是因為中間每篇文章的Tag數不固定,所以也不能確定幾次Tab鍵一定會到要發的草稿文上。

這時候我們去觀察草稿文連結網址,發現他是以下形式:

https://ithelp.ithome.com.tw/articles/<文章代碼>/draft

所以我們應該可以搜尋https://ithelp.ithome.com.tw/articles/.......開頭的連結,往下數到一個特定的個數,就會是當天要發的草稿連結按鈕,但是!後來發現這需要用到Invoke-WebRequest cmdlet,還需要登入狀態所以要提取cookie等等就變得更複雜了。

為了符合我們的簡單暴力美學,最後我想到一個可行替代方案,只要我們用ctrl+F搜尋特定關鍵字,這個關鍵字必須是每篇文章都包含的,例如由 <用戶名> 分享這個詞,就可以尋找到10個符合的結果,剛剛好對應10篇文章,這時候再由上往下數到第todayArticleNumber個結果,以這裡的例子來看就是第7個,會剛剛好選到我們要發表的文章。
Imgur

不過我們用的由 <用戶名> 分享這個關鍵字因為是在文章的結尾,當我們關閉搜尋功能後按下Tab鍵選中的會是下一篇文章連結,所以實際上我們要尋找第(todayArticleNumber – 1)個結果,中間就需要按(todayArticleNumber – 2)次Enter,這樣後面按Tab才會到當天要發的文章連結,像在這個例子中我們需要按5次Enter選中第6篇文章的由 Zero皇 分享

在這中間我們還需要設定剪貼簿的功能,因為在搜尋框中打字會發生不確定現在是什麼輸入法的問題,所以這部分的流程就是先把關鍵字由 <用戶名> 分享放到剪貼簿,Ctrl+F搜尋,Ctrl+V貼上關鍵字,Enter todayArticleNumber – 2次到目標文章的前一篇文章結尾,接著按三次Tab選中搜尋框的叉叉,Enter關閉搜尋框。
Imgur
最後按Tab一次到目標文章,再Enter一次進入草稿編輯頁。

接著我們到當天的草稿區後,如果要按到發表文章的按鈕,直接Tab往下按最後會遇到內文區的文字框,變成輸入Tab空格沒辦法繼續選到頁尾的發文鈕,我們可以用個變通方法,那就逆向選取從網頁末端選回來就好,Google一下會發現Shift+Tab是逆向選取的方法。

實際測試我們發現,當按到第11個逆向Tab(Shift+Tab)的時候,會選中儲存文章旁邊的三角形選項。
Imgur

下一個指令按Enter打開選單。
Imgur

再按一次Tab會選中我們最後的目標按鈕。
Imgur

最後Enter出去應該就可以發文成功了!

所以全部的流程應該如下:

  1. 用瀏覽器開啟文章列表網址。
  2. 設置關鍵字由 <用戶名> 分享到剪貼簿。
  3. 按Ctrl+F搜尋。
  4. 按Ctrl+V貼上關鍵字。
  5. 按Enter todayArticleNumber – 2次到目標文章的前一篇結尾。
  6. 按三次Tab選中搜尋框的叉叉。
  7. 按Enter關閉搜尋框。
  8. 按一次Tab到目標文章
  9. 按Enter進入草稿編輯頁。
  10. 逆向按11個Tab選中三角形按鈕。
  11. 按Enter點開選單。
  12. 按一個Tab,這時候應該要選中發表文章的按鈕。
  13. 最後按Enter會發出文章,測試時要確保前12步驟沒有失誤。

但是為了避免瀏覽器反應的速度跟不上一連串的按鍵指令,所以我們在必要時還是要暫停幾秒的程式。

所以我們會需要四類指令,下面順便給各位官網連結:

  1. 開啟瀏覽器打開網頁的指令
  2. 設置特定字串到剪貼簿的指令
  3. 各種按鍵輸入的指令
  4. 暫停程式的指令

實作程式

首先我們打開Windows PowerShell ISE來寫.ps1檔~

  1. 用瀏覽器開啟文章列表網址,建立$personCode跟$page兩個變數來存草稿所在網頁的資訊,不過powerShell的字串加上數字變數會被自動轉型為全字串,所以兩個變數有沒有加雙引號都可以。
#用瀏覽器開啟文章列表網址
$personCode = "個人網址代碼"
$page = "最後草稿所在頁碼"
START-Process chrome.exe "https://ithelp.ithome.com.tw/users/$personCode /articles?page=$page"
  1. 設置剪貼簿的指令是Set-Clipboard -Value <要被複製到剪貼簿字串>所以我們一樣使用一個變數$keyWord來存放字串。
#把$keyWord複製到剪貼簿中
$keyWord = "由 <用戶名> 分享"
Set-Clipboard -Value $keyWord
  1. 往後要進行按鍵輸入的指令,通常前面會寫兩行先聚焦在特定視窗的指令,但上面我們用指令打開瀏覽器的時候焦點就在就在瀏覽器上了,所以下面這段主要是在測試的時候可以使用:
#聚焦當前視窗在<程式名>上
[void] [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
[Microsoft.VisualBasic.Interaction]::AppActivate("程式名")

下接著寫一行載入System.Windows.Forms功能的指令,這是模擬按鍵事件需要的功能物件,後面就可以直接提取。

#載入按鍵輸入必要物件System.Windows.Forms
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

再來是輸入按鍵的指令,特殊鍵用{}表示,例如{TAB},{ENTER},Ctrl用^,Shift用+,不過使用組合鍵的時候有些程式會區分大小寫需要測試才知道,例如Ctrl+F筆者發現用"^F"會無效,用"^f"則會成功:

#模擬按鍵輸入
[System.Windows.Forms.SendKeys]::SendWait("要輸入的字串")

然後為了讓前面開啟網頁還有聚焦視窗有緩衝時間,必須先讓程式暫停數秒,其指令為

#暫停程式<秒數>秒
Start-Sleep -Seconds <秒數>

所以第3步的程式如下:

#暫停3秒瀏覽器開啟緩衝時間
Start-Sleep -Seconds 3
#聚焦當前視窗在chrome上
[void] [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
[Microsoft.VisualBasic.Interaction]::AppActivate("chrome")
#暫停1秒視窗聚焦緩衝時間
Start-Sleep -Seconds 1
#載入按鍵輸入必要物件System.Windows.Forms
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
#模擬按鍵輸入Ctrl+f
[System.Windows.Forms.SendKeys]::SendWait("^f")
  1. 按Ctrl+V貼上關鍵字,這個時候會瀏覽器會直接搜尋,所以再暫停1秒緩衝。
[System.Windows.Forms.SendKeys]::SendWait("^V")
Start-Sleep -Seconds 1
  1. 按Enter(todayArticleNumber - 2)次到目標文章的前一篇結尾,這個todayArticleNumber就必須提前先算好,一樣用個變數存起來,而重複N次單按某個鍵可以用{鍵名 N}表示,但是N必須事先計算好,所以我們再建一個變數$EnterCount = $todayArticleNumber-2來代替N,就會像以下這樣:
$todayArticleNumber = 7
$EnterCount = $todayArticleNumber-2
[System.Windows.Forms.SendKeys]::SendWait("{Enter $EnterCount}")

6.-9. 按三次Tab選中搜尋框的叉叉,再按Enter關閉搜尋框,再按一次Tab到目標文章,再按Enter進入草稿編輯頁,最後再等待網頁緩衝時間3秒。

[System.Windows.Forms.SendKeys]::SendWait("{TAB 3}")
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
[System.Windows.Forms.SendKeys]::SendWait("{TAB}")
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
Start-Sleep -Seconds 3
  1. 逆向按11個Tab,用+代表Shift,跟著後面連按的11次Tab鍵,這時候要注意程式應該要選到三角形按鈕,選錯可能會選到隱藏的刪除文章按鈕,在測試的時候多注意。
#逆向按11個Tab鍵
[System.Windows.Forms.SendKeys]::SendWait("+{TAB 11}")

11.-12. 按Enter,再按一個Tab,這時候應該要選中發表文章的按鈕,測試也要多注意。

[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
[System.Windows.Forms.SendKeys]::SendWait("{TAB}")
  1. 最後按Enter會發出文章,測試時要確保前12步驟沒有失誤。
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")

最後我們可以加上個貼心的設計,因為在程式被排程執行的時候使用者不能跟電腦互動否則步驟會亂掉,所以可以在第一行加上一句timeout /t 10,讓程式暫停到數10秒後再開始,或按任意鍵可以直接開始,最前面再顯示一行”鐵人自動發文程序即將於10秒後啟動,請暫停操作系統”就更帥了!

“鐵人自動發文程序即將於10秒後啟動,請暫停操作系統”
timeout /t 10

然後要用全Windows內建程式操作的話就把Chrome改成Edge登入即可,操作過程都一樣,不過筆者建議用自己習慣的瀏覽器才是最好的,方法就是把chrome改成microsoft-edge:

START-Process microsoft-edge:"https://ithelp.ithome.com.tw/users/$personCode /articles?page=$page"

全部湊起來就會長這樣,我們把需要設置的變數提到倒數秒數之前先載入,另外提醒,程式暫停的緩衝秒數因應個人電腦狀況不同也要自行調配:

"鐵人自動發文程序即將於10秒後啟動,請暫停操作系統"

#個人設置區
$personCode = "20140925"
#改成個人網址代碼
$page = "1"
#改成最後草稿所在頁碼
$todayArticleNumber = 7
#改成下篇要發的草稿在文章列表由上往下數來的第幾篇文章
$keyWord = "由 Zero皇 分享"
#改成"由 <用戶名> 分享"

timeout /t 10

#1.
#要用Edge則把"chrome.exe "改成"microsoft-edge:"
START-Process chrome.exe "https://ithelp.ithome.com.tw/users/$personCode/articles?page=$page"

#2.
Set-Clipboard -Value $keyWord

#3.
#暫停3秒瀏覽器開啟緩衝時間
Start-Sleep -Seconds 3
#聚焦當前視窗在chrome上
#[void] [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
#[Microsoft.VisualBasic.Interaction]::AppActivate("chrome")
#暫停1秒視窗聚焦緩衝時間
#Start-Sleep -Seconds 1
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.SendKeys]::SendWait("^f")

#4.
[System.Windows.Forms.SendKeys]::SendWait("^V")
#暫停1秒搜尋緩衝時間
Start-Sleep -Seconds 1

#5.
$EnterCount = $todayArticleNumber-2
[System.Windows.Forms.SendKeys]::SendWait("{Enter $EnterCount}")

#6.-9.
[System.Windows.Forms.SendKeys]::SendWait("{TAB 3}")
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
[System.Windows.Forms.SendKeys]::SendWait("{TAB}")
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
#暫停3秒網頁開啟緩衝時間
Start-Sleep -Seconds 3

#10.
[System.Windows.Forms.SendKeys]::SendWait("+{TAB 11}")
#確認選到三角形按鈕

#11.-12.
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
[System.Windows.Forms.SendKeys]::SendWait("{TAB}")

#13.
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
#確保前12步驟沒有失誤

Imgur

最後我們把他放進工作排程器,就完成每天自動執行的功能了,這部分可以參考工作排程器--Windows的忠實程序秘書這篇,設定完大概像這樣:
Imgur
Imgur


30天最終大結局

其實這個想法很單純天真,前後基本上只用了四種指令,就是用powerShell直接操作GUI介面,暴力找出我們要的連結然後全部用按鍵點進去而已,也不用提取cookie或是判斷標籤ID,說實在這支程式可能不耐用,或許一個官網改版就要修一次,但他符合一種,只要你想做就可以用最基本的方式做到的心態,想學就去學,身為工程師這種想要對自己電腦好一點的想法,去愛上自己獨一無二的Windows很正常吧?

妄想對她優化對她Debug,改造成自己想要的樣子,如果你還稱得上她一聲夥伴,油然而生的感情會讓你跟她走上一輩子,解除軟體生命週期的約束,超越系統的堅定,這樣的妄想是一種熟悉感,一種踏實感,一種幸福感。

好比自行車手愛著他的自行車,我們也該對自己的Windows抱有期待和支持,一套Windows系統你至今用了多久,他還有無止盡的One Piece等你去找,期待你繼續探索下去,對我這30天的內容有興趣的話,都歡迎你在下方留言跟我討論,Zero皇感謝看到這裡的各位啦~ 後會有期囉!

Imgur

Imgur
參考資料:
https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/sendkeys-statement
https://stackoverflow.com/questions/3429863/sendkeys-ctrl-a-not-working
https://stackoverflow.com/questions/57053312/how-to-open-edge-using-powershell-variable


上一篇
PowerShell--除了CMD你還可以用他追求你想要的Windows
系列文
妄想對自己的Windows優化兼Debug的工程師很正常吧30
0
jafarwu
iT邦新手 5 級 ‧ 2021-10-09 20:49:57

/images/emoticon/emoticon37.gif恭喜完賽~~

Zero皇 iT邦新手 5 級 ‧ 2021-10-10 19:16:38 檢舉

謝謝啦~~

0
Marshal
iT邦新手 5 級 ‧ 2021-10-09 21:00:53

每篇都非常用心地撰寫!!推推!!!
資工冷知識實在是太受用了!!
恭喜完賽!!!!/images/emoticon/emoticon12.gif

Zero皇 iT邦新手 5 級 ‧ 2021-10-10 19:17:45 檢舉

謝謝推薦哈哈~ 冷知識很好玩(但一開始沒想過會堅持30天就是了XD)

0
zoeke9011
iT邦新手 5 級 ‧ 2021-10-13 00:09:33

耶!!!恭喜完賽!!你的題材真的很酷!!

Zero皇 iT邦新手 5 級 ‧ 2021-10-13 00:56:28 檢舉

謝謝哈哈~ 單純因為很酷有興趣才選的XD

我要留言

立即登入留言