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

利用 Instagram 基本顯示 API 取得圖片

$
0
0
instagram-widget-sidebar.jpg-利用 Instagram 基本顯示 API 取得圖片IG 原本很方便就能存取圖片,做成各種小工具在網站展示,例如「在部落格側邊欄安裝 Instagram 小工具,顯示九宮格圖片」。

不過 IG 被 FB 收購後,於日前宣布舊的 IG API 於 2020 年 6 月 30 日失效,請參考「IG 官網公告」。此公告並說明將來 IG 圖片的存取,需要使用 FB 提供的「Instagram 基本顯示(Basic Display) API」。

這個新的 API 非常難用,本文後續會說明。舊的 IG API 提供的是永久金鑰,申請一次就可永久存取圖片,但 FB 提供的 API 不提供永久金鑰,最多只提供 60 天金鑰,期限一到就得重新取得授權。

如此一來要製作純前端的 IG 小工具幾乎不可能,必須結合後端才能不斷更新金鑰,本篇會說明大致的流程。




一、IG 取得金鑰 token


1. 準備動作

取得 IG 金鑰的標準作法是使用官方 API,需要寫程式才能取得。但並不是直接寫程式就有用,得需要先申請 FB 開發人員帳號,流程很多,請參考官網說明:



2. 取得金鑰語法

官方文件提供了取得金鑰的語法,做起來不輕鬆,可參考以下文件:


短期 access token 大致是一小時的期限,長期則是 60 天的期限。



二、取得長期金鑰的簡易方法


FB 另外提供了一個途徑,不需寫程式就能輕鬆取得 IG 60 天金鑰,請參考以下流程。

1. 建立 FB 應用程式

這部分的流程請參考「申請 Facebook 應用程式 APP ID 流程


2. 設定 IG 基本顯示

接續流程,選擇一個已建立的 FB 應用程式

ig-api-long-term-token-get-images-1.jpg-利用 Instagram 基本顯示 API 取得圖片

點擊左側選單「產品+」→ 右側選擇「Instagram 基本顯示」→ 按「設定」,即可將「Instagram 基本顯示」加入產品選單


ig-api-long-term-token-get-images-2.jpg-利用 Instagram 基本顯示 API 取得圖片

上圖按「建立新的應用程式」


3. 產生長期金鑰

ig-api-long-term-token-get-images-3.jpg-利用 Instagram 基本顯示 API 取得圖片

右側內容往下捲,可看到「User Token Generator」,但此時還不能建立金鑰,需要先建立一個 IG 測試帳號,按下「Add or Remove Instagram Testers」。


ig-api-long-term-token-get-images-4.jpg-利用 Instagram 基本顯示 API 取得圖片

畫面會跳到左側選單「角色」,右側內容往下捲到底,可看到「Instagram 測試人員」。

點擊紅框的「新增 Instagram 測試人員」,選一個 FB 帳號成為測試人員。


ig-api-long-term-token-get-images-5.jpg-利用 Instagram 基本顯示 API 取得圖片

這個測試人員邀請不會有任何通知,且很難找到,請進入以下 IG 網址:


如上圖,左側選單點「應用程式和網站」,右側選擇「測試員邀請」,即可接受剛剛 FB 應用程式發送過來的邀請。


ig-api-long-term-token-get-images-6.jpg-利用 Instagram 基本顯示 API 取得圖片

回到 FB 應用程式上圖畫面,此時「User Token Generator」已出現測試員頭像,右邊按鈕「Generate Token」按下後即可產生長期金鑰(60天)。

將來金鑰到期後,再回來這裡產生新的金鑰即可。



三、取得 IG 圖片資訊


有了金鑰後,接下來可以操作「IG 基本顯示 API」來取得圖片資訊,詳細作法可參考官網文件「取得用戶個人檔案和用戶媒體」。


1. 取得 user-id

要查詢圖片資訊得使用「user id」來查詢,所以第一步為取得使用者 id,請使用以下語法:

https://graph.instagram.com/me?access_token=金鑰字串
紅字請置換為前面取得的金鑰 token 字串,返回的 id 字串即為「user id」


2. 取得圖片 media id

使用以下語法可取得 IG 相簿的圖片資訊:

https://graph.instagram.com/user id?fields=media&access_token=金鑰字串
  • 藍字請置換回前面取得的 user id
  • 紅字請置換為前面取得的金鑰 token 字串


返回的畫面大致像這樣:

ig-api-long-term-token-get-images-7.png-利用 Instagram 基本顯示 API 取得圖片

畫面中 data 裡所有的 id 字串就是個別圖片的 media id,之後每張圖得逐一抓個別的圖片網址、連結等。


3. 取得圖片資訊

接下來可看出 FB 這個 API 非常蠢,因為無法一次查詢多張圖片的資訊,得每張圖一個個送出 http 請求,這不但累死要讀取圖片的人,也累死 FB 自己的伺服器,請見以下語法:

https://graph.instagram.com/media id?fields=media_url,permalink,media_type,caption&access_token=金鑰字串
  • 藍字請置換回前面取得的 media id
  • 紅字請置換為前面取得的金鑰 token 字串

返回的畫面大致像這樣:

ig-api-long-term-token-get-images-8.jpg-利用 Instagram 基本顯示 API 取得圖片

  • media_url:圖片網址
  • permalink:圖片頁面網址
  • caption:圖片描述

這個 API 能查詢的資訊很少,以上這幾個為必要資訊。以前舊的 IG API 還能提供留言數、愛心數等等,這些在 FB 提供的 API 都查不到。



四、整合前後端


1. 純前端的缺點

經由本篇的 API 操作說明,做出一個前端 IG 圖片展示工具不難,但是如前曾提過的缺點,這將會是一個很糟糕的前端工具,因為:

  • 如果使用固定金鑰字串,那麼每 60 天需要手動更新金鑰
  • 如果讓前端每次自動更新金鑰,那麼 IG 帳號的 client_id、secret 等機密資訊就暴露在前端,那天 IG 帳號被賣了都不曉得
  • 每次執行前端就要發出十幾個 http 請求,只要網站流量一多,API 每日使用額度很快就耗盡
  • 那麼這個工具將會很常看到開天窗的狀態


2. 後端資料庫

因此最好的方法為使用後端來操作 IG API、取得 IG 圖片、儲存在後端資料庫,好處為:

  • 機密資訊不會像前端一般暴露
  • 可以設定為一天更新資料即可,如此不會耗費太多 http 請求
  • 那麼自然不會超過 API 使用額度



五、總結


很可惜的,IG API 經 FB 這般的重新開發後,基本上已經不存在前端的有效解決方案。想要這個小工具能正常運作,就得另外製作後端資料庫才行。

市面上的付費 IG 工具,付費方式大多為月付一定金額,長期來看也是一筆不小開銷。如果 IG 有製作後端資料庫的需求可聯絡本站,此為一次性費用,會比長期月付划算。



更多網站小工具:

Javascript 字串加密解密範例研究

$
0
0
最近需要用 JS 保存一些,不想被很容易就判讀出來的資料,因此研究了一下 JS 如何對字串進行加密及解密。

結果搜尋發現網路上這方面的實用工具不多,原因大致是:

  • JS 是攤在陽光下的語言,加密解密流程會被看到
  • 因此很少純前端進行加密與解密,大多是配合後端進行運算,一邊加密另一邊解密

不過只要加密用的金鑰(key)不被知道,就算純前端也是能有一些應用,本篇會提供一些前端的 JS 字串加密解密範例。

(圖片出處: pixabay.com)


一、Base64 編碼


1. 瀏覽器編碼 API

瀏覽器自帶了 Base64 編碼 API,不用任何外掛非常方便:

  • btoa():將字串轉換為 Base64 編碼
  • atob():將 Base64 編碼還原回字串

這是最簡便的加密編碼方式,不容易看出編碼如何產生,可用於不重要的資料、瞞過不懂語法的人。


2. 範例程式碼

var str = "WFU BLOG", // 原始字串
encodeStr = encode(str), // 編碼後的字串
decodeStr = decode(encodeStr); // 將編碼還原

console.log(encodeStr); // 加密後的字串 "V0ZVIEJMT0c="
console.log(decodeStr); // 解密後的字串 "WFU BLOG"

function encode(str) {
return btoa(str);
}

function decode(str) {
return atob(str);
}


3. 處理中文字串

瀏覽器 base64 編碼 API 的缺點為,無法處理中文字串,因此需先將 UNICODE 字串進行編碼,再進行 base64 編碼,例如使用 escape():

btoa(escape("這裡是中文字串"))

解碼的時候也要記得使用 unescape() 還原,例如:

unescape(atob("JXU5MDE5JXU4OEUxJXU2NjJGJXU0RTJEJXU2NTg3JXU1QjU3JXU0RTMy"))


二、AES 加密


1. 介紹及 CDN

Advanced Encryption Standard(AES) 是一個主流的加密演算法,也可以說是本篇最正規的加密演算法,因此外掛的體積相當龐大,壓縮後也有 13k 之多。如果不是非常重要的資訊,殺雞並不需要用此牛刀。

此演算法非常知名,所以找到 CDN 連結不難,使用以下外連即可:

https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js

2. 加密方式

AES 有兩種加密的操作方式方式,詳細說明可參考這篇「兩種JavaScript的AES加密方式」:
  • 使用「金鑰」+「金鑰偏移量」:加密效果更佳,越難破解
  • 單純使用「金鑰」:操作上比較簡便

以下提供的範例程式碼使用比較簡便的「金鑰」加密方式。


3. 範例

<script src='https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js'></script>
<script>
var str = "Blogger 調校資料庫", // 原始字串
key = "wfublog", // 加密金鑰
encodeStr = encode(str), // 編碼後的字串
decodeStr = decode(encodeStr); // 將編碼還原

console.log(encodeStr); // 加密後的字串 "U2FsdGVkX1+0+TBcZVnH29Wy8Qmk26FKtIWMQAsD0cWiJcfL5Cw+1UdUNYkyDvdu"
console.log(decodeStr); // 解密後的字串 "Blogger 調校資料庫"

function encode(str) {
return CryptoJS.AES.encrypt(str, key).toString();
}

function decode(str) {
return CryptoJS.AES.decrypt(str, key).toString(CryptoJS.enc.Utf8);
}
</script>

可自行修改「原始字串」及「加密金鑰」的值。



三、簡易加密演算法


正規加密演算法的檔案很大,而且還需要花費 http 外連。接下來介紹的都不是正規演算法,但檔案很小,且還能使用加密金鑰,也算是相當安全。

1. 演算法出處

此演算法出自這個網頁「一個完美的 JavaScript 字符串 加密 和 解密」:

  • 原始碼經壓縮後不到 2k
  • 原程式還需引用 js 外連,我修改後的版本不需外連,直接複製程式碼即可使用


2. 範例程式碼

var str = "Blogger 調校資料庫", // 原始字串
key = "wfublog", // 加密金鑰
encodeStr = encode(str, key), // 編碼後的字串
decodeStr = decode(encodeStr, key); // 將編碼還原

console.log(encodeStr); // 加密後的字串 "b6443a79b4a1f1ba09eaa5999ae8824de1aa389300c5e3236e00f86e47f15bf581c059d56a5aed5d408398a704d338fda08497a82044ea6e04962174"
console.log(decodeStr); // 解密後的字串 "Blogger 調校資料庫"

function encode(f,j){f=btoa(escape(f));var l="";for(var c=0;c<j.length;c++){l+=j.charCodeAt(c).toString()}var g=Math.floor(l.length/5);var b=parseInt(l.charAt(g)+l.charAt(g*2)+l.charAt(g*3)+l.charAt(g*4)+l.charAt(g*5));var a=Math.ceil(j.length/2);var h=Math.pow(2,31)-1;var d=Math.round(Math.random()*1000000000)%100000000;l+=d;while(l.length>10){l=(parseInt(l.substring(0,10))+parseInt(l.substring(10,l.length))).toString()}l=(b*l+a)%h;var e="";var k="";for(c=0;c<f.length;c++){e=parseInt(f.charCodeAt(c)^Math.floor((l/h)*255));if(e<16){k+="0"+e.toString(16)}else{k+=e.toString(16)}l=(b*l+a)%h}d=d.toString(16);while(d.length<8){d="0"+d}k+=d;return k};

function decode(f,j){var l="";for(var c=0;c<j.length;c++){l+=j.charCodeAt(c).toString()}var g=Math.floor(l.length/5);var b=parseInt(l.charAt(g)+l.charAt(g*2)+l.charAt(g*3)+l.charAt(g*4)+l.charAt(g*5));var a=Math.round(j.length/2);var h=Math.pow(2,31)-1;var d=parseInt(f.substring(f.length-8,f.length),16);f=f.substring(0,f.length-8);l+=d;while(l.length>10){l=(parseInt(l.substring(0,10))+parseInt(l.substring(10,l.length))).toString()}l=(b*l+a)%h;var e="";var k="";for(c=0;c<f.length;c+=2){e=parseInt(parseInt(f.substring(c,c+2),16)^Math.floor((l/h)*255));k+=String.fromCharCode(e);l=(b*l+a)%h}return unescape(atob(k))};

可自行修改「原始字串」及「加密金鑰」的值。



四、極簡加密演算法


1. 演算法出處

這個演算法更簡單,出自網頁「JS自己實現字符串加密和解密算法」:

  • 原始碼經壓縮後只有 1k 左右
  • 由於演算法太簡單,導致加密後的字串過於單調,較容易破解


2. 範例程式碼

var str = "Blogger 調校資料庫", // 原始字串
key = "wfublog", // 加密金鑰
encodeStr = encode(str, key), // 編碼後的字串
decodeStr = decode(encodeStr, key); // 將編碼還原

console.log(encodeStr); // 加密後的字串 "flluflubfuugfogfwfuwuufbuwfufwfofubuflwfulfoufwwflufugfbwfulfloufwfoufwwflfuffuwoubuflwflgfoufwwflufugflffuoflfubufoufwwflfufffoffwlfluflgfoufwwflffoffoufubflluwoffoffo"
console.log(decodeStr); // 解密後的字串 "Blogger 調校資料庫"

function encode(f,m){f=btoa(escape(f));var c=m.length;var k=m.split("");var n="",h,j,g,e;for(var d=0;d<f.length;d++){h=f.charCodeAt(d);j=h%c;h=(h-j)/c;g=h%c;h=(h-g)/c;e=h%c;n+=k[e]+k[g]+k[j]}return n};

function decode(str,key){var l=key.length;var b,b1,b2,b3,d=0,s;s=new Array(Math.floor(str.length/3));b=s.length;for(var i=0;i<b;i++){b1=key.indexOf(str.charAt(d));d++;b2=key.indexOf(str.charAt(d));d++;b3=key.indexOf(str.charAt(d));d++;s[i]=b1*l*l+b2*l+b3}b=eval("String.fromCharCode("+s.join(",")+")");return unescape(atob(b))};

可自行修改「原始字串」及「加密金鑰」的值。


更多 Javascript 相關技巧:

Adsense 出現惡意蓋版廣告事件紀實

$
0
0
adsense-malvertising.jpg-Adsense 出現惡意蓋版廣告事件紀實前陣子接獲報案,表示網站出現蓋版廣告,找不到排除的方法,因而尋求協助。

原本猜想到 Adsense 後台改一下設定、調整一下安裝碼就好,結果處理後才發現事情不單純,因此特別整理一篇做為記錄。



一、Adsense 後台設定


進入「Adsense 官網」:

  • 左側選單 → 廣告 → 總覽
  • 選擇網域 → 編輯

adsense-malvertising-1.jpg-Adsense 出現惡意蓋版廣告事件紀實

出現上圖畫面,右邊的選項中,如果有開啟「自動廣告」,以及開啟「穿插廣告」,就會出現 Adsense 提供的蓋版廣告。

不過進入客戶 Adsense 後台後,發現此處「穿插廣告」早已關閉,那麼蓋版廣告是從何而來的呢?



二、被植入蓋版廣告


請案主提供蓋版廣告的相關截圖,她表示粉絲團有很多人回報,以下為幾個範例:

adsense-malvertising-2.jpg-Adsense 出現惡意蓋版廣告事件紀實

adsense-malvertising-3.jpg-Adsense 出現惡意蓋版廣告事件紀實

另外也有 pcHome 的「恭喜中獎」廣告(如開頭封面圖),但從網址來看應是偽裝成 pcHome 的詐騙廣告。仔細看了一下,這些畫面與正常的 Adsense 蓋版廣告不同,至少跟我在 WFU BLOG 手機上看到的穿插廣告形式不同,Adsense 蓋版廣告在網頁範圍內,左上角或右上角有個大大的「X」可以關閉該頁面(而不是如上圖「X」出現在網址列旁邊)

所以判斷此案件的可能性為:

  • 這些使用者的裝置可能安裝過某些外掛、或逛過某些網站,感染了病毒或惡意軟體
  • 案主的網站可能被植入病毒或惡意程式碼

總之初步研判非 Adsense 所致,需要先想辦法釐清以上兩點。



三、清除可能的禍根


1. 排除瀏覽器問題

為了縮小問題根源範圍,一步一步來排除變數,首先是讓瀏覽器環境單純化,清除可能的病毒或外掛。

操作方法找到這兩篇文章可做為參考:


由於瀏覽器會有一些快取的時效機制,就算清除了可能還是要等一陣子才可能確保看到真實的網站執行效果。


2. 排除網站問題

仔細檢查了案主網站所有 JS 外連、JS 內容,並沒有發現問題,因此可排除是網站被植入惡意程式碼的因素。


3. 剩餘問題

當然還是不能排除有微小的可能性,問題是出自 Adsense,而且測試方式很簡單,只要犧牲一點 Adsense 收益就能知道答案。

所以一方面請案主在粉絲團通知粉絲們,按照說明把自己的瀏覽器清潔乾淨,等待一段時間看是否可恢復正常。

一方面建議案主可暫時關閉 Adsense 廣告,來進行交叉測試比對,就能知道是否為 Adsense 廣告產生的問題。

可惜案主的 Adsense 廣告收益不少,捨不得關閉一段時間,只好先當作是使用者瀏覽器被感染了。



四、Adsense 才是主因


按照以上流程處理後,案主有表示抱怨的人變少了,看來前述步驟是有一定成效的。然而,過一段時間後,還是會有人反應,手機上只有逛案主的網站會跳出那些蓋版廣告,別人的網站都不會。

這下可就麻煩了,因為能排除的變數都排除了,只剩案主不敢關閉的 Adsense 沒測試,難道那些惡意、詐騙廣告真的來自 Adsense 嗎?明明長得就不像 Adsense 廣告啊?

沒辦法,接下來調查方向必須轉向 Adsense。


1. 尋找案例

以下找到一些 Adsense 產生惡意廣告的案例:


