Quantcast
Channel: WFU BLOG
Viewing all 784 articles
Browse latest View live

2023 使用 Google 新版 API 取得使用者個人資訊﹍實作範例

$
0
0
之前寫的「使用 Google People API 取得使用者生日、性別等多種個人資訊」,因為「Google 更新登入 API 及相關模組」,官方公告在 2023/3/31 舊版程式碼會失效,連帶也使能夠取得 Google 帳號個人資訊的 People API 必須更新程式碼。 由於變動相當大,例如原本使用的 gapi.auth2 模組將被 google.accounts.oauth2 模組取代,載入 API 的方式也差別很大,詳情可參考官網文件「網站適用的 Google 第三方 JavaScript 程式庫」。直接更新上一篇原文可能會造成相當大的混亂,乾脆重新寫一篇。因此關於 Google People API 的介紹、測試方法請看上一篇,本篇直接提供新版操作流程及範例程式碼。 (圖片出處: pixabay.com)

一、準備動作

操作 API 之前需要先建立 Google API 專案,如果還沒建立過的話,請完成以下流程:

二、程式碼範例

以下使用「Bootstrap 按鈕」、jQuery 處理點擊按鈕,作為範例程式碼: <!--jQuery--> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script> <!--Bootstrap--> <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" /> <!--登入、登出按鈕--> <button id="google_login" class="btn btn-large btn-primary">GOOGLE 登入</button> <button id="google_logout" class="btn btn-large btn-warning">GOOGLE 登出</button> <p /> <p>目前狀態:</p> <div id="persoanl_info">尚未授權</div> <script> var client_id = "5432xxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com", apiKey = "AIzaxxxxxxxxxxxxxxxxxxxxxx", scope = "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/user.gender.read https://www.googleapis.com/auth/user.birthday.read", personFields = "names,emailAddresses,photos,genders,birthdays", discovery_doc = "https://www.googleapis.com/discovery/v1/apis/people/v1/rest", resourceName = "people/me", $persoanl_info = $("#persoanl_info"), tokenClient; loadApi(); // 載入 google api function loadApi() { // 載入 gapi $.getScript("https://apis.google.com/js/api.js", function() { gapi.load("client", function() { gapi.client.init({ apiKey: apiKey, discoveryDocs: [discovery_doc], }); }); }); // 載入 gsi $.getScript("https://accounts.google.com/gsi/client", function() { tokenClient = google.accounts.oauth2.initTokenClient({ client_id: client_id, scope: scope, callback: signIn_callback, error_callback: error_callback }); }); // 登入後 callback function signIn_callback(res) { // 登入失敗時 if (res.error !== undefined) { console.log(res.error); $persoanl_info.html(res.error); } // 登入成功後 if (res && res.access_token) { // 顯示帳號資訊 listAccountInfo(); } } // 捕捉非 OAuth 錯誤 或是在傳回 OAuth 回應前遭到關閉 function error_callback(res) { console.log(res); $persoanl_info.html(res.message); } // 顯示帳號資訊 function listAccountInfo() { // 呼叫 people api 取得資料 gapi.client.people.people.get({ "resourceName": resourceName, "personFields": personFields, }).then(function(res) { // 顯示資料 var result = res.result, errorMessage = "請重新登入, 並勾選生日及性別!", html = "", id, name, imgUrl, email, gender, birthday, birthdayStr; // 沒有勾選生日、性別時 if (!result.genders || !result.birthdays) { alert(errorMessage); $persoanl_info.html(errorMessage); return; } id = result.resourceName.split("/")[1]; name = result.names[0].displayName; imgUrl = result.photos[0].url; email = result.emailAddresses[0].value; gender = result.genders[0].formattedValue; birthday = result.birthdays[1].date; birthdayStr = birthday.year + "-" + birthday.month + "-" + birthday.day; html += "ID: " + id + "<br/>"; html += "暱稱: " + name + "<br/>"; html += "頭像:<img style='width: 40 px;' src='" + imgUrl + "'/><br/>"; html += "email:" + email + "<br/>"; html += "性別:" + gender + "<br/>"; html += "生日:" + birthdayStr + "<br/>"; $persoanl_info.html(html); }); } } // 點擊登入按鈕 $("#google_login").click(function() { // 進動畫 $persoanl_info.html("<img src='https://lh5.googleusercontent.com/-EyVZ0f8J0qQ/UCeEG7aa8nI/AAAAAAAADtY/9sXw53XkYXM/s512/indicator-light.gif' /> <span>請稍後...</span>"); if (gapi.client.getToken() === null) { // 未登入則彈出登入視窗 tokenClient.requestAccessToken(); } else { // 已登入則不彈出視窗 tokenClient.requestAccessToken({ prompt: "" }); } }); // 點擊登出按鈕 $("#google_logout").click(function() { var token = gapi.client.getToken(); if (token !== null) { google.accounts.oauth2.revoke(token.access_token); gapi.client.setToken(""); // 登出後的動作 $persoanl_info.html("已登出"); } }); </script>紅字字串參數請特別注意以下說明:
  • client_id:請置換為前面「處理 OAuth 憑證」流程取得的「用戶端 ID」
  • apiKey:請置換為前面「取得 Google API 金鑰流程」取得的「API 金鑰」字串
  • scope:請參考上一篇「一、People API 說明」填入需要的 scope 授權,每個網址字串用空格隔開
  • personFields:請參考上一篇「一、People API 說明」填入想取得的個人資料項目,每個項目用小寫逗號隔開
  • 範例程式碼請放在前面「處理 OAuth 憑證」流程設定的網站才能執行

三、DEMO 效果展示

以下為範例程式碼的執行效果,可進行操作並注意對應狀態的顯示資訊:
目前狀態:
尚未授權
更多 Google 相關文章:

輕鬆打造網站置頂公告,抓住訪客注意力!

$
0
0
大部分情況下網站公告通常放在首頁,或是有經營 FB 粉絲團、社群媒體的話,重大事情在社群發佈的效果會比較好。然而這樣的處理方式,只能將公告內容擴散給粉絲。至於非粉絲的話,例如從搜尋引擎而來、或從別的網站連結過來的訪客,基本上只會到達文章頁面,不會看到首頁或社群媒體。 所以網站最好的處理方式,是在每個頁面最上方都出現置頂公告,如此可以確保通知所有訪客無漏網之魚。 對於一般部落格而言,可能不會經常有大事件需要公告,不過倒是可以借用置頂公告當作廣告,拿來宣傳「特定的熱門文章」、「社群媒體」等等。如果是商業用途的網站,那麼置頂公告更好用了,可以作為長期宣傳「商品」、「經營業務」、「加入會員」等等之用。 置頂公告這樣的功能已經有現成的免費/付費工具,不過因為這功能很簡單,乾脆自己開發一個,網站也可減少不必要的外連 JS(可參考「為何部落格最好避免第三方外掛」),避免拖慢網頁速度。 以下先介紹這個小工具的功能,想要直接安裝可跳至「二、安裝程式碼」 (圖片出處: pexels.com)

一、置頂公告功能介紹

  • 如果是注重使用者體驗的站長,會希望置頂公告可以讓訪客看過後選擇關閉,將來不再出現同樣的重複訊息,所以此工具可以設定置頂公告是否永遠顯示
  • 比較低調的站長或許希望公告不要浮動「置頂」,而是改為浮動置底,此工具也可選擇出現在網頁底部。
  • 如果開啟「關閉公告」的功能,當訪客關閉公告後,系統會自動記憶上次的公告內容。當下次站長更新公告內容時,置頂公告會重新顯示,直到訪客按下關閉為止。
  • 此工具預設了幾種顏色模版,可自行修改背景顏色、按鈕顏色等等
  • 如果熟悉 CSS 的話,可以自行調整所有 CSS 細節

二、安裝程式碼

在修改範本之前,如果第一次安裝本站工具的讀者,建議先閱讀「備份範本的訣竅」系列文章。 請到後台「主題」→ "自訂" 按鈕右方的下拉圖示 →「編輯 HTML」,游標點進範本區塊,按 Ctrl-F 搜尋 </body>這個字串,找到後在此字串的前一行,插入以下程式碼: <script src='//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js'></script><!--Blogger 置頂公告 --> <script> //<![CDATA[ (function($) { var newBulletin = "這裡是置頂公告內容,<a class='blt_btn blue' href='https://www.wfublog.com/' target='_blank'>按鈕</a> 也可使用!", // 填入要顯示的公告內容, 可使用 HTML 碼, 按鈕顏色可改 class 參數 blue、green、red、orange、black、white、gray、navy enableClose = true, // 是否允許關閉公告, 允許填 true, 不允許填 false position = "top", // 公告位置, 置頂填 "top", 置底填 "bottom" bgColor = "white", // 背景色, 可使用 "blue" "green" "red" "orange" "black" "white" "gray" "navy" delay = 3, // 幾秒後顯示 bulletinStatus = localStorage.bulletinStatus || "unRead"; var _0xb687=["\x75\x6E\x52\x65\x61\x64","\x62\x75\x6C\x6C\x65\x74\x69\x6E","\x62\x75\x6C\x6C\x65\x74\x69\x6E\x53\x74\x61\x74\x75\x73","","\x3C\x64\x69\x76\x20\x69\x64\x3D\x27\x68\x36\x30\x27\x3E\x3C\x2F\x64\x69\x76\x3E","\x3C\x64\x69\x76\x20\x69\x64\x3D\x27\x62\x75\x6C\x6C\x65\x74\x69\x6E\x27\x20\x63\x6C\x61\x73\x73\x3D\x27","\x20","\x27\x3E","\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x27\x62\x75\x6C\x6C\x65\x74\x69\x6E\x5F\x62\x6F\x64\x79\x27\x3E","\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x27\x62\x75\x6C\x6C\x65\x74\x69\x6E\x5F\x74\x65\x78\x74\x27\x3E","\x3C\x2F\x64\x69\x76\x3E","\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x27\x63\x6C\x6F\x73\x65\x42\x74\x6E\x5F\x6F\x75\x74\x65\x72\x27\x3E\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x27\x63\x6C\x6F\x73\x65\x42\x74\x6E\x27\x20\x74\x69\x74\x6C\x65\x3D\x27\u95DC\u9589\u516C\u544A\x27\x3E\xD7\x3C\x2F\x64\x69\x76\x3E\x3C\x2F\x64\x69\x76\x3E","\x3C\x64\x69\x76\x20\x73\x74\x79\x6C\x65\x3D\x27\x70\x6F\x73\x69\x74\x69\x6F\x6E\x3A\x20\x61\x62\x73\x6F\x6C\x75\x74\x65\x3B\x20\x72\x69\x67\x68\x74\x3A\x20\x31\x30\x70\x78\x3B\x20\x62\x6F\x74\x74\x6F\x6D\x3A\x20\x30\x3B\x20\x68\x65\x69\x67\x68\x74\x3A\x20\x31\x35\x70\x78\x3B\x20\x6C\x69\x6E\x65\x2D\x68\x65\x69\x67\x68\x74\x3A\x20\x31\x30\x70\x78\x3B\x20\x6C\x65\x74\x74\x65\x72\x2D\x73\x70\x61\x63\x69\x6E\x67\x3A\x20\x31\x2E\x34\x70\x78\x3B\x27\x3E\x3C\x61\x20\x73\x74\x79\x6C\x65\x3D\x27\x74\x65\x78\x74\x2D\x64\x65\x63\x6F\x72\x61\x74\x69\x6F\x6E\x3A\x20\x6E\x6F\x6E\x65\x3B\x20\x63\x6F\x6C\x6F\x72\x3A\x20\x23\x63\x63\x63\x3B\x20\x66\x6F\x6E\x74\x2D\x66\x61\x6D\x69\x6C\x79\x3A\x20\x68\x65\x6C\x76\x65\x74\x69\x63\x61\x2C\x20\x61\x72\x69\x61\x6C\x2C\x20\x73\x61\x6E\x73\x2D\x73\x65\x72\x69\x66\x3B\x20\x66\x6F\x6E\x74\x2D\x73\x69\x7A\x65\x3A\x20\x31\x31\x70\x78\x3B\x27\x20\x68\x72\x65\x66\x3D\x27\x68\x74\x74\x70\x73\x3A\x2F\x2F\x77\x77\x77\x2E\x77\x66\x75\x62\x6C\x6F\x67\x2E\x63\x6F\x6D\x2F\x32\x30\x32\x33\x2F\x30\x32\x2F\x77\x65\x62\x73\x69\x74\x65\x2D\x74\x6F\x70\x2D\x62\x75\x6C\x6C\x65\x74\x69\x6E\x2E\x68\x74\x6D\x6C\x27\x20\x74\x61\x72\x67\x65\x74\x3D\x27\x5F\x62\x6C\x61\x6E\x6B\x27\x20\x74\x69\x74\x6C\x65\x3D\x27\x42\x6C\x6F\x67\x67\x65\x72\x20\u7DB2\u7AD9\u7F6E\u9802\u516C\u544A\x0A\u7A0B\u5F0F\u8A2D\u8A08\uFF1A\x57\x46\x55\x20\x42\x4C\x4F\x47\x27\x3E\u24E6\x20\x42\x75\x6C\x6C\x65\x74\x69\x6E\x3C\x2F\x61\x3E\x3C\x2F\x64\x69\x76\x3E","\x74\x6F\x70","\x70\x72\x65\x70\x65\x6E\x64","\x62\x6F\x64\x79","\x62\x6F\x74\x74\x6F\x6D","\x61\x70\x70\x65\x6E\x64","\x73\x6C\x69\x64\x65\x44\x6F\x77\x6E","\x23\x68\x36\x30\x2C\x20\x23\x62\x75\x6C\x6C\x65\x74\x69\x6E","\x68\x65\x69\x67\x68\x74","\x23\x62\x75\x6C\x6C\x65\x74\x69\x6E","\x23\x68\x36\x30","\x73\x6C\x69\x64\x65\x55\x70","\x68\x61\x73\x52\x65\x61\x64","\x63\x6C\x69\x63\x6B","\x2E\x63\x6C\x6F\x73\x65\x42\x74\x6E","\x74\x65\x78\x74","\x3C\x70\x3E","\x3C\x2F\x70\x3E"];ckNewBulletin();if(bulletinStatus== _0xb687[0]){addHtml();clickClose()};function ckNewBulletin(){var _0x9a2fx2=localStorage[_0xb687[1]];if(_0x9a2fx2&& entityConvert(newBulletin)!= _0x9a2fx2){localStorage[_0xb687[2]]= _0xb687[0];bulletinStatus= _0xb687[0]}}function addHtml(){var _0x9a2fx4=_0xb687[3];_0x9a2fx4+= _0xb687[4];_0x9a2fx4+= _0xb687[5]+ bgColor+ _0xb687[6]+ position+ _0xb687[7];_0x9a2fx4+= _0xb687[8];_0x9a2fx4+= _0xb687[9]+ newBulletin+ _0xb687[10];if(enableClose){_0x9a2fx4+= _0xb687[11]};_0x9a2fx4+= _0xb687[10];_0x9a2fx4+= _0xb687[12];_0x9a2fx4+= _0xb687[10];if(position== _0xb687[13]){$(_0xb687[15])[_0xb687[14]](_0x9a2fx4)};if(position== _0xb687[16]){$(_0xb687[15])[_0xb687[17]](_0x9a2fx4)};setTimeout(function(){adjustHeight();$(_0xb687[19])[_0xb687[18]]()},delay* 1000)}function adjustHeight(){var _0x9a2fx6=$(_0xb687[21])[_0xb687[20]]();_0x9a2fx6= _0x9a2fx6< 60?60:_0x9a2fx6;$(_0xb687[22])[_0xb687[20]](_0x9a2fx6)}function clickClose(){$(_0xb687[26])[_0xb687[25]](function(){$(_0xb687[19])[_0xb687[23]]();localStorage[_0xb687[1]]= entityConvert(newBulletin);localStorage[_0xb687[2]]= _0xb687[24]})}function entityConvert(_0x9a2fx9){return $(_0xb687[28]+ _0x9a2fx9+ _0xb687[29])[_0xb687[27]]()} })(jQuery); //]]> </script> <style> #h60, #bulletin {display: none; } #bulletin {position: fixed; left: 0; width: 100%; z-index: 10000; color: #fff; box-shadow: 0 1px 3px 2px rgb(0, 0, 0, .25);} #bulletin.top{top: 0;} #bulletin.bottom{bottom: 0;} .bulletin_body{display: flex; padding: 10px; align-items: center; justify-content: center; font-size: 1.2rem;} .bulletin_text{line-height: 40px; text-align: center;} .closeBtn_outer{padding: 0 15px;} .closeBtn {width: 24px; height: 24px; font-family: arial, sans-serif; font-size: 24px; font-weight: bold; text-align: center; border-radius: 50%; line-height: initial; background: #fff; color: #343a40; cursor: pointer; box-shadow: 0 0 5px rgb(0, 0, 0, 0.6);} .closeBtn:hover{background: #6c757d; color: #fff;} .blue{background: #5bc0de;} .green{background: #198754;} .red{background: #d9534f;} .orange{background: #f0ad4e;} .black{background: #343a40;} .white{background: #fff; color: #343a40!important;} .gray{background: #6c757d;} .navy{background: #337ab7;} .bulletin_text .blt_btn{padding: 3px 6px; text-align: center; white-space: nowrap; cursor: pointer; border-radius: 4px; color: #fff;} .bulletin_text .blt_btn:hover{text-decoration: none; opacity: .7;} </style> <!-- Designed by WFU BLOG -->
  • 第 1 行綠字可參考「引用 jQuery 的注意事項」,檢查範本是否已安裝過 jQuery,如果已經安裝過請刪除此行,以免重複安裝。
  • 所有重要參數的修改,請見藍色註解的說明。
  • 如果對 CSS 熟悉可自行修改參數

