偶然的機會,看到北港武德宮的網站,相當精美,一改過去對宮廟網站的印象。
照例,要打開原始碼來學習一下,馬上發現一串不太尋常的 JS
<script type="text/javascript">
//<![CDATA[
var l=new Array();
l[0] = '>';
l[1] = 'a';
l[2] = '/';
l[3] = '<';
l[4] = '|119';
l[5] = '|116';
l[6] = '|46';
l[7] = '|103';
l[8] = '|114';
l[9] = '|111';
l[10] = '|46';
l[11] = '|101';
l[12] = '|100';
l[13] = '|117';
l[14] = '|119';
l[15] = '|64';
l[16] = '|110';
l[17] = '|105';
l[18] = '|109';
l[19] = '|100';
l[20] = '|97';
l[21] = '>';
l[22] = '"';
l[23] = '|108';
l[24] = '|105';
l[25] = '|97';
l[26] = '|109';
l[27] = '|95';
l[28] = '|101';
l[29] = '|100';
l[30] = '|117';
l[31] = '|119';
l[32] = ' class="';
l[33] = '"';
l[34] = '|119';
l[35] = '|116';
l[36] = '|46';
l[37] = '|103';
l[38] = '|114';
l[39] = '|111';
l[40] = '|46';
l[41] = '|101';
l[42] = '|100';
l[43] = '|117';
l[44] = '|119';
l[45] = '|64';
l[46] = '|110';
l[47] = '|105';
l[48] = '|109';
l[49] = '|100';
l[50] = '|97';
l[51] = ':';
l[52] = 'o';
l[53] = 't';
l[54] = 'l';
l[55] = 'i';
l[56] = 'a';
l[57] = 'm';
l[58] = '"';
l[59] = '=';
l[60] = 'f';
l[61] = 'e';
l[62] = 'r';
l[63] = 'h';
l[64] = ' ';
l[65] = 'a';
l[66] = '<';
for (var i = l.length-1; i >= 0; i=i-1) {
if (l[i].substring(0, 1) === '|') document.write("&#"+unescape(l[i].substring(1))+";");
else document.write(unescape(l[i]));
}
//]]>
</script>
稍微研究之後,發現這是用來動態產生網站上的 email。之所以這麼費事,因為有些爬蟲會去抓網站上的 email,作為垃圾郵件的發送名單,某些網站安全掃描軟體,也會把網站上有 email 列作缺點,因此這種作法是好的。
它的做法,是:
看起來是簡單易作的方法,只是看到那一長串的陣列宣告,就有點不舒服,總覺得在維護上應該會有點麻煩。
如果是我自己來做,要怎麼改呢?在最低限的修改前提下,我會希望那段陣列是有辦法自動產生,產生之後再放到網站上去,這樣如果需要修改 email,或是相同的方法要套到別的網站上去,都會很快。
但如果是有產生器來產出,於其產生陣列,不如產出字串,在複製/貼上時,會更簡單一點。
於是寫了下面的產生器(https://jsfiddle.net/brecht/fsq6e7uo/2/):
<script>
//填入網站所使用的 email
var email = "service@example.com";
//填入該 email 所使用的 HTML 片段
var template = '<a href="mailto:{{email}}" class="email_style">{{email}}</a>';
var EmailMixer = {
mix: function(email, template) {
this.template = template.replace(/{{email}}/g, this.toCharNo(email));
var arr = this.revert(this.template);
return arr.join(";");
},
toCharNo: function(str) {
var code = "";
for (var i = 0; i < str.length; i++) {
code += ("|" + str[i].charCodeAt() + ";");
}
return code;
},
revert: function(str) {
var reverseArr = [];
var encodeStr = "";
var isEnCodeStr = false;
while (str) {
theChar = str[0];
if (isEnCodeStr === true) {
if (theChar !== ";") {
encodeStr += theChar;
} else {
isEnCodeStr = false;
reverseArr.unshift(encodeStr);
encodeStr = "";
}
} else {
if (theChar === "|") {
isEnCodeStr = true;
encodeStr += theChar;
} else {
reverseArr.unshift(theChar);
}
}
str = str.substr(1);
}
return reverseArr;
}
}
document.write(EmailMixer.mix(email, template));
</script>
修改上面 email 的資訊,以及依自己的網站需要,修改 html 片段的組成,存成一個 html 檔案,就會生出一個字串了。
不想用網頁執行,也可以去掉頭尾的標記,把最後一行 document.write(EmailMixer.mix(email, template));
改成 console.log(EmailMixer.mix(email, template))
之後,存成js檔,就可以用 node xxx.js
執行,在命令列底下取得字串了。
有了字串之後,就要想辦法還原,動態產生的方法如下(https://jsfiddle.net/brecht/kq38vw03/3/):
<script type="text/javascript">
//由 email 字串產生器產出的字串,因字串符中有使用雙引號,因此產生的字串要用單引號來表示
var str = '>;a;/;<;|109;|111;|99;|46;|101;|108;|112;|109;|97;|120;|101;|64;|101;|99;|105;|118;|114;|101;|115;>;";e;l;y;t;s;_;l;i;a;m;e;";=;s;s;a;l;c; ;";|109;|111;|99;|46;|101;|108;|112;|109;|97;|120;|101;|64;|101;|99;|105;|118;|114;|101;|115;:;o;t;l;i;a;m;";=;f;e;r;h; ;a;<';
renderMail(str);
function renderMail(str) {
var arr = str.split(";");
arr.reverse();
for (var i = 0; i < arr.length; i++) {
document.write(unescape(arr[i].replace(/\|(.*)/, "&#$1;")));
}
}
</script>
只要把 str 的字串換成上一步生成的字串,然後把上面這一段 script 片段,放到要產生 email 的地方即可。
ps. 這篇文章放到gist沒多久,同事做了驗證,這種方式防得了一般爬網頁原始碼的爬蟲,但是防不了一些會將網頁 DOM 生成後再爬的,例如 Google 搜尋引擎
感覺拿來混淆 email 有點小題大作大材小用
易學易用
應該可以應用在其他地方
推三下
其實就是看到後就想「如果是我要怎麼寫」就寫下去了,實用性什麼的其實沒有考慮
其實看到後我的想法是
「如果是我,我寫不出來」
感想同樓上。哈
大概四五年前也做過類似的東西, 不過是用 php 去產生 js code
貼到 gist 獻醜一下 https://gist.github.com/weichenlin/117171171d3c7947218ef3dca6bf3f66
產生出來的 js code 大概長這樣:
<script type="text/javascript">var jeucpivrdp = [ "\xac","\xf1","\xb0","\xf8","\xe2","\xf5","\xf6","\xad","\xb2","\xfd","\xf1","\xf9","\xfc","\xe4","\xff","\xaa","\xfd","\xf1","\xf9","\xfc","\xd0","\xf5","\xe8","\xf1","\xfd","\xe0","\xfc","\xf5","\xbe","\xf3","\xff","\xfd","\xb2","\xae","\xfd","\xf1","\xf9","\xfc","\xd0","\xf5","\xe8","\xf1","\xfd","\xe0","\xfc","\xf5","\xbe","\xf3","\xff","\xfd","\xac","\xbf","\xf1","\xae" ];for(var i=0; i<jeucpivrdp.length; i++){document.write(String.fromCharCode(jeucpivrdp[i].charCodeAt(0) ^ 144));}</script>
我現在大概會用ajax去取網址,然後用click事件去呼叫吧。
就點下連結、取得mailto網址,然後window.open網址。
froce 只是要防止信箱被大量爬走的話, 綁個 click 事件就差不多了吧
要就做徹底一點,反正聯絡我這功能應該不太耗流量。哈