既然有案例,代表我大腦內建「Adsense 廣告八成不會有問題」的想法必須修正了,原來還是會有漏網之魚的。

這道理就像 Android、iOS、或是 Chrome 上架的 App 及外掛套件, 不代表全然合法,除了初步審核,也是得靠使用者檢舉才能揪出非法內容。


2. 非官方聲明

前面引用的那篇「【詐騙】手機跳出中獎廣告?」有提到「富盈數據」這家公司,然而該公司另外有發出一篇聲明稿:


表示他們從未接受媒體採訪,因此關於 Adsense 的意見並非該公司的聲明。

不過從這篇聲明,的確可以看到一些事實,以下內容引用該篇聲明:

富盈數據建議,若您在瀏覽網頁時發現有惡意廣告出現,可回想是在哪篇文章點擊了哪個廣告;若有找到稍早點擊到的廣告,可以直接點擊廣告右上角的回報 Bad Ads,針對惡意的廣告進行檢舉。

這代表著的確存在可能的機率,某些惡意廣告是伴隨 Adsense 廣告而生的,其原理我們接下來說明。


3. Adsense 官方文件

這是從 Adsense 官網找到的說明文件:


Adsense 既然會警告廣告商,廣告內容不可夾帶惡意軟體,代表除了申請審查,也有事後審查的機制,如果廣告商做了某些違反規定的行為,就會受到處置。

會有這樣的機制就代表一定有某些鋌而走險的廣告商,偷偷夾帶詐騙廣告,只要沒被檢舉就能得利。

然而會不會被檢舉一方面要看使用者是否那麼閒,一方面也要使用者有功力能抓出是哪個廣告違規。

從此篇的案例來看,這些惡意、詐騙廣告出現的途徑類似彈出視窗、蓋版廣告,而且有可能自動觸發,代表廣告商的語法經由專業高手設計。如果是我都很難輕易判讀由原本頁面的哪個原始廣告產生,那麼一般民眾更不可能知道有問題的廣告是哪一個了,自然談不上要怎麼檢舉,說不定還誤判檢舉到正常的廣告。



五、結論


1. 術業有專攻

案子查到最後原本以為找到真相了,卻發現就算知道真相也不一定能輕鬆解決。由於本站主要的業務是架站,查 Adsense 問題原本只是提供客戶服務,現在覺得花時間處理這樣的事 CP 值不高,專業還是要分工比較合適。

所以建議如果網站的 Adsense 出現惡意、詐騙廣告,可以請「Adsense 廣告代理商」協助處理,我相信他們會樂意幫忙找出有問題的廣告在哪裡,畢竟惡意廣告商的數量越少,對 Adsense 的商譽才是加分。

如果沒找過 adsense 代理商,本站推薦可找「劉玲君 FB」。


2. 可能出現詐騙廣告的網站

客戶粉絲的一句話引起了我的興趣:「只有你的網站會跳出那些蓋版廣告,別人的網站都不會」。如果真是這樣的話,明明詐騙廣告出現在越多網站越好,為何這個惡意廣告商要選定我客戶的網站來出現詐騙廣告呢?是不是代表這個網站的訪客比較好騙呢

客戶網站資訊我當然不能隨意透露啦,不過從這個現象我倒是完全瞭解了,詐騙廣告所使用的「人性觀察」、「心理學」知識,以下是惡意廣告可能使用的程式碼:

  • 程式碼會分析該網站、該頁面所使用的關鍵字,是否符合詐騙廣告鎖定的目標
  • 詐騙廣告的主題多半是「恭喜妳被抽中...」、「你運氣很好得到這個機會...」
  • 會上鉤的人通常會相信「命運(命定)」、「運氣」、「結果由特定事物、現象決定(而非努力或嚴謹的推論)」,或者是很少思考、習慣接受指令的人
  • 那麼詐騙廣告鎖定的關鍵字、或是網站性質,會依照這些人格特質進行設計
  • 所以不會所有網站都出現惡意廣告,只有當頁面的關鍵字能吸引特定人格特質時,程式才會觸發這類廣告,這也可稱為精準行銷

一般人當然早已對這類廣告免疫,但人格特質是常態分布的,總有一部份族群能養活這些惡意廣告商。

所以,我想本篇這個現象站長們可以不用過度擔心,而如果你的網站關鍵字會吸引某類族群時,就需要關注一下這件事了!


更多 Adsense 相關文章:

自然輸入法購買使用心得及許氏鍵盤替代方案

$
0
0
going-hsu-keyboad.jpg-自然輸入法購買使用心得及許氏鍵盤替代方案最近買了新的 Win10 筆電,將原本 Win10 筆電的自然輸入法解安裝後,在新筆電重新安裝(我買的是單機版、非多機版)。沒想到使用原版光碟、序號安裝後,卻變成只有試用版的功效,設定畫面許多選項都是反白、無法變更的狀態,我最需要的「許氏鍵盤」被封鎖了。

習慣許氏鍵盤後,一般的注音排列完全不會使用,在無法順利輸入中文的情況下,完全沒辦法進行新筆電的基礎工程。只好發郵件給客服詢問如何解決,接下來的故事一言難盡,稍後會做記錄。

簡而言之,自然輸入法的客服與軟體能力讓我失去信心,不過自然輸入法對我而言最核心的需求就是「許氏鍵盤」,還好仔細 google 後找到不少替代方案,那麼將來就可不再糾結於更新改版後每況愈下的自然輸入法。

(圖片出處: wikipedia)


一、簡介


以前為了加快中打速度,曾嘗試過練不起來的倉頡,後來練了「許氏鍵盤」排列方式,完全可以進行中文盲打。而自然輸入法搭配「許氏鍵盤」有學習功能,使用越久也會越順手。

如果讀者不熟悉自然輸入法,可參考這個討論串「講講自然輸入法」,差不多就是自然輸入法的介紹及發展史。

以前也有用過酷音輸入法,可使用「許氏鍵盤」,不過因為是業餘愛好者自行研發維護,版本更新很慢,技術上比不上商業軟體。

但自然也有自然的問題,如上面討論串連結的內容所提,只要改了版本,系統的自動學習功能可能就要重頭來,而且改版後用起來的感覺常常一代不如一代。



二、客服處理過程


一開始我的自然輸入法安裝在 Win8 小筆電,後來移到 Win10 筆電後,不管是解安裝、重新安裝都沒什麼問題,發生問題的是最新的 Win10 筆電,以下記錄與客服的往返過程。

1. 官方客服郵件格式

安裝自然後在 Windows 開始功能表會產生一個跟客服反應的捷徑,點擊後會自動產生郵件內容,記錄使用者的作業系統環境相關資料,這一點對於 debug 相當不錯:

going-hsu-keyboad-1.jpg-自然輸入法購買使用心得及許氏鍵盤替代方案

從上圖資訊來看:

  • 倒是首次知道,原來我買的是 V10 版本,當初在購物網站只知道是「新自然輸入法」
  • 上面的日期也許是第一次安裝的日期(或出廠日期?)
  • 明明我安裝在 Win10,怎麼作業系統是 Win8?猜測這裡記錄的是第一次安裝時的作業系統
  • 明明我記憶體是 8G,結果顯示只有 2G?猜測這裡記錄的是第一次安裝的 Win8 小筆電

如果工程師要 debug 的話,以上資訊我覺得只有版本號比較有意義,因為感覺上作業系統環境都不是正確的,真要查原因也很難查。

郵件裡把我操作過程都詳細描述了,把解安裝後得到的「產品序號」、「反註冊碼」都列出來,也上過官方的「產品離線反註冊」頁面進行反註冊,其他狀況就是文章開頭描述的情形。


2. 客服 1st 回覆

going-hsu-keyboad-2.png-自然輸入法購買使用心得及許氏鍵盤替代方案

  • 從客服的回覆,原來我的 V10 版本叫做「注音倉頡版本」,而且 "原本就沒有提供許氏鍵盤哦",那我以前筆電自然上面的「許氏鍵盤」不就看到鬼了?
  • "此版本為特殊開發規格,單機版且未提供更新服務"→ 我買的是單機版,的確是特殊規格,但我沒有要你們的更新服務喔,我只是解安裝後要重新安裝而已啊


不曉得為何我提供的資訊,會讓客服誤判我買的版本,於是重新澄清一下:

going-hsu-keyboad-3.png-自然輸入法購買使用心得及許氏鍵盤替代方案


3. 客服 2nd 回覆

going-hsu-keyboad-4.jpg-自然輸入法購買使用心得及許氏鍵盤替代方案

  • 原來客服是從序號開頭字串來判定版本,但我買的版本明明就有許氏鍵盤啊
  • 而且一開始的客服郵件格式就有提供版本號 "V10.1.0.28451 64Bit"難道這還無法判斷出軟體版本嗎?
  • 把這版本號拿去問工程師應該就有答案了,偏偏這個客服一直糾結在序號字串,不知道何時才能開始處理我的問題


由於這是多年前買的軟體,翻箱倒櫃找出了當初的包裝,拍了照寄給客服:

going-hsu-keyboad-5.jpg-自然輸入法購買使用心得及許氏鍵盤替代方案


圖片有包裝、光碟、序號,這樣應該一切都清楚了吧?

同時購物網站頁面因年代久遠已找不到,不過根據產品名稱 google 了一下,找到這個頁面「EZ備份精靈+新自然輸入法(標準版簡易包)」,一併給了客服參考。

另外也查了「新自然輸入法(標準版簡易包)」是什麼內容,找到這個網頁「新自然輸入法(標準版簡易包)+McAfee 2011可升級版(簡易包)」,清清楚楚寫了這個版本是有「許氏鍵盤」的。

說起來我這麼累幹嘛,用了這麼多年的「許氏鍵盤」功能,居然還要四處找資料給客服,證明我自己真的有使用「許氏鍵盤」,你們的產品不該是客服自己要想辦法查嗎?著實荒謬!


4. 客服 3rd 回覆

going-hsu-keyboad-6.jpg-自然輸入法購買使用心得及許氏鍵盤替代方案

這是客服最後一次的回覆,看了我真是徹底沒力了,原來我買的版本最後只剩下「倉頡」版本了,連注音都沒有了呢~~

我提供了那麼多的資料,你只會在序號開頭字串上打轉,我在想,自然輸入法會每況愈下,從客服能力大概能見微知著了吧。

遇到這樣的咖我也不想再溝通下去了,只是浪費時間而已,不如趕快來找替代方案比較實在,已經解除安裝、無法再安裝的自然輸入法就當作掉到水裡吧。



三、許氏鍵盤替代方案


1. 微軟注音 + 許氏鍵盤

微軟注音有學習功能,可惜鍵盤排列方式並沒有許氏鍵盤。不過 google 搜尋後已有高手解決了這個問題,請參考這篇「在微軟新注音中設定許氏鍵盤取代自然注音」,同時該篇提供了下載連結:


把這個檔案匯入微軟注音,就能用許氏鍵盤盲打了!


2. 最新版酷音輸入法

拜自然輸入法搞這一齣戲之賜,再次搜尋多年沒動靜的「酷音輸入法」竟然今年更新版本了,立刻安裝到 Win10 試試,並搭配「讓 WIN8 / WIN10 能用 CTRL + SPACE 切換中英輸入」會更順手。

酷音輸入法除了支援「許氏鍵盤」,還有非常多的細部選項,可以讓操作更方便,逐項調校一定可以建立起順手的操作環境。



四、小結


文章開頭的「講講自然輸入法」有提到許多在 Win10 下產生的問題,也許不全然是自然輸入法的錯,也有可能是 Windows 的問題,但總之結果就是「自然輸入法在 Win10 會有不少問題」。

我的版本遇到比較有感的是「Win10 Edge Google搜索欄、開始功能表右邊的搜尋框...」多處無法用自然輸入法輸入中文,只能輸入英文

改裝酷音輸入法後,在這些地方都可以正常輸入中文無誤。最麻煩的「許氏鍵盤」解決後,其他的就是慢慢習慣酷音的環境,而且從版本號來看,不久後又會有更新版的酷音了。


更多 Windows 軟體相關心得:

前端使用 JS 裁切圖片並壓縮再存到後端﹍Croppie 實作範例

$
0
0
jquery-croppie-image-cropper.jpg-前端使用 JS 裁切圖片並壓縮再存到後端﹍Croppie 實作範例最近接到一個需求,希望使用者上傳圖片時,可先在前端進行裁切,以免存到後端的圖片尺寸比例不對,也可避免檔案過大。

之前寫過一篇「前端如何使用 JS 先壓縮圖片尺寸大小再進行上傳」,其實也能處理前述大部分需求,但稍有不足之處:

  • 只能對上傳的圖片等比例縮小
  • 不能防止使用者上傳錯誤比例的圖片,例如需要橫躺長方形的比例,卻上傳了直立長方形,這會導致將來圖片顯示時版面效果不佳
  • 所以最好能對上傳的圖片進行裁切,可確保圖片比例是正確的
  • 如果裁切時有預覽畫面就更好了,讓使用者自行拖曳、縮放來調整裁切範圍,操作介面看起來會更專業

本篇要介紹的 jQuery 外掛 Croppie,完全符合以上的描述,效果也相當有水準,以下會提供 DEMO 效果及範例程式碼。




一、Croppie 介紹


1. Github 官網

以下是官網提供的相關連結:


雖然此外掛功能很強,可惜沒有一個很好的教學說明,只能想辦法從官方提供的幾個範例程式碼,來猜測參數要如何使用。


2. 範例效果

首先進入官方範例網址:



jquery-croppie-image-cropper-1.jpg-前端使用 JS 裁切圖片並壓縮再存到後端﹍Croppie 實作範例

進入網頁後會立即看到上圖的範例效果,可直接在頁面上操作此區塊:

  • 上方箭頭的區塊,可用滑鼠拖曳,讓圖片出現在適當的位置
  • 中間箭頭的置中圓框,為設定好的裁切區域,可選擇圓形或方形,也可設定裁切尺寸
  • 下方箭頭的調整軸,可對圖片進行縮放

這個範例效果即為此外掛的核心概念,往下捲還可看到各種 DEMO 範例,說明對應的程式碼要如何寫。

由於本篇為客製需求,因此後面只說明上傳圖片的裁切操作。



二、圖片裁切壓縮範例程式碼


需先載入 jQuery、Bootstrap,如網頁已安裝過,請移除綠字部分的程式碼:

<link href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css' rel='stylesheet'></link>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet"></link>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.css" rel="stylesheet"></link>

<label class="btn btn-info"><input id="upload_img" style="display:none;" type="file" accept="image/*"><i class="fa fa-photo"></i> 上傳圖片</label>

<div id="oldImg" style="display:none;"></div>

<button id="crop_img" class="btn btn-info"><i class="fa fa-scissors"></i> 裁剪圖片</button>

<div id="newImgInfo"></div>
<div id="newImg"></div>

<script>
(function($) {
var width_crop = 300, // 圖片裁切寬度 px 值
height_crop = 200, // 圖片裁切高度 px 值
type_crop = "square", // 裁切形狀: square 為方形, circle 為圓形
width_preview = 350, // 預覽區塊寬度 px 值
height_preview = 350, // 預覽區塊高度 px 值
compress_ratio = 0.85, // 圖片壓縮比例 0~1
type_img = "jpeg", // 圖檔格式 jpeg png webp
oldImg = new Image(),
myCrop, file, oldImgDataUrl;

// 裁切初始參數設定
myCrop = $("#oldImg").croppie({
viewport: { // 裁切區塊
width: width_crop,
height: height_crop,
type: type_crop
},
boundary: { // 預覽區塊
width: width_preview,
height: height_preview
}
});

function readFile(input) {
if (input.files && input.files[0]){
file = input.files[0];
} else {
alert("瀏覽器不支援此功能!建議使用最新版本 Chrome");
return;
}

if (file.type.indexOf("image") == 0) {
var reader = new FileReader();

reader.onload = function(e) {
oldImgDataUrl = e.target.result;
oldImg.src = oldImgDataUrl; // 載入 oldImg 取得圖片資訊
myCrop.croppie("bind", {
url: oldImgDataUrl
});
};

reader.readAsDataURL(file);
} else {
alert("您上傳的不是圖檔!");
}
}

function displayCropImg(src) {
var html = "<img src='" + src + "' />";
$("#newImg").html(html);
}

$("#upload_img").on("change", function() {
$("#oldImg").show();
readFile(this);
});

oldImg.onload = function() {
var width = this.width,
height = this.height,
fileSize = Math.round(file.size / 1000),
html = "";

html += "<p>原始圖片尺寸 " + width + "x" + height + "</p>";
html += "<p>檔案大小約 " + fileSize + "k</p>";
$("#oldImg").before(html);
};

function displayNewImgInfo(src) {
var html = "",
filesize = src.length * 0.75;

html += "<p>裁切圖片尺寸 " + width_crop + "x" + height_crop + "</p>";
html += "<p>檔案大小約 " + Math.round(filesize / 1000) + "k</p>";
$("#newImgInfo").html(html);
}

$("#crop_img").on("click", function() {
myCrop.croppie("result", {
type: "canvas",
format: type_img,
quality: compress_ratio
}).then(function(src) {
displayCropImg(src);
displayNewImgInfo(src);
});
});
})(jQuery);
</script>

紅、藍字參數可自行調整參數,可參照註解說明:
  • width_crop、height_crop 可改為自訂裁切尺寸
  • type_crop 可調整裁切形狀
  • width_preview、height_preview 可改為自訂預覽區塊尺寸
  • compress_ratio 可調整圖片壓縮比例,0 ~ 1 之間的數值
  • type_img 為圖檔格式


jquery-croppie-image-cropper-2.jpg-前端使用 JS 裁切圖片並壓縮再存到後端﹍Croppie 實作範例

此範例程式碼的效果大致如上圖,後面有展示用的「上傳圖片」、「裁切圖片」按鈕可自行玩玩看效果。



三、圖片裁切壓縮效果展示


點擊下方的「上傳圖片」,並用滑鼠拖曳、縮放、選擇裁切區域後,點擊「裁切圖片」,會將圖片裁切為 300x200 px,並壓縮為 85%,有效縮減檔案大小,又不降低圖片品質。












更多網頁開發工具:

2020 Lazy Load 延遲載入圖片原理及實作分析研究

$
0
0
lazy-load.jpg-2020 Lazy Load 延遲載入圖片原理及實作分析研究前陣子接到一個需求,客戶網站做完「PageSpeed Insights」檢測後分數很低,於是尋求協助。對客戶網站進行健檢後提供一份報告與建議,客戶詢問「延遲載入畫面外圖片」的部分如何改善。

這個項目「PageSpeed Insights」本身提供了不少參考資料,不過研究後才發覺水相當深,本篇為心得記錄整理。

(圖片出處: lozad.js)


一、PageSpeed Insights 測試


1. 測試畫面