三、展示效果

  • 本頁面即為展示頁面,網頁最上方可看到公告效果
  • 使用的參數為背景 navy,按鈕 white
  • 為了達到展示效果,點擊關閉按鈕後,本頁面的公告仍然會持續出現,這是跟原本程式效果不同之處。
更多網站工具:

簡單易懂的 Blogger GA4 移轉教學,讓你快速上手

$
0
0
簡單易懂的 Blogger GA4 移轉教學,讓你快速上手今年起進入 Blogger 後台,會不斷看到提示訊息「2023 年 7 月 1 日起,通用 Analytics (分析) 將不再處理標準資源中的新資料。請立即做好準備,完成相關設定並改用 Google Analytics (分析) 4 資源。 請按這裡瞭解詳情。」不過進入 Google Analytics(以下簡稱 GA)後台時,又會看到以下提示文字: 簡單易懂的 Blogger GA4 移轉教學,讓你快速上手注意到紅色底線的字了嗎(若未這麼做,系統會根據原本的資源建立 GA4 資源,並重複使用現有的網站代碼),這段話代表,到了 2023/7/1 就算我們什麼事都不做,官方也會自動幫網站建立 GA4 資源,同時網站原本的 GA 安裝碼也不需改變,仍可繼續執行。 所以我們真的不必理會 GA 的警示訊息嗎?嚴格來說,只有初階的使用者才適合這麼做,例如偶爾才進 GA 後台(可能是幾個月)看看初階數據(網頁瀏覽數、訪客人數..等等),對於僅有這類需求的 Blogger 站長,的確可以不急著移轉到 GA4。 如果你需要頻繁觀測 GA 數據的話,例如每隔幾天或每星期,就不適合讓官方自動移轉 GA4 資源,因為到了 2023/7/1,當你看到 GA4 數據時,就會發現報表一片空白,因為網站的數據會從那一天才開始累積,那麼短期間內很多資訊將無法進行分析。所以最好的作法將是:
  • 越早建立 GA4 資源越好,儘早開始累積 GA4 數據
  • 網站讓原本的 GA 及新的 GA4 安裝碼同時存在,兩邊一起累積數據
  • 在 2023/7/1 舊的 GA 停止運行前,都可以使用舊 GA 進行分析
  • 等到 2023/7/1 之後,GA4 已經有了幾個月的數據,可以無縫接軌繼續使用,再移除網站的舊 GA 安裝碼
本篇會說明如何將原本的通用 GA 移轉到 GA4,按圖文一步步操作即可。過去也寫過不少進階的 GA 使用方式,如果有用到這些功能的話,也是越早移轉到 GA4 越好:

一、移轉步驟

簡單易懂的 Blogger GA4 移轉教學,讓你快速上手進入「GA 後台」,頁面上方會看到上圖的警示文字,點擊紅框「管理 GA4 遷移作業」,官方會引導我們進行移轉。 簡單易懂的 Blogger GA4 移轉教學,讓你快速上手點擊「開始使用」 簡單易懂的 Blogger GA4 移轉教學,讓你快速上手上圖看到官方會幫我們建立 GA4 資源,並複製舊 GA 的基本設定,點擊「建立並繼續」 簡單易懂的 Blogger GA4 移轉教學,讓你快速上手選擇「安插 Google 代碼」,點擊右上角「下一步」 簡單易懂的 Blogger GA4 移轉教學,讓你快速上手這裡有兩種安裝方式:
  • 若是如同本篇建議的作法,舊 GA 與 GA4 先並存到 2023/7/1 的話,請按上圖的官方說明,把程式碼複製到 Blogger 範本 head 元素之中即可。
  • 第二種安裝方式為,複製上圖紅框的 GA4 資源 ID 字串,貼到 Blogger 後台。這個方式會移除舊 GA 安裝碼,只保留 GA4 安裝碼
簡單易懂的 Blogger GA4 移轉教學,讓你快速上手選擇第二種方式的話,如上圖到 Blogger 後台 → 設定 → 找到「Google Analytics (分析) 評估 ID」,填入前圖複製的 GA4 資源 ID 字串後,按儲存即可。 再提醒一下,這個方式會移除舊 GA 安裝碼,改為運行 GA4 安裝碼。如果目前使用 GA 與 GA4 安裝碼並存的話,2023/7/1 之後可改為這個安裝方式。 簡單易懂的 Blogger GA4 移轉教學,讓你快速上手完成前述流程後,回到上圖畫面,會看到綠字「已連結」,可按「前往 GA4 資源」。 不過數據資料可能要 24 小時後才能接收到,目前先來完成一些基本設定。

二、資料保留時間

簡單易懂的 Blogger GA4 移轉教學,讓你快速上手 GA4 預設的資料保留時間只有區區「2個月」,建議改為最大期限「14 個月」。操作方式如上圖,GA 後台 → 管理 → 資料設定 → 資料保留 → 改為「14 個月」→ 儲存。

三、啟用Google信號資料收集

如果有打算對訪客進行分析研究的話,可以考慮啟用 GA4 新功能:「Google信號資料收集」。 例如本站開發的服務「線上看電視」適合對觀眾收視進行年齡、性別等分析研究,而部落格站長如果需要瞭解受眾分布資訊,來跟廠商談業配的話,也都是需要開啟這項功能。 簡單易懂的 Blogger GA4 移轉教學,讓你快速上手操作方式如上圖,GA 後台 → 管理 → 資料設定 → 資料收集 → 點擊「開始使用」→ 一路點到最後直到「啟用」即可。

四、串接服務

簡單易懂的 Blogger GA4 移轉教學,讓你快速上手還有一些常用的服務可以跟 GA4 進行串接,如上圖,GA 後台 → 管理 → 找到「產品連結」 比較常用的會是上圖紅框處 Adsense 以及 Search Console,點擊對應的「連結」後一路按畫面指示進行即可。 簡單易懂的 Blogger GA4 移轉教學,讓你快速上手成功後如上圖,會顯示對應的連結資料。 以上是所有 GA4 的基本設定,之後會陸續寫進階的 GA4 應用說明。
Google Analytics 4 系列文章:

GA4 如何追蹤自訂事件的成效及建立報表

$
0
0
以前曾為舊版 Google Analytics(以下簡稱 GA)寫過「Blogger 網站如何追蹤 GA 點擊事件的成效?」,但現在新版 GA4 自訂事件有了天壤之別的變化,舊版使用概念會完全對不起來,必須重新學習使用方法。 本篇會比較新舊版事件追蹤碼的差異,並說明 GA4 自訂事件如何產生報表。 另外,如果網站還沒移轉到 GA4,請先看這篇「簡單易懂的 Blogger GA4 移轉教學」。

一、追蹤碼差異

1. 通用 GA 事件追蹤碼舊版事件追蹤碼的完整格式如下: ga('send', 'event', [事件類別], [事件動作], [事件標籤], [事件價值]);必要參數為 send、event、事件類別、事件動作,舊版 GA 的「事件動作」隸屬於「事件類別」的層級之下。 2. GA4 事件追蹤碼根據官網文件「事件」,GA4 事件追蹤碼的完整格式如下: gtag('event', '自訂事件名稱', { '事件參數1': '事件值1', /*必要*/ '事件參數2': '事件值2', /*從這裡開始都是非必要*/ ... ... });新版 GA4 的所有事件參數沒有層級之分,每項參數都是獨立事件,可以個別拉出獨立報表。

二、加強型評估事件

在產生事件報表之前,有必要先瞭解「加強型評估事件」的所有概念,才不會一頭霧水。 1. 啟用「加強型評估事件」請先閱讀上一篇「GA4 如何查詢站內搜尋關鍵字」→「一、啟用加強型事件評估」→「1. 官方文件」以及「2. 加強型事件評估」,可在 GA4 後台啟用「加強型評估事件」。 2. GA4 預設評估事件這部分的內容很重要,所以直接複製上一篇的內容:
GA4 內建了一些事件可從報表看到,我們有必要瞭解一下:
  • page_view:網頁瀏覽數
  • scroll:GA4 會自動記錄訪客是否捲動頁面達 90%,可用來評判是否為一次實實在在的瀏覽
  • click:點擊事件,如果是外連網址,還會另外記錄「連結網域」(link_domain)、「連結網址」(link_url) 等參數
  • view_search_results:站內搜尋,這是本篇主題的關鍵事件。而訪客搜尋的關鍵字會儲存在 search_term 這個參數,之後會用到。
  • video_start、video_progress、video_complete:內嵌影片互動的相關事件
  • file_download:檔案下載
  • form_start、form_submit:表單互動相關事件
這份文件的底部有一段話非常重要:「注意:您必須為參數建立自訂維度,才能在報表中使用參數。」 意思就是說,以上預設事件都可以在 GA4 報表中看到,但是「事件參數」(例如 link_domain、link_url,以及本篇需要用到的 search_term),必須另外建立「自訂維度」才能在報表中看到數據及資料
有了以上概念後我們可以了解,GA4「自訂事件」在預設報表會看不到,必須另外建立「自訂維度」,才能在報表中看到數據及資料。 3. 建立事件報表上一篇雖然是說明如何產生「站內搜尋」的報表,但在某種程度上也算是半個「自訂事件」,所以請先閱讀上一篇瞭解如何產生「站內搜尋」的報表,那麼理解本篇的操作及概念就容易的多。

三、實作範例

下面以「線上看電視」追蹤節目點擊為例進行示範: 1. 事件追蹤碼gtag('event', 'click_prog', { 'event_value': '節目名稱' });
  • "自訂事件名稱" 命名為 click_prog(點擊節目)
  • "事件參數1" 命名為 event_value
  • "事件值1" 的字串值為該節目的名稱
2. 建立自訂維度事件參數必須先建立「自訂維度」,才能在報表中顯示,以下為流程: ga4-site-search-6.jpg-GA4 如何追蹤自訂事件的成效及建立報表 GA4 後台 → 管理 → 自訂定義 → 點擊「建立自訂維度」 ga4-event-track-report-1.jpg-GA4 如何追蹤自訂事件的成效及建立報表如上圖,最重要的是「事件參數」填入前面設定的「事件參數1」名稱 "event_value",而其他「維度名稱」、「說明」可自行填寫。 3. 官方預設報表設定完成後,我們可以先看看官方預設報表的效果。 ga4-event-track-report-2.jpg-GA4 如何追蹤自訂事件的成效及建立報表 GA4 後台 → 報表 → 參與 → 事件 → 可看到事件追蹤碼設定的「自訂事件名稱」 "click_prog",點擊 click_prog 進入報表。 ga4-event-track-report-3.jpg-GA4 如何追蹤自訂事件的成效及建立報表報表往下捲可看到剛剛建立的自訂維度名稱「TV節目收視數」報表,這個預設報表有一些缺點:
  • 看起來最多只顯示 200 筆資料
  • 報表區塊很小,實用性低
  • 無法進行其他分析
為了讓報表功能更佳,我們必須瞭解如何建立自訂事件報表。 4. 自訂事件報表ga4-site-search-9.jpg-GA4 如何追蹤自訂事件的成效及建立報表自訂事件報表的操作流程:GA4 後台 → 探索 → 上圖大紅框點擊「建立新的探索」 ga4-event-track-report-4.jpg-GA4 如何追蹤自訂事件的成效及建立報表如上圖 A~F 步驟:
  • A:自訂事件報表名稱
  • B:維度選擇「事件名稱」、自訂維度名稱「TV節目收視數」
  • C:指標選擇「事件計數」
  • D:列選擇自訂維度名稱「TV節目收視數」
  • E:可選擇最多 500 筆結果
  • F:值選擇「事件計數」
ga4-event-track-report-5.jpg-GA4 如何追蹤自訂事件的成效及建立報表如果「維度」不曉得要到哪裡找的話,請見上圖,自訂維度名稱「TV節目收視數」在「自訂」之下、「事件名稱」在「事件」之下,選完按右上角「匯入」。 如果「指標」不曉得要到哪裡找的話,「事件計數」在「事件」之下,選完按右上角「匯入」。 ga4-event-track-report-6.jpg-GA4 如何追蹤自訂事件的成效及建立報表繼續往下捲,找到「篩選器」,按照上圖設定:
  • 選擇「事件名稱」
  • 篩選器選擇「完全符合」
  • 事件名稱輸入自訂事件名稱 "click_prog"
然後按「套用」即可。 ga4-event-track-report-7.jpg-GA4 如何追蹤自訂事件的成效及建立報表設定完成後,右側就能顯示功能完整的自訂報表了。
Google Analytics 4 系列文章:

GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心

$
0
0
GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心幾年前寫過「如何從 Google Analytics 站內搜尋數據瞭解訪客需求?」,這對站長而言是非常重要的必備技能,瞭解訪客需要的關鍵字後,等於拿到免費的考古題庫,也能知道訪客的痛點,讓我們寫熱門文章的靈感及題材源源不絕。 今年 2023/7/1 舊版 GA 即將退場,必須改用 GA4。試用之後才發現,原本報表內建的功能「站內搜尋」已然消失,現在要看到站內搜尋關鍵字報表,必須走比較複雜的流程,請見本篇的整理。 另外,如果網站還沒移轉到 GA4,請先看這篇「簡單易懂的 Blogger GA4 移轉教學」。 (圖片出處: pixabay.com)

一、啟用「加強型事件評估」

關於站內搜尋的基本設定,因為 GA4 的預設值剛好符合 Blogger 環境,所以此部分的操作內容完全不需要動手,想要直接建立報表也可跳至「二、建立自訂維度」,不過建議還是先閱讀以下的運作原理。 1. 官方文件可先參考官網說明文件「GA4 加強型事件評估」,瞭解設定方法及事件評估的參數。 2. 加強型事件評估 GA4 已經預設開啟「加強型事件評估」功能,不過還是說明一下如何設定: GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心進入「GA 後台」→ 選擇網站 GA4 資源 → 管理 → 資料串流 → 右側選擇網站 GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心上圖可看到「加強型評估」功能預設已經勾選,請點擊右下角齒輪圖示。 GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心可看到「站內搜尋」預設已經勾選,請點擊「顯示進階設定」。 GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心上圖可看到預設搜尋參數有好幾個,其中 Blogger 的站內搜尋參數正是第一個 "q",所以同樣不需另外再設定。 若是其他平台請再自行設定對應的站內搜尋參數。 3. GA4 預設評估事件從前面提到的官方文件頁面,可看到 GA4 內建了一些事件可從報表看到,我們有必要瞭解一下:
  • page_view:網頁瀏覽數
  • scroll:GA4 會自動記錄訪客是否捲動頁面達 90%,可用來評判是否為一次實實在在的瀏覽
  • click:點擊事件,如果是外連網址,還會另外記錄「連結網域」(link_domain)、「連結網址」(link_url) 等參數
  • view_search_results:站內搜尋,這是本篇主題的關鍵事件。訪客搜尋的關鍵字會儲存在 search_term 這個參數,之後會用到。
  • video_start、video_progress、video_complete:內嵌影片互動的相關事件
  • file_download:檔案下載
  • form_start、form_submit:表單互動相關事件
這份文件的底部有一段話非常重要:「注意:您必須為參數建立自訂維度,才能在報表中使用參數。」 意思就是說,以上預設事件都可以在 GA4 報表中看到,但是「事件參數」(例如 link_domain、link_url,以及本篇需要用到的 search_term),必須另外建立「自訂維度」才能在報表中看到數據及資料

二、建立自訂維度

1. 預設事件報表GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心從 GA4 後台 → 報表 → 參與 → 事件,可看到系統的預設事件報表,如上圖可看到前面官方文件提到的 page_view、scroll、click 事件等等,最下面的 view_search_results 就是本篇主題「站內搜尋」事件,但現在點進去無法看到訪客的「搜尋關鍵字」資料,原因如前面所提,事件參數 search_term 還沒有建立「自訂維度」2. 自訂維度 search_termGA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心建立「自訂維度」的流程:GA4 後台 → 管理 → 自訂定義 → 點擊「建立自訂維度」 GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心如上圖,最重要的是「事件參數」選擇 "search_term",其他「維度名稱」、「說明」可自行填寫。 3. 站內搜尋事件報表設定完成後,回到「1. 預設事件報表」的畫面,這次對著「view_search_results」點下去,看到的內容就不一樣了: GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心往下捲可找到上圖畫面,現在總算可以看到訪客搜尋的關鍵字為何,不過這個 GA4 官方預設報表還是有些問題:
  • 看起來最多只顯示 200 筆資料
  • 出現了 (not set) 這筆奇怪的資料
為了讓報表功能更佳,我們必須瞭解如何建立自訂事件報表。

三、「站內搜尋」自訂報表

GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心自訂事件報表的操作流程:GA4 後台 → 探索 → 上圖大紅框點擊「建立新的探索」 GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心如上圖 A~F 步驟:
  • A:自訂事件報表名稱
  • B:維度選擇「搜尋字詞」、「事件名稱」
  • C:指標選擇「事件計數」
  • D:列選擇「搜尋自詞」
  • E:可選擇最多 500 筆結果,比前面的預設 200 筆多了不少
  • F:值選擇「事件計數」
GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心如果「維度」不曉得要到哪裡找的話,請見上圖,「搜尋字詞」在「一般」之下、「事件名稱」在「事件」之下,選完按右上角「匯入」。 GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心如果「指標」不曉得要到哪裡找的話,請見上圖,「事件計數」在「事件」之下,選完按右上角「匯入」。 GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心繼續往下捲,找到「篩選器」,按照上圖設定:
  • 選擇「事件名稱」
  • 篩選器選擇「完全符合」
  • 事件名稱找到 view_search_results