下圖為網站經「PageSpeed Insights」檢測後的報告範例:

lazy-load-1.jpg-2020 Lazy Load 延遲載入圖片原理及實作分析研究

每個項目 Google 都提供了改善建議,點擊右方的向下箭頭圖示即可展開。而「延遲載入畫面外圖片」上圖可看到若改善的話,可節省 15 秒的載入時間,是非常巨大的優化。此項的改善建議,可進入「Defer offscreen images」這個網頁。


2. 改善建議

這個頁面先顯示了「延遲載入畫面外圖片」可能出現的畫面:

lazy-load-2.jpg-2020 Lazy Load 延遲載入圖片原理及實作分析研究

列出進入網站首屏後,螢幕外的圖片有哪些、圖片大小等資訊。如果我們能讓這些圖片先不載入,就能節省大量網頁載入時間。

而如何延遲圖片載入,可藉助「Lazy Load」這類外掛,官方此頁面推薦的外掛為「lazysizes」,接下來介紹運作原理。



二、Lazy Load 運作原理


1. Lozad.js 介紹

Lazy Load 的外掛相當多,其運作原理都是類似的,但請注意要找年份較近、版本比較新的外掛,才能支援最新的瀏覽器技術,效能較佳。

本篇非以 Google 介紹的 lazysizes 來說明,而是另一個外掛「Lozad.js」,原因為:

  • 檔案相當小,不佔空間
  • DEMO 頁面效果一目了然


2. Lazy Load 原理

  • 延遲圖片載入的構思為,讓所有的 img 標籤沒有 src 屬性,圖片自然就無法載入
  • 取而代之,將原本 src的圖片網址,改放在 data-src屬性
  • Lazy Load 外掛的工作就是,偵測目前螢幕範圍內有哪些 img 需要顯示,再將這些 img 的 data-src圖片網址改放到 src,就能讓圖片載入
  • Lazy Load 會持續偵測螢幕捲到哪裡,並載入該處的 img
  • 而如何偵測網頁的所有圖片,距離螢幕多遠,則利用瀏覽器最新的 Intersection Observer API


3. Intersection Observer API

關於「 Intersection Observer API」,除非我們要自己寫外掛,否則可以忽略這部分如何操作,其介紹可參考這篇「如何用 Intersection Observer API 實作 Infinite Scroll/Lazy Loading」的說明即可。


4. Lazy Load 效果

可進入「Lozad」的展示頁面:



當捲動螢幕,載入其他圖片時,右上角會顯示目前載入了什麼圖片:

lazy-load-3.jpg-2020 Lazy Load 延遲載入圖片原理及實作分析研究

每捲動一次,便會顯示載入了不同圖片,這就是 Lazy Load 的絕佳展示效果。

如果沒有使用 Lazy Load 外掛的話,進入網站會一次載入所有圖片,有可能導致網頁效能不佳,出現前面「PageSpeed Insights」的不良效果報告。


5. 實作

Lazy Load 的原理幾句話就說完了,但要實作的話就會發現很痛苦,因為在使用外掛之前,得自行將網站的所有圖片,也就是 img 標籤的 src屬性,全部改成 data-src,想來就不是輕鬆的事。



三、實作困境


1. 一頁式、形象網站

比較有可能做到的是「一頁式、形象網站」這類網站,因為網站內容變動較少,img 標籤可以一次處理。

但日後若需增加內容、且有圖片時,所有圖片也需將 src 改為 data-src。


2. 部落格網站

部落格類的網站就麻煩了,除了新文章得改 img 格式,所有舊文章的 img 如何處理將十分頭疼。


3. 動態生成的內容

一些常見的網站小工具例如「最新文章」、「熱門文章」、「相關文章」、「輪播」等等,會用 js 動態產生縮圖。如果這些圖片也需考慮延遲載入的話,那麼得全部修改這些工具的 js 程式碼,讓圖片的 src 都改成 data-src,並且每個外掛都得個別執行一次 Lazy Load,才能產生延遲載入的效果。



四、SEO 問題


1. 圖片能否被搜尋引擎索引

使用 Lazy Load 效果後,背後隱藏的問題為,網站的圖片資訊能否被搜尋引擎正確收錄。

img 缺少正確 src 後,搜尋引擎來爬的時候如果沒抓到,網站會失去「圖片搜尋」而來的流量。

如果網站的「圖片搜尋流量」佔比很低,那麼這點損失應該是可以承受的。


2. 搜尋引擎不保證索引 js 生成內容

其實 Google 有能力索引 Lazy Load 動態產生的圖片 src 內容,運氣好的話,網站並不會損失「圖片搜尋流量」。

然而這也要看網站權重,並不是每個網站 Google 都有耐心去爬 js 產生的內容,所以使用 Lazy Load 後,心理上做好準備得不到「圖片搜尋流量」會比較好。


3. noscript

SEO 界有個技巧,把 js 動態生成的原始內容,放一份在 noscript標籤之中,可以確保被 Google 索引。那麼也許可以試試看吧,除了使用 Lazy Load,另外把所有原始的 img 標籤內容放一份在 noscript 之中,說不定是兩全之計。



五、Chrome 原生 Lazy Load


1. 瀏覽器內建功能

這是一年前 2019 的消息:「Chrome 75 將原生支援 lazy loading」,意思就是說,不需安裝任何外掛,Chrome 就能讓圖片有 Lazy Load 效果。

同時也可參考 Google 的英文說明「Native lazy-loading for the web」,這裡提供了 DEMO 頁面,可實際看到 Lazy Load 效果。

這功能生效的話真的非常棒,因為:

  • 再也不需圖片 img 的 src扭曲成 data-src,少了很多麻煩事
  • img 一定可被索引,不影響圖片 SEO
  • 不需另外安裝、執行外掛
  • 但是仍需要為 img 加上屬性 loading="lazy"

只是不能高興太早,因為並非所有瀏覽器都支援原生 Lazy Load 參數。


2. 效果測試

不過我還是測試了一下,想體驗 Chrome 的原生 Lazy Load 效果如何,結果試不太出來...

Google 了一下,發現很多人跟我有同樣遭遇,可參考這個討論串「Native lazy-loading not working even with flags enabled」,摘要一下:

  • 這功能用 Chrome 試不出來,FireFox 反而有效果
  • 推測 Chrome 比 FireFox 在更早的時候就載入圖片
  • 經實驗,Chrome 遠在 3200px 之前的距離就載入圖片了

難怪我沒看到效果,因為我的測試網頁沒有 3200px 這麼長。

不過如果是以圖片為主的部落格,例如美食、旅遊,因為圖片多網頁自然很長,就適合使用原生瀏覽器的 Lazy Load 功能了。



六、總結


我發現要對這篇的探勘做出結論還滿困難的,每一條路的結果都不算最佳解,而且也都不輕鬆。

也許以下的作法,會是我認為 CP 值比較高、比較有彈性的構想:

  • 一頁式、形象網站:適合進行 Lazy Load 這樣的大工程,對所有 img 標籤進行修改
  • 部落格網站:
    • 文章以外的區塊若有圖片,進行 Lazy Load 優化
    • 文章區塊不強求使用 Lazy Load 效果,取而代之的是,對所有圖片進行瘦身
    • 圖片長寬尺寸不要過大,圖片都要經過壓縮,盡量單檔都在 100k 以下
    • 只要圖檔不大,那麼圖片有無延遲載入,對網頁載入的負擔都不會太大
  • 如果某個單獨頁面的圖片太多,再對該頁面進行優化,實施 Lazy Load 為佳
  • 圖片多的網站,盡量在製作新的文章(頁面)時,使用瀏覽器原生的 Lazy Load 參數


優化網站效能 相關文章:

檢舉侵權抄襲的 APP 使其下架(連同 Adsense)﹍流程實錄

$
0
0
app-plagiarism-prosecute.jpg-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄約一年前由本站研發及架設的網頁服務「線上看電視」,推出後迴響不錯,累積了一批數量不小的忠實觀眾。

而手機版的網頁,覺得介面、效果還算堪用,暫時尚未研發 APP 版本,不過偶爾也會觀摩一下同性質的 APP,看看別的開發者如何設計,有無優點可學習、或缺點可改進。

在今年五月左右,於 Google Play 搜尋類似的 APP 時,發現了似乎以前沒見過的 APP「電視看到飽」:

app-plagiarism-prosecute-1.jpg-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

下載來玩看看後,越測試越不對勁,怎麼很眼熟的感覺啊!後來整個雞皮疙瘩都起來了,才意識到我的「線上看電視」被抄襲了,節目選單簡直整個照搬過去...

(圖片出處: 699pic.com)


一、序盤開戰


1. 留一星評論

查了一下該 APP 的聯絡資訊,作者 email 用 Google 搜尋了一下,沒有任何相關資訊,代表只是人頭帳號,相信聯絡了也不會有回應。這很合理,做壞事當然會做好隨時被抓的心理準備,不可能讓正式帳號被停權。

一開始腦袋滿是怒火,第一個想到的就是給這個 APP 一星評價,並留下評論讓其他使用者知道,這個 APP 是個抄襲者。

這麼做的目的很簡單,讓之後下載的使用者知道這是有問題的 APP,看能不能造成卻步、阻止 APP 下載量與評論繼續成長。


2. 粉絲團公告

接著就是在粉絲團發公告,說明網站被抄襲了,並附上詳細的說明及截圖證據:


希望有正義感的粉絲能挺身而出,一同抵制這個抄襲的 APP。


3. 過於衝動

然而公告發沒多久,就有粉絲留言說在 Google Play 搜尋不到「電視看到飽」,原來抄襲者已經心虛自行下架了...

靜下心進行檢討,覺得跑去該 APP 留下評論是個敗筆,造成打草驚蛇不說,就算粉絲們有心想幫忙灌爆評論也無從發力,因為該 APP 的頁面已經暫時不存在了

跟粉絲討論的結果也大致有共識,這個 APP 雖然暫時自行下架,但說不定哪天稍微改改後又偷偷上架,我也沒空一直盯著這件事。

造成敵暗我明的確是個失策,第一回合必須承認以失敗作收,之後只能等有緣發現該 APP 重新上架時再說了。



二、向 Google Play 申訴


1. APP 重新上架

到了六月底發現有知名 3C 資訊網站在 FB 分享這個 APP,才發現對方又偷偷上架了,而且檢查了 APP 的更新日期,原來 5 月 31 日就上架了,我等到一個月後才發現。

這次決定調整戰略不再驚動對方,否則他又自行下架,玩貓捉老鼠的遊戲只是浪費時間。

讓這個 APP 永久下架的方式就是向 Google Play 直接檢舉,若成功了將來就不用在這件事花費心力。


2. 申訴 SOP

說到檢舉抄襲,過去也算小有經驗,這是以前檢舉盜文所歸納的 SOP、經驗以及過程記錄:


之前的投訴單位分別是 Google 搜尋以及 Blogger 平台,都是 Google 同樣的申訴系統。而這次只是投訴單位改為 Google Play,流程與 SOP 是大同小異的。


3. 向 Google Play 進行申訴

首先進入「從 Google 移除內容」頁面,這裡可選擇 Google 各種產品、平台:

app-plagiarism-prosecute-2.jpg-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

選擇「Google Play」進行申訴,接下來每個問題會佔用一個頁面,就不一一截圖了。


app-plagiarism-prosecute-3.jpg-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

這是回答完所有初步問題的畫面,上圖紅框即為我勾選的項目,請依自己的狀況作答,然後按下方的「提出申訴」。


app-plagiarism-prosecute-4.jpg-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

接下來的畫面,才是申訴需要正式填寫的細節內容,包括個人資料、侵權細節描述、附上截圖等等。由於畫面很長就不截圖了,而且這部分的表格、說明跟之前寫的「Blogger 檢舉盜文流程實錄」一模一樣,請參考該篇文章作答即可。


4. 等待時間

檢舉侵權跟以往的檢舉盜文不太一樣,盜文只要證據確鑿,沒多久就會將網頁移除、或是從搜尋結果移除。侵權的話流程比較長,一方面會給侵權者答辯時間,並且還會要求提出申訴者證明版權所有。

而檢舉 Google Play 的 APP 侵權需要時間多久,我就真的不太知道了,除非某個人的 APP 被檢舉,他才會知道 Google Play 給他的答辯時限有多長。

所以,接下來我只能等待了。



三、自立自強


1. 一個月沒有任何消息

app-plagiarism-prosecute-5.jpg-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

這是六月底提出申訴後,收到的 Google 回應。乍看之下很是高興,以為等個幾天 Google Play 就會將該 APP 移除,否則就會通知我對方有抗辯。

然而我每過幾天就去看一下,該 APP 不但完好如初,評價、評論還不斷上升,而且我也一直都沒有得到 Google 的消息,只好猜測 Google 給 APP 的抗辯期很長吧。

一直等到七月底,過了一個月都沒有任何動靜,這個 APP 的成長曲線也不受影響,我都開始懷疑,Google 大概不認為這樣子算侵權吧?是不是還要去申請專利才進行申訴呢?

當然這是玩笑話,但在摸不透 Google 的規則下就可能會胡思亂想。而既然都等了一個月也不見成效,就不過份冀求正義於司法,靠自己來懲治壞人比較實在。

而且這樣的事我也做過很多次了,本站的熱門文章就可找到一例。


2. 評論攻勢

app-plagiarism-prosecute-6.jpg-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

很高興有觀眾在得知該 APP 重新上架後,特地主動去留了一顆星的評論,代表支持正義的人還是很多。

因此等待一個月後決定主動出擊,在粉絲團公告該 APP 重新上架的訊息,希望支持者能前往該 APP 進行伸張正義。


3. SEO 攻勢

另外為了防堵「電視看到飽」在搜尋結果造成的影響力,我也整理了這個抄襲事件的記錄、證據等等,寫成一篇文章:


用意為就算 Google 不處理抄襲事件,那麼若是該 APP 日後紅了,使用者將來搜尋「電視看到飽」應該也能看到這篇文章,就可以讓這個 APP 原形畢露。



四、終獲 Google 垂青


該做的都做了,能拿回這些正義也算還可以接受,接下來不準備把這件事放心上,畢竟還有很多事要做。


1. 證明版權所有人

app-plagiarism-prosecute-7.png-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

沒想到這件事過了快兩個月,突然間收到 Google 的郵件,要我證明是「線上看電視」的版權所有人,難道 Google 終於要開庭查案了?

也罷,雖然等很久但總比沒有好。這件事簡單,把 Blogger 網站後台管理員的畫面截圖給他,就能證明我是「線上看電視」的站長了。


2. 提供侵權證據

app-plagiarism-prosecute-8.png-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

過了兩天收到以上郵件,要我提供侵權證據。其實看了有點小生氣,因為當初提交表格資料時,除了詳細描述之外還上傳了一大堆圖片,難道是時間過了太久把圖片搞丟了嗎?

不過既然 Google 願意開庭,還是不要搞錯該生氣的對象,耐心地再回覆一次資訊,並且把我在粉絲團的公告,以及為了 SEO 而寫的那篇來龍去脈文章都附給 Google,資料提供的詳詳細細沒有半分疏漏,讓 Google 好好辦案為是。


3. 遲來的正義

app-plagiarism-prosecute-9.png-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

守得雲開見月明,這下終於等到了,隔天 Google 便通知會將「電視看到飽」下架,同時我也趕緊測試了一下,果然該 APP 在 Google Play 的頁面已經消失了!



五、跟 Adsense 檢舉


至此 APP 已經下架了,但是已經下載的使用者仍可以正常使用,那麼抄襲者雖然賺不到新使用者的 Adsense 廣告收益,仍然可以賺到現存使用者的不當收益,因此下一步就是讓該 APP 的 Adsense 下架

1. 檢舉流程

app-plagiarism-prosecute-10.jpg-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

開啟我原先安裝在手機上的抄襲 APP,在 Adsense 廣告右上角有 "打叉"、"英文小寫 i + 圓圈"的按鈕,點擊後會出現上圖選項,選擇「為什麼會出現這則廣告?」。


app-plagiarism-prosecute-11.jpg-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

如上圖,點擊「檢舉違規網站或應用程式」。


app-plagiarism-prosecute-12.jpg-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

如上圖,請勾選你認為的違規原因。以本案為例,因為是抄襲本站、侵犯著作權,請勾選「該網頁在其他方面違反了 Adsense 計畫政策」。如不放心可點擊 "Adsense 計畫政策"連結, 瞭解對方違規事項是否列於該頁面。

這個頁面下面還有一些細節要填寫,由於畫面太長就不截圖了,請依照實際狀況跟 Adsense 陳述即可。


2. 檢舉成功

app-plagiarism-prosecute-13.jpg-檢舉侵權抄襲的 APP 使其下架(連同 Adsense)流程實錄

過了幾天之後,檢查一下 Adsense 處理得如何,結果發現速度也滿快的,如上圖,原本螢幕底部會是 Adsense 廣告,現在發現已經沒有 Adsense 了。

抄襲者自己也知道 Adsense 被拔了,取而代之這是另外擺的廣告,收益自然是遠遠比不上 Adsense,也算是有懲罰到對方了。



六、總結


1. 檢討

這是個人的臆測,我不清楚實情如何。也許因為今年全球疫情影響,所以人力不足處理變慢,才會拖了快兩個月才處理到我的申訴。

不過後來想想,根據本篇的檢舉流程,說不定是我檢舉的網頁非 Google Play 直轄單位,導致公文旅行了一陣子才轉手到經手人員。

如果當初直接從 Google Play 相關頁面檢舉的話,說不定會比較快:


這件事就留給有緣人研究了,因為從這裡前往的檢舉表格,看起來沒有本篇流程的表格詳盡,我不能確定結果一定比較好。


2. 抄襲者 APP 的後續

抄襲者雖然仍可以繼續靠已經下載 APP 的使用者來營利,不過我認為已經是強弩之末,可以不用太在意了,因為:

  • APP 被下架後就無法再更新,代表日後無法新增功能、也無法修正 bug
  • 那麼將來只要出現更具競爭力的 APP,解安裝的人數定會穩定成長,歸零也是指日可待
  • 只要抄襲者敢改名稱再另外重新申請於 Google Play 上架,有了本篇流程後,將來檢舉易如反掌
  • 屆時多次抄襲的帳號、甚至是 Adsense 帳號,說不定會被永久停權,我想抄襲者應該不太敢挑戰這樣的事


3. 小結

前面寫的「檢討」畢竟也是屬於個人臆測,說不定有另外一種可能性:

  • 正因為我開始覺得不能完全依賴公權力,才開始一連串的自力救濟舉動
  • 說不定公權力一開始審視侵權事件的當下,他們也無法知道實際上究竟是誰在抄襲誰
  • 而因為我開始自行伸張正義後,這些舉動被看到,讓公權力知道該站在誰那邊,才開始了判案流程
  • 又或許是我的這些自力救濟的證據,附上郵件後讓判官可以立即做出處置,從而快速結案

也許整件事純粹只是官方延遲了處理流程,但也有可能是「天助自助者」,最終達到了我想要的結果,然而實情如何有誰能知道呢?

那麼不如都記錄下來讓讀者參考,將來有抄襲事件時本篇都將會是非常好的案例輔佐。


更多「著作權保護」相關文章:

如何讓新版 Blogger 按 Enter 能建立換行符號

$
0
0
Blogger 推出新版後台介面後,原本我一直是手動切回舊版來操作,以規避新版介面產生的各種問題。但是前幾天官方已消滅了舊版後台,也就是強制一律只能使用新版介面,這下真的沒輒只能硬著頭皮使用。 其實這段期間以來,新版很多問題都已反應給官方,有些是有改進,但官方沒有動作的部分想必是沒有意願調整,沒辦法只好自己來了。 對我而言最無法接受的一項是,官方拔掉了文章編輯器的選項「按 Enter 建立換行符號」,沒有這個功能我還真不知道要如何寫文章,大致原因可參考「2020 Blogger 新版文章編輯器使用心得」→「二、HTML 模式的變革」→「5. 取消按 Enter 建立換行符號功能」。 因為我只用 HTML 模式寫文章,若按 Enter 不能換行的話,那文章內容豈不全部黏在一起無法斷行,這樣版面能看嗎? 本篇會說明這個功能的重要性,並提供我想出的各種解決方案。 (圖片出處: hippopx.com)

一、為何需要使用 HTML 模式

因為官方刻意拿掉 HTML 模式的換行功能,從 Blogger 新版文章編輯器的介面、功能限制,很明顯有以下意涵:
  • 希望使用者主要利用「撰寫模式」來產生文章,才能順利按 Enter 換行
  • 避免使用者習慣「HTML 模式」,瞭解太多 HTML 語法,或更進一步使用 script 程式碼
  • 因為使用 script 會產生更多變數,造成維護難度,所以官方傾向面對更多 "技能單純"的使用者,這一點是可以理解的。
然而打壓 HTML 模式、提倡「撰寫模式」所產生的問題,對於使用者而言是不容易解決的,過去我已寫過一篇文章詳述「我如何寫一篇 Blogger 文章的流程」,這會造成:
  • 不易修改 CSS
  • 會產生雜亂、無用多餘的 HTML 碼
  • 不易控制文章版本,會有遺失文章內容的風險(本站接獲太多案例,也寫了多篇相關文章)
建議讀者詳讀該篇文章,只有使用純文字編輯軟體來撰寫文章,才能避免以上問題,還能使用建立好的文章範本節省寫作時間,並可搭配 dropbox 來隨時自動備份不同時間點的文章版本,完全不必擔心各種人為操作失誤、或是網路伺服器錯誤,而導致的文章內容消失。 而使用純文字編輯軟體所寫的文章,因為使用了文章範本,包含了一些 HTML 碼,所以只適合使用「HTML 模式」。

二、按 Enter 換行的原理

為了解決「HTML 模式」下,從純文字編輯軟體撰寫的文章,貼上後將無法換行的問題,研究了一下按 Enter 換行的原理。 基本上我們在任何文書軟體、甚至是「HTML 模式」下,按了 Enter 後都會產生一個 "看不見的符號",稱為「換行符號」。雖然稱為符號卻看不到,但這個符號跟所有其他字元一樣,都有個 ASCII 碼,數值為 10,可參考「回車、換行、空格的ASCII碼值」。 意思就是 "ASCII 10"這個字元資訊仍然會被文書軟體以及「HTML 模式」儲存起來,所以在文書軟體上我們看到的效果就是「換行」。那麼問題來了,為何「HTML 模式」下不會換行呢? 請參考這篇「html 文本换行 \n 不换行 空格无效」,原來 HTML 不會解析換行符號,如果要換行只能使用 HTML 標籤 br。 同時根據該篇提供的解決方式,我們就能自行實現換行效果了。

三、JS 自動轉換技巧

1. 注意事項瞭解原理後,我們可以寫 JS 自行將看不到的換行符號,轉換成 br標籤。 然而以下提供的程式碼只適合新建的部落格使用,因為舊文章看不見的換行符號也會轉換成 br標籤,再加上原本就有的 br標籤,會導致有兩倍的換行間隔。 這也就是說,如果網站的文章都是使用新版後台產生的,才適合使用本篇的 JS 程式碼。 2. 安裝程式碼以下操作需要修改範本,在進行之前,如果第一次安裝本站工具的讀者,建議先閱讀「備份範本的訣竅」系列文章。 請到後台「主題」→ "自訂"按鈕右方的下拉圖示 →「編輯 HTML」,游標點進範本區塊,按 Ctrl-F 搜尋以下字串: <b:include data='post' name='post'/>找到後在此字串的下一行,插入以下程式碼: <!--自動轉換換行符號為 br--> <script> //<![CDATA[ (function() { var $postBody = document.querySelector(".post-body"), html = $postBody.innerHTML, newContent = html.replace(/\n/g, "<br/>"); $postBody.innerHTML = newContent; })(); //]]> </script> <!--Designed By WFU BLOG-->儲存後即可看到效果。

四、HTML 手動轉換技巧

要新舊文章都不會版面異常的話,還是手動處理比較保險,也就是前面參考文章的連結提到的,幫文章加入 pre標籤,可以讓看不見的換行符號,產生 br標籤的換行效果。 然而直接使用 pre標籤會讓「撰寫模式」版面異常,以下介紹更好的處理方式。 1. 每篇文章處理方式 Blogger 後台切換到「HTML 模式」,每篇文章寫完之後,前後要加入以下字串:
  • 在文章開頭前面插入字元 <div class="pre">
  • 在文章結尾後面插入字元 </div>
2. CSS 設定接著要在範本中新增 CSS,請按以下步驟:
  • 後台 → 主題 → 自訂 → 進階 → 新增 CSS
然後加入以下 CSS 碼: .pre{white-space: pre-line;}輸入完按右下角的「儲存」圖示按鈕,可回到文章頁面看效果。 需要看範例的話,本篇文章就是使用這樣的技巧所撰寫。 3. 後台使用文章範本前面的步驟「1. 每篇文章處理方式」,每篇手動處理比較麻煩,也可在後台「設定」→「文章」→「文章範本」這裡,直接輸入頭尾字串,新文章就會自動出現不必再另外輸入,記得文章內容要放在頭尾字串之間即可。 4. 補充:使用熱鍵產生換行符號另外補充 Blogger 社團成員提供的密技,請參考「新版文章編輯器產生 br 標籤的操作技巧」,官方秘密提供了一組熱鍵:
  • Shift + Enter
在「撰寫模式」及「HTML 模式」都有效,按下後會自動產生 br標籤,達到換行效果。
更多 Blogger 相關技巧:

前端 JS 如何避免 callback 地獄?Fetch API 及 Promie 使用技巧

$
0
0
js-asynchronous-callback-hell-fetch-promise-api.jpg-前端 JS 如何避免 callback 地獄?Fetch API 及 Promie 使用技巧由於早期 Javascript 設計上的缺陷,一方面使用 Ajax 送出 http 請求時,舊時代的 API 異常繁複,非得使用 JS 框架操作 Ajax 才比較方便,例如 jQuery。 一方面 Ajax 是非同步(Asynchronous)執行緒,會造成前端工作一些麻煩,例如後續事務需要 Ajax 返回資料才能處理時,必須另外拉個 callback 函數執行才不會報錯。但又不能真的把 Ajax 工作變成 "同步"來執行,否則網頁的渲染都被 Ajax 請求給塞住,如此就失去 Ajax 非同步處理的意義了。 如果該頁面的工作單純,只有一、兩個 callback 倒也不妨事。但若一個頁面有十幾個、甚至數十個 callback 要執行,那麼這些非同步執行緒的管理就十分頭疼,此時就是所謂「callback 地獄」的感受了。 最近剛好遇到多種複雜的 Ajax callback 需要處理,於是學習一下 HTML5 新的 API,不但讓 Ajax 的操作變得異常簡潔,還能完美解決 callback 地獄的窘境。本篇會整理一些常用情境的範例程式碼,可視需求快速套用。 (圖片出處: pexels.com)

一、經典非同步 callback 金字塔

首先看一下什麼是異步執行的「callback 地獄」,下圖是經典程式碼,取自「asynchronous flow control made right」: js-asynchronous-callback-hell-fetch-promise-api-1.jpg-前端 JS 如何避免 callback 地獄?Fetch API 及 Promie 使用技巧連綿不絕的 callback 函數會形成九十度的金字塔形狀,有時甚至會造成程度判讀上的障礙。這篇「你懂 JavaScript 嗎?Callback」提供了一個不錯的範例,巢狀 callback 太多層時,函數的執行順序是難以判讀的: js-asynchronous-callback-hell-fetch-promise-api-2.jpg-前端 JS 如何避免 callback 地獄?Fetch API 及 Promie 使用技巧

二、jQuery 非同步排程

如果能夠打破巢狀結構,讓 callback 函數依序執行,就不會讓程式有閱讀與判讀上的困擾了。jQuery 不但是很好的 JS 框架,還提供了非同步執行緒很好的處理方式,也許後來 HTML5 API 處理非同步的部分就是從 jQuery 借鏡的。 以下是 jQuery 執行 Ajax 的語法結構,可參考「jQuery.ajax()」: $.ajax({參數}) .then(callback_A) .then(callback_B) .then(callback_C)這是非常優雅的處理方式,邏輯上也比巢狀結構容易理解:
  • 先執行完第一次 Ajax 的 http 請求
  • 完成之後,才依序執行 callback_A ~ callback_C 函數
  • 而 callback_A ~ callback_C 函數也能放入非同步的 Ajax 程式碼,讓所有非同步的執行緒,可以全部依照順序執行
同時 jQuery Ajax 官網文件還有其他用法,非只有 then() 的方式,例如 fail() 用來處理 http 請求失敗時的執行緒,done() 用來處理 http 請求成功時的執行緒,always() 則是不論成功失敗都會執行。

三、Fetch API 非同步排程

HTML5 增加了 Fetch API 來操作 http 請求,如此就不必另外求助於 JS 框架,可參考官網文件「Using Fetch」。 以下來看看 Fetch 的基本使用方式: fetch("網址", {參數}) .then(callback_A) .then(callback_B) .then(callback_C)使用方式是不是跟 jQuery 一模一樣啊?在 IE 被淘汰後,多數瀏覽器使用 HTML5 已經沒什麼問題,使用瀏覽器原生的 Fetch API 就能擺脫以往的 callback 地獄了。

四、處理未知排程數:Promise

如果一個頁面固定只有少數幾個 http 排程要處理,那麼程式只要重複寫幾個 then() 就解決了。然而如果 http 排程數不固定,不同頁面的 http 排程數也不一樣,但都得等這些 http 取回資料後才能處理特定工作,那就無法使用 then() 了,因為根本不知道要使用幾個 then()。 Javascript ES6 推出了新的 Promise 物件,剛好可以用來處理非同步的問題,可以一次執行所有等待中的 http 請求,以及相關排程的需求,詳細介紹可參考「JavaScript ES6 Promise Object 物件」。 這個討論串「Fetch in fetch in a loop JS」提供了 Promise 的應用方式,可以處理迴圈中的多個 fetch 請求,以下提供範例程式碼,說明如何處理未知排程數的 http 請求: var fetchUrl = ["網址1", "網址2", "網址3"], // 這裡是所有要執行的 http 請求, 當排程數未知時, 可將所有排程網址用函數製作出一個陣列 promises = [], i; for (i in fetchUrl) { promises.push(fetch(fetchUrl[i])); // 將所有要 fetch 的 http 請求,放入 promises 陣列 } Promise.all(promises) // 利用 Promise.all 先執行所有排程 .then(function() { // 然後執行後續的動作 }); 程式碼作用請見註解文字說明,當有排程數量不固定時,使用函數製作出 fetchUrl 陣列,利用以上 Promise.all 的功能就可先執行所有排程,再執行 then() 後續的處理動作。

五、JSONP 未知排程數

「未知排程數」一個很好的應用方式為,製作 Blogger 平台的 "相關文章"工具,首先必須取得某篇文章的所有標籤,並撈取所有這些標籤的最新 n 篇文章來做為母體樣本。 但是此時就會發現 fetch API 並非萬能,因為要撈 Blogger feed 內容必須使用 JSONP 的方式執行,而 fetch 卻只能取得 JSON 格式資料,不支援 JSONP,所以 fetch 將無法製作 Blogger feed 的相關工具。 要處理 JSON 的非同步多執行緒,我找到的工具為 jQuery 的 getJSON,再搭配 Promise.all 即可,請見以下範例程式碼: var fetchUrl = ["blogger feed 網址1", "blogger feed 網址2", "blogger feed 網址3"], // 這裡是所有要執行的 JSONP http 請求, 當排程數未知時, 可將所有排程網址用函數製作出一個陣列 promises = [], i; for (i in fetchUrl) { // 將所有 JSONP http 請求,放入 promises 陣列 promises.push($.getJSON(fetchUrl[i], function(json) { getFeed(json); // 處理 feed 資料 })); } Promise.all(promises) // 利用 Promise.all 先執行所有排程 .then(function() { // 顯示所有相關文章 }); // 處理 feed 資料 function getFeed(json) { // 整理 json 格式的 feed 資料 } 原理跟前個章節大同小異,請見註解說明即可。

六、async await 非同步排程

除了本篇前述的處理非同步排程技巧,從 Javascript ES7 開始,還多了 async、await 可以更好操作函數,來實現非同步執行緒,詳細的說明可參考這篇教學「簡單理解 JavaScript Async 和 Await」。 雖然新技術很好用,但由於 ES7 目前來說瀏覽器支援度有一定的問題,可參考「Can I Use」這個網站的支援性一覽表。前端工程師貿然大量使用的話,可能會有一部份使用者拼命報錯,所以這部分的內容點到為止,建議等 ES7 普及後再來研究。
更多 Javascript 相關技巧:

Blogger「作者簡介」已恢復顯示,功能設定教學

$
0
0
blogger-author-profile.jpg-Blogger「作者簡介」已恢復顯示,功能設定教學多年前寫過「讓 Blogger 能在文章下方顯示作者簡介 」,不過去年有讀者於留言 #5 表示 "作者資料無法顯示",此變故的主因大致是這樣:
  • 當年 Google 為了推廣 Google+,用了各種方法勸誘使用者將 Blogger 帳號移轉為 G+ 帳號
  • Blogger 各處的作者相關資訊也連帶改為顯示 G+ 帳號資訊
  • 然而 G+ 在 2019 年關閉,導致 Blogger 各處作者相關資訊無法讀取 G+ 資料,從此便呈現當機狀態。
今年「Blogger 更新後台介面」,猜想官方應會一併修復此問題,測試之後果然如此,本篇就來整理一下設定的注意事項。 (圖片出處: pexels.com)

一、各處小工具效果

blogger-author-profile-1.jpg-Blogger「作者簡介」已恢復顯示,功能設定教學如上圖,都設定正確的話,Blogger 有兩處可顯示作者資訊: 1. 文末「作者簡介」
  • 只能出現在文末位置
  • 可顯示作者名稱、頭像、簡介內容
2. 側邊欄「關於我」
  • 此為官方提供的小工具「個人資料」,安裝後可拖曳到任何想顯示的位置
  • 可顯示作者名稱、頭像、簡介內容、所在國家及城市
  • 可連結到完整的簡介頁面

二、作者簡介設定

首先必須在後台將 Blogger 帳號資料內容填寫、設定正確,才能在網頁相關位置顯示資訊。有兩種處理方式: 以下只說明與正確顯示「作者簡介」資訊相關的項目: blogger-author-profile-2.jpg-Blogger「作者簡介」已恢復顯示,功能設定教學務必勾選「分享我的簡介」,否則任何資訊都將無法顯示。 blogger-author-profile-3.png-Blogger「作者簡介」已恢復顯示,功能設定教學必須設定作者頭像才能顯示,可上傳圖片或填入圖片網址。 blogger-author-profile-4.png-Blogger「作者簡介」已恢復顯示,功能設定教學如果想顯示作者居住地的話,此處可以填入相關資訊。 blogger-author-profile-5.jpg-Blogger「作者簡介」已恢復顯示,功能設定教學此處務必填寫「簡介」的內容,而其他欄位若是填寫了也不會顯示在網站上。 最後按「儲存簡介」即可。

三、小工具設定

1. 文末「作者簡介」依照以下操作可讓文末出現「作者資訊」:
  • Blogger 新版後台 → 版面配置 → 編輯「網誌文章小工具」→ 勾選「在文章下方顯示作者簡介」→ 儲存
2. 側邊欄「關於我」依照以下操作可讓側邊欄出現「關於我」小工具:
  • Blogger 新版後台 → 版面配置 → 新增小工具 → 選擇「個人資料」小工具→ 填入小工具標題、勾選想要顯示的資訊 → 儲存
額外補充一點,如果後台有超過一個管理員或作者的話,那麼這個小工具將無法出現作者資訊,只會列出所有的管理員或作者名稱。所以想要顯示作者資訊的話,必須移除管理員或作者,直到只剩一個為止。

四、為何文末無法出現作者簡介

如果依照本篇說明,仍然在文末無法出現作者簡介,這也是有可能的事,原因在開頭那篇多年前的文章提過了:
  • 如果範本很乾淨、沒有變動過的情況下,Blogger 可以輕易地依據範本中程式碼相關字串順序,來找到並搬移例如 "星號評等"、"分享按鈕"、"作者簡介"等等的位置。
  • 因為 Blogger 的自由度,使我們很方便地自行修改範本中的程式碼。那麼當範本的程式碼位置有變更、甚或增減內容後,整個字串順序已經不一樣,Blogger 就不一定能判斷出 "星號評等"、"分享按鈕"等等的位置,因此別說要搬移,有時連顯示都很困難。
在這種情況下,可使用以下的簡易修復方法(修改之前務必先備份範本):
  • Blogger 新版後台 → 主題 → 「自訂」按鈕右方的下拉圖示 → 編輯 HTML → 上方找到一顆還原圖示的按鈕「將小工具還原為為預設值」→ 選擇「Blog1」→「還原所選小工具」 → 按右上方「儲存」圖示
但為了修復此處的「作者簡介」功能,還原小工具後,也可能讓你過去安裝的某些外掛、或某些範本修改內容消失,如對範本修改不熟悉的話,建議還是需請專業人士處理。
更多 Blogger 官方工具使用技巧:

使用 JS 追蹤訪客﹍裝置指紋辨識原理 + 實用工具介紹