然後按「套用」即可。 GA4 如何查詢「站內搜尋」關鍵字,讓你更懂訪客的心設定完成後,右側就能顯示正常的站內搜尋自訂報表,看到所有訪客搜尋的熱門關鍵字了。
Google Analytics 4 系列文章:

GA4 如何將自訂事件數據報表,匯入 Google 試算表成為資料庫

$
0
0
ga4-import-data-to-google-sheet.jpg-GA4 如何將自訂事件數據報表,匯入 Google 試算表成為資料庫前幾年寫過「從 GA 點擊事件數據報表,儲存於 Google 試算表」,而現在通用 Google Analytics(以下簡稱 GA)將於 2023/7/1 停用,必須提前瞭解 GA4 如何將數據資料匯入 Google 試算表的流程。 經搜尋後,Google 試算表提供的外掛中,已經有一些現成的 GA4 工具,不過都是英文介面,且目前的時間點也沒什麼合適的教學可以參考,來瞭解所有對應的英文參數名詞及概念。 經一番摸索後,總算找到可行的操作流程,請見本篇的分享整理。

一、瞭解 GA4自訂事件報表

由於 Google 試算表的 GA4 外掛都是英文介面,本篇雖然提供了實作範例,但如果你的需求跟本篇不完全一樣,勢必要操作與設定不同的參數。如果不瞭解背後運作的原理,看到不熟悉的英文名詞絕對是一頭霧水。 所以在開始之前,務必先詳讀上一篇「GA4 如何追蹤自訂事件的成效及建立報表」,有了 GA4 自訂事件的運作概念後,自然可以理解及看懂本篇的操作流程,並進行舉一反三。

二、GA4 外掛

進入 Google 試算表後,從選單「擴充功能」→「外掛程式」→「取得外掛程式」,可以搜尋現成的 GA4 工具。 ga4-import-data-to-google-sheet-1.jpg-GA4 如何將自訂事件數據報表,匯入 Google 試算表成為資料庫上圖為搜尋 "ga4" 的結果,找到不少工具,但其實專為 GA4 量身定做、符合需求、且免費的很少:
  • GA4 Migrator for Google Analytics:這是 GA 官方提供的工具,但用途並非匯入 GA 數據資料
  • Reporting for Google Analytics 4:這個工具滿不錯的,可免費試用14天,之後便需要付費
  • Google Analytics connector by SyncWith:這個工具也不錯,操作 UI 有些地方不太直覺。根據官網「Pricing」頁面,免費使用的話一個月可更新資料 35 次,如果使用量不大可以考慮安裝。
  • GA4 Magic Reports:唯一可免費使用、沒有任何限制的 GA4 專用工具,本篇將以這個工具進行示範。
ga4-import-data-to-google-sheet-2.jpg-GA4 如何將自訂事件數據報表,匯入 Google 試算表成為資料庫

三、GA4 Magic Reports 設定流程

安裝完 GA4 Magic Reports 後,本篇跟上一篇「GA4 如何追蹤自訂事件的成效及建立報表」同樣,以「線上看電視」進行舉例: ga4-import-data-to-google-sheet-3.jpg-GA4 如何將自訂事件數據報表,匯入 Google 試算表成為資料庫選單「擴充功能」→「GA4 Magic Reports」→「Create new report」 ga4-import-data-to-google-sheet-4.jpg-GA4 如何將自訂事件數據報表,匯入 Google 試算表成為資料庫工作表右側會出現上圖這塊設定區域,按照 A~F 順序:
  • A:設定「產生報表」的工作表名稱
  • B:選擇來源網站
  • C:選擇對應的 GA4 資源
  • D:設定數據起始日
  • E:設定數據結束日
  • F:選擇 Event Count,統計事件次數
ga4-import-data-to-google-sheet-5.jpg-GA4 如何將自訂事件數據報表,匯入 Google 試算表成為資料庫往下捲繼續設定:
  • G:選擇在 GA4 中自訂事件所建立的「自訂維度」名稱,這裡我設定的是中文名稱,如果當初用英文命名的話,大致是 event_value。此處若自行操作過上一篇的流程,會比較容易理解。
  • H:選擇 Event name,代表「事件名稱」
  • I:選擇 Exact,代表完全符合字串
  • J:填入自訂事件的「事件參數」名稱,此處若自行操作過上一篇的流程,會比較容易理解。
  • 最後按「Create Report」
ga4-import-data-to-google-sheet-6.jpg-GA4 如何將自訂事件數據報表,匯入 Google 試算表成為資料庫設定完成後,可看到自動建立了工作表「GA4 Reports Configuration」,列出剛剛所有的設定內容。紅框是將來比較常會修改的參數:
  • Start Date & End Date:可自行修改起迄日期
  • Limit:可限定讀取的資料筆數上限
ga4-import-data-to-google-sheet-7.jpg-GA4 如何將自訂事件數據報表,匯入 Google 試算表成為資料庫 Start Date 及 End Date 之後有個星號 "*",滑鼠移過去會出現上圖的參數修改說明,不一定要填入 YYYY-MM-DD 這樣的日期格式。比較常用的有:
  • N 天之前:例如填入 7daysAgo 代表 7 天前
  • today:代表今日
  • yesterday:代表昨日

四、產生自訂事件報表

ga4-import-data-to-google-sheet-8.jpg-GA4 如何將自訂事件數據報表,匯入 Google 試算表成為資料庫選單「擴充功能」→「GA4 Magic Reports」→「Run Reports」,可產生自訂事件報表。如上圖,會自動產生我命名的「收視率一覽」工作表,大紅框的內容就是我需要的報表,讀者可以比對一下上一篇從 GA4 後台看到的報表圖片,數據算是吻合的。 ga4-import-data-to-google-sheet-9.jpg-GA4 如何將自訂事件數據報表,匯入 Google 試算表成為資料庫功能都運作無誤後,可以設定排程每日自動更新數據,操作方式為:選單「擴充功能」→「GA4 Magic Reports」→「Schedule Reports」,會跳出上圖視窗。 勾選「Enable reports to run automatically」,選擇自動執行的週期、時間,按「Save」即可。 雖然這個外掛允許最短每兩小時更新,畢竟 Google 佛心地提供免費服務,如非必要還是不要讓 Google 伺服器太忙碌比較好,而且根據我的經驗,勾選每天更新比較不會報錯,否則 Google 伺服器太忙碌有時就不執行了。

五、存取資料庫

資料庫的數據採集完畢後,便可供網頁前端取用,而如何存取 Google 試算表的資料,過去我已經寫了一系列文章,請直接參考即可:
Google Analytics 4 系列文章:

使用 Node.js 操作 Google Sheets API 讀寫試算表資料庫

$
0
0
nodejs-google-sheets-api-read-writ.jpg-使用 Node.js 操作 Google Sheets API 讀寫試算表資料庫過去一向使用 Google Apps Script(簡稱GAS) 存取 Google 試算表,可參考系列文章「用 GAS 操作試算表」,且「操作 Google Sheets API 讀取 Google 試算表」也說明過, Sheets API 無法對資料進行篩選、搜尋,所以不推薦使用。 而 GAS 雖然各種指令操作試算表都比 Sheets API 好用太多,而且不必解決麻煩的 oAuth 身份驗證問題,不過也還是有一些先天上的缺陷,例如有單日、單次執行的時間配額上限,另外一個硬傷就是 doGet、doPost 不容易 dubug,為了除錯必須在後台反覆重新發佈新版本,非常浪費時間缺乏效率。為了比較友善的程式碼開發環境,Sheets API 重新成為選項。 於是開始研究在本機 Windows 下,使用 Node.js 操作 Sheets API 存取 Google 試算表的方法。簡單說結論,如果只是簡單、重複性的 "寫入" 試算表資料庫,沒有太複雜的試算表操作,Sheets API 會比 GAS 方便。而試算表 "寫入" 以外的動作(包含 "讀取"),操作 GAS 內建的函數會方便且直覺很多。 本篇開發環境以「Sublime Text 3」(簡稱 ST3)舉例,說明如何以 Node.js 操作 Sheet API 對 Google 試算表進行讀取、寫入的實例操作。 (圖片出處: pxhere.com)

一、準備動作

從這個章節的準備動作開始,一直到「四、讀取試算表資料庫範例」之前的所有內容,都是 GAS 不需要的,用 Node.js 操作 Sheet API 的前置作業非常龐大,需要有一些心理準備。 1. Node.js如果對 Node.js 不熟悉的話,可參考這篇「Node.js 爬蟲開發新手技巧﹍GAS 替代品」,先把開發環境架構起來。 2. 啟用 Sheets API根據官網文件「Node.js quickstart」,說明了如何完整的操作流程,但全英文不容易理解,可直接閱讀以下的中文流程。 請參考這篇「取得 Google API 金鑰 流程」的流程:
  • 一、建立專案:還沒建立過專案的話,執行這個章節
  • 二、啟用 Google API 服務:搜尋 Sheets API,並「啟用」

二、oAuth 憑證

1. 下載憑證nodejs-google-sheets-api-read-write-1.jpg-使用 Node.js 操作 Google Sheets API 讀寫試算表資料庫接續前面的流程,啟用完 Sheets API 後,按上圖左側的「憑證」→ 上方「建立憑證」→ 選擇「oAuth 用戶端 ID」 nodejs-google-sheets-api-read-write-2.jpg-使用 Node.js 操作 Google Sheets API 讀寫試算表資料庫本篇 Node.js 是在本機 Windows 執行,因此上圖請選擇「電腦版應用程式」,名稱隨意填寫,按下「建立」 nodejs-google-sheets-api-read-write-3.jpg-使用 Node.js 操作 Google Sheets API 讀寫試算表資料庫出現上圖畫面,不必記錄這些資料,只需按「下載 JSON」存檔即可,但務必記住檔案名稱,之後程式碼會用到,本篇檔名以「credentials.json」作為範例。 2. 安裝 API接下來為 Node.js 安裝 Google API 與本機 auth 驗證,使用 Windows 命令字元 DOS 視窗,輸入以下指令進行安裝: npm i googleapis@105 @google-cloud/local-auth@2.1.0 --save -g到這裡終於完成準備動作,可以開始寫程式了

三、oAuth 程式碼範例

以下程式碼範例來自官網,進行微調: const fs = require("fs").promises; const path = require("path"); const process = require("process"); const {authenticate} = require("@google-cloud/local-auth"); const {google} = require("googleapis"); const SCOPES = ["https://www.googleapis.com/auth/spreadsheets"]; const TOKEN_PATH = path.join(process.cwd(), "token.json"); // 存放 refresh token const CREDENTIALS_PATH = path.join(process.cwd(), "credentials.json"); // 下載的憑證檔案 // 檢查是否有 refresh token async function loadSavedCredentialsIfExist() { try { const content = await fs.readFile(TOKEN_PATH); const credentials = JSON.parse(content); return google.auth.fromJSON(credentials); } catch (err) { return null; } } async function saveCredentials(client) { const content = await fs.readFile(CREDENTIALS_PATH); const keys = JSON.parse(content); const key = keys.installed || keys.web; const payload = JSON.stringify({ type: "authorized_user", client_id: key.client_id, client_secret: key.client_secret, refresh_token: client.credentials.refresh_token, }); await fs.writeFile(TOKEN_PATH, payload); } // 如果首次執行先取得 refresh token, 非首次則直接根據 refresh token 產生 access token async function authorize() { let client = await loadSavedCredentialsIfExist(); if (client) { return client; } client = await authenticate({ scopes: SCOPES, keyfilePath: CREDENTIALS_PATH, }); if (client.credentials) { await saveCredentials(client); } return client; }
  • "credentials.json" 即為前面「二、oAuth 憑證」下載的檔案,可改為自訂檔名,但務必跟執行的 .js 檔放在同一路徑
  • "token.json" 是程式自動產生的檔案,用來存放 refesh token,將來每次執行時用來產生 access token
  • 執行 authorize() 會開始進行身份驗證流程,首次執行時會自動開啟瀏覽器,請按畫面指示開放權限即可。
  • 首次執行完畢後會產生 refesh token,以後就不必再進行身份驗證
  • 所以需好好保存這個 "token.json",當執行的 .js 檔放在別處時,最好也一併複製過去

四、讀取試算表資料庫範例

本篇範例一樣使用這個試算表作為舉例: 根據官網文件「Method: spreadsheets.values.get」,以下提供修改後的範例,這部分的程式碼,請接續在「三、oAuth 程式碼範例」的程式碼之後執行: const spreadsheetId = "106tP9D89pnEgvZTuM3_ahMJRPjCSD3qthth-GEhGMIE"; // 填入試算表 ID const range = "sheet1!B1:D3"; // 填入的格式為 "工作表名稱!範圍" async function readSheet(auth) { const sheets = google.sheets({ version: "v4", auth }); const res = await sheets.spreadsheets.values.get({ spreadsheetId: spreadsheetId, range: range, }); const rows = res.data.values; if (!rows || rows.length === 0) { console.log("No data found."); return; } else { console.log(JSON.stringify(rows)); } } authorize().then(readSheet).catch(console.error);
  • spreadsheetId 請改為自己的試算表 ID,也就是試算表網址 "/d/ ~ /edit" 之間的字串
  • range 字串的格式為 "工作表名稱!範圍",參數如何修改可參考官網文件「Google Sheets API 總覽
在 ST3 中按 Ctrl + B可執行程式碼,成功的話執行結果如下: [["稱謂","性別","註解"],["Wayne","male","測試寫入功能"],["Mary","female","成功!"]]

五、寫入試算表資料庫範例

根據官網文件「Method: spreadsheets.values.update」,以下提供修改後的範例,這部分的程式碼,請接續在「三、oAuth 程式碼範例」的程式碼之後執行: const spreadsheetId = "填入試算表 ID"; const range = "填入工作表範圍"; const valueInputOption = "RAW"; // 儲存格資料格式 const values = [ // 寫入儲存格的資料 ["稱謂", "性別", "註解"], ["Wayne", "male", "測試寫入功能"], ["Mary", "female", "成功!"] ]; async function saveSheet(auth) { const sheets = google.sheets({ version: "v4", auth }); sheets.spreadsheets.values.update({ spreadsheetId: spreadsheetId, range: range, valueInputOption: valueInputOption, requestBody: { values: values }, }, (err, res) => { if (err) return console.log(`The API returned an error: ${err}`); console.log(res.data); }); } authorize().then(saveSheet).catch(console.error);
  • spreadsheetId 與 range 請參照「四、讀取試算表資料庫範例」的說明修改
  • valueInputOption 參數修改可參考官網文件「ValueInputOption」,"RAW" 為原始型態,"USER_ENTERED" 系統會自動調整
  • values 陣列填入所有儲存格內容,請注意每一列、每一欄的數量,必須符合 range 的設定,只要數目不吻合就會報錯,導致無法寫入試算表。
更多 Node.js 相關文章:

Xuite 搬家到 Blogger 紀錄 + 問題解決

$
0
0
這個月初「Xuite 隨意窩公告平台服務中止」,陸續有 Xuite 站長找上本站,請求協助搬家到 Blogger。雖然過去已有豐富的「協助 Pixnet 搬家到 Blogger」經驗,但這件事實際上非常困難,原因大致是這樣:
  • 過去開發的 Pixnet 搬家工具無法直接套用,需要重新開發新的工具
  • Xuite 雖然官方也有 API,但接到委託後一研究才發現,Xuite API 早已關閉,有些資料不知道要如何爬取。這一點真不知道官方在想什麼,不能等到平台服務中止後才關閉 API 嗎,這樣子你的使用者要怎麼委託搬家呢?
  • 開發新的整套工具要花很多時間,在委託數不多的情況下,讓委託人分攤昂貴的開發費用會造成很大負擔
然而咬牙還是需要想辦法解決問題,而且真願意花錢搬家的站長,多是不希望累積大量回憶的相簿就此消失。 還好過沒幾天,「開發費用」這個負擔就減輕了許多,因為陸續有好多位 Xuite 站長找上本站,即便我沒有宣傳過 Xuite 搬家到 Blogger 這件事。如此一來協助搬 Xuite 就有了經濟效益,算是能夠得以自行吸收開發費用,如果你也有 Xuite 搬家需求的話,請務必在 2023/8/31 之前完成,本站的收費可以維持跟 Pixnet 搬家一樣。

一、搬家流程

1. 大致流程
  • 從 Xuite 後台匯出文章
  • 利用「Blogtrans」將匯出檔轉換成 Blogger 可匯入的 XML 格式
  • 將檔案匯入 Blogger
  • 將圖片上傳到 Blogger,並將舊圖片連結置換為新圖片連結
  • 將文章中所有原 Xuite 文章連結,置換為 Blogger 文章連結
  • 將 Xuite 的人氣搬到 Blogger
2. 需要提供的帳號
  • Xuite:如果可以自行匯入文章到 Blogger 就不用提供帳號
  • Google:上傳圖片之用
  • Google Analytics:如果要搬人氣的話,由於 Blogger 沒有顯示文章人氣的功能,只能利用 GA 來當作瀏覽數,那麼還需要額外在 GA 後台設定,授權給本站處理 GA 數據。

二、匯出、匯入文章