$
0
0
現在消費者已越來越能接受線上付費觀看影音服務,例如官方提供的職業運動直播(NBA、MLB 等),以及各種追劇平台(Netflix、Line TV、愛奇藝等)。而消費者總是希望花錢的效益最大化,如果買一個帳號能在三台裝置收看,則可能找三個人合資分攤費用。 站在影音平台的立場,技術上必須防堵消費者同時登入超過 3 個裝置,因此得辨識出個別使用者上網的機器之硬體差異性。那麼這件事要如何做到呢?判斷單一裝置的身份認證,最簡單的方式為獲取「MAC address」,也可看做是該裝置的網卡實體位址,這絕對是獨一無二可供辨識的 ID 字串。 非網頁的環境下取得「MAC 位址」不難,然而線上影音平台架設在網頁,前端使用 Javascript 並沒有權限可取得「MAC 位址」,否則會有安全性問題。那麼網頁環境是否還有辦法能用 JS 追蹤訪客裝置?不然的話消費者買一個帳號卻得以從 100 個裝置登入,這些影音服務就得關門了! 最近有研究網頁裝置辨識的需求,本篇就來整理相關心得,讓每個訪客的裝置都能產生獨有的指紋辨識記錄,並介紹實用的 JS 瀏覽器指紋工具。

一、產生瀏覽器指紋的原理

由於網頁環境不易取得實體裝置資訊,退而求其次產生瀏覽器指紋較為可行。消費者一次只會從裝置使用一個瀏覽器上影音平台,所以辨識瀏覽器指紋已經足夠。 如何辨識使用者的瀏覽器特徵,大致整理出下面這些作法: 1. Google、FB 建立的使用者 ID其實每個人上網的一舉一動已被 Google、FB 監控,這些大公司早就研發出辨識使用者的技術,在每個人的電腦中埋下記錄。既然輪子已經造好,最簡單的作法是直接拿來用而不是自己研發,只要找出 Google、FB 留下的痕跡即可。 參考這個討論串「How do I uniquely identify computers visiting my web site?」,原 PO 詢問如何辨識他網站的訪客裝置,有人回答 Google Analytics(簡稱GA) 會使用 cookie 留下每個裝置的 ID 字串,讀取這個 ID 字串會是最方便的捷徑。 那麼要如何讀取 GA 留下的 ID 字串呢?這個討論串「how to get the Google analytics client ID」說明了如何讀取 GA 提供的「Client ID」(訪客 ID): ga.getAll()[0].get('clientId');使用 JS 執行以上語法就行了,非常簡單吧!同樣的原理,網站如果有安裝 FB pixel 相信也能取得類似的 ID。 只不過這方法雖然簡單,但 cookie 畢竟可能會被清除、修改,比較難讓人安心。2. Canvas 畫布指紋最早被利用來製作瀏覽器指紋的技術是 HTML5 的 Canvas 畫布功能,詳細原理可參考這篇「解讀瀏覽器指紋」→「canvas指紋」。 其原理簡單說就是,因每個裝置的軟硬體設備不同,那麼經由 canvas 技術繪製出來的同一幅圖像都會有肉眼看不出的微小差別,而經由演算法就能產生該圖像的一組 ID 字串,可做為瀏覽器指紋之用。 如果不了解這樣的說明,可以閱讀原文的幾張附圖,就能瞭解微小像素在不同裝置會有什麼樣的差異。 3. 綜合指紋 Canvas 指紋已經足以分辨出大部分的使用者裝置,但演算法產生的 ID 字串難免會遇到重複的情形,此時再組合該裝置的其他軟硬體資訊,只要加入的資訊越多,遇到重複的機率就越低。 而其他可以做為指紋的資訊可參考「瀏覽器指紋追蹤技術簡述」:
  • 基本資訊:例如瀏覽器版本、作業系統、語系、時區、螢幕解析度、字體....等等
  • Audio 指紋:類似 Canvas 的作法,利用軟硬體差異,將一段音訊經演算法產生 ID 指紋
  • WebRTC:利用較新瀏覽器版本才支援的功能,來取得經通信技術才能獲得的資訊。
瞭解以上原理後,接下來介紹網路上已開發出的一些 JS 工具,能直接產生個別瀏覽器對應的指紋 ID。

二、FingerprintJS V3