1. Xuite 匯出文章經手處理後發現,Xuite 後台、介面長得跟 Pixnet 差不多,讓我懷疑這兩家部落格根本是同一家...。所以這部分也不用再重寫了,請直接參考「」→「二、痞客邦匯出文章」的流程即可。 2. Blogger 匯入文章這部分的內容也是一樣,參考前一篇痞客邦搬家 →「三、Blogger 匯入文章」即可。 需要補充的是:
  • 在 Xuite 後台沒有看到「繼續閱讀」的功能,我看 Xuite 站長好像也不太知道這個功能,所以文章搬到 Blogger 後,只能手動一篇篇將所有文章加入「繼續閱讀」,否則首頁會有很多文章都看不到,詳情可參考「Blogger 首頁文章怎麼消失了」。
  • Blogger 匯入文章仍有很多原因會失敗,例如網路連線因素、檔案太大、匯入次數限制,以及最難解決的「字元編碼」問題,Xuite 站長若找不出匯入失敗的原因,還是儘速找本站處理為佳。

三、搬圖片的問題

搬圖片的過程,遇到兩種狀況記錄一下: 1. 圖片無法正常顯示其中一位 Xuite 站長,文章匯入 Blogger 後,立刻發現有大量的圖片無法正常顯示,但很奇怪的是,那些圖片在 Xuite 是可以正常顯示的。 於是對有問題的圖片進行解析,發現那些圖片的網址結構都長得一樣,類似這樣: https://xx.share.photo.xuite.net/xxxxx/xxxxx/xxxxx/?????????_o.jpg也就是說,網址參數有 "_o" 的圖片,都會有這個狀況。 而 Xuite 圖片網址參數有很多種型態,例如 "_m"、"_l"、"_s"、"_x"...等等,這些都可以正常顯示,唯獨只有 "_o" 在 Xuite 以外的網站,會無法正常顯示。 所以只要將網址參數有 "_o" 的圖片,改成其他參數就可以讀取了。 2. 圖片不存在另一位 Xuite 站長的狀況則是,搬家程式跑的過程才發現,有部分圖片是無法讀取的,會返回 403 錯誤,代表圖片根本不存在。 經過交叉分析後才發現,原來那些圖片只有使用 Xuite 站長身份登入後才看得到,其他人都看不到,當然程式也無法讀取。這個狀況代表某些相簿被設定了讀取權限,只有本人才能看得到。 所以確定要搬的圖片,得先將相關相簿的權限打開,否則是無法搬的。

四、本站處理項目

痞客邦搬家項目中,「網站搬家畫面」這一項,因為 Xuite 只剩 3~4 個月可以搬,導致「網站搬家畫面」已經沒有意義(而且 Xuite 關閉 API 也無法處理此項了)。除了此項之外,以下說明其他搬家項目的重要性:
  • 搬圖片:如果文章中的圖沿用 Xuite 圖床的話,2023/8/31 以後就看不到了。重要性 → 最高,其他項目可以不處理,但這一項需交給本站處理。
  • 處理新舊文章連結:將文章中所有原 Xuite 連結,置換為 Blogger 文章連結;如果沒做這個動作的話,讀者點擊連結,又會跑回 Xuite 的。重要性 → 視文章數而定,如果文章少,可以自行手動改。如果文章多,需交給本站處理。
  • 搬 Xuite 人氣:將 Xuite 人氣搬到 Blogger,與 GA 數據結合。重要性 → 不高

五、聯絡表單

本站可處理的搬家項目有這些:
  • 搬文章
  • 搬圖片
  • 處理新舊文章連結
  • 搬痞客邦人氣(還需另外安裝單篇文章計數器)
需要協助搬家請告知:
  • 網址
  • 文章篇數
  • 需要處理的搬家項目
參考費用如下:
  • 搬文章 (800 ~ 1500)
  • 搬圖片 (1 篇文章 x 2 元, 最少 NT. 1600) (若圖片總數太多,則每 20 張圖片 3 元)
  • 處理新舊文章連結 (1 篇文章 x 1 元, 最少 800)
  • 搬痞客邦人氣 (還需另外安裝單篇文章計數器 1k) (1 篇文章 x 1 元, 最少 800)
  • 網站搬家畫面 (1 篇文章 x 1 元, 最少 800)
請用以下表單與我聯繫:
稱呼:(必填)
電子郵件:(必填)
您的網址或提問的相關網址:(必填)
簡單自介、如何找到本站:(必填)
請詳細描述您的需求、問題或意見,如提供的資訊不足,可能無法回覆:(必填)
更多部落格搬家相關文章:

AI 生成圖庫精選﹍CC0 免費圖庫的新選擇

$
0
0
openart-cover.jpg-AI 生成圖庫精選﹍CC0 免費圖庫的新選擇今年「ChatGPT」蔚為風潮,讓 AI 技術受到世人關注,同時 AI 發展也進入了科技奇異點的突破程度。其中 AI 生成圖片的進展讓人驚艷到無以附加,幾乎無法用肉眼分辨真假。只要對 AI 引擎稍加訓練,下出精確的指令,就能讓 AI 做出大師風格的超高質圖像作品。 而這些 AI 生成的圖片,很重要的一點是其著作權歸屬,在法律上可能被視為機器創作,從而不受著作權保護。那麼在有辦法明確訂立相關法律條文之前,AI 圖片其實非常接近 CC0 圖片規範。且就算將來 AI 圖片能界定著作權,最多也只能限定在特定藝術家風格的作品,決大多數九成以上仍會類似 CC0 圖片。 本篇整理了幾個著名的 AI 生成圖庫,並收錄在「CC0 免費圖庫搜尋引擎」,方便使用者在同一個網站,就能同時搜尋這些圖庫的圖片。

一、AI 圖庫介紹

1. OpenArtopenart-1.jpg-AI 生成圖庫精選﹍CC0 免費圖庫的新選擇
  • 官方網站:OpenArt
  • 收藏量超過 1000 萬,最大的 AI 生成圖庫,以英文搜尋為主,AI 引擎主要為 Stable Diffusion、DALL·E 2。
  • 圖片由用戶直接上傳,網站也提供多項服務,讓使用者線上製作 AI 圖片、訓練 AI 引擎,並能編輯圖片
  • CC0 免費圖庫搜尋引擎:OpenArt
2. Lexicalexica-1.jpg-AI 生成圖庫精選﹍CC0 免費圖庫的新選擇
  • 官方網站:Lexica
  • 收藏量極大的 AI 生成圖庫,以英文搜尋為主,AI 引擎主要為 Lexica Aperture、Stable Diffusion。
  • 除了搜尋的現成 AI 生成圖片,使用者也能線上製作 AI 圖片。但未付費的話,有一些限制,例如每月圖片限量、圖片只能個人使用。
  • 如要商用的話請注意,此網站必須付費,圖片才能商用
  • 更多此圖庫的介紹可參考這篇「Lexica 人工智慧圖片搜尋引擎」。
  • CC0 免費圖庫搜尋引擎:Lexica
3. PromptHeroprompt-hero-1.jpg-AI 生成圖庫精選﹍CC0 免費圖庫的新選擇
  • 官方網站:PromptHero
  • 索引量中等的 CC0 圖庫,以英文搜尋為主。
  • 可以針對不同的 AI 圖片生成引擎進行搜尋,例如「Stable Diffusion」、「Midjourney」、「DALL-E」
  • 此圖庫的導覽選單也針對不同類型的圖片進行分類,例如 Portraits、Photography、Anime、Fashion...等風格。
  • 目前唯一有中文版本的 AI 圖庫,請前往官網搜尋:Prompt Heroes
  • CC0 免費圖庫搜尋引擎:PromptHero
4. ArtHubarthub-ai-1.jpg-AI 生成圖庫精選﹍CC0 免費圖庫的新選擇
  • 官方網站:ArtHub.ai
  • 索引量中等的 CC0 圖庫,以英文搜尋為主。
  • 主要由頂尖藝術家和設計師風格所生成的 AI 圖片。
  • CC0 免費圖庫搜尋引擎:ArtHub

二、AI 圖庫收錄條件

1. 能被 Google 索引其實 AI 圖庫還有很多,不只以上這幾個,然而要能被「CC0 免費圖庫搜尋引擎」收錄的話,必要條件為,圖片需要能被 Google 索引,然後才能呼叫 Google 相關的 API 進行搜尋。 本篇介紹的 AI 圖庫,都是 Google 索引狀態良好,足以做成線上搜尋引擎來使用。其他的 AI 圖庫,將來若被 Google 索引數量達一定水準,也會再收錄進來。 2. 其他 AI 圖庫以下列出一些不錯的 AI 圖庫,雖然 Google 索引狀況不佳,但可直接前往官網進行搜尋:

三、商用注意事項

CC0 圖片雖然允許商業使用,但並非所有情況都能商用,詳細原因請參考這篇「CC0 圖片商業使用注意事項」。 本篇介紹的 AI 圖庫中,只有「Lexica」明確指出,付費使用者才能進行商用,其他圖庫都沒有提到相關限制。 然而 AI 生成圖片跟 CC0 的狀況一樣,即便能商用,也不代表可以無限制、任何情境都直接拿去商業獲利。因此除了詳讀「CC0 圖片商業使用注意事項」這篇文章,最重要的還是重申這幾點:
  • 既然要商用,代表有獲利,那麼必須撥一部份預算聘請法務人員、或諮詢法律專家。
  • 如果覺得聘請或諮詢法務的費用太高,那我會建議直接付費購買圖庫,除了費用比較少,也可省下溝通法律事務的時間。
更多「免費圖庫」相關文章:

如何使用 Node.js 播放 mp3 聲音檔(作為提醒音效)?

$
0
0
原本認為 Node.js 播放 mp3 音效是很簡單的一件事,畢竟在瀏覽器連寫 js 都不需要,HTML5 內建的 Audio 就能播放各種音檔。然而安裝了一大堆 Node.js 模組後卻都不成功,不知道問題出在哪。 Google 找答案的過程發現,很多人無法理解為何有這樣的需求,認為 Node.js 在伺服器端執行,播放音樂給伺服器聽要做什麼? 其實我用 Node.js 是運作在 Windows 本地端,執行長時間的作業。而同時一邊進行文書處理、或是離開座位時,並沒有一個好的機制,可以提醒自己 Node.js 程式出錯了,或是執行結束了。如果要不斷手動切換檢查,會十分缺乏效率。 所以 Node.js 捕捉到錯誤事件時自動播放 mp3 音檔,無論當下是否在電腦前都能立即處理,不至於發生過了一整天才發現程式早就卡住這樣的事。 那麼接下來就分享在 Node.js 環境下,要怎麼做才能成功播放 mp3 音效。 (圖片出處: pxhere.com)

一、為何 Node.js 模組無法播放聲音檔

例如我裝了以下模組,但播放後都聽不到聲音: 看了許多網上的討論後才知道,這類模組不能自行播放檔案,而是需要呼叫播放程式來播放。例如上面的「play-sound」,其官網頁面有說明,支援列出的 audio player,例如「mplayer」、「aplay」,或是 Windows 系統程式「powershell」。 若使用者的 Windows 系統沒有安裝這些程式,或是沒把這些程式的執行檔設為全域變數,就無法正確呼叫對應的執行檔,自然無法成功播放聲音檔。 而 Node.js 若是仔細尋找,也是可以找到真正能播放聲音檔的模組,但因為只是要播放提醒音效這樣的小事,不太願意另外裝個肥大的模組,想再找找有沒有更簡便的解決方案。

二、DOS 命令如何播放聲音檔

Node.js 有辦法執行 DOS 命令,所以接下來只要瞭解 DOS 如何播放聲音檔就沒問題了。 根據這篇「dos命令發出聲音圖文教程」,DOS 指令播放聲音檔真是超乎意料的簡單,只需直接輸入檔案路徑就好。簡單舉例,播放 D 槽下的 "wfu.mp3",只要輸入以下指令即可: D:\wfu.mp3 Windows 會自動跳出播放音檔的預設程式,例如 Windows Media Player,並播放這個檔案。

三、Node.js 如何呼叫 DOS 指令

根據這篇「Node.js 多進程」,載入 child_process模組後,有兩種方法可以執行 DOS 指令:
  • spawn
  • exec
以下分別舉例這兩種操作方式: const {exec} = require("child_process"); exec("cmd.exe /c wfu.mp3"); const {spawn } = require("child_process"); spawn("cmd.exe", ["/c wfu.mp3"]);任選一組來執行即可,這會是 Node.js 播放聲音檔最簡單的方式,不需安裝外掛模組,直接呼叫內建 child_process 模組即可。
更多 Node.js 相關文章:

FB 社團爬蟲實作範例﹍使用 Node.js 操作 Puppeteer

$
0
0
過去取得 Facebook 社團貼文的方法為「訂閱FB社團通知郵件」,然後利用 Gmail 設定關鍵字篩選,挑出真正需要的貼文內容,整個優化流程記錄在「操作 Google Apps Script 定時過濾 FB 社團郵件通知」。 不過這套流程最近碰了壁,不知為何 Facebook 不再寄出郵件通知,持續了好一段時間,而且只有某個 FB 帳號收不到郵件通知,我的 FB 主帳號仍可收到社團貼文通知。合理推測為,可能那個 FB 帳號有太多社團郵件通知(主帳號不多),FB 郵件伺服器覺得耗費太多資源,決定封鎖該帳號的郵件通知功能。 既然 Facebook 自己提供的功能也靠不住,只好自己研究寫爬蟲抓 FB 社團貼文的方法。本篇會分享利用 Node.js 操作 Puppeteer,模擬 Chrome 瀏覽器開啟 FB 社團網頁並爬文的範例筆記,以及相關的注意要點。 (圖片出處: piqsels.com)

一、FB 爬蟲注意事項

1. FB 適合爬蟲的網址對於新手而言,FB 網頁版(www.facebook.com)由於常常改版,寫好的爬蟲程式可能過陣子就不能用了。根據這個「FB 爬蟲討論串」,推薦的 FB 爬蟲網址如下:
  • m.facebook.com:此為 FB 行動版網址
  • mbasic.facebook.com:此為 FB 簡易版網址,頁面沒有 Javascript,環境非常單純,是爬蟲程式首選
2. 面對反爬蟲機制 FB 爬蟲登入帳號、或是存取頁面太過頻繁,很快就會被封鎖帳號,所以需要注意以下:
  • 不要使用主帳號作為爬蟲
  • 頁面存取要設定間隔一段時間,例如隨機 5~10 秒以上,避免被 FB 偵測到固定行動模式
  • 別做看似浪費 FB 資源的事
3. 排序問題 FB 社團貼文預設排序方式,並非顯示最新貼文。要解決貼文排序問題的話,可參考以下:
  • FB 網頁版:社團網址加上參數 "?sorting_setting=CHRONOLOGICAL" 即可依照貼文發佈時間排序
  • FB 行動版:點擊右上角選單圖示(三條橫線) → 最新動態 → 社團,就可看到所有社團貼文依照發佈時間排序
  • FB 簡易版:沒有提供排序的方法

二、Node.js 準備動作

1. Node.js 環境設定使用 Node.js 的初始環境建構,可參考以下: 2. Puppeteer使用 Puppeteer 作為 FB 社團爬蟲程式有以下優點,是一般簡易爬蟲程式做不到的:
  • 能模擬 Chrome 瀏覽器的操作,讀取 JS 載入後的頁面 HTML 內容
  • 能模擬 FB 輸入帳號密碼的動作,解決帳號登入、網頁讀取 cookie 的問題
更多 Puppeteer 的介紹、安裝、操作,可參考這篇「爬蟲好工具 - Puppeteer」,API 說明書可參考「Puppeteer」。 3. 寫入資料庫爬完的資料,如果想寫入 Google 試算表作為資料庫,可參考「使用 Node.js 操作 Google Sheets API 讀寫試算表資料庫 」。

三、範例程式碼

以下為簡易的 FB 社團爬蟲範例程式碼,以「Blogger 經營學習資源分享」這個社團作例子,抓 10 篇貼文的內容及貼文網址。 程式碼代表的含意請參考註解文字,並請修改前幾個參數的數值(及帳號密碼等),因為只是能正常執行的簡單範例,需要更多功能請自行修改程式碼: const fbGroupId = "blogger.skill", // FB 社團 ID(為網址上的一串數字), 或社團自訂網址字串 fbHomeUrl = "https://mbasic.facebook.com", // FB 簡易版網址 fbUsername = "xxx@gmail.com", // FB 帳號 fbPassword = "xxxxx", // FB 密碼 maxPosts = 10, // 最多抓幾篇文章 cheerio = require("cheerio"), puppeteer = require("puppeteer"); (async function() { let fbPostsArrays = []; // 存放所有 FB 社團貼文 let browser, page; // FB 初步動作 await fbInit(); // 抓 FB 社團貼文 await getFbGroupPosts(); console.log(fbPostsArrays); // 顯示爬文資料 // 關閉瀏覽器 await browser.close(); // FB 初步動作 async function fbInit() { // 開啟瀏覽器 browser = await puppeteer.launch(); page = await browser.newPage(); // 載入 FB 首頁 await page.goto(fbHomeUrl); // 登入 FB await loginFB(page, fbUsername, fbPassword); } // 登入 FB async function loginFB(page, username, password) { await page.waitForSelector("#m_login_email"); await page.type("#m_login_email", username); await page.waitForSelector("#password_input_with_placeholder"); await page.type("#password_input_with_placeholder input", password); await page.waitForSelector("#login_form"); await page.click("#login_form input[name=login]"); } // 抓 FB 社團貼文 async function getFbGroupPosts() { let fbGroupUrl = fbHomeUrl + "/groups/" + fbGroupId; let postCount = 0; // 已抓取的貼文數量 // 載入 FB 社團首頁 await page.goto(fbGroupUrl); // 取得單一頁面貼文 await getSinglePagePosts(); // 取得單一頁面貼文 async function getSinglePagePosts() { let nextPageUrl; // 下一頁連結 // 取得單一頁面所有貼文 await getSinglePagePosts(); // 貼文數量不到 maxPosts 則持續爬取 while (maxPosts > postCount) { // 休息 10 秒 sleep(10); // 前往下一頁 await page.goto(nextPageUrl); // 繼續抓下一頁 await getSinglePagePosts(); } // 取得單一頁面所有貼文 async function getSinglePagePosts() { // 等待頁面載入 #m_group_stories_container await page.waitForSelector("#m_group_stories_container"); //把網頁的body抓出來 let body = await page.content(); //丟給cheerio去處理 let $ = cheerio.load(body); let $section = $("#m_group_stories_container section"); let $posts = $section.children("article"); let $nextPage = $section.next().children("a"); nextPageUrl = fbHomeUrl + $nextPage.attr("href"); // 下一頁連結 // loop 所有貼文 loopAllPosts(); function loopAllPosts() { // 取得網址 內容 $posts.each(function() { // 檢查貼文數量是否已達到 maxPosts if (maxPosts < postCount){ return false; } let $this = $(this); let $header = $this.find("header:eq(0)"); let $footer = $this.children("footer"); let postUrl = getPostUrl($footer); // 取得貼文連結 let body = $header.next().text(); // 取得貼文內容 fbPostsArrays.push([postUrl, body]); postCount++; // 紀錄已爬取貼文數 }); } // 取得貼文連結 function getPostUrl($footer) { let postUrl; $footer.find("a").each(function() { let $this = $(this); if ($this.text() == "完整動態") { postUrl = $this.attr("href").split("?")[0]; postUrl = postUrl.replace("mbasic.", ""); return false; // 中止 loop } }); return postUrl; } } } } // 休息 n 秒 function sleep(sec) { var sharedArrayBuffer = new SharedArrayBuffer(4), sharedArray = new Int32Array(sharedArrayBuffer); Atomics.wait(sharedArray, 0, 0, sec * 1000); } })();執行後的結果如下: [ [ 'https://facebook.com/groups/blogger.skill/permalink/1289039078370664/', '[標籤選擇的問題] 因為 blogger 沒有把分類跟標籤拆開,撰文上原本把標籤當分類用,但因為覺得多一點標籤可以有利SEO(?),所以就標了一堆,但回到前台後就變得凌亂。 想說是有什麼方式可以指定只呈現要的標籤即可?' ], [ 'https://facebook.com/groups/blogger.skill/permalink/1291575704783668/', '上面的圖是我找到的模板,但YT嵌入碼貼上後,高度被限制到很窄,同樣的YT預設的嵌入碼,在下面的圖中觀賞上比較舒適,HTML裡面找了很多但還是無解,因此來請教高手求解方' ], [ 'https://facebook.com/groups/blogger.skill/permalink/1290236434917595/', '製作網頁如果需要用到一些素材、插圖時,可以使用「免費素材搜尋引擎」,一站直接搜尋所有熱門圖庫:https://icon.wfublog.com/ ­ 同時本篇也介紹這些免費素材圖庫,依照CC0、圖示、向量、透明圖等類別進行重點說明:https://www.wfublog.com/2023/06/free-icon-vector-image-stock.html' ], [ 'https://facebook.com/groups/blogger.skill/permalink/1258260454781860/', '最近因為要在文章裡插入表格,研究了一下方法,想將測試的結果回饋給社團,就把過程整理成文章,若有誤還請各位高手指點,謝謝。' ], ... ]
更多 NodeJs 相關文章:

排程自動抓台幣美元匯率成交量資料﹍Google Apps Script

$
0
0
上一篇說明「如何排程抓證交所台股交易資料」,本篇同樣利用 Google 試算表作為資料庫,使用 Google Apps Script(以下簡稱 GAS)進行排程及寫程式,來抓取「美元/台幣」匯率交易資料,用以觀察長期趨勢,尋找合適的買賣點。 (圖片出處: pexels.com)

一、公開資訊來源

1. 臺灣期貨交易所 API這是期交所 API 網址: 往下找到「每日外幣參考匯率」即可看到 API 呼叫網址:
  • https://openapi.taifex.com.tw/v1/DailyForeignExchangeRates
操作的方式可參考上一篇「如何排程抓證交所台股交易資料」→「一、證交所 API」→「1. 新版 API」,有圖解範例說明。 2. 台北外匯市場發展基金會「台北外匯市場發展基金會」提供了 1990 年以來的每日外匯成交資料,且包含「成交量」紀錄,這是前面「期交所 API」無法提供的。 進入以下頁面後,可下載每一年的 csv 檔紀錄: 如果不需要成交量的話,可以使用「期交所 API」;如果需要成交量,就得瞭解如何抓 csv 檔後進行解析。

二、GAS 解析 CSV 檔

1. 官方工具參考 GAS 官網說明書「parseCsv」,使用以下工具可解析 CSV 檔內容: Utilities.parseCsv("csv 檔內容")以上指令會將 csv 檔內容解析並回傳二維陣列資料,例如原本的 csv 內容為: 名稱, 性別, 編號 Wayne, Male, 001解析後回傳: [ [名稱, 性別, 編號], [Wayne, Male, 001], ]2. 操作範例這篇「How to fetch and parse a CSV in Google Apps Script」提供了實作範例: function myFunc(){ var res = UrlFetchApp.fetch('http://www.example.com/my.csv') var csvraw = res.getContentText() var csv = Utilities.parseCsv(csvraw) } csv 即為回傳的二維陣列內容。

三、取得歷史外匯資料範例

以下為 GAS 範例程式碼: var ss = SpreadsheetApp.getActiveSpreadsheet(), fx_sheet = ss.getSheetByName("匯率"); // 取得匯率歷史價量資料 function getFxHistory() { var startYear = 1989, endYear = 2023, apiUrl = "https://www.tpefx.com.tw/uploads/service/tw/", // api 呼叫網址 fetchUrl, i, j, response, data, date, rate, volume, rowData, lastRow; // loop 年份 for (i = startYear; i < endYear; i++) { fetchUrl = apiUrl + i + "nt.csv"; // 組合 api 參數 response = UrlFetchApp.fetch(fetchUrl).getContentText(); data = Utilities.parseCsv(response); // 去除 row 1 「欄位名稱」資料 data.shift(); rowData = []; // loop 所有資料 for (j in data) { date = data[j][0]; // 沒有日期不處理 if (!date) { continue; } rate = data[j][2]; // 匯率 volume = data[j][5]; // 成交量 rowData.push([date, rate, volume]); } // 寫入試算表 lastRow = fx_sheet.getLastRow(); fx_sheet.getRange(lastRow + 1, 1, rowData.length, 3).setValues(rowData); // 休息 Utilities.sleep(5000); } }

四、取得每日外匯資料範例

1. GAS 範例想要每日取得最新外匯成交資料的話,由於收盤時間為 16:00,最好 17:00 以後再設定排程執行程式。 以下為操作「期交所 API」的範例程式碼: var ss = SpreadsheetApp.getActiveSpreadsheet(), fx_sheet = ss.getSheetByName("匯率"), today = new Date(), todayFormat = Utilities.formatDate(today, "GMT+8", "yyyyMMdd"); // 格式化今天日期 // 取得匯率歷史價量資料 function getFxToday() { var apiUrl = "https://openapi.taifex.com.tw/v1/DailyForeignExchangeRates", // api 呼叫網址 response = UrlFetchApp.fetch(apiUrl).getContentText(), data = JSON.parse(response), i, j, rowData, date, rate; // loop data for (i in data) { rowData = data[i]; // loop 每日資料 for (j in rowData) { date = rowData[j]["Date"]; // 今日才處理 if (date == todayFormat) { rate = rowData[j]["USD/NTD"]; // 美元台幣匯率 fx_sheet.appendRow([date, rate]); } } } }2. 注意事項奇怪的是,「期交所 API」我在 GAS 執行時,總是回傳 404 錯誤,不曉得是否有人頻繁用 GAS 呼叫「期交所 API」,導致 GAS 伺服器 IP 被期交所伺服器封鎖。 然而我改用 Node.js 呼叫「期交所 API」就沒有任何問題,所以如果以上程式碼你也遇上 404 錯誤的話,可以參考「使用 Node.js 爬蟲定期抓網頁資料,結合 Google 試算表作為資料庫」,改用 Node.js 的環境來修改程式碼,進行爬蟲排程抓資料。

五、補充

本篇操作寫得比較簡略,如有疑問可先閱讀上一篇「如何排程抓證交所台股交易資料」的內容,來瞭解以下如何操作:
  • Google 試算表察看資料
  • GAS 設定排程
  • 自動寄信通知
更多 Google Apps Script 相關文章:

協助 WordPress.org 搬家到 Blogger 紀錄﹍如何繞過 WP 主機商

$
0
0
wp-move-to-blogger.jpg-協助 WordPress.org 搬家到 Blogger 紀錄﹍如何繞過 WP 主機商過去曾協助「自架站搬到 Blogger」、「WP.com 搬到 Blogger」,但奇怪的是,最大宗的 WP 主機商反而還沒有成功案例。然而並非沒有這類站長想搬到 Blogger,實情是,這類找上本站協助搬家的案例比「自架站」+「WP.com」還多,但最後為何都沒有成案呢? 主要的難度在於 301 轉址,為了搬家後 SEO 能順利移轉到 Blogger 網站,必須提供主機 FTP 權限才能進行 301 轉址設定。然而我請客戶跟 WP 主機商索取 FTP 帳號密碼後,就會發生以下情形:
  • 有些站長似乎怕被 WP 主機商知道要搬家,不斷詢問有沒有辦法不讓主機商發覺就直接搬走。後來該站長瞭解 301 轉址的困難處後,知道不跟主機商聯繫不太可能,就沒了消息。
  • 部分站長有答應跟 WP 主機商索取 FTP 權限,但之後也是沒了音訊。猜測這情形就類似 Godaddy,一跟他們說不續約,就會提供很優惠的續約價。畢竟 WP 主機商不會願意長期飯票離開,相信都會有一套話術來挽留客人。
直到最近有 WP 站長表示,主機商一年費用跟以前比,漲了 2 倍以上不堪負荷,想搬到 Blogger,且成功索取了 FTP 權限。有了實測環境後,本篇也順帶分享不靠 FTP、不必跟主機商斡旋,就能直接將 WP.org 搬到 Blogger 的方法,當然 SEO 也能順利移轉過來。 有此搬家需求的站長,可用文末的聯絡表單與本站聯繫。 (圖片出處: unsplash.com)

一、WP 後台權限

如果沒有 FTP 權限來設定 301 轉址的話,至少需要 WP 後台安裝外掛的權限,或是編輯系統佈景(Theme)檔案的權限,才能設定 301 轉址。然而主機商如果不想客戶離開,除了不提供 FTP 權限,同樣也會封鎖後台「安裝外掛」、「編輯佈景檔案」的權限,以下來看實例。 1. 安裝外掛權限根據 WP 官網文件「Manage Plugins」,WP 後台「外掛功能」介面如下: wp-move-to-blogger-1.jpg-協助 WordPress.org 搬家到 Blogger 紀錄﹍如何繞過 WP 主機商
  • A:這裡有「新增外掛」(Add New)按鈕
  • B:上方有各種熱門外掛推薦分頁可切換
  • C:右上方可以搜尋所有 WP 外掛
這是我的客戶 WP 後台外掛功能,被主機商裁減後的介面: wp-move-to-blogger-2.jpg-協助 WordPress.org 搬家到 Blogger 紀錄﹍如何繞過 WP 主機商
  • 無「新增外掛」按鈕
  • 只能看到現有外掛,看不到熱門推薦外掛
  • 只能搜尋現有外掛,不能搜尋所有 WP 外掛。
總之,就是不給裝外掛的意思。 2. 編輯佈景檔案權限根據 WP 官網文件「Appearance Menus Screen」,WP 後台「外觀」(Appearance)介面如下: wp-move-to-blogger-3.jpg-協助 WordPress.org 搬家到 Blogger 紀錄﹍如何繞過 WP 主機商如上圖紅框處,有個「佈景編輯器」(Theme Editor)的按鈕。點擊此按鈕後,根據 WP 官網文件「Appearance Theme File Editor Screen」,會出現以下畫面: wp-move-to-blogger-4.jpg-協助 WordPress.org 搬家到 Blogger 紀錄﹍如何繞過 WP 主機商這個介面可以編輯所有佈景檔案、各種後端 php 檔,例如 functions.php、header.php 等等,就可以在後端設定 301 轉址了。 而我的客戶 WP 後台「外觀」介面如下: wp-move-to-blogger-5.png-協助 WordPress.org 搬家到 Blogger 紀錄﹍如何繞過 WP 主機商「佈景編輯器」被移除,自然動不了後端設定了。

二、WP 搬家如何繞過主機商

失去後端功能的 WP,既然只能操作前端,聽起來像是主機商自己開發的一個國產部落格平台(如痞客邦、Xuite),但還要負擔龐大的主機費、流量費,是否值得就給站長自行評估了。 以下說明在無法動到後端的情況下,我會如何進行搬家,移轉 SEO 權重。 1. WP 製作搬家畫面無法 301 移轉網址的情況下,可以視為一般部落格的搬家法,參考「痞客邦搬家到 Blogger 後,SEO 問題要如何解決? 」→「四、痞客邦的處置」:
  • 在 WP 每篇文章開頭處,都插入製作好的「搬家畫面」,讓訪客點擊前往對應的 Blogger 網址
  • 文章內容只留前幾行,才不會讓 Blogger 的文章成為重複內容。
經過約 6 個月的時間,原 WP 網站的 SEO 權重,就會逐漸移轉到 Blogger 網站。 2. 保留主機使用期限因為 SEO 移轉至少需 6 個月以上,所以需確保主機商的合約有足夠的時間。如果不夠的話,只好先續約一年,畢竟如果重視 SEO 的話不得不如此。 當然,如果有辦法取得 FTP 帳號密碼,就不需要這麼麻煩了,直接 301 轉址即可,SEO 權重可以立即移轉。 3. 修改文章內容製作搬家畫面需要批次修改文章內容,手動一篇篇改只有在文章數很少的情況下才有可能。 要做到此點,可以操作 WP 官方提供的 API,完全不需求助主機商,自己用程式執行就能跑完所有文章。

三、搬文章

這次搬 WP 文章遇到比較大的狀況,所以特別記錄一下。 1. 線上轉檔工具 WP 後台可以匯出 xml 檔,但格式與 Blogger 不相容,以往都是靠下面這個網頁的服務轉檔: 進行轉檔時,一方面會顯示錯誤訊息,說檔案太大,必須切割成小於 1MB 的檔案。另一方面我若找個小檔案測試,網站又會呈現當掉的狀態,不曉得是否作者沒有在維護了? 也許以前沒遇到問題,是因為搬的文章數比較少,而這次搬的文章數非常多。但切成一大堆小檔也很麻煩,還是另外找個一勞永逸的解決方案比較好。 2. WP 外掛由於有了 FTP 權限,我找到一個可以轉換匯出檔的 WP 外掛,利用 FTP 安裝到後台: 經測試後,的確可以成功轉檔,並匯入 Blogger,但卻發現一個嚴重問題 → 這個外掛並沒有匯出「留言」,只有匯出文章及分類(也就是 Blogger 的標籤),如此一來,該外掛也不是個完美的解決方案。 3. 自行轉換格式由於多年來協助各種部落格平台搬家到 Blogger,我對 Blogger 匯入檔的格式、編碼等等還是相當瞭解的,所以決定自行研究轉換格式:
  • 比對 WP 後台匯出檔的文章、留言、分類、草稿等等格式與編碼
  • 轉換為 Blogger 匯入檔對應的格式與編碼
最後總算生成可以成功匯入 Blogger 的檔案。

四、搬其他項目

其他的搬家項目,大致有這些:
  • 搬圖片
  • 更換新舊文章連結
  • 搬人氣
  • 301 轉址或搬家畫面
以上這些項目都需要用程式處理,如果文章數不多的話,某些可手動自行處理。 如果需要瞭解以上這些項目的含意,可參考「協助痞客邦搬家到 Blogger 流程紀錄」→「四、本站處理項目」,有比較詳細的說明。

五、聯絡表單

如果有預算需處理 WP 搬家到 Blogger 事宜,請告知要搬家的項目有哪些:
  • 搬文章
  • 搬圖片
  • 處理新舊文章連結
  • 搬人氣
  • 301 轉址或搬家畫面
以及告知以下資訊:
  • 您的網址
  • 文章篇數
並用以下表單與本站聯繫:
稱呼:(必填)
電子郵件:(必填)
您的網址或提問的相關網址:(必填)
簡單自介、如何找到本站:(必填)
請詳細描述您的需求、問題或意見,如提供的資訊不足,可能無法回覆:(必填)
更多 WordPress 相關文章:

徹底解決 Win 10 中英輸入法切換的各種問題(含微軟注音、酷音)

$
0
0
win10-chinese-english-input-switch.jpg-Win 10 解決中英輸入法切換的各種問題中文輸入自 Win 8 以後就是大災難,微軟官方從未根本解決問題。我在 Win 8 的解決方式可參考「讓 WIN8 能用 CTRL + SPACE 切換中英輸入」,主要解決這兩件事:
  • 預設輸入改為英數模式
  • 可按 Ctrl+Space切換中英輸入
由於微軟宣布不支援 Win 7,很多常用軟體也逐漸不支援 Win 7,不得已必須強迫自己適應 Win 10。然而正式使用 Win 10 後,發現狀況跟多年前 Win8/Win10 又不一樣了,得重新解決擾人的中英文輸入法切換問題。費了一番功夫總算解決各種狀況,請見本篇的心得整理。 (圖片出處: unsplash.com)

一、基本二訴求