1. 工具介紹免費開源軟體中功能最強的要屬 FingerprintJS: 以上提供的官網是 V3 版本,官方提供的範例程式碼如下,執行後即可取得瀏覽器辨識 ID 字串。: <script> function initFingerprintJS() { FingerprintJS.load().then(fp => { // The FingerprintJS agent is ready. // Get a visitor identifier when you'd like to. fp.get().then(result => { // This is the visitor identifier: const visitorId = result.visitorId; console.log(visitorId); }); }); } </script> <script async src="//cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.min.js" onload="initFingerprintJS()"></script>2. 指紋含括資訊js-track-user-device-fingerprint-1.jpg-使用 JS 追蹤訪客﹍裝置指紋辨識原理 + 實用工具介紹上圖為 DEMO 頁面提供的資訊,顯示了 FingerprintJS V3 計算瀏覽器指紋 ID 時,所包含的所有資訊,例如瀏覽器版本、語系、記憶體、解析度...等等,截圖只是一部份資訊,螢幕往下捲可看到一共有將近 30 項資訊。 3. 優缺點此工具的優點為:版本最新,會包含較新的辨識技術,不同裝置的指紋 ID 要重複較困難。 不過從我的角度來看無法做為首選,因為有以下缺點:
  • 新版本沒有 API 使用說明,也就是阻止了前端工程師的開發工作,只能取得指紋 ID,無法進行其他操作
  • 新版本程式碼經過壓縮不易辨識,使用彈性極差,原因在於要另外推廣其付費版本(官網推薦使用者升級為 PRO 版),付費版本可前往「FingerprintJS
  • 新版本一律強制偵測、檢驗所有近 30 種項目,所以 JS 執行時間會比較長一些
因為缺乏彈性、強制檢驗所有項目,這會帶來潛在的缺點,有可能使用者環境設定稍稍有些微變動而已,就會被此工具判定為不同裝置,反而造成開發者的困擾或訪客的麻煩。 我的看法是這樣,有些項目需要檢測的必要性沒那麼大,例如「字型」這一項,除了要花費額外時間檢測,對判定精確度也提升不了多少,還可能導致使用者動了系統的字型設定後,就被此工具判定為不同裝置了。

三、FingerprintJS V2

1. 工具介紹還好 FingerprintJS 的作者還保留了舊版 V2 的頁面: 這個版本提供了 JS 原始碼,可瞭解程式的運作原理,官網頁面提供了 API 完整操作說明。 所以使用 V2 版本就能彈性檢測必要項目就好,不一定要含括所有將近 30 個項目。 2. 指紋含括資訊此版本沒有 DEMO 頁面,不過使用官網提供的範例程式碼,就能看到 V2 檢測了哪些項目: <script src='//cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@2/dist/fingerprint2.min.js'> </script> <script> if (window.requestIdleCallback) { requestIdleCallback(function() { Fingerprint2.get(function(components) { console.log(components) // an array of components: {key: ..., value: ...} }) }) } else { setTimeout(function() { Fingerprint2.get(function(components) { console.log(components) // an array of components: {key: ..., value: ...} }) }, 500) } </script>使用瀏覽器開發人員工具可看到檢測項目: js-track-user-device-fingerprint-2.jpg-使用 JS 追蹤訪客﹍裝置指紋辨識原理 + 實用工具介紹
  • 上圖可與 V3 比對一下,看檢測項目有哪些差異
  • 標示「not available」的項目可考慮不用檢測,因為可能在其他使用者的裝置也是測不出來
  • 「canvas」與「webgl」兩者可考慮選一個就好,都是對繪圖能力做檢驗,但「webgl」含括的資訊量更多
3. 取得指紋 ID官網文件捲到「get and getPromise」這裡,說明了如何取得瀏覽器指紋 ID: Fingerprint2.get(options, function (components) { var values = components.map(function (component) { return component.value }) var murmur = Fingerprint2.x64hash128(values.join(''), 31) })murmur 的數值即為「瀏覽器指紋 ID」。 4. 操作補充說明 V2 可對所有檢測項目進行設定,也可排除要檢測的項目,操作說明可在官網文件捲到「Options」,官方提供的範例如下: var options = {fonts: {extendedJsFonts: true}, excludes: {userAgent: true}}此範例可對「fonts」進行設定,也可排除「userAgent」這一項,細節需參照官方原始碼第 245 行「defaultOptions 」 設定完 options 後就能執行工具,可參考前面「3. 取得指紋 ID」的範例程式碼,該處程式碼即為帶入 options 的結果。

四、canvas 指紋

以前端開發工程師而言,使用「三、FingerprintJS V2」算是最佳選擇。 不過這裡提供另外一個選項,如果覺得研究 V2 的說明書太麻煩,有國外網友從 FingerprintJS 擷取幾個重要項目來使用,檢驗項目算是以 Canvas 為主,發佈在 Github:所有檢測的項目作者已列在官網,程式碼較短小也節省執行時間,我算是認同此作法。 以下提供此版本整理過的程式碼,直接執行就能取得瀏覽器指紋 ID: var Fingerprint=function(a){var b,c;b=Array.prototype.forEach;c=Array.prototype.map;this.each=function(j,h,g){if(j===null){return}if(b&&j.forEach===b){j.forEach(h,g)}else{if(j.length===+j.length){for(var f=0,d=j.length;f<d;f++){if(h.call(g,j[f],f,j)==={}){return}}}else{for(var e in j){if(j.hasOwnProperty(e)){if(h.call(g,j[e],e,j)==={}){return}}}}}};this.map=function(g,f,e){var d=[];if(g==null){return d}if(c&&g.map===c){return g.map(f,e)}this.each(g,function(j,h,i){d[d.length]=f.call(e,j,h,i)});return d};if(typeof a=="object"){this.hasher=a.hasher;this.canvas=a.canvas}else{if(typeof a=="function"){this.hasher=a}}};Fingerprint.prototype={get:function(){var a=[];a.push(navigator.userAgent);a.push(navigator.language);a.push(screen.colorDepth);a.push(this.getScreenResolution().join("x"));a.push(new Date().getTimezoneOffset());a.push(this.hasSessionStorage());a.push(this.hasLocalStorage());a.push(this.hasIndexDb());if(document.body){a.push(typeof(document.body.addBehavior))}else{a.push(typeof undefined)}a.push(typeof(window.openDatabase));a.push(navigator.cpuClass);a.push(navigator.platform);a.push(navigator.doNotTrack);a.push(this.getPluginsString());if(this.canvas&&this.isCanvasSupported()){a.push(this.getCanvasFingerprint())}if(this.hasher){return this.hasher(a.join("###"),31)}else{return murmurhash3_32_gc(a.join("###"),31)}},hasLocalStorage:function(){try{return !!window.localStorage}catch(a){return true}},hasSessionStorage:function(){try{return !!window.sessionStorage}catch(a){return true}},hasIndexDb:function(){try{return !!window.indexedDB}catch(a){return true}},isCanvasSupported:function(){var a=document.createElement("canvas");return !!(a.getContext&&a.getContext("2d"))},isIE:function(){if(navigator.appName==="Microsoft Internet Explorer"){return true}else{if(navigator.appName==="Netscape"&&/Trident/.test(navigator.userAgent)){return true}}return false},getPluginsString:function(){if(this.isIE()){return this.getIEPluginsString()}else{return this.getRegularPluginsString()}},getRegularPluginsString:function(){return this.map(navigator.plugins,function(b){var a=this.map(b,function(c){return[c.type,c.suffixes].join("~")}).join(",");return[b.name,b.description,a].join("::")},this).join(";")},getIEPluginsString:function(){if(window.ActiveXObject){var a=["ShockwaveFlash.ShockwaveFlash","AcroPDF.PDF","PDF.PdfCtrl","QuickTime.QuickTime","rmocx.RealPlayer G2 Control","rmocx.RealPlayer G2 Control.1","RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)","RealVideo.RealVideo(tm) ActiveX Control (32-bit)","RealPlayer","SWCtl.SWCtl","WMPlayer.OCX","AgControl.AgControl","Skype.Detection"];return this.map(a,function(b){try{new ActiveXObject(b);return b}catch(c){return null}}).join(";")}else{return""}},getScreenResolution:function(){return(screen.height>screen.width)?[screen.height,screen.width]:[screen.width,screen.height]},getCanvasFingerprint:function(){var c=document.createElement("canvas");var b=c.getContext("2d");var a="CANVAS_FINGERPRINT";b.textBaseline="top";b.font="14px 'Arial'";b.textBaseline="alphabetic";b.fillStyle="#f60";b.fillRect(125,1,62,20);b.fillStyle="#069";b.fillText(a,2,15);b.fillStyle="rgba(102, 204, 0, 0.7)";b.fillText(a,4,17);return c.toDataURL()}}; var javaHashCode = function(string, K) {var hash = 0;if (string.length === 0) {return hash;}for (var i = 0; i < string.length; i++) {char = string.charCodeAt(i);hash = K*((hash<<5)-hash)+char;hash = hash & hash;}return hash;}; var fingerprint = new Fingerprint({hasher: javaHashCode}); console.log(fingerprint.get())
更多 Javascript 使用技巧:

Sublime Text 檢測 JS 最佳工具 ESLint﹍安裝 + 設定技巧

$
0
0
sublime-text-js-syntax-linter-eslint.jpg-Sublime Text 檢測 JS 最佳工具 ESLint﹍安裝 + 設定技巧一直以來使用 Sublime Text 3(簡稱 ST3)檢查 Javascript 錯誤的工具是「JSHint Gutter」,優點為執行快速、圖形介面操作友善、自訂參數完善,沒想過有可能要放棄他的一天。 基於網路環境所發展的技術實在進展太快,也可說是變化太快,JS 過去難解的非同步問題在 ES6、ES7 之後已獲得緩解,也特地寫了一篇「前端 JS 如何避免 callback 地獄?Fetch API 及 Promie 使用技巧」。現在使用 async/await 後簡直上了隱,完全不想再使用舊的非同步操作方式,而且 async/await 可讓 JS 更有架構、有條理,日後管理真是太舒服了! 然而這也產生了新的問題,JS 的 ECMAScript 規範已經發展到最新的 ES11,但 JSHint 開發無法這麼快跟上,我 ST3 使用的 JSHint Gutter 套件查了一下,開發者最近一次更新是 2018 年,那麼想要支援 ES7 的 async/await 檢測將是遙遙無期! 不得已只好研究 ST3 是否有其他 JS 套件可檢測 async/await,最終找到的解決方案為 ESLint,只是安裝、設定過程十分崎嶇,請見本篇最佳化心得整理。 以下先說明 ST3 套件 JSHint 與 ESLint 的基本問題,想要直接看安裝設定流程,請跳至「三、ST3 最佳 ESLint 安裝流程」。 (圖片出處: eslint.org)

一、JSHint 套件的問題

1. JSLint、JSHint、ESHint 差異知名的 JS 語法檢測工具主要有這三個,可參考這篇「JSLint,JSHint,ESLint的區別」的說明:
  • JSLint:最早的 JS 檢測工具,無法更改設定,安裝即可使用,大部分的 JS 程式碼可能都無法通過檢測
  • JSHint:基於 JSLint 的開源工具,檢測項目的設定比較有彈性,執行速度不錯
  • ESLint:基於 JSHint 另外開發的工具,環境設定最有彈性,執行速度慢。但也因為太有彈性,一開始需要自定義的細節太多,不容易上手。
2. ST3 套件的問題 JSHint 的開發速度較慢,當 ES6 的箭頭函數開始普及後,我安裝的 JSHint Gutter 無法辨識,試著解安裝、重新安裝最新版本後,發現箭頭函數已支援,代表 ES6 的大部分語法功能是可以相容的。 而最近測試 ES7 async/await 發現不相容後,查到這個討論串「Does JSHint support async/await?」,得知 JSHint 在 2019 年發佈的訊息,只要把版本設定為 ES9 就能支援。 然而不能高興太早,因為 JSHint 的開發環境並非針對 ST3,就算 JSHint 有支援 async/await,也得 ST3 的 JSHint 相關套件作者有進行對應的更新。 查了一下 ST3 的所有 JSHint 套件,最近一次的更新日期都在 2~3 年之前。身為開發者我也可以理解,免費開源的工具一開始主要是為了自己的需求,不太可能時常無償更新,甚至忽然間因故不再維護了也是有可能。 因此,我有必要先做 JSHint Gutter 可能不再更新的心理準備。

二、ESLint 套件的問題

查到的資料顯示,ST3 能支援 async/await 檢測的工具剩下 ESLint,但我很擔心相關套件能否做的跟 JSHint Gutter 一樣好,如果用不順手的話,有可能還是必須勉強用 JSHint Gutter。 以下整理遇到的問題: 1. ST3 版本一開始怎麼裝都不會動,後來更新到 ST3 最新版才總算瞭解原因(因為我平常關閉 ST3 更新)。 2. NodeJS 版本按照網路上教學安裝 ST3 的 ESLint 套件,但卻問題一堆不能執行,自行上 「ESLint 在 Github 的官網」查詢才發現,以下才是正確的安裝步驟:
  • NodeJS 版本:官方建議安裝 v12 以後的版本。如果沒安裝過 NodeJS 的話,就不會有此問題,因為新安裝一定是最新的版本。而我原本就有安裝 NodeJS,由於版本過舊自然無法執行 ESLint。
  • 初始化設定:在 Windows 下安裝完 ESLint 後,官方說明需要先執行 eslint --init指令,會問我們許多問題,根據回答後系統會自動建立設定檔。沒有此設定檔的話,ESLint 會報錯無法執行
  • ST3 ESLint 套件:以上兩個動作沒有正確完成,就安裝 ST3 套件的話,會報錯無法執行
3. 介面不好操作sublime-text-js-syntax-linter-eslint-1.jpg-Sublime Text 檢測 JS 最佳工具 ESLint﹍安裝 + 設定技巧以上圖來舉例:
  • 右上方紅框為 JSHint Gutter 偵錯畫面,操作方便,要跳到錯誤的的行號很容易
  • 下方紅框為 ESLint 偵錯畫面,版面設計、顏色配置極差 → 訊息不易辨識,字太大 → 可視範圍小,要捲到錯誤行號困難
如果將來 JSHint Gutter 有更新的話,仍會是我的首選。 4. HTML 內的 JS 無法檢測初步測試 ESLint 只能檢測 .js 檔,無法處理 html 內 script 內容,但 JSHint Gutter 可以處理。 不過這一點後來找到解決辦法,之後會補充。

三、ST3 最佳 ESLint 安裝流程

雖然 ST3 的 ESLint 套件無法讓我滿意,不過最終總算讓我找到比較能接受的操作介面,也就是使用另一個套件 SublimeLinter,這是一個管理介面套件,用來管理各種程式語言的檢測工具,以下為 Windows 環境完整安裝流程: 1. ST3 版本請確認 ST3 是最新版本,若不確定的話請按選單 Help → Check for Updates 強制更新。 2. 安裝 node.js使用 Dos 命令列輸入 node -v可查看 NodeJS 版本,若版本低於 12 或沒安裝過的話,請到「NodeJS 官網」下載安裝。 3. 安裝 ESLint使用 Dos 命令列輸入以下安裝 ESLint: npm install eslint -g使用 Dos 命令列輸入以下來進行設定及產生設定檔,請按自己需求來回答即可: eslint --init4. 安裝 SublimeLinter如果 ST3 還沒安裝「Package Control」的話,可按照網頁的英文說明進行即可。若需要中文教學可參考「Sublime 文字編輯器安裝」。 安裝完後,在 ST3 按快速鍵 ctrl+shift+p,輸入 Install Package,再輸入 sublimelinter 即可安裝。 5. 安裝 SublimeLinter-eslint 套件在 ST3 按快速鍵 ctrl+shift+p,輸入 Install Package,再輸入 SublimeLinter-eslint 即可安裝。

四、SublimeLinter-eslint 效果

sublime-text-js-syntax-linter-eslint-2.png-Sublime Text 檢測 JS 最佳工具 ESLint﹍安裝 + 設定技巧 SublimeLinter-eslint 的檢測效果如上圖:
  • 預設會在背景執行,一有錯誤馬上會標示出來,不需按熱鍵才進行檢測
  • 標示效果1:有錯誤的那一行最前面標示紅點
  • 標示效果2:發生錯誤之處用紅框標示出來,滑鼠移到紅框會說明錯誤原因
  • 可按熱鍵在最下方顯示錯誤列表,滑鼠雙擊該行可快速前往該行,並反白該行
SublimeLinter-eslint 錯誤列表的排版、顏色配置效果比 ESLint 套件好上太多,資訊閱讀一目了然,且背景執行可立即發現錯誤也很實用。

五、設定技巧

1. 修改 ESLint 設定檔 ESLint 的設定檔名為 .eslintrc.js,位置通常在「C:\Users\使用者名稱」這裡,以下為我的設定檔內容,根據當初的回答自動產生: module.exports = { "env": { "browser": true, "es2021": true }, "extends": "eslint:recommended", "parserOptions": { "ecmaVersion": 12 }, "rules": { "semi": ["error", "always"] // 此行由 WFU 添加 } };裡面只有 WFU 註解那一行是我自行加入的,意思為 JS 檢測每行結束是否有加分號 ";"。 2. ESLint 所有設定項目說明 ESLint 設定檔可修改、調整的內容非常彈性,請參考這份翻譯過的中文說明「常見的.eslintrc.js配置及rules說明」。 3. 修改 SublimeLintersublime-text-js-syntax-linter-eslint-3.png-Sublime Text 檢測 JS 最佳工具 ESLint﹍安裝 + 設定技巧 SublimeLinter 有些重要設定需要調整,參考上圖,選項位於「Preference」→「Package Settings」→「SublimeLinter」→「Settings」,即可叫出設定檔。 預設值位於左半邊視窗,是唯讀狀態不能修改,我們必須複製到右半邊視窗的使用者設定檔才能生效。 sublime-text-js-syntax-linter-eslint-4.jpg-Sublime Text 檢測 JS 最佳工具 ESLint﹍安裝 + 設定技巧上圖為修改範例,下面會進行說明。 4. 讓 HTML 也能檢測 JS前面有提到 SublimeLinter-eslint 預設無法檢測 HTML 檔內的 JS,這一點官網有提供解決方法,請參考「eslint doesn't lint my HTML files anymore」,將以下內容加入使用者設定檔,參考上圖即可: "linters": { "eslint": { "selector": "source.js - meta.attribute-with-value" } } 5. 手動檢測 JS SublimeLinter 預設會背景自動檢測,如果習慣手動按熱鍵才檢測的話,可參考上圖紅框之處,lint_mode 參數提供了 4 個選項,其中 "manual"就是手動,所以使用者設定檔加入以下即可: "lint_mode": "manual"
更多網頁開發工具:

嘗試徹底解決 Line、FB 手機 APP 無法正確開啟網頁的困境

$
0
0
前幾年曾寫過「用 Line、FB 手機 APP 開啟網頁對前端工程師的困擾﹍JS 辨識內建瀏覽器(webView)的方法」,除了解釋 "什麼是內建瀏覽器"、"為何 APP 要用 webView"、"內建瀏覽器會產生什麼問題"等等,還有提到幾個要點:
  • 這件事以目前的網路技術,沒有治本的解決方法
  • 手機 APP 只會越來越多,不可能針對所有 APP 個別處理
  • 暫時只能針對少數熱門 APP 處理,例如 Line、FB..
最近因為一些需求,想要徹底解決手機內建瀏覽器( webView 問題,想說過了兩三年看看技術上有沒有新進展,結果依然沒有根治之法。 不過在測試過程發現有些微不一樣的地方,如此一來讓我產生了不同的想法,說不定可以從別的角度切入,來解決手機內建瀏覽器的問題。 (圖片出處: pixabay.com)

一、webView 的進展

1. JS 基本問題上一篇的留言 #2 整理了「你知道你的網站可能在 InAppBrowser/webview 無法使用嗎?」的 Javascript 基本問題:
  • alert() :無法出現對話提醒視窗
  • confirm() :對話確認視窗無法出現
  • window.open 或 window.opener:與電腦上完全不同的顯示行為。
  • window.close 或 self.close :失效。
兩三年過去軟硬體的發展都有進步,現在使用我手邊的裝置進行實測,分別有 Android 5.0 及 iOS 12 的機型:
  • alert() 與 confirm() 都能正常執行
  • window open、close 問題不變
看起來 webView 的進展並非一成不變,隨著 Android 與 iOS 的更新,JS 的支援程度也會越來越高。window open 的問題最後再敘,目前 alert 與 confirm 能用的話,就可提醒使用者改用正規瀏覽器開啟網頁,手機 APP 使用 webView 的問題要完全解決至少是看得到希望的2. 測試 ES6、ES7 支援度會想要徹底解決 webView 的問題,其實主要原因來自 JS 的非同步問題。自從 ES7 的 async/await 徹底解決了非同步問題的 callback 地獄,同時讓 JS 的架構井然有序,沒有再回去使用舊語法的道理。 然而 ES7 別說可能較舊的網頁瀏覽器不支援,webView 就更別提。所以如果 JS 不能辨識手機 webView 的話,就不可能放心讓自己的網頁全面啟用 async/await。那麼 webView 的技術目前究竟支援到 ES 哪個版本,有必要實地測試一下。 這個討論串「Can I detect async/await available in browser?」提供了以下測試方法: try { eval('async () => {}'); } 這行語法包含了 async 以及箭頭函數,我們來看看結果如何。 3. Android、iOS 相容性以下是我的手機內建瀏覽器(非網頁瀏覽器)實測結果:
  • Android 5 webView:出現了箭頭函數的錯誤訊息,代表 ES6 的支援就有問題了,更不可能支援 ES7
  • iOS 12 webView:出現了 SyntaxError 錯誤訊息,代表支援 ES6 箭頭函數、不支援 async,所以 ES7 一定不行
android 5 相對於 iOS12 是比較舊的版本,不過代表市場上還會有這樣的機型。這也可看出不同機型的內建瀏覽器對 JS 的支援度並非都一樣,而隨著時間進展 webView 將來是有可能追上 JS 主流技術的。 這個測試結果算是給我不少信心,目前當然需要解決 webView 的問題,但很可能隨著手機的進步,將來有一天 webView 的問題就微不足道了。 4. window open、close 問題雖然提供了 webView 的鼓舞訊息,但接下來要潑點冷水,來談談 window open、close 問題。先說結論,這很有可能是無解的狀況。 webView 的先天設計有其限制,功能只是單純用來開啟網頁,並沒有像一般網頁瀏覽器有分頁(tab)的功能,這導致所謂的 window.open() 並不會另開視窗,而只是在原網頁開啟另一個網址。 看到這裡讀者應該就懂了,不會另開視窗的話,那麼不同頁面間要切換只能執行上一頁、下一頁,自然 window.opener 的對象就跟網頁瀏覽器不一樣了,而 window open、close 所有相關問題也都是同樣原理。 所以如果開發者的網頁一定要使用 window.opener 相關功能,就絕對不能在 webView 執行,就算將來有一天 webView 的 JS 相容性已經跟網頁瀏覽器一模一樣也不行,這算是 "硬體上的限制"!

二、JS 處理 webView 的構思

前一篇文章我認為沒有一勞永逸的解法,所以只提供了解決問題的概念,瞭解如何判斷各種內建瀏覽器的語法。而這段時間以來,網路上陸續出現了各種有助於解決 webView 的拼圖,大致整理一下其他的關鍵技術,最後會提出我的整合構想: 1. Chrome 網址協定如果手機有裝 Chrome 瀏覽器的話,可以使用 Chrome 提供的網址協定,參考這篇「手機網頁跳出WebView強制使用Chrome開啟」。 Android 使用範例: googlechrome://navigate?url=www.wfublog.com iOS 使用範例: googlechrome://www.wfublog.com2. Line 開啟外部瀏覽器手機使用 Line 開啟連結網址時,官方留了一個後門可以用外部瀏覽器開啟,只要在網址後面加上參數 ?openExternalBrowser=1,例如: https://www.wfublog.com?openExternalBrowser=13. 偵測是否為行動裝置根據「讓 Line 按鈕只在手機+行動裝置顯示」→「三、判斷行動裝置」,以下為判斷語法: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) )4. 測試 async 支援度因為 webView 對 JS 支援度不夠,那麼可以偵測瀏覽器是否支援較新的 JS 規範,例如測試瀏覽器是否支援 async 語法,前面有範例語法。 當然,這並非嚴謹的測試方法:
  • 因為不支援 async 的瀏覽器不一定只有 webView,也包含了舊瀏覽器與瀏覽器舊版本
  • 但這仍不失為可行的檢驗方法,並竟我的網頁就是需要支援 async 才能正常顯示
  • 所以任何不支援 async 的瀏覽器,我都需要提醒訪客改用最新的主流瀏覽器版本,例如 Chrome
  • 但如果開發者的網頁並沒有使用 async/await 語法,則不一定要測試 async,只要測試夠用的 JS 規範即可,例如前面提過可測試箭頭函數
  • 如果將來 webView 支援了 async,而網頁又有想排除 webView 的必要需求時,必須改為偵測更新的 JS 規範
4. 整合構思整合前述的所有概念後,以下為我構思的 JS 偵測 webView 概念流程:
  • 偵測瀏覽器 JS 是否支援 async,若支援則不處理
  • 瀏覽器不支援 async 時,偵測是否為行動裝置
    • 如果不是行動裝置,則 alert 提醒使用最新 chrome 瀏覽器
  • 如果是行動裝置時,偵測是否為 Line 內建瀏覽器
    • 如果是 Line 內建瀏覽器,直接用外部瀏覽器開啟網頁
  • 如果不是 Line 內建瀏覽器時
    • 偵測是否為 Android
      • 如果是 Chrome 則 alert 提醒更新為最新版本
      • 不是 Chrome 則 confirm 請訪客同意用 Chrome 開啟網頁,或是請自行改用外部瀏覽器開啟
    • 偵測是否為 iOS
      • 如果是 Chrome 則 alert 提醒更新為最新版本
      • 不是 Chrome 則 confirm 請訪客同意用 Chrome 開啟網頁,或是請自行改用外部瀏覽器開啟

三、範例程式碼

根據前面提出的構想,以下為範例程式碼,請自行修改相關字串: (function() { var chrome_warning_text = "建議使用最新版 chrome 瀏覽器才能正常執行本網頁", chrome_protocol_text = "如您已安裝 Chrome,請同意用 Chrome 開啟網站,才能確保執行正常。或是請取消後自行改用外部瀏覽器開啟本頁面網址", userAgent = navigator.userAgent, thisHref = location.href, thisUrl = location.hostname + location.pathname, isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent), isLine = userAgent.indexOf("Line") > -1, isAndroid = userAgent.indexOf("Android") > -1, isIOS = /iPhone|iPad|iPod/i.test(userAgent), isChrome = /Chrome/.test(userAgent) && /Google Inc/.test(navigator.vendor); try { // 測試是否支援 async eval("async () => {}"); } catch { // 測試是否為行動裝置 if (isMobile) { // 偵測是否為 Line 內建瀏覽器 if (isLine) { // Line 內建瀏覽器 直接用外部瀏覽器開啟網頁 location.href = thisHref.indexOf("?") > 0 ? thisHref + "&openExternalBrowser=1" : thisHref + "?openExternalBrowser=1"; } else { // 偵測是否為 Android if (isAndroid) { // 偵測是否為 Chrome if (isChrome) { // 建議使用最新版 chrome alert(chrome_warning_text); } else { // 不是 Chrome 則請訪客同意用 Chrome 開啟網頁 if (confirm(chrome_protocol_text)){ location.href = "googlechrome://navigate?url=" + thisUrl; } } return; } // 偵測是否為 iOS if (isIOS) { // 偵測是否為 Chrome if (isChrome) { // 建議使用最新版 chrome alert(chrome_warning_text); } else { // 不是 Chrome 則請訪客同意用 Chrome 開啟網頁 if (confirm(chrome_protocol_text)){ location.href = "googlechrome://" + thisUrl; } } return; } // 其他作業系統建議使用最新版 chrome alert(chrome_warning_text); } } else { // 非行動裝置建議使用最新版 chrome alert(chrome_warning_text); } } })();

四、Babel

除了以上本篇提供的思路,跨瀏覽器、跨裝置讓 JS 相容還有一套知名的工具「Babel」 ,可以將 ES6 以上的 JS 新規格語法轉換回 ES5 語法,讓較低階的瀏覽器也能執行。 1. 線上轉換工具這是 Babel 提供的線上轉換工具: 不過我把 async/await 語法丟進去後,並沒發現這個工具能夠轉換為相容的 ES5 語法,也許這個線上工具並非最新 Babel 版本。 2. 安裝 Babel從這篇「以 async/await 為例,說明 babel 插件怎麼搭」看來,Babel 有辦法可以處理 async,也許要找到對的版本。 不過 Windows 下使用看來要花一些功夫,此參考資料提供給開發者來研究了。 3. 感想那麼 Babel 是否能做為 webView 相容性的解答?也許有可能,但需要持續關注 Babel 的開發狀況、版本差異性、以及 webView 的進展,感覺上是滿累的,就看前端開發者如何抉擇了。

五、如何解決 window.opener

前面提過 window.opener 會是 webView 的無解難題,本篇提供的任何方案都無法完全消除隱憂。不過畢竟 window.opener 使用機率低,所以這裡單獨另開一個章節說明解法,解決思路大致是這樣:
  • 前面提過 webView 無法另開視窗,只能開在同一視窗
  • 那麼可以在新開的頁面檢測 window.opener 相關資訊
  • 如果是正常網頁瀏覽器,window.opener 的網址會是原頁面的網址
  • 但在 webView 之下 window.opener 的網址就不會是原頁面的網址
  • 那麼新開的頁面當檢測到異常後,就可以 alert 提醒訊息
  • 也可按本篇範例程式碼的流程引導訪客使用 Chrome 瀏覽器

六、總結

  • 本篇的內容嚴格來說是「async 語法跨瀏覽器、跨裝置的解決方案」,而 webView 的問題只能說是順道一併解決。
  • 不過內容全是圍繞內建瀏覽器產生的問題,所以也不算偏離主題。
  • 本篇的範例程式碼算是有時效性,將來 JS 的發展若出現另一個突破性的階段,那麼 webView 的問題就要再改成「XXX 語法跨瀏覽器、跨裝置的解決方案」了
  • 即便如此,到時只要修改範例程式碼 try{...}這裡的內容就好,那麼本篇程式碼的泛用性還是不錯的
更多 Javascript 技巧相關文章:

搜尋結果標題被 Google 安插額外的字串(或品牌名稱)怎麼辦?

$
0
0
google-search-result-add-brand-name.jpg-搜尋結果標題被 Google 安插額外的字串(或品牌名稱)怎麼辦?為了讓搜尋結果能增加網站知名度,常見的作法是在網站範本中,為 title標籤加入網站名稱。操作的方式可參考「Blogger 只要做到這幾件事, 就能輕鬆加強 SEO 搜尋排名」→「三、文章優化」→「1. 網頁標題」,將網站名稱字串放在文章標題後面。 這樣的技巧有個優點,如果網站名稱包含了某些關鍵字,同時在搜尋結果顯示文章標題與網站名稱,可增加一些搜尋的能見度,也就是稍微能提升 SEO 排名。 不過前陣子接獲一個奇特的狀況,搜尋結果除了「文章標題+網站名稱」,後面還被 Google 添加了其他字串。檢視過案主的範本內容,沒發現任何錯誤的設定,代表人為因素可排除,那麼原因只剩一個──Google 刻意加上去的。 為何如此呢? (圖片出處: pixabay.com)

一、Google 會調整搜尋結果標題

其實每篇文章設定的標題,在搜尋結果不一定會如實顯示,Google 有可能做細微調整,也可能改得面目全非,原因可參考過去寫的「Google 搜尋結果顯示的文章標題、摘要、縮圖,跟你想的不一樣(各種案例整理) 」。 Google 為何要改搜尋結果的標題,簡單說有這些可能:
  • 某些站長在標題堆積關鍵字,形成不通順的字句,有操縱 SEO 的意圖
  • Google 會根據不同的搜尋關鍵字,提供對應的標題字串
  • Google 會根據不同的語意,給予不同的回答。文章的原始標題不一定能做到這一點,那麼 Google 就會修改標題以符合搜尋關鍵字
瞭解以上基本觀念是非常重要的,無論是搜尋文字、圖片的結果,Google 的中心準則都是想辦法找出對搜尋者最有幫助的答案,並且不惜修改原始標題字串。更多的 Google 官方說明請參考「建立描述性網頁標題」。 不過本篇要探討的案例不太一樣,因為並非標題內容被修改,而是標題最後面被 Google 安插了特定字串,以下來看看是什麼情形。

二、Google 安插額外字串實例

google-search-result-add-brand-name-1.jpg-搜尋結果標題被 Google 安插額外的字串(或品牌名稱)怎麼辦?上圖是「照護線上」提供的案例,範本中設定的 title標籤格式為「文章標題 - 網站名稱」,但不知為何搜尋結果最後面被 Google 加上了字串 "- 診所藥局"。 google-search-result-add-brand-name-2.jpg-搜尋結果標題被 Google 安插額外的字串(或品牌名稱)怎麼辦?上圖是另一個搜尋結果,前兩筆都是「照護線上」的文章,不過第一個沒被安插額外字串,第二個就被加上字串 "- 診所藥局"。 為了搞清楚這是怎麼回事,先來找找國外有無相關事件的討論。

三、國外實例調查

1. 搜尋結果被添加不正確的品牌名稱這個討論串「My post Title is Adding extra Words on Google Search Result」案主抱怨網站文章的搜尋結果,最後面被加上不正確的品牌名稱。 似乎是 Google 會主動幫網站搜尋結果加上「品牌名稱」,但實際顯示的品牌名稱與案主有一點點不一樣,這的確會造成混淆及影響商譽。 而該討論串頁面的確還可找到許多案例,證實「 Google 正在搜尋結果幫網站加上品牌名稱」。 2. 搜尋結果被添加地名這個討論串「Google adding Location to Title Tags in SERPs incorrectly」,案主抱怨網站文章的搜尋結果最後面被加上 "- London" (倫敦) 的字樣,造成了他的困擾,因為如果只是倫敦的公司就沒差,然而案主的業務並不僅僅在 London,還包含了其他地區(例如 Scotland)。 為何 Google 會主動加上地名,還是說 Google 誤把地名判定成了他的「品牌名稱」呢? 3. 國外 SEO 觀察家看法這個國外 SEO 網站寫了篇「New SERP feature truncates title tag in favor of location」,作者發現 Google UK 地區的搜尋結果有了變化,某些搜尋結果會加上地名,例如 "- London",他的觀察有這些情況:
  • 搜尋「公司、代理商、商業相關」的時候
  • 如果標題沒指定地點的時候
  • Google 似乎會從一頁式網頁抓地點資訊
作者認為這可以增加商業搜尋的效益,並給予 Google 正面肯定。

四、Google 正在幫我們安插品牌名稱

除了前述的國外 SEO 觀察家,這一篇「Google Automatically Adds Brand Name to Titles」也值得參考,作者很早就發現 Google 的動作(2020年4月),明確指出 Google 會在搜尋結果加上「品牌名稱」。 前面有提到,正常來說搜尋結果主要會顯示 title設定的標題字串,而此篇作者提供了搜尋結果範例:
  • 網站名稱為「S4S Coding」
  • 文章標題在 title的字串為「xxxxx」
  • 搜尋結果該篇文章標題變成了「xxxxx - S4S Coding」
從這個現象來看可得出一個結論,將來 SEO 可以少做一個動作,以往我們會主動在範本中,將 title字串加上網站名稱,也就是 Google 判定的品牌名稱。既然 Google 會主動幫我們加品牌名稱,我們就不必另外修改 title設定了。 不過這牽涉到一個問題,Google 怎麼知道我們的網站名稱是什麼,品牌名稱究竟是什麼,畢竟我們沒有跟 Google 提交過這個資訊,如果 Google 判斷錯誤了怎麼辦

五、品牌名稱跟你想的不一樣

1. 照護線上 Google 判定的「品牌名稱」就一定正確嗎?前面我們已經看到一個抱怨的例子,現在回過頭來看本篇開頭「二、Google 安插額外字串實例」的實例「照護線上」,以下為該站某搜尋結果標題範例: 把握治療時機,才有機會逆轉血糖- 照護線上 - 診所藥局
  • Google 會先檢查標題有無網站標題(或品牌名稱)字串,沒有時才補上品牌名稱
  • 該網站的標題名稱為「照護線上」
  • 而 Google 會補上「診所藥局」這樣的字串,很明顯代表 Google 認為這才是品牌名稱
  • 如果 Google 認為「照護線上」是品牌名稱,就不會增加額外字串了
所以這個奇特的案例告訴我們,Google 認定的品牌名稱跟你想的不一樣。 2. 線上看電視「照護線上」並非個案,因為本站經營的另一個服務「線上看電視」也發生了類似狀況。 google-search-result-add-brand-name-3.jpg-搜尋結果標題被 Google 安插額外的字串(或品牌名稱)怎麼辦?
  • 第一個搜尋結果來看,網站名稱很明顯為「線上看電視」
  • 然而第二個搜尋結果,標題被另外加了「- TV 線上看電視」
  • 這代表 Google 認定的品牌名稱為「TV 線上看電視」
標題出現重複的累贅字串著實不太舒服,感覺比「診所藥局」的案例更差,得想個辦法將「線上看電視」與「TV 線上看電視」留下其中一個就好。 3. 更多案例從「照護線上」提供的截圖,還看到另一個被改品牌名稱的案例(跟網站名稱截然不同)「器官漏餡」了?疝氣修補讓器官乖乖歸位」,而且不同時間點搜尋,看到的品牌名稱還差滿多的: google-search-result-add-brand-name-4.jpg-搜尋結果標題被 Google 安插額外的字串(或品牌名稱)怎麼辦?google-search-result-add-brand-name-5.png-搜尋結果標題被 Google 安插額外的字串(或品牌名稱)怎麼辦?4. 綜合分析從以上得到的案例來分析,似乎 Google 不認為「網站標題」等於「品牌名稱」,而且 Google 對「品牌名稱」有一套演算法來分析,以下是我的推論:
  • 所謂的「品牌名稱」不是我們自己認定的,而是別人認定的
  • 別人網站擺放我們網站的連結(也就是外連),所使用的字串,Google 可能認為這才是品牌名稱
  • 訪客搜尋我們網站所使用的字串,Google 可能認為這才是品牌名稱
  • Google 藉由記錄所有連往我們網站的來源,以及所使用的字串,綜合分析出最適合成為我們網站的「品牌名稱」字串
簡單說,「品牌名稱」就是 Google 認為絕大多數市場根據對我們網站的認知,而使用的字串

六、不喜歡被安插的品牌名稱字串怎麼辦?

Google 利用大數據算出來的「品牌名稱」嚴格來說有其根據,但要 100% 讓所有人滿意當然不太可能,還是需要獲得意見回饋並進行修正才能更為完美。。 1. 自行調整 title「線上看電視」的案例還算好解決,因為只差了 "TV"這兩個字母,我可以選擇將範本中 title原本的網站標題拿掉,那麼 Google 會自動幫我在搜尋結果補上 "- TV 線上看電視"這樣的品牌名稱,就會跟原本搜尋結果差異不大。 2. 跟官方反應如果 Google 判定的品牌名稱差異太大,且極度不喜歡,那麼可參考官方文件「為什麼搜尋結果標題可能與網頁的 title 標記不同」,這裡有提到:
如果您仍然認為原始標題比較好,請透過 Google 搜尋中心說明論壇告訴我們。
請到官方論壇開新的討論串跟官方反應,讓 Google 修改搜尋結果。 3. 衡量 SEO 效益以「照護線上」的案例來看,如果網站標題依然存在,只是尾巴多了「診所藥局」這樣的字串,其實我覺得反而賺到。 因為 "診所"、"藥局"都是很大的關鍵字,現在每篇文章憑空多了兩個火熱關鍵字,對於增加文章曝光度有非常大的幫助。 綜合評量品牌效益與 SEO 效益之下,也許可以考慮不用跟 Google 提出修改搜尋結果。 4. Google 索引要花很長的時間可能是疫情的關係,今年 Google 人力比較不足,處理索引的時間會比較長。「線上看電視」改了 title之後過了一兩個禮拜,搜尋結果的標題更新沒看到太明顯的進度,導致各種型態的標題結尾字串都有: google-search-result-add-brand-name-6.png-搜尋結果標題被 Google 安插額外的字串(或品牌名稱)怎麼辦?目前每種排列組合都看得到,代表索引更新很緩慢,大家就先別苛責 Google 的效率了,疫情期間互相體諒為是。

七、總結

簡短為本篇內容作個整理:
  • Google 搜尋結果,有可能為某些網站加上品牌名稱
  • 基於索引速度及網站權重,也許不會所有網站都能獲得品牌名稱
  • 所以如果 Google 沒有動作的話,還是自行為網站的 title標籤加上網站名稱較保險
  • 如果不喜歡 Google 判定的品牌名稱,可以跟官方反應
  • 如果 Google 判定的品牌名稱能獲得額外的 SEO 關鍵字效益,可以考慮保留
更多 SEO 相關文章:

CC0 免費圖庫搜尋引擎﹍2021 版

$
0
0
cc0.jpg-CC0 免費圖庫搜尋引擎﹍2021 版CC0 免費圖庫搜尋引擎」今年進行大改版,核心的搜尋功能不變,不過操作介面、及行動版操作都有很大的改善:
  • 原本受限於版面寬度,能夠進行搜尋的圖庫大致不到 10 個。新的版面對於管理而言很方便,圖庫數量可無限增加
  • 原本的設計使用顏色來區分不同功能的圖庫,但對新手而言可能一頭霧水,新版則非常直覺,很方便就能找到需要的圖庫
  • 針對提供特殊功能的圖庫進行分類,方便不同需求的使用者操作
其實新版的介面應該不需操作手冊也能很快上手,不過還是記錄一下操作說明,可以讓使用者瞭解更深入的設計功能。

一、網頁版

1. 選單基本操作先點擊上方的分類群組,例如「中文圖庫」、「英文圖庫」,再點擊左側列表的圖庫名稱即可。 cc0-1.jpg-CC0 免費圖庫搜尋引擎﹍2021 版如果左側圖庫列表太長不好捲動,可參考上圖點擊左上方紅框處的「分類標籤」收合該分類的圖庫列表;也可點「全部收合」隱藏全部的分類。 同時「分類標籤」的開合狀態會自動記憶,下次開啟網站時仍會維持之前的操作結果,對於選擇常用的圖庫而言非常方便。 右側搜尋框直接輸入字串即可搜尋對應的圖庫,「中文圖庫」可輸入中文字串,「英文圖庫」只能搜尋英文字串。 2. 切換圖庫如果搜尋結果沒有滿意的圖片,直接點擊選單上的其他圖庫,會使用相同字串繼續搜尋,使用 Ajax 動態載入技術,不需重整頁面,速度非常快。 cc0-2.png-CC0 免費圖庫搜尋引擎﹍2021 版如果搜尋框沒有輸入關鍵字,切換圖庫時會載入該圖庫的介紹資訊,方便瞭解該圖庫的功能與特色。 3. 群組分類cc0-3.jpg-CC0 免費圖庫搜尋引擎﹍2021 版上方的群組主要分成兩大類:
  • 中文圖庫、英文圖庫、需註冊:這些群組可進行站內搜尋
  • 其他群組標明「外部」代表會連往外部該圖庫網站的搜尋結果頁面
「需註冊」這個群組獨立出來的用意為,這些圖庫需要註冊後才能下載圖片,操作上稍微比較麻煩一些,讓使用者有心理準備。 4. 外部群組cc0-4.jpg-CC0 免費圖庫搜尋引擎﹍2021 版如果站內搜尋找不到滿意的圖片,那麼可以使用「外部群組」。如上圖,搜尋 "花"的圖片之後,如果想要前往「Pixabay」這個圖庫進行搜尋,直接點擊上圖外部群組中的圖庫「Pixabay」,會另開新頁並顯示該網站 "花"的搜尋結果,不必先進入 Pixabay 再進行搜尋,比較節省時間。 另外說明一下「外部特殊」的幾個分類:
  • 進階篩選:這些圖庫的進階篩選功能很強大
  • 顏色找圖:這些圖庫提供了依照不同顏色來找圖片的功能
  • 多種下載尺寸:這些圖庫讓我們下載圖片時可以選擇各種尺寸

二、行動版

基本上大部分的操作跟網頁版一樣,但由於行動版設計的介面略有不同,以下為重點說明: 1. 選擇分類群組cc0-5.jpg-CC0 免費圖庫搜尋引擎﹍2021 版手機由於寬度不夠,無法顯示所有群組,那麼上圖紅框處的群組,請用觸控方式「左右滑動」,來拖曳顯示所有群組。2. 切換選單與搜尋區塊由於手機寬度限制,只能將圖庫清單與搜尋區塊個別呈現在螢幕上,那麼要如何切換這兩個畫面呢? cc0-6.jpg-CC0 免費圖庫搜尋引擎﹍2021 版cc0-7.jpg-CC0 免費圖庫搜尋引擎﹍2021 版請分別看上面兩張圖的紅框處,設計了兩個翻頁按鈕,直接點擊就能切換到另一個畫面。 另外也設計了「手勢滑動」的操作方式,直接在手機上滑左、滑右,也能快速切換圖庫清單與播放器的畫面喔! 3. 設定手機桌面捷徑在行動裝置開啟本站頁面沒有桌面上的 APP 捷徑方便,因此請參照這篇的流程為本站設定桌面捷徑,即可在手機上快速開啟本站:「在手機設定捷徑,顯示桌面圖示
更多免費圖庫相關文章:

處理 Google OAuth 憑證流程

$
0
0
google-api-oauth.jpg-處理 Google OAuth 憑證流程上一篇「取得 Google API Key(金鑰) 流程」說明了開發者如何申請 API 金鑰,可以使用 Google 提供的各種服務,存取比較公開、不敏感的資訊。 而當需要存取比較機密、需要驗證身份的資訊時,就不能只靠 API 金鑰,需要另外走驗證身份的流程。Google 提供的驗證機制目前版本為 OAuth 2.0,詳細原理可直接參考這篇「Google OAuth 2.0」。 本篇說明如何為應用程式申請 OAuth 憑證,將來應用程式可以直接拿憑證產生 Access Token(存取權杖),就不必每次另外走驗證身份的流程了。

一、準備動作

本篇接續上一篇「取得 Google API Key(金鑰) 流程」的內容,因此請先完成上一篇的動作,再申請憑證:
  • 一、建立專案
  • 二、啟用 Google API 服務

二、建立 OAuth 憑證

接著進入「Google API 官網首頁」:
  • A. 下拉選單選擇建立的專案名稱
  • B. 進入左側選單「憑證」
  • C. 建立憑證
  • D. 選擇「OAuth 用戶端 ID」
  • 選擇「網頁應用程式」
  • 命名「名稱」
  • 「已授權的 Javascript 來源」、「已授權的重新導向 URI」填入自己的首頁即可
  • 最後按「建立」
彈出視窗可取得「用戶端 ID」以及「用戶端密鑰」,將來應用程式都可能會用到。這兩組字串將來可隨時回來查詢,不一定現在就得記錄下來。 同時上圖的提醒文字可得知,若是沒有設定「OAuth 同意畫面」,現在申請的憑證使用次數限制在 100 次,所以請繼續接下來的流程。

三、設定 OAuth 同意畫面

如上圖紅框:
  • 點擊左側選單「OAuth 同意畫面」
  • 點擊「編輯應用程式」
  • 命名「應用程式名稱」
  • 上傳「應用程式標誌」圖片
要填寫的項目很多,請繼續往下捲。
  • 填入自己的首頁網址
  • 應用程式隱私權政策連結:可參考這個範例網頁的內容「個資及隱私權保護宣告」,修改相關敘述後做一個放到自己網站。
  • 填寫「開發人員聯絡資訊」
  • 最後按「儲存並繼續」
接下來每個畫面都按「儲存並繼續」直到結束即可。
更多 Google 相關文章:

使用 Google API 處理登入登出功能,取得使用者基本資料﹍實作範例

$
0
0
上一篇「用 FB API 製作登入登出按鈕」,說明了會員系統的登入功能外包給第三方服務的優點,同時提供實作範例,本篇繼續說明 Google API 登入登出功能的製作流程,及提供自製按鈕實作範例。


(圖片出處: pixabay.com)


一、準備動作


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




二、安裝官方 Google 登入按鈕


1. 官方文件

官方提供了登入按鈕的詳細說明文件:


但可惜沒有中文版說明。


2. 安裝程式碼及範例

<script src="https://apis.google.com/js/platform.js" async="async"></script>
<meta name="google-signin-client_id" content="5432xxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com"/>
<div class="g-signin2" data-onsuccess="onSignIn"></div>

目前狀態:
<span id="GOOGLE_STATUS_1"></span>

<script>
// 登入之後
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile(),
target = document.getElementById("GOOGLE_STATUS_1"),
html = "";

html += "ID: " + profile.getId() + "<br/>";
html += "會員暱稱: " + profile.getName() + "<br/>";
html += "會員頭像:" + profile.getImageUrl() + "<br/>";
html += "會員 email:" + profile.getEmail() + "<br/>";
target.innerHTML = html;
}
</script>

  • 紅色字串請置換為前面「處理 OAuth 憑證」流程取得的「用戶端 ID」
  • 程式碼請放在前面「處理 OAuth 憑證」流程設定的網站

以下是「官方 Google 登入按鈕」的範例效果,可進行操作並注意對應狀態的文字:


目前狀態:




三、自製登入登出按鈕


如不使用官方按鈕,想要自製「登入」、「登出」的樣式,那麼就得自行處理以下:

  • 分別製作兩個按鈕
  • 分別處理兩個按鈕的點擊

參考官網文件「Building a custom Google Sign-In button」,以下用「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"></link>

<script src="https://apis.google.com/js/api:client.js"></script>

<!--登入、登出按鈕-->
<button id="GOOGLE_login" class="btn btn-large btn-primary">GOOGLE 登入</button> <button id="GOOGLE_logout" class="btn btn-large btn-warning">GOOGLE 登出</button>

目前狀態:
<span id="GOOGLE_STATUS_2"></span>
<script>
// 進行登入程序
var startApp = function() {
gapi.load("auth2", function() {
auth2 = gapi.auth2.init({
client_id: "5432xxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com", // 用戶端 ID
cookiepolicy: "single_host_origin"
});
attachSignin(document.getElementById("GOOGLE_login"));
});
};

function attachSignin(element) {
auth2.attachClickHandler(element, {},
// 登入成功
function(googleUser) {
var profile = googleUser.getBasicProfile(),
$target = $("#GOOGLE_STATUS_2"),
html = "";

html += "ID: " + profile.getId() + "<br/>";
html += "會員暱稱: " + profile.getName() + "<br/>";
html += "會員頭像:" + profile.getImageUrl() + "<br/>";
html += "會員 email:" + profile.getEmail() + "<br/>";
$target.html(html);
},
// 登入失敗
function(error) {
$("#GOOGLE_STATUS_2").html("");
alert(JSON.stringify(error, undefined, 2));
});
}

startApp();

// 點擊登入
$("#GOOGLE_login").click(function() {
// 進行登入程序
startApp();
});

// 點擊登出
$("#GOOGLE_logout").click(function() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function() {
// 登出後的動作
$("#GOOGLE_STATUS_2").html("");
});
});
</script>


主要修改地方為紅字的「用戶端 ID」,也可修改點擊按鈕後要處理的 JS,其餘注意事項參考前面範例即可。。

以下是「自製登入登出按鈕」的範例效果,可進行操作並注意對應狀態的文字:



目前狀態:


更多 Google 相關文章:

推薦線上工具:台灣景點人潮監視器 + 一整天的音樂

$
0
0
monitor.jpg-台灣景點人潮監視器 + 一整天的音樂前陣子 FB 粉絲團收到訊息,通知「贊助管道」的 PayPal 按鈕產生錯誤。當下沒有很在意,因為通常是不在台灣的客戶、讀者,需要從海外付款我才會請他們使用 PayPal。 幾年前 PayPal 退出台灣支付業務,一方面是稅務問題(會增加成本),一方面台灣金融法規進化速度慢,國外新創企業不見得願意遷就台灣這小小市場,所以 PayPal 離開之後,短期內仍應不會有支付業務的跨國企業想進入台灣。 大致跟讀者解釋了一下,留著 PayPal 是保留海外支付管道,詢問讀者的目的是否為會員儲值點數?如果在台灣的話可以用轉帳就好,同時也研究了一下布丁大寫的「綠界支付贊助管道」,加入台灣刷卡的支付管道。結果後來讀者轉了一筆不小的金額,表示是對本站的贊助,讓我有些驚喜、也有點汗顏。 由於開發「線上看電視」已經有一年多,而從去年底開始 WFU BLOG 就沒有更新文章,準備專心研發「線上看電視」的各種新構想、會員功能等等,直到今年三月底,因為「CC0 免費圖庫搜尋引擎」全面改版才有了第一篇文章。也許這筆贊助的目的是 CC0 工具,不過既然有受到贊助,主站仍應對得起讀者的期待,因此這陣子會找空檔更新 WFU BLOG,本篇也算是藉機會介紹一下本站開發的另外兩個線上工具。

一、台灣景點人潮監視器

monitor.jpg-台灣景點人潮監視器 + 一整天的音樂去年(2020)初發生了全球性肺炎疫情,風景區、各大觀光人潮密集區成了防範病毒散播的戒備重點,當時就想到製作一個這樣的線上工具,供出遊前查詢現場狀況: 雖然已經有一些同質性的網站,但操作起來不是很順手,既然技術的問題我可以解決,乾脆自己做一個工具更方便,操作介面也更順暢。 1. 優點介紹這個工具整理了 YouTube 上的景點即時影像,或各縣市官方提供、或政府單位提供的即時影像,也有少部分非官方個人提供的影像,優點如下:
  • 將所有即時影像依照台灣各縣市,整合成選單介面,方便操作。
  • 可以非常快速地切換不同即時影像,又不需重整頁面,也不會被 Youtube 廣告中斷。
  • 手機操作也可使用手勢滑動,跟 APP 操作起來應該差不多,應該不需要另外開發 APP 版本
  • 出遊前除了可參考人潮狀況,也能知道是否下雨、下雪等,提前進行因應
  • 有些景點也提供一些額外資訊,例如花季時期
2. 使用方式選單介面的 UI 從「線上看電視」移植,其實滿直覺得,沒有操作說明應該也知道如何使用。如果需要說明書的話,可參考「使用說明3. 補充說明
  • YouTube 影像端賴官方或影片提供者是否持續維護,一段時間後就可能因影片長度限制而直播中斷,當初就是因為在許多同質網站看到一堆無效影片,決定自己做一個工具
  • 本站每天會定期檢查影像連結是否有效,若發現失連會進行更新、或是從選單暫時移除,待影片提供者更新後再重新上架。
  • 不是台灣所有景點都有即時影像放上 YouTube,不過因為幾乎每個縣市都有選單,所以若要查詢景點是否下雨,也可查詢同縣市的景點便可得知大致的情形。
  • 今年 2021 進行全面改版,介面與「線上看電視」UI 一致,手機操作也更為方便,參考「在手機設定網站捷徑 顯示桌面圖示」後,就能利用行動裝置隨時查詢。

二、一整天的音樂

music.jpg-台灣景點人潮監視器 + 一整天的音樂無論是工作或是閱讀時,我很需要有音樂陪伴。拜 YouTube 之賜有大量的直播音樂頻道、或音樂 MV 播放清單、OST 音樂等等,不愁找不到喜愛的音樂來陪伴。 不過在 YouTube 網站聽音樂並不是什麼好主意,除了個別影片播放前可能會先進廣告,播放音樂清單也會不定時插入廣告,無法長時間持續線上聽音樂。 因此很早我便做了一個可線上播放 YouTube 音樂影片的網站,但介面很陽春,純粹自用。直到開發了「線上看電視」之後,有了現成的 UI 介面可套用,這才公開釋出: 1. 優點介紹這個工具整理了 YouTube 上各種類型的音樂直播頻道、播放清單,依照類別分配到各個群組之下,優點如下:
  • 可以非常快速地切換不同音樂頻道,又不需重整頁面,也不會被 Youtube 廣告中斷。
  • 各種類型的音樂都有,可依照喜好切換純組
  • 手機操作也可使用手勢滑動,跟 APP 操作起來差不多
  • 清單會隨機播放,所有影片播完後會自動重複播放,只要不按停止,任何頻道、清單的音樂都可播放一整天,這也是網站名稱的由來
2. 使用方式選單介面的 UI 從「線上看電視」移植,其實滿直覺得,沒有操作說明應該也知道如何使用。如果需要說明書的話,可參考「使用說明3. 補充說明
  • 本站每天會定期檢查影像連結是否有效,若發現失連會進行更新、或是從選單暫時移除,待影片提供者更新後再重新上架。
  • 今年 2021 進行全面改版,介面與「線上看電視」UI 一致,手機操作也更為方便,參考「在手機設定網站捷徑 顯示桌面圖示」後,就能利用行動裝置隨時播放。
更多相關線上工具:

免外掛實現各種複雜 HTML5 表單驗證功能﹍實作範例(電話+生日+郵件+密碼確認)

$
0
0
html5-validator.jpg-免外掛實現各種複雜 HTML5 表單驗證功能﹍實作範例(電話+生日+郵件+密碼確認)前幾年介紹過一個不錯的表單驗證外掛「Validator」,最近為了想增進網頁效能,研究一下能否不靠外掛就實現這些複雜功能。 由於 HTML5 已經內建基本的表單驗證功能,效果、質感都滿有水準的,那麼剩下的就是一一想辦法解決實作上容易遇到的情境。 本篇會舉例各種常見的表單項目如何進行驗證,並提供實作範例。 (圖片出處: pxhere.com)

一、強制驗證參數 required

HTML5 表單要觸發驗證的機制有這兩個:
  • 表單項目的 html 加上 required="required"參數
  • 表單 form 之中必須有送出按鈕 input type="submit"
只要符合以上兩項要點,瀏覽器會檢查表單項目(例如 input)、以及其類型(type),執行對應的驗證。 html5-validator-1.png-免外掛實現各種複雜 HTML5 表單驗證功能﹍實作範例(電話+生日+郵件+密碼確認)如上圖,在設定為輸入 Email 的表格加上 required 參數,在送出表單之時便會對輸入內容進行檢驗,發現文字不符合 email 格式時,便會出現提示文字,而這樣的效果完全是 HTML5 內建的,不需要安裝任何外掛。 瞭解以上前提後,以下針對常用的表單項目進行說明。

二、文字 text(電話)

文字輸入是最常見的型態,若要限制輸入的文字格式:
  • 可使用正規表示式,加上參數 pattern="填入正規表示式參數"
  • 可限制文字位元數,加上參數 maxlength="填入數字"
以下為輸入手機號碼的範例,利用正規表示式就能限制輸入的內容為 "09xx-xxxxxx"這樣的格式: <input type="text" required="required" maxlength="11" pattern="09\d{2}-\d{6}"/>

三、日期 date(生日)

HTML5 表單的日期(date)型態內建了日期選擇器,可不用另裝外掛就有不錯的效果。以輸入生日為例,利用一些參數限制輸入範圍,就可避免使用者惡搞,假扮成嬰兒或是古人,以下為範例: <input type="date" min="1900-01-01" max="2021-01-01" required="required"/>以上的參數限定日期只能輸入 "1900-01-01"到今年元旦為止,避免出現未來日期以及上古人類。

四、Email

HTML5 表單支援 email 型態,但內建的檢驗功能過於陽春,只能檢測是否輸入了 "@"符號,無法遏止使用者輸入惡搞、不存在的郵件信箱。 想要得到較佳的檢驗效果,最好使用正規表示式參數,以下為範例程式碼: <input type="email" required="required" pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$"/>以上的參數可限定使用者至少必須輸入 "xxx@xxx.xxx"這樣的英數字串形式。

五、密碼(包含英數字串)

輸入密碼也是常用的表單項目,但困難的是如何限定輸入字串,避免使用者設定過於簡單的密碼。以下範例程式碼使用的正規表示式,可以限定使用者設定的密碼,至少要使用 6 個位元,至少使用一個英文字元,至少使用一個數字<input type="password" pattern="^(?=.*[a-zA-Z])(?=.*[0-9]).{6,}$" required="required"/>

六、自訂錯誤訊息

HTML5 表單內建的錯誤訊息大部分情況算是夠用,但我們總是會想要客製自訂的訊息,而且有些情況如果無法提示正確的字串格式,使用者怎麼知道要如何輸入呢? 以「二、文字 text(電話)」為例,我們設定的手機號碼形式 "09xx-xxxxxx"說不定使用者根本不知道要輸入 "-",那麼錯誤訊息顯示半天使用者依然只輸入 10 個號碼,這個表單可就永遠發送不出去了! HTML5 支援的自訂錯誤訊息為呼叫 setCustomValidity 函數,詳細說明可參考這篇「html5 自訂表單驗證的警示文字 - setCustomValidity」,這篇提到幾個自訂錯誤訊息非常重要的 bug,以及解決方法,請務必詳讀。 這裡只摘錄重點與結論:
  • 自訂錯誤訊息需利用事件 oninvalid(當驗證錯誤時) 來觸發
  • 但只要使用了 oninvalid 且發生錯誤時,使用者每輸入一個字元都會持續跳出錯誤訊息,造成體驗不佳
  • 因此設定 oninvalid 事件後,需同時設定 oninput 事件,抹除錯誤訊息
用文字敘述比較難理解,建議實際操作一次就可瞭解,也可參照原文的程式碼,或之後的實作範例程式碼。

七、驗證密碼

既然前面提供了輸入密碼的例子,那麼表單下一個欄位必定是「驗證密碼」,而這件事要如何做到呢?而且很重要的一點,驗證密碼必須搭配「自訂錯誤訊息」才能讓整個功能完整,以下是範例程式碼: <input id="inputPassword" type="password" pattern="^(?=.*[a-zA-Z])(?=.*[0-9]).{6,}$" required="required" oninput="setCustomValidity('');" oninvalid="setCustomValidity('請輸入正確的密碼格式:含英數至少六個字元');"/> <input id="ConfirmPassword" type="password" required="required" oninput="setCustomValidity('');" onchange="if(document.getElementById('inputPassword').value != document.getElementById('ConfirmPassword').value){setCustomValidity('密碼不吻合');}"/>
  • 第一個輸入框輸入原始密碼,格式錯誤時會提醒輸入 "含英數至少六個字元"
  • 第二個輸入框再次輸入密碼,輸入完會檢查兩個輸入框的字串是否一致,如果不一致便會出現提醒訊息 "密碼不吻合"

八、實作範例

以下統整前面的所有範例,利用 Bootstrap 修飾版面,製作一個常見的會員註冊表單: <!--引用 Bootstrap--> <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/> <!--表單內容--> <form> <div class="form-group"> <input name="Name" type="text" class="form-control" placeholder="姓名" required="required" minlength="2"/> </div> <div class="form-group"> <input name="Email" class="form-control" type="email" placeholder="電子郵件地址" required="required" pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$"/> </div> <div class="form-group"> <input name="Birthday" class="form-control" type="date" min="1900-01-01" max="2021-01-01" required="required"/> </div> <div class="form-group"> <input name="Tel" class="form-control" type="text" placeholder="手機號碼(格式:09xx-xxxxxx)" required="required" maxlength="11" pattern="09\d{2}-\d{6}" oninput="setCustomValidity('');" oninvalid="setCustomValidity('請輸入正確的手機號瑪格式:09xx-xxxxxx');"/> </div> <div class="form-group"> <input id="inputPassword" name="Password" class="form-control" type="password" placeholder="設定密碼(格式:含英數至少六個字元)" pattern="^(?=.*[a-zA-Z])(?=.*[0-9]).{6,}$" required="required" oninput="setCustomValidity('');" oninvalid="setCustomValidity('請輸入正確的密碼格式:含英數至少六個字元');"/> </div> <div class="form-group"> <input id="ConfirmPassword" name="ConfirmPassword" class="form-control" type="password" placeholder="確認密碼" required="required" oninput="setCustomValidity('');" onchange="if(document.getElementById('inputPassword').value != document.getElementById('ConfirmPassword').value){setCustomValidity('密碼不吻合');}"/> </div> <input type="submit" value="註冊" class="btn btn-danger btn-block" /> </form>實際效果如下面的表單,讀者可自行玩看看:
更多 HTML 相關技巧:

Google Apps Script 操作試算表資料庫防駭技巧﹍防止程式碼注入攻擊

$
0
0
過去曾介紹過一系列利用「Google Apps Script 操作試算表資料庫」的文章,如果這樣的簡單資料庫只是私用,自然不必考慮安全性。然而如果資料公開的話,防駭反而是第一要務,比學習任何 Google Apps Script(簡稱 GAS) 程式技術都更重要。 本站的「會員系統」正是使用 Google 試算表做為資料庫,當初並沒有太擔心被駭,原因有二:
  • 如果真能駭入後端試算表伺服器,那 Google 會比我更擔心,這可是世界級的駭客實力
  • 這個會員系統主要功用為,讓會員有權限看到我寫的一些工具程式碼,就算被破解導致內容被看到,我也沒什麼實質上的損失,本站的程式碼本來就是個人筆記用途
所以最早並沒有太在意資料庫的防護問題,一方面真正付費儲值點數的會員沒那麼多,所以沒有花時間研究可能被入侵的手法,一方面覺得等有人動歪腦筋時再來解決就好,知名度不夠的話宵小還看不上眼。 結果幾年前還真的遇到 script kid(腳本小子)竄改了後台資料,但我後台都有留 log 記錄,聯絡上他後表示只是好玩,小朋友也不會真的對他怎麼樣。無論如何這麼做只能動到他自己帳號的部分,不影響其他會員的資料。這種程度的破壞談不上被駭,只是被輕微的 code injection(程式碼注入攻擊),以下就來看如何做到。 (圖片出處: pexels.com)

一、injection 注入攻擊

以 Google 試算表做為資料庫的情況,高端駭客手法就交給 Google 伺服器應對,我們要自行負責不被程式碼注入攻擊,無論是前端網頁或是後端 GAS。 這篇「身為 Web 工程師,你一定要知道的幾個 Web 資訊安全議題」,介紹了幾個常見的網頁攻擊手法,請參照該篇的範例,以下說明跟本文相關的部分: 1. Stored XSS XSS 是 Cross-site scripting(跨站指令碼攻擊) 的縮寫。 Stored XSS 是儲存型 XSS,利用網頁上的輸入欄位,輸入 <script>內容,儲存到後端資料庫,當前端讀取時便會執行指令碼。 破解方法:很簡單,前、後端都對 < >這些字元進行處理,指令碼就不能執行了。 2. Reflected XSS反射型 XSS,攻擊方式為將 script 藏在網址,傳遞到後端資料庫儲存。 破解方法同上,後端對單、雙引號及特殊字元進行處理即可。 3. DOM Based XSS此攻擊方式為前端,在輸入欄位填入一些 HTML 碼,例如在 <img>網址藏惡意 script 連結就能執行。 破解方法同上,前端對特殊字元進行處理即可。 4. SQL Injection SQL 注入是很常見的攻擊方式,不過 Google 試算表後端用 GAS 執行,並不會用到 SQL 語法,所以不需擔心。 但還是需要特別瞭解這種攻擊方式,因為 SQL 攻擊是鑽 SQL 語法的漏洞,那麼 GAS 使用 Javascript,那我們就需要小心有沒有 JS injection 的手法。 5. DOS DOS 是很常見的後端攻擊方式,利用短時間大量的請求轟炸伺服器。但前面提過了,Google 伺服器自然知道如何應對,所以我們不必擔心。

二、防止 injection 攻擊

1. 哪些特些字元需要預防從前面的常見攻擊手法來看,針對 Google 試算表資料庫而言,一般 JS 前後端要防止程式碼注入攻擊,只要針對以下這 4 個特殊符號進行處理,駭客就沒搞頭了: "'< >2. 其他特殊字元這篇「SQL Injection 的多種攻擊方式與防護討論」滿不錯的,提供了許多特殊字元建議站長針對資料庫安全逐一測試,不過這畢竟是 SQL 的環境。 至於 JS 環境,我想若是有某些程式碼使用了 eval(),那麼被偷偷塞入函數時是有可能被執行的,所以檢測過上面這篇提出的特殊字元,我建議也對以下這些符號進行處理,多做總比少做好: = () {} ? + |3. HTML Entity 編碼這篇「HTML Entity 簡介」說明了對特殊字元進行 HTML Entity 編碼後,可在網頁上正常顯示符號。 舉例來說,左箭頭符號 <經 HTML Entity 編碼後成為 &#60;,前端將此字串存入後端資料庫後,將來讀取並顯示在網頁上時,依然會顯示 <符號,如此就可避免被偷塞 HTML 碼且執行。 這麼做的好處是,無論使用者是有意或無心輸入了某些特殊符號,前端都能正常顯示這些特殊符號,且不執行 HTML 碼。

三、範例程式碼

瞭解本文所有概念後,對於 Google 試算表資料庫防止 code injection,我們需要做的就是將前端使用者輸入的字串內容,以及後端接收的所有字串內容,針對某些特殊字元進行編碼,轉成 HTML Entity。 這個討論串「Encode HTML entities in JavaScript」提供了 JS 範例,以下為我整理過的程式碼: var reg = /["'<>=(){}?+|]/g, // 所有要轉換的特殊字元 inputStr = "這裡是使用者輸入的字串'<script>(function(){alert(\"你被駭了\")})()<\/script>", // 原始字串 newStr = htmlEntity(inputStr); // 轉譯字串 alert(newStr); // 彈跳視窗可看到轉譯字串 document.write(newStr); // 在網頁上呈現的轉譯字串 function htmlEntity(str) { return str.replace(reg, function(i) { return "&#" + i.charCodeAt(0) + ";"; }); }轉譯後的字元可以安心存入後端資料庫,不會有任何危險。而前端顯示的轉譯字串,又能跟當初輸入時一模一樣,使用者不會察覺任何異狀。
更多 Google Apps Script 相關技巧:
Viewing all 571 articles
Browse latest View live