先簡單說明一下,為何 Windows 輸入及切換要順暢、有效率,必須解決這基本的兩大訴求: 1. 預設輸入英數模式雖然我們使用的系統環境是中文,然而進入 windows 後,最先開始輸入的字元,卻不一定是中文。將預設字元改為英數的理由如下:
  • 進入系統後,第一個要輸入的字元,是中文的機率小於 50% 的話,就應該改為英數模式,才會是有效率的配置。
  • 使用瀏覽器常會輸入網址字串,必須是英數模式
  • 前端工作者、工程師等很多領域,會很常使用英數
當然,如果使用 Windows 以中文輸入為主的使用者,可忽略這個部分。 2. 按 Ctrl+Space 切換中英輸入古早年代 Windows 切換輸入法的快速鍵就是使用 Ctrl+Space,但 Win8 以後必須改用以下幾種組合鍵:
  • Shift:切換中英模式 → 缺點為很常誤按 Shift,導致要常常切來切去,非常不方便
  • Ctrl+Shift:切換同語言的輸入法 → 這個組合鍵很不好按,比較浪費時間
  • Win+Shift:切換不同語言的輸入法 → 這個組合鍵比 Ctrl+Shift稍微好按,但仍然沒 Ctrl+Space順暢
總之切換中英輸入法,還是 Ctrl+Space最省時間,且不會誤按。

二、微軟注音

首先以最多人使用的微軟注音來說明。 1. 預設英數模式如果只有一種輸入法「微軟注音」,沒安裝任何其他語系、輸入法時,狀況最為單純,只要按照以下流程,就能預設使用英數來輸入: win10-chinese-english-input-switch-1.jpg-Win 10 解決中英輸入法切換的各種問題 Windows 開始 → 設定 → 時間與語言 → 語言 → 繁體中文 → 選項 win10-chinese-english-input-switch-2.jpg-Win 10 解決中英輸入法切換的各種問題選擇「微軟注音」 → 選項 → 一般 → 預設輸入模式選擇「英數字元」即可 2. 按 Ctrl+Space 切換中英輸入只要按照以下流程,就能使用 Ctrl+Space 切換中英輸入: win10-chinese-english-input-switch-3.jpg-Win 10 解決中英輸入法切換的各種問題回到 Windows 開始 → 設定 → 裝置 → 輸入 → 進階鍵盤設定 win10-chinese-english-input-switch-4.jpg-Win 10 解決中英輸入法切換的各種問題輸入語言快速鍵 → 進階按鍵組合
  • 切換輸入語言:改成「無」,可取消按 Shift切換中英輸入
  • 繁體中文輸入法 - 啟用/停用輸入法切換:確認這裡設定的是 Ctrl+空格鍵即可
  • 這一點為補充,使用多種輸入法時,「讓我針對每個應用程式視窗使用不同輸入法」最好不要勾選,免得切換後亂跳
3. 其他預設輸入法除了微軟注音,Windows 預設還可新增這些輸入法:速成、大易、行列、倉頡。基本上要做到「預設英數模式」、「按 Ctrl+Space 切換中英輸入」,如果只使用一種輸入法,那麼參考前面微軟注音的設定流程即可。 4. 推薦使用酷音雖然這個章節講的是微軟注音,但如果你習慣用微軟注音的話,不如改用「酷音輸入法」,鍵盤排列方式不變(且選擇性更多),功能及操作的便利性則是酷音大勝,可參考「四、自行安裝輸入法(酷音)」。

三、安裝多種輸入法

前面說的是只有一種輸入法,以下說明安裝多種輸入法時,如何作到「基本二訴求」。 1. 預設英數模式假設主要中文輸入法是「微軟注音」:
  • 需要另外選一個輸入法作為英數模式,例如可以新增 Windows 預設的「Microsoft 速成」輸入法
  • 將「速成」輸入法的 "預設輸入模式" 設定為 "英數字元"
  • 然後將「微軟注音」的 "預設輸入模式" 設定為 "中文"
win10-chinese-english-input-switch-5.jpg-Win 10 解決中英輸入法切換的各種問題接著從 Windows 開始 → 設定 → 裝置 → 輸入 → 進階鍵盤設定,如上圖畫面:
  • 覆寫預設輸入法:選擇「速成」輸入法 → 如此預設就能進入英數模式
  • 切換輸入法:務必取消勾選「讓我針對每個應用程式視窗使用不同輸入法」,才能確保預設使用「速成」輸入法
完成以上設定後,除了預設可英數輸入,另外可使用組合鍵 Win+Space切換輸入法,接下來說明如何改成用 Ctrl+Space切換。 2. 按 Ctrl+Space 切換中英輸入首先須下載自訂鍵盤快速鍵的軟體,例如本篇舉例的 HotKeyz: 接著參考我之前寫的「讓 WIN8 / WIN10 能用 CTRL + SPACE 切換中英輸入」→「二、用 Hotkeyz 調整切換按鍵」,就能實現用 Ctrl+Space切換輸入法。

四、自行安裝輸入法(酷音)

除了 Windows 預設輸入法,如果使用自行安裝的輸入法,例如嘸蝦米輸入法,或是我為了許氏鍵盤所使用的酷音輸入法,作法可能都不太一樣,得視該軟體功能設計是否完善、照顧使用者體驗。 1. 酷音輸入法 Win10 最新版本下載網頁可前往 Github 官網:酷音輸入法 PIME最新版本叫出設定畫面時,會改用瀏覽器開啟,往下捲到底可看到一個非常重要的選項:
  • 預設以停用輸入法模式啟動(Windows 8 以上適用)
請勾選這個選項後套用,這麼做以後,就能滿足基本二訴求:
  • 預設輸入以英數模式開始
  • 也能使用 Windows 系統預設的 Ctrl+Space切換中英輸入
記住別安裝別的輸入法,保持只有一個酷音輸入法,就能在 Win10 爽快打字了。 2. 安裝兩個輸入法若安裝酷音以外的輸入法,如果軟體設計上不像酷音能夠「預設以停用輸入法模式啟動」,可能就需要安裝兩個輸入法,才能滿足基本二訴求。 作法原理可參照前面「三、安裝多種輸入法」的流程:
  • 新增「速成」輸入法,並預設英數輸入
  • 將自行安裝的輸入法預設為中文輸入(如果有這個設定的話)
  • 其他相關設定與自訂快速鍵的操作同樣即可

五、多種輸入法產生的問題

1. 微軟注音的問題補充一下安裝多種輸入法可能會產生的問題,一開始我的操作方式為:
  • 使用「微軟注音」作為英數輸入
  • 使用「酷音」作為中文輸入
有時輸入法切換後,再回到「微軟注音」就不是英數輸入了。 搜尋後也有人遇到同樣問題:「Windows 10 輸入法自動切換問題」,這位網友是在微軟社群發問,他觀察到「微軟注音」有個設定會 "雞婆" 幫使用者自動切換,該設定的名稱叫做「智慧型輸入模式自動切換」,但我們並不知道他的判斷邏輯,導致我們想輸入英數時,偏偏「微軟注音」覺得我們想輸入中文,就自動切換為中文模式了。 該網頁有不少人回答,最終有找出修改該設定的方式,然而我測試了之後,找不到相關的設定,後來才知道是不同 Win10 版本的關係。 該網頁的狀況應該是 Win10 家用版,而 Win10 專業版不太一樣,可參考以下設定流程: win10-chinese-english-input-switch-6.jpg-Win 10 解決中英輸入法切換的各種問題 Windows 開始 → 設定 → 時間與語言 → 語言 → 繁體中文 → 選項 → 選擇「微軟注音」 → 選項 → 一般 捲到最底部後,可看到上圖畫面:
  • Win10 專業版沒有進階設定選項,難怪找不到「智慧型輸入模式自動切換」設定
  • 如上圖,選擇「開啟」舊版微軟注音,然後「開啟進階設定」,就可進行設定了
win10-chinese-english-input-switch-7.jpg-Win 10 解決中英輸入法切換的各種問題先在「一般」分頁,確定輸入模式有選擇「英數模式」 win10-chinese-english-input-switch-8.jpg-Win 10 解決中英輸入法切換的各種問題切換到「進階」分頁:
  • 按照前面「微軟社群討論串」的網頁進行修改即可
  • 取消按 Shift切換中英輸入的按鍵
  • 最重要的是將「輸入自動切換」改為「不使用」,就不會使用「智慧型輸入模式自動切換」了
2. 後續狀況按照前面的設定後,多種輸入法之間的切換比之前好很多,「微軟注音」比較不會被系統強制切換成中文輸入。 然而奇怪的是,這也非 100% 絕對,用久了有時還是會發生切換到「微軟注音」時,自動變成中文輸入的狀況,找不出確切的邏輯與規律。 我的結論大概是:
  • 預設英數輸入使用「微軟注音」似乎會有 bug,可以改用其他系統輸入法,例如本篇舉例的「速成」,比較不會出意外。
  • 另一種作法為參考「讓 WIN8 / WIN10 能用 CTRL + SPACE 切換中英輸入」→「三、移除新注音」→「1. 新增英文輸入」,為 Windows 新增語言「英文」,作為英數輸入
3. 新增語言「英文」為 Windows 新增語言「英文」,是網路上常見的英數輸入替代方案,本篇沒有將此作法當作主要方案,主要原因為:
  • 操作比較麻煩
  • 新增過程需要另外安裝檔案、使用一些磁碟空間
然而,如果習慣新增語言「英文」的話,採用這個方案也是可行的。
更多 Windows 相關文章:

Win7 如何自動校正系統時間

$
0
0
windows-auto-update-system-time.jpg-Windows 如何自動校正系統時間買了多年的 Intel NUC 準系統,Windows 7 最近開機後系統時間總是自動回到 2009 年,除了 Chrome 瀏覽器不給上網,會報錯提示系統時間錯誤,還有一些其他程式也會因為系統時間偏差而無法正常執行。 根據多年經驗,這是主機板電池沒電的狀況,換一顆 CR2032 電池就解決了。然而辛苦拆解準系統後才發現,Intel NUC 做了個特規 CR2032 電池,無法自行更換電池。根據官網「Intel® NUC 上的 CMOS 電池」圖片,電池組件長得如下圖: windows-auto-update-system-time-1.jpg-Windows 如何自動校正系統時間這個電池組件使用一條 2 pin 連接線插到主機板,剝開包覆的膠帶後發現,組件的設計不讓使用者自行替換電池,導致只能想辦法買完整的電池組件替換。然而前面的官網文件也說明了,這整組零件不零賣,要自行上網找 "線上零售商" 購買... 想到要大老遠跑光華商場找電子零件就很麻煩,而上網買除了運費會是零件的好幾倍,也不知是否插上 Intel NUC 就能跑,解決硬體問題的時間不如想辦法解決軟體問題比較快,如果我能讓 Windows 開機後自動校正系統時間,就可以無視主機板電池沒電的問題了。 (圖片出處: pexels.com)

一、原理說明

1. 最方便的作法網路上爬了相當多文章,有些是內容很專業、原理說明得很詳細,可惜我除了看得頭昏眼花,也測不出效果。直到這一篇內容及步驟都相當簡短的文章,才測試成功: 這篇主要需進行 3 個動作:
  • 製作 .bat 批次檔
  • 修改 Windows 登錄檔
  • 建立工作排程器
2. bat 批次檔內容內容只有兩行,說明如下: net start "windows time" → 這一行會強制啟用系統服務 "windows time" w32tm /resync → 進行時間校正windows-auto-update-system-time-2.jpg-Windows 如何自動校正系統時間檔案總管 → 電腦 → 右鍵選「管理」→ 服務與應用程式 → 服務,可看到上圖畫面,紅框處即為 "Windows Time" 服務,啟用後方可允許系統時間校正。 如果沒有上述 bat 檔第一行,就逕自執行第二行,有可能無法進行時間校正(有第一行可確保這個系統服務被啟用)。 3. 其他補充
  • 「修改 Windows 登錄檔」這個動作非常必要,如同原文說明,Windows 預設在系統時間差距太大時,例如正負差距 15 個小時以上,不允許進行時間校正,所以將登錄檔中的預設值 15 小時修改掉
  • 「建立工作排程器」這部分的流程比較麻煩且容易出錯,主要目的為讓開機後自動執行 bat 檔,其實有更簡單的作法,請見後面優化流程。

二、優化版流程

因為部分操作流程還可以更簡化,下面會提供我的優化版本。 1. 製作 bat 批次檔net start "windows time" w32tm /resync將以上內容存成 bat 檔即可。 2. Windows 登錄檔修改 Windows 登錄檔內容比較麻煩,我已製作下面這個 reg 登錄檔,下載後執行即可: 3. 開機自動執行windows-auto-update-system-time-3.jpg-Windows 如何自動校正系統時間只要將前面製作的 bat 批次檔放到 Windows「啟動」資料夾,開機就會自動執行:
  • Win + R→ 輸入 shell:Startup
  • 出現的資料夾就是 Windows「啟動」資料夾
4. 工作排程補充如果習慣使用 Windows 內建的「工作排程」,原文的操作流程,觸發條件設定為「開機啟動」,但實測結果如下:
  • 觸發條件設定為「啟動時」,那麼開機時並不會執行
  • 必須將觸發條件設定為「登入時」,開機才會執行
更多 Windows 相關文章:

了解文章沒被 Google 收錄的真正原因__減少在 Search Console 打轉的時間

$
0
0
why-google-search-console-not-index-posts.jpg-了解文章沒被 Google 收錄的真正原因__減少在 Search Console 打轉的時間不少站長看到 Google 網站管理員(Search Console)的錯誤訊息會很恐慌,在 FB 社團「Blogger 經營學習資源分享」不時看到相關提問,私下我也會收到諸如這些問題:
  • Search Console 後台通知「頁面會重新導向」怎麼辦
  • 文章即使手動建立索引也無法成功(Google也搜尋不到)
  • 錯誤訊息「已檢索/已找到-目前尚未建立索引」要怎麼辦
前幾年我寫了「文章更新速度與 Google 索引頻率、及 SEO 的關係」,2019 已有知名美食部落客感覺文章收錄的速度變慢,難怪站長們看到 Search Console 錯誤訊息常有驚弓之鳥的感覺,可能認為沒有解決這些錯誤的話,文章就沒法被收錄,讀者也無法從 Google 搜尋到這些文章。 網路的世界變化很快,很久以前或許只要正確提交網站地圖,文章就能被收錄,但我上面 2019 的文章也提到了:
以前 Google 的工作量,不等於現在 Google 的工作量。每天全世界文章增加的數量,跟以前比絕對是天文數字。那麼 Google 伺服器一定得有所取捨,才有辦法處理這麼大的資料量。
既然遊戲規則 Google 隨時可以更改,我認為 Google 對收錄文章這件事的態度已經跟一、二十年前大不相同,如果不想從遊戲登出的話,我們只能努力從各種官方釋放的訊息,以及各種蛛絲馬跡來察覺背後運作模式的更迭。 (圖片出處: pxhere.com)

一、Search Console 常見索引錯誤

1. 教學說明進入主題之前,需先了解一下基本知識,看看 Search Console 常見的索引錯誤狀況有哪些。這篇文章「【完整指南】2022 Google Search Console教學(附索引問題處理)」非常詳盡可以作為參考。 由於內容很多,跟本篇相關的內容請直接看「Google Search Console 涵蓋範圍」→「4. 排除」,這邊就是所有索引錯誤的狀況說明。 2. 常見索引錯誤從上述內容,直接摘錄比較重要的常見索引錯誤,為以下兩項:
1. 已檢索 – 目前尚未建立索引 原因:Google 已經爬取你的頁面了,但是還沒索引,可能會在未來索引(或不索引)。 解決辦法:這個狀態蠻常有錯報的情況發生,建議先透過『site:該頁面網址』檢查該頁面是否被索引,若無則透過網址審查工具檢查該頁面是否有問題。 2. 已找到 – 目前尚未建立索引 原因:Google 已經找到該頁面,但還沒看裡頭是什麼內容。 說明:有可能是檢索預算不夠,或是網站流量超載,所以 Google 安排下次檢索,常常有此情況則記得優化檢索預算。
為何這兩項需要特別提出來,是因為這兩個錯誤都只能靠 Google 哪天想到了,才會幫我們處理。其他的索引錯誤,點進去後都能看到處理說明,多半照著做就能進行修正與驗證。 3. 名詞解說前面第2點的教學,如有不熟悉的名詞,請見以下說明:

二、龍頭網站的文章索引狀態

「已檢索/已找到-目前尚未建立索引」這兩個棘手的索引錯誤要怎麼解決呢?剛好我手上有多個網站可以進行測試,都是自行開發的網路服務,各種等級都有,以下就來逐一檢視 Search Console 的索引狀況。 1. 索引狀態首先是「CC0 免費圖庫搜尋引擎」,雖然流量普通,但因為進入市場的時間很早,從建立之初就已經是龍頭,搜尋關鍵字 CC0一定會看到。 why-google-search-console-not-index-posts-1.jpg-了解文章沒被 Google 收錄的真正原因__減少在 Search Console 打轉的時間進入 Search Console 檢視索引狀態:
  • 已檢索-目前尚未建立索引:雖有 16 個,但都非文章頁面,可以無視
  • 所有其他未編入索引的原因:點開後都沒有文章頁面,可以無視
  • 已找到-目前尚未建立索引:0 個,非常完美,代表所有文章頁面都已被收錄
2. 流量狀態why-google-search-console-not-index-posts-2.jpg-了解文章沒被 Google 收錄的真正原因__減少在 Search Console 打轉的時間這是後台文章流量狀態,可看到上圖紅框標示的兩個圖庫,從 2021 年發布,到現在 2023 年 10 月,兩年半的時間瀏覽數只有 100 出頭,依然不影響 Google 將其收錄的決心。 3. 觀測結果這個龍頭搜尋圖庫網站其實鮮少更新,但今年中有新增數個 AI 圖庫頁面,看來早已全數收錄,那麼可以做個簡單的註腳:
  • 權重相當大的龍頭網站,即便流量不大,不常更新,新文章頁面依然很快會被 Google 收錄

三、熱門網站的文章索引狀態

接下來觀察的是「台灣景點人潮即時影像」,由於進入市場較晚,尚未撼動該領域的龍頭地位,但流量跟一般部落格相比,算是相當之大。若要有個基準點,跟前面的「CC0 免費圖庫搜尋引擎」相較之下,流量是其十多倍以上,不是同一個等級。 搜尋即時影像的相關關鍵字時,大多會是第一頁名列前茅,評比為熱門網站算是不為過。 1. 索引狀態why-google-search-console-not-index-posts-3.jpg-了解文章沒被 Google 收錄的真正原因__減少在 Search Console 打轉的時間進入 Search Console 檢視索引狀態:
  • 已檢索-目前尚未建立索引:雖有看起來嚇人的 617 個,但非文章頁面,都是無用的 feed、標籤、搜尋頁面,可以無視
  • 其他不重要的就不再贅述
  • 已找到-目前尚未建立索引:2 個,這需要解決
why-google-search-console-not-index-posts-4.jpg-了解文章沒被 Google 收錄的真正原因__減少在 Search Console 打轉的時間點進去後,看到上圖紅框處,這個頁面是「台北信義路-基隆路即時影像」,等會找出這個頁面看是怎麼回事。 上圖另一個網址非文章頁面,不必處理。 2. 流量狀態why-google-search-console-not-index-posts-5.jpg-了解文章沒被 Google 收錄的真正原因__減少在 Search Console 打轉的時間這是後台文章畫面,上圖紅框即為前面沒建立索引的頁面,跟其他頁面相比之下的確滿突兀的,原來是流量特別低,三年來瀏覽數不到 100,看來這就是沒被 Google 收錄的原因3. 觀測結果這個熱門網站不但流量大,而且更新頻繁。即便如此,也不代表所有文章都會受到 Google 青睞,那麼可以做個簡單的註腳:
  • 權重大、但非龍頭的熱門網站,只要某篇文章的流量不大,Google 依然不會收錄

四、冷門網站的文章索引狀態

最後觀察的是「ICON 免費素材搜尋引擎」,這個網站沒什麼知名度,也沒什麼流量,幾乎搜尋不到這個網站,剛好跟前兩個觀測對象形成強烈對比,可以驗證更多不一樣的正反面資訊。 1. 索引狀態why-google-search-console-not-index-posts-6.jpg-了解文章沒被 Google 收錄的真正原因__減少在 Search Console 打轉的時間這個網站今年(2023)六月時進行大更新,新增了二十多個圖庫,但時至今日(十月中)只有一個頁面被 Google 收錄。上圖為進入 Search Console 後看到的索引狀態:
  • 二十多個未被索引的頁面,分別出現在兩處紅框的項目:「重新導向錯誤」、「已找到-目前尚未建立索引」
  • 「重新導向錯誤」這個項目,在前兩個網站都沒看到,為首次出現
  • 究竟這些未被收錄的頁面,為何有些被 Google 視為「重新導向錯誤」、有些被視為「已找到-目前尚未建立索引」,目前看不出規律
  • 請注意,上圖還可看到,「已找到-目前尚未建立索引」我已提交驗證完畢,但對文章收錄一點幫助也沒有
  • 這二十幾篇文章,我也一一手動提交過,但現在你終於可以知道,以上做的這些事,Google 可說是連正眼也不會瞧一下,都是做心酸的!
why-google-search-console-not-index-posts-7.jpg-了解文章沒被 Google 收錄的真正原因__減少在 Search Console 打轉的時間試著點進首次看到的「重新導向錯誤」,上圖紅框發現兩個值得注意的網址,之後可用來交互比對及論述。 2. 流量狀態why-google-search-console-not-index-posts-8.jpg-了解文章沒被 Google 收錄的真正原因__減少在 Search Console 打轉的時間這是後台文章畫面,上圖紅框有三個可以重點說明的頁面:
  • Pixabay:這是今年 6 月至今唯一被收錄的頁面,可看到「瀏覽數」是新文章中最高,也是我能 100% 認定 Google 就是依據「流量」來收錄頁面。因為當初可是幾乎天天盯著 Search Console,監看新文章何時能被收錄,直到這篇的流量達到某個數字門檻後,才被 Google 正式索引。
  • 另外兩篇沒被收錄的,瀏覽數至今分別為 140、135,算是新文章中第 2、3 高,就是前面「重新導向錯誤」紅框標示的那兩篇。我只能臆測,「重新導向錯誤」大概會是,Google 有意收錄,但流量還未達標需要再加把勁,所以先掛在這個項目之下。(然而這些文章跟 "重新導向錯誤" 實則沒任何關聯)
  • 延續同樣邏輯臆測,掛在「已找到-目前尚未建立索引」的文章,代表的意思就是:這些文章流量低到 Google 懶得理,暫時不用抱太大希望
現在知道 Google 收錄文章的評判重點是「流量」後,如果本篇文章你有看仔細的話,會發現這個網站沒被收錄的兩篇文章,四個月累積的瀏覽數 140、135,甚至比第一個網站紅框標起來的兩篇文章,累積兩年半的瀏覽數還要高(130、111)。 但要提醒的是,第一個網站可是所有文章都被收錄了呢!相信從這個結果,你也看出更多 Google 收錄文章的邏輯了~ 3. 觀測結果經由以上觀察,可以做出以下推論:
  • Google 有可能對某些重點網站(知名度高、龍頭)極度信賴,只要有文章就會立即收錄
  • 也有可能是,現今 2023 年的時間點,Google 開始變得極為嚴苛,文章必須先達到一定的流量門檻才會被收錄。
  • 無論實際情況是以上那一種,總之對於流量小、知名度不夠的冷門網站,文章流量若沒有衝到一定的數字,是不會獲得 Google 青睞的。
  • 小網站新文章想被收錄,要付出的努力,可能必須比大網站還來的多

五、Google 對文章索引速度的態度

1. 官方說詞做完實驗與觀察後,來找找官方是否對文章索引速度有什麼相關看法,我搜尋到 Google 官網這個頁面: 以下節錄與文章索引速度相關的重要資訊:
Gary:Tom Baker 問,我有 16,000 個網頁,系統是否需花費六個月以上的時間才能建立索引?我發現每週增加的數量為 5 到 15 個,但我覺得這個速度很慢。 建立網站索引的速度取決於許多因素,但最重要是網站的品質,其次是網站在網際網路上的熱門程度。如果您已盡可能提供最高品質的內容,可以嘗試在社群媒體上投放宣傳內容,或許就會有人開始討論您的網站,這樣應該會有幫助。
重點就是這兩句話:
  • 最重要是網站的品質
  • 其次是網站在網際網路上的熱門程度
這其實滿微妙也滿有趣的,Google 明確給出了要點,但又似乎什麼也沒說,這些不都是所有關注 SEO 的站長們知道的事嗎? 微妙的地方在於,即便站長們都知道這些事,但注意力卻鮮少放在這兩點,反而可能覺得要解決 Search Console 的索引錯誤訊息,文章才能被收錄,這是非常大的盲點。 而我也是在處理「ICON 免費素材搜尋引擎」這個網站的索引錯誤過程,發現「流量」才是 Google 收錄文章的關鍵,自此不在 Search Console 的各種無用提示資訊上打轉2. SEO 永遠是內容為王 Google 說的「網站的品質」及「網路上的熱門程度」,最終必須有個量化指標,才能交由程式進行文章收錄與否的基準判斷。這個量化指標是商業機密,Google 不會公佈,我們也不可能知道。 但是從我們自己的角度來判斷何謂「網站的品質」及「網路上的熱門程度」,最簡單直白的量化數據就是「流量」。雖然「文章內容好壞與否」是主觀、抽象的,但只要內容夠好、能滿足越多人的需要,自然能帶來「流量」,也越能「在網路上引起討論」。 說到底,SEO 的核心「內容為王」,終究是可以套用在所有層面,包括本篇主題「Google 收錄文章的速度」。

六、部落格文章是否被 Google 收錄就能帶來流量?

其實看到這裡,相信讀者都了解重點是什麼了,也知道本篇要傳達的訊息是什麼,接下來只是再稍稍補充一些想法。 1. SEO 與流量的關係很久以前寫過一篇「部落格網站是否加強 SEO 就能帶來流量?」,是因為看到太多站長對 SEO 技巧入魔,完全對 SEO 本末倒置,把 SEO 看成 "因",把流量看作 "結果",殊不知完全相反:
  • Google 不會雪中送炭:如果你的網站原本流量就很低、沒什麼人看,Google 給你的起始排名也會非常低,就像 "米其林評鑑" 不會去找沒沒無名的餐館打分數。
  • Google 只會錦上添花:如果你的網站經營得很好,人潮絡繹不絕,Google 也會主動把第一頁的排名頒給你。就像出了名的餐廳,各大媒體、雜誌都會爭相報導一樣。
結論就是:你必須先有流量,Google 才能給你更多的流量2. 文章收錄與流量的關係一間圖書館的空間是有限的,一開始藏書不夠多時,有新書來圖書館都會收。等到書櫃都放滿了,圖書館就會開始去蕪存菁,把沒人借的書剔除,從新書中篩選熱門書籍上架,如此反覆。 搜尋排名也是一樣,一般人點完前三頁就很少按下一頁了,假設 Google 肯收錄 40 頁就不錯了,那麼全世界每天不斷產生的新文章,你覺得 Google 會如何取捨及收錄呢?Google 若是收錄超過 40 頁份量的文章有什麼意義嗎? 或許以前我們覺得只要網站地圖有提交,文章就會被收錄,說不定是因為書櫃還沒滿。現在我們不得不面對的狀況是,文章得先夠有水準,才能獲得上架的資格。 所以,當站長們想著「只要想辦法讓文章被 Google 收錄,就能帶來搜尋流量」,現在這個想法也成為因果錯置了—— 結論就是:你必須先有流量,Google 才會收錄你的文章

七、小結

簡單為本篇做個總結:
  • 以往 Google 索引還沒飽和時,只要提交網站地圖,文章就能被收錄
  • 現在索引飽和了,文章需要先向 Google 證明有資格被收錄
  • 在 Search Console 做了再多事,都不會左右 Google 是否收錄文章的標準
  • 要證明文章有資格被 Google 收錄,需要提昇「網站的品質」及「網路上的熱門程度」
  • 所以「內容為王」是永遠的精髓
  • 站長也需要自行利用各種社群媒體替文章宣傳,才能提昇「網路上的熱門程度」
  • 最終讓以上所有努力反應在「流量」上,達到一定門檻後,Google 自然會收錄文章
更多 SEO 相關文章:
更多 Search Console 相關文章:

會員系統公告:FB 登入功能失效 請重新註冊

$
0
0
本站「會員系統」在進入 2024 後,就有讀者反應無法加入會員,FB 登入時會顯示 "應用程式錯誤",這是因為「FB 登入 API」現在已經不開放給個人使用,強制開發人員一定要完成「商家驗證」才能取得權限。這也就是說,現在只要沒開公司、取得營業登記證明,網站將不能裝 FB 登入功能。 其實我對 Facebook 的使用體驗一向不太好,開發應用程式時也常感到挫折,由於頻繁改版、權限一再變更,導致功能失效或工具不能使用,近年則是每隔一段時間就要求開發人員進行許多細節審查。而這些事在開發 Google 應用程式時則很少遇到,因此決定全面將會員系統改用 Google 登入。 只是如此一來,原本的會員資料將失效,必須重新註冊帳號,不過主要影響的僅是「加值會員」,請 WFU BLOG 舊會員注意本篇說明。 (圖片出處: pixabay.com)

一、加值會員資料移轉

舊的會員註冊資料是以 Facebook 帳號資訊作為儲存依據,現在起註冊會以 Google 帳號資訊作為依據,然而兩種帳號如何轉換會是一個大問題。能作為相同資訊進行判別的只有「Email 郵件地址」,然而使用者註冊 FB 與 Google 帳號時不一定會使用相同 Email,所以也不宜貿然使用程式自動轉換新舊帳號,以免發生誤判。 「加值會員」由於在本站會員系統內儲存過點數,本站會協助轉換點數到新帳號,不過只能採人工方式處理(不宜由程式判斷)。請用 Google 帳號重新註冊後,填寫文末的「三、聯絡表單」留下這些資訊:
  • 註冊帳號名稱
  • 以前 FB 登入註冊使用的 Email
  • 現在註冊使用的 Google 帳號 Email
填寫之後會儘速處理,若一段時間仍發現沒有移轉點數,可再與本站聯繫。

二、一般會員重新註冊

沒有儲值過點數的「一般會員」,加入會員系統的主要功能為閱讀「會員限定文章」,那麼新舊帳號資料沒有轉換並不會造成什麼問題,所以請用 Google 帳號重新註冊即可。

三、聯絡表單

加值會員使用上有任何問題,請用下面的表單與我聯繫:
稱呼:(必填)
電子郵件:(必填)
填寫表單網址:(必填)
填寫表單目的:(必填)
請詳細描述您的需求、問題或意見:(必填)
更多會員系統相關文章:

Blogger 日期格式化詳解

$
0
0
Blogger 如果想調整日期格式的話,後台就有許多排列組合可選,照理說總能找到一個滿意的。只不過 Blogger 是外國人設計的產品,日期格式主要按照西式的排列習慣,還真不一定能符合所有人喜好。 雖然 Blogger 官方並沒公開關於日期格式化的說明,不過網路上倒有一些資料可循,本篇會詳細說明如何將日期改成自己想要的格式,中英文都可以。 (圖片出處: unsplash.com)

一、注意事項

1. 參考資料本篇所有修改語法的資料來源為這兩篇: 從發布時間來看,都是至少 4 年(2020)之前的資訊,代表 4 年以前按照這些資料來修改 Blogger 範本是沒問題的,那麼現在呢? 2. 官方 RWD 範本經實測後,官方 RWD 範本例如 Contempo、Soho、Emporio、Notable、Essential,如果想要搜尋範本中跟日期相關的語法,會發現已經找不到能修改的地方了,代表過去幾年 Blogger 官方 RWD 範本又做了不小的變更,不讓使用者修改的地方又更多了。 這個狀況我一點都不意外,因為當初 2017 年「Blogger 推出官方 RWD 範本」時,我就做了這樣的結論:
官方的設計明顯希望站長們不要動這個 RWD 範本,預設的效果就放手去接受它吧
所以對於官方 RWD 範本的使用者,如果你是熟練的老手,才建議參考本篇的資料,自行在範本中找合適的地方進行改。如果是新手的話,建議要嘛接受預設效果,要嘛改用「官方非 RWD 範本」 3. 官方非 RWD 範本怕新手不知道什麼是「官方非 RWD 範本」,所以多說明一些,例如 Simple、Picture Window、Awesome、Watermark、Ethereal、Travel。 本篇的語法建議使用在以上這些範本,比較不會有挫折感。

二、首頁每篇文章都顯示日期(新版)

1. 修改方式多年前寫過一篇「讓 Blogger 首頁每篇文章都能顯示日期」,剛好本篇參考資料的語法,可以讓這件事變得簡單,所以提供新版的作法。 在範本中搜尋以下字串: <b:if cond='data:post.dateHeader'> <h2 class='date-header'><span><data:post.dateHeader/></span></h2> </b:if>改成以下字串即可: <h2 class='date-header'><span><data:post.date/></span></h2>2. 原理說明 Blogger 語法現在多了 <data:post.date/>日期資料,不必再像以前須判斷是否為該日期的第一篇文章,改用 <data:post.date/>後就能每篇文章都顯示日期。

三、自訂日期格式

1. 日期格式化語法根據參考資料,Blogger 日期格式化語法如下: <b:eval expr='data:post.date format "YYYY-MM-dd"'/>
  • YYYY: 年份 4位數
  • MM: 月份 2位數(自動補0)
  • dd: 日期 2位數(自動補0)
由於我們使用了分隔符號 "-",在網頁上顯示的效果如下: 2024-01-252. 修改技巧可任意使用中文字串,就便成了中文日期,例如: <b:eval expr='data:post.date format "YYYY年M月d日"'/>顯示效果如下,不會自動補 0: 2024年1月25日3. 修改範本接著以「官方非 RWD 範本」為例,說明如何修改日期格式。範本中代表日期資料的有這些標記: <data:post.dateHeader/> <data:post.timestamp/> <data:post.date/>在想要修改日期格式的地方,將以上這幾個 Blogger 標記語法改成前述的日期格式化語法即可。

四、日期格式化參數

前面提到的參考資料「blogger-snippets-Date」整理了許多日期格式化參數,建議修改時可以參考,以下列出一些實用的修改方式,請注意大小寫有分:
  • 月份:MMM → 顯示三個字英文縮寫,例如 Jan, Nov
  • 月份:MMMM → 顯示完整英文,例如 January, November
  • 星期:ww → 顯示兩位數
  • 天:EEE → 顯示三個字英文縮寫,例如 Mon, Tue
  • 天:EEEE → 顯示完整縮寫,例如 Monday, Tuesday
  • 上下午:aaaa → 例如 AM, PM
  • 小時:hh → 顯示12小時制,兩位數
  • 小時:HH → 顯示24小時制,兩位數
  • 分鐘:mm → 顯示兩位數
更多「Blogger 語法」相關文章:

FB 社團如何篩選貼文通知﹍操作 Google Apps Script 定時過濾 Gmail 郵件

$
0
0
相信每個人加入的 FB 社團都很多,但不見得每篇新貼文都有興趣看,主要也是時間不夠。如果能篩選出有興趣的貼文才收到通知,就能節省相當多時間。 如果是 FB 社團管理員,官方提供了「關鍵字提醒」的功能,但一般社員無法享受這個福利。 以下的「基本作法」是一種常見思路:
  • 參照「訂閱FB社團通知郵件」→「二、Facebook 設定」,可將 FB 社團所有新貼文寄到 Gmail
  • 在 Gmail 設定關鍵字篩選
這個流程對於多數情況已經很夠用了,能夠接受的話可以不用看本篇的作法。而本篇的 Google Apps Script(以下簡稱 GAS) 解決方案,主要能改善以下幾點:
  • Gmail 篩選器如果關鍵字只設定「一個中文字」時,會漏掉許多郵件,代表篩選器的關鍵字必須為設定「兩個中文字」以上,形成一個詞彙,才能精確篩選。
  • 如果 Chrome 有安裝「Gmail 檢查新信」這類套件,FB 社團新郵件通知可能還是很頻繁,會打擾工作頻率。
  • 使用 GAS 可定時檢查 Gmail,每隔一段時間才統整相關郵件,並寄發通知,讓我們在指定時間才接收 FB 社團新貼文。
(圖片出處: unsplash.com)

一、原理說明

1. 操作流程接續「基本作法」的流程:
  • 在 Gmail 篩選器,針對 FB 社團寄來的新貼文郵件,「標上星號」,並「標示為已讀取」,如此 Chrome 套件就不會頻繁通知有新郵件
  • 在 GAS 寫程式,讀取所有「標上星號」郵件的內容,與自行設定「關鍵字」進行比對
  • 所有符合的郵件整合成一封郵件,寄到指定 Gmail 信箱
  • 將所有處理過的郵件移除星號
  • 依照需求設定排程,例如每 n 小時執行一次程式,或是每天指定時刻執行,如此只有固定時刻會收到篩選後的郵件內容
2. GAS 說明文件過去在「使用 Gmail API 寄信的簡易管道」曾經說明,操作 Gmail API 是非常難搞的一件事,最方便的處理管道還是交給 GAS,因為 GAS 提供了一個 GmailApp 服務,可以用最無腦的方式寄送 Gmail 郵件。 以下提供操作 GmailApp 需要瞭解的官方文件: 根據官方文件,使用 GmailApp 的額度一天可寄 100 封郵件,可讀取 20000 封郵件,每封郵件內容最多 200KB。

二、範例程式碼

以下為 GAS 範例程式碼,修改參數請參考註解說明: function filterEmail() { var receiver = "xxx@gmail.com", // 收件者郵件 subject = "FB 社團篩選郵件", // 郵件標題 keyword = ["關鍵字1", "關鍵字2"], // 所有要篩選的字串 from = "facebookmail.com", // 寄件者網域 query = "is:starred from:(" + from + ")", // 搜尋參數 篩選標記星號、從 FB 寄來的郵件 quota = 200000, // 郵件大小上限 200k threads = GmailApp.search(query), // 搜尋符合條件的郵件 l = threads.length, // 郵件數量 html = "", // 寄出的郵件 html 內容 i = 0, serial = 0, newHtml, thread; // 沒有郵件則不處理 if (!l) { return; } for (i; i < l; i++) { thread = threads[i]; // 篩選郵件 html 內容 newHtml = getHtml(thread); // 內容超過郵件大小上限時 分批寄出信件 if ((html + newHtml).length > quota) { serial++; sendMail(html, serial); html = newHtml; } else { // 沒超過時 多封郵件內容整合為一封 html += newHtml; } // 郵件移除星號 GmailApp.unstarMessages(thread.getMessages()); } // 寄出整合後的郵件 if (html) { serial++; sendMail(html, serial); } // 取得郵件 html 內容 function getHtml(thread) { var message = thread.getMessages()[0], from = message.getFrom(), // 寄件者 subject = message.getSubject(), // 郵件主旨 body = message.getBody(), // 郵件 html 內容 plainBody = message.getPlainBody(), // 郵件純文字內容 html = "", i; // 篩選關鍵字 for (i in keyword) { // 內容可根據自己需求 選擇使用 body 或 plainBody 來產生 if (body.indexOf(keyword[i]) > -1) { html += "寄件者:" + from + "<br/>"; html += "主旨:" + subject + "<br/>"; html += "內容:" + body + "<br/><br/><br/>"; return html; } } return html; } // 寄出郵件 function sendMail(html, serial) { var date = Utilities.formatDate(new Date(), "GMT+8", "yyyy-MM-dd HH:mm"), // 日期格式化 subject_date = subject + " " + date + "-" + serial; // 組合信件標題 加上日期及序號 // 寄信 MailApp.sendEmail({ to: receiver, subject: subject_date, htmlBody: html }); } }

三、補充說明

1. 授權第一次執行 GAS 程式碼時,因為取得 Gmail 郵件需要權限,請參考「製作可執行 GAS 指令碼的圖片按鈕」→「三、撰寫 Apps Script 指令碼」的流程進行授權。 2. 排程執行程式碼 GAS 設定排程自動執行的操作,可參考「排程抓證交所台股交易資料」→「五、設定排程」。
更多 Google Apps Script 相關文章:

輕鬆打造網站置頂公告,抓住訪客注意力!

$
0
0
大部分情況下網站公告通常放在首頁,或是有經營 FB 粉絲團、社群媒體的話,重大事情在社群發佈的效果會比較好。然而這樣的處理方式,只能將公告內容擴散給粉絲。至於非粉絲的話,例如從搜尋引擎而來、或從別的網站連結過來的訪客,基本上只會到達文章頁面,不會看到首頁或社群媒體。 所以網站最好的處理方式,是在每個頁面最上方都出現置頂公告,如此可以確保通知所有訪客無漏網之魚。 對於一般部落格而言,可能不會經常有大事件需要公告,不過倒是可以借用置頂公告當作廣告,拿來宣傳「特定的熱門文章」、「社群媒體」等等。如果是商業用途的網站,那麼置頂公告更好用了,可以作為長期宣傳「商品」、「經營業務」、「加入會員」等等之用。 置頂公告這樣的功能已經有現成的免費/付費工具,不過因為這功能很簡單,乾脆自己開發一個,網站也可減少不必要的外連 JS(可參考「為何部落格最好避免第三方外掛」),避免拖慢網頁速度。 以下先介紹這個小工具的功能,想要直接安裝可跳至「二、安裝程式碼」 (圖片出處: pexels.com)

一、置頂公告功能介紹

  • 如果是注重使用者體驗的站長,會希望置頂公告可以讓訪客看過後選擇關閉,將來不再出現同樣的重複訊息,所以此工具可以設定置頂公告是否永遠顯示
  • 比較低調的站長或許希望公告不要浮動「置頂」,而是改為浮動置底,此工具也可選擇出現在網頁底部。
  • 如果開啟「關閉公告」的功能,當訪客關閉公告後,系統會自動記憶上次的公告內容。當下次站長更新公告內容時,置頂公告會重新顯示,直到訪客按下關閉為止。
  • 此工具預設了幾種顏色模版,可自行修改背景顏色、按鈕顏色等等
  • 如果熟悉 CSS 的話,可以自行調整所有 CSS 細節

二、安裝程式碼

在修改範本之前,如果第一次安裝本站工具的讀者,建議先閱讀「備份範本的訣竅」系列文章。 請到後台「主題」→ "自訂" 按鈕右方的下拉圖示 →「編輯 HTML」,游標點進範本區塊,按 Ctrl-F 搜尋 </body>這個字串,找到後在此字串的前一行,插入以下程式碼: <script src='//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js'></script><!--Blogger 置頂公告 --> <script> //<![CDATA[ (function($) { var newBulletin = "這裡是置頂公告內容,<a class='blt_btn blue' href='https://www.wfublog.com/' target='_blank'>按鈕</a> 也可使用!", // 填入要顯示的公告內容, 可使用 HTML 碼, 按鈕顏色可改 class 參數 blue、green、red、orange、black、white、gray、navy enableClose = true, // 是否允許關閉公告, 允許填 true, 不允許填 false position = "top", // 公告位置, 置頂填 "top", 置底填 "bottom" bgColor = "white", // 背景色, 可使用 "blue" "green" "red" "orange" "black" "white" "gray" "navy" delay = 3, // 幾秒後顯示 bulletinStatus = localStorage.bulletinStatus || "unRead"; var _0xb687=["\x75\x6E\x52\x65\x61\x64","\x62\x75\x6C\x6C\x65\x74\x69\x6E","\x62\x75\x6C\x6C\x65\x74\x69\x6E\x53\x74\x61\x74\x75\x73","","\x3C\x64\x69\x76\x20\x69\x64\x3D\x27\x68\x36\x30\x27\x3E\x3C\x2F\x64\x69\x76\x3E","\x3C\x64\x69\x76\x20\x69\x64\x3D\x27\x62\x75\x6C\x6C\x65\x74\x69\x6E\x27\x20\x63\x6C\x61\x73\x73\x3D\x27","\x20","\x27\x3E","\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x27\x62\x75\x6C\x6C\x65\x74\x69\x6E\x5F\x62\x6F\x64\x79\x27\x3E","\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x27\x62\x75\x6C\x6C\x65\x74\x69\x6E\x5F\x74\x65\x78\x74\x27\x3E","\x3C\x2F\x64\x69\x76\x3E","\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x27\x63\x6C\x6F\x73\x65\x42\x74\x6E\x5F\x6F\x75\x74\x65\x72\x27\x3E\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x27\x63\x6C\x6F\x73\x65\x42\x74\x6E\x27\x20\x74\x69\x74\x6C\x65\x3D\x27\u95DC\u9589\u516C\u544A\x27\x3E\xD7\x3C\x2F\x64\x69\x76\x3E\x3C\x2F\x64\x69\x76\x3E","\x3C\x64\x69\x76\x20\x73\x74\x79\x6C\x65\x3D\x27\x70\x6F\x73\x69\x74\x69\x6F\x6E\x3A\x20\x61\x62\x73\x6F\x6C\x75\x74\x65\x3B\x20\x72\x69\x67\x68\x74\x3A\x20\x31\x30\x70\x78\x3B\x20\x62\x6F\x74\x74\x6F\x6D\x3A\x20\x30\x3B\x20\x68\x65\x69\x67\x68\x74\x3A\x20\x31\x35\x70\x78\x3B\x20\x6C\x69\x6E\x65\x2D\x68\x65\x69\x67\x68\x74\x3A\x20\x31\x30\x70\x78\x3B\x20\x6C\x65\x74\x74\x65\x72\x2D\x73\x70\x61\x63\x69\x6E\x67\x3A\x20\x31\x2E\x34\x70\x78\x3B\x27\x3E\x3C\x61\x20\x73\x74\x79\x6C\x65\x3D\x27\x74\x65\x78\x74\x2D\x64\x65\x63\x6F\x72\x61\x74\x69\x6F\x6E\x3A\x20\x6E\x6F\x6E\x65\x3B\x20\x63\x6F\x6C\x6F\x72\x3A\x20\x23\x63\x63\x63\x3B\x20\x66\x6F\x6E\x74\x2D\x66\x61\x6D\x69\x6C\x79\x3A\x20\x68\x65\x6C\x76\x65\x74\x69\x63\x61\x2C\x20\x61\x72\x69\x61\x6C\x2C\x20\x73\x61\x6E\x73\x2D\x73\x65\x72\x69\x66\x3B\x20\x66\x6F\x6E\x74\x2D\x73\x69\x7A\x65\x3A\x20\x31\x31\x70\x78\x3B\x27\x20\x68\x72\x65\x66\x3D\x27\x68\x74\x74\x70\x73\x3A\x2F\x2F\x77\x77\x77\x2E\x77\x66\x75\x62\x6C\x6F\x67\x2E\x63\x6F\x6D\x2F\x32\x30\x32\x33\x2F\x30\x32\x2F\x77\x65\x62\x73\x69\x74\x65\x2D\x74\x6F\x70\x2D\x62\x75\x6C\x6C\x65\x74\x69\x6E\x2E\x68\x74\x6D\x6C\x27\x20\x74\x61\x72\x67\x65\x74\x3D\x27\x5F\x62\x6C\x61\x6E\x6B\x27\x20\x74\x69\x74\x6C\x65\x3D\x27\x42\x6C\x6F\x67\x67\x65\x72\x20\u7DB2\u7AD9\u7F6E\u9802\u516C\u544A\x0A\u7A0B\u5F0F\u8A2D\u8A08\uFF1A\x57\x46\x55\x20\x42\x4C\x4F\x47\x27\x3E\u24E6\x20\x42\x75\x6C\x6C\x65\x74\x69\x6E\x3C\x2F\x61\x3E\x3C\x2F\x64\x69\x76\x3E","\x74\x6F\x70","\x70\x72\x65\x70\x65\x6E\x64","\x62\x6F\x64\x79","\x62\x6F\x74\x74\x6F\x6D","\x61\x70\x70\x65\x6E\x64","\x73\x6C\x69\x64\x65\x44\x6F\x77\x6E","\x23\x68\x36\x30\x2C\x20\x23\x62\x75\x6C\x6C\x65\x74\x69\x6E","\x68\x65\x69\x67\x68\x74","\x23\x62\x75\x6C\x6C\x65\x74\x69\x6E","\x23\x68\x36\x30","\x73\x6C\x69\x64\x65\x55\x70","\x68\x61\x73\x52\x65\x61\x64","\x63\x6C\x69\x63\x6B","\x2E\x63\x6C\x6F\x73\x65\x42\x74\x6E","\x74\x65\x78\x74","\x3C\x70\x3E","\x3C\x2F\x70\x3E"];ckNewBulletin();if(bulletinStatus== _0xb687[0]){addHtml();clickClose()};function ckNewBulletin(){var _0x9a2fx2=localStorage[_0xb687[1]];if(_0x9a2fx2&& entityConvert(newBulletin)!= _0x9a2fx2){localStorage[_0xb687[2]]= _0xb687[0];bulletinStatus= _0xb687[0]}}function addHtml(){var _0x9a2fx4=_0xb687[3];_0x9a2fx4+= _0xb687[4];_0x9a2fx4+= _0xb687[5]+ bgColor+ _0xb687[6]+ position+ _0xb687[7];_0x9a2fx4+= _0xb687[8];_0x9a2fx4+= _0xb687[9]+ newBulletin+ _0xb687[10];if(enableClose){_0x9a2fx4+= _0xb687[11]};_0x9a2fx4+= _0xb687[10];_0x9a2fx4+= _0xb687[12];_0x9a2fx4+= _0xb687[10];if(position== _0xb687[13]){$(_0xb687[15])[_0xb687[14]](_0x9a2fx4)};if(position== _0xb687[16]){$(_0xb687[15])[_0xb687[17]](_0x9a2fx4)};setTimeout(function(){adjustHeight();$(_0xb687[19])[_0xb687[18]]()},delay* 1000)}function adjustHeight(){var _0x9a2fx6=$(_0xb687[21])[_0xb687[20]]();_0x9a2fx6= _0x9a2fx6< 60?60:_0x9a2fx6;$(_0xb687[22])[_0xb687[20]](_0x9a2fx6)}function clickClose(){$(_0xb687[26])[_0xb687[25]](function(){$(_0xb687[19])[_0xb687[23]]();localStorage[_0xb687[1]]= entityConvert(newBulletin);localStorage[_0xb687[2]]= _0xb687[24]})}function entityConvert(_0x9a2fx9){return $(_0xb687[28]+ _0x9a2fx9+ _0xb687[29])[_0xb687[27]]()} })(jQuery); //]]> </script> <style> #h60, #bulletin {display: none; } #bulletin {position: fixed; left: 0; width: 100%; z-index: 10000; color: #fff; box-shadow: 0 1px 3px 2px rgb(0, 0, 0, .25);} #bulletin.top{top: 0;} #bulletin.bottom{bottom: 0;} .bulletin_body{display: flex; padding: 10px; align-items: center; justify-content: center; font-size: 1.2rem;} .bulletin_text{line-height: 40px; text-align: center;} .closeBtn_outer{padding: 0 15px;} .closeBtn {width: 24px; height: 24px; font-family: arial, sans-serif; font-size: 24px; font-weight: bold; text-align: center; border-radius: 50%; line-height: initial; background: #fff; color: #343a40; cursor: pointer; box-shadow: 0 0 5px rgb(0, 0, 0, 0.6);} .closeBtn:hover{background: #6c757d; color: #fff;} .blue{background: #5bc0de;} .green{background: #198754;} .red{background: #d9534f;} .orange{background: #f0ad4e;} .black{background: #343a40;} .white{background: #fff; color: #343a40!important;} .gray{background: #6c757d;} .navy{background: #337ab7;} .bulletin_text .blt_btn{padding: 3px 6px; text-align: center; white-space: nowrap; cursor: pointer; border-radius: 4px; color: #fff;} .bulletin_text .blt_btn:hover{text-decoration: none; opacity: .7;} </style> <!-- Designed by WFU BLOG -->
  • 第 1 行綠字可參考「引用 jQuery 的注意事項」,檢查範本是否已安裝過 jQuery,如果已經安裝過請刪除此行,以免重複安裝。
  • 所有重要參數的修改,請見藍色註解的說明。
  • 如果對 CSS 熟悉可自行修改參數

三、展示效果

  • 本頁面即為展示頁面,網頁最上方可看到公告效果
  • 使用的參數為背景 navy,按鈕 white
  • 為了達到展示效果,點擊關閉按鈕後,本頁面的公告仍然會持續出現,這是跟原本程式效果不同之處。
更多網站工具:
Viewing all 784 articles
Browse latest View live