2018年5月2日 星期三

用node js與amazon S3同步資料

前言:

最近專案上有需求,需要把專案資料夾底下的某個資料夾與amazon S3的某個資料夾同步,一開始看到有人使用go寫了相關功能,小編就想說javascript都快一統天下了,沒理由辦不到啊!!所以去載了s3的node js sdk來看,發現密密麻麻的......
於是開始尋求比較簡單的做法,無意中發現了s3-sync-aws這個套件最接近我想要的功能,但唯一缺點是無法指定我要cpoy到哪個資料夾下面,所以小編fork了這個專案,再稍微改編一下成了jac-s3-sync-aws,多了一個dest參數可以塞,已達到小編要的效果。

效果:

以下這範例程式碼,可以把專案中./resource/資料夾下的所有資料直接同步到aws bucket/resource/上面(這裡指的同步,其實是增量提交,如果有刪除的檔案,s3上面還會依舊保存,所以如果有大量異動檔案,請記得定時清理s3)

另外如果aws會自動備份,分裂,等動作,同步就會被判斷為不同檔案,所以會全部掃描一次(但只會掃描,不會真的上傳),小編實際上測試用程式同步會比cyberduck速度快上將近一倍,當然實際情況因人因機器而異啦,那廢話不多說,我們就來看程式吧。

var path = require('path')
, level = require('level')
, s3sync = require('jac-s3-sync-aws')
, readdirp = require('readdirp')
require('dotenv').config()

var db = level(path.join(__dirname, '../dist', 'cache'))

var files = readdirp({
root: './resource', // 要上傳的資料夾
directoryFilter: ['!.git', '!cache'] // 忽略的資料夾
})

var bucket = 'bucket',
index = 0

var uploader = s3sync(db, {
key: process.env.AWS_ACCESS_KEY,
secret: process.env.AWS_SECRET_KEY,
bucket: bucket, // bucket
dest: 'resource', // 只掉的資料夾
concurrency: 100 // 同時上除檔案數
}).on('data', function (file) {
if (file.fresh) { // 資料比對有異動才執行
console.log(`${file.fullPath}\n->${file.url}\nindex:${index++}\n`)
}
})

files.pipe(uploader)

2018年1月22日 星期一

使用node js跟puppeteer初學爬蟲

主題:
發現好久沒更新網誌了,去年工作太忙,卻也學習到了不少東西,希望陸續能跟各位進行分享。希望啦.....

今天回去看以前的網誌,發現......媽的字好多啊!!好吧我平常內心戲,加上本來話就比較多,難怪觀看人數持續低迷==好啦我以後會少說些話。

另外這只篇是筆記文,所以不會跟大家太過申論工具的原理,我也不知道。小編是個不求甚解的攻城屍,大部分情況都是會用工具就好,該踩坑就踩坑,該google就google。所以!今天就是要跟各位介紹一款google官方推出的爬蟲工具puppeteer,這同時也是好用的自動化測試工具。

原因:
其實小編一開始接觸的是nightmare.js,寫法像是jQuery那樣,一直obj.goto(src).wait(300).type()....(小編忘記這種寫法叫什麼了,就是function處理完之後return this這種寫法,只要事情還沒處理完就可以一直點下去,知道這寫法叫什麼名字的人也歡迎留言告訴我)

這東西有什麼好處呢?易讀性佳,api也沒有很多,學習容易。
但小編認為唯一的缺點,也是我最終棄用的原因:他沒有promise,所以沒辦法在前端取到資料,即時傳回後端處理(有些東西畢竟還是要後端處理比較容易)

所以再次搜索下才發現了這套功能強大好用,完全可以取代nightmare的工具puppeteer,再搭配es7的async await 寫法,完全變成一套無懈可擊的工具。因為他的核心是用webkit撰寫,可以進行瀏覽器的操作,特別它又是google開發的,可以直接運行chromium,所以任何瀏覽器你所想得到的行為,無論是每天那些繁瑣又重複的抓取報表,或者是爬文,或是機器人等行為,都可以用此自動化實現,也可以搭配一些圖形辨識api做到一些.....你想做的事。

事前準備:
1.打開你的命令提示字元,鍵入:
npm install puppeteer

好了,完成了,可以開始寫程式了。
const puppeteer = require('puppeteer')
;(async () => {
  browser = await puppeteer.launch({
    headless: false // 開啟瀏覽器
  })


  page = await browser.newPage()
  await page.goto('https://www.google.com') // 打開google頁面
  await page.waitFor('#lst-ib') // 等待搜尋bar的出現
  await page.type('#lst-ib', 'test') // 在搜尋bar鍵入 'test'
  await page.keyboard.press('Enter') //鍵盤按下 Enter
  await page.waitFor('._NId') // 等待查詢列表的出現
  const contentHandle = await page.$('._NId') // 取的查詢列表DOM
  const html = await page.evaluate(content => {
    return content.getElementsByClassName('g')[0].innerHTML // 取得第一筆
  }, contentHandle)
  console.log(html) // 印出html
  await page.wait(30000) // 等待30秒,方便觀察
  await browser.close() // 關閉瀏覽器
})()

小結:
結束了!!沒錯,小編的文章一向說明文字多過於程式碼==
上述一隻簡單的程式就可以模擬網頁打開google頁面並搜尋test,且取得返回值。
puppeteer真的可以算是簡單好用的工具。唯一小缺點就是程式碼會有一堆await,畢竟每個行為都是異步,必須等待回傳值。不過相較於缺點,非常直觀地模擬著瀏覽器操作行為此強大功能應該瑕不掩瑜。
完成了小編小時候希望去破解別人無名當一名很厲害的駭客或爬蟲,用程式幫助到人減少繁瑣事務纏身的夢想。
以上就是這次的分享,我們有緣下次再見了。

備註:
如果你在linux上遇到了些麻煩,可以參考以下網址
https://segmentfault.com/a/1190000011382062

2017年1月24日 星期二

Google Search sitelink(JSON-LD)

在上一篇文章中有介紹到到microdata的使用方式,在此要更進一步介紹能讓自己內部網站曝光的sitelinks search box(如下圖)

從Google官方文件中得知,要讓google search解析出search box 的撰寫方式有兩種,第一種是今天要介紹的方法microdata,另一種是JSON-LD
JSON-LD是一套使用 JSON 格式標注數據的方案。最大的優點是 JSON 格式便於解析,缺點是數據冗余,尤其是在需要 HTML 展示大段內容的情況下。但因為出線的時間更晚,所以支援的程度更低,可以說幾乎就只有google search支援而已,但從google官網有很多效果,範例也只提供JSON-LD感覺是要推行這種方式,但這不在今天討論範圍,稍微了解一下這是什麼就好。
接著我們來看官方提供的範例
<div itemscope itemtype="http://schema.org/WebSite">
  <meta itemprop="url" content="https://www.example.com/"/>
  <form itemprop="potentialAction" itemscope itemtype="http://schema.org/SearchAction">
    <meta itemprop="target" content="https://query.example.com/search?q={search_term_string}"/>
    <input itemprop="query-input" type="text" name="search_term_string" required/>
    <input type="submit"/>
  </form>
</div>
事實上已經講得差不多了,要新增的HTML就這麼多,只要注意對應到自己的網站上就好了,剩下的就是有幾點注意事項。
  1. 在網站上安裝一個搜尋引擎(您可以使用google的自訂搜尋)
  2. “url"—與首頁的 URL 相同。
  3. target—搜尋結果的URL.
  4.  query-input—required name的值必須與 target 大刮號中的變量相同。
  5.  首頁必須設定一個canonical URL。
  6.  確定您的伺服器支援UTF-8編碼。
  7.  相關的標記只需要放在首頁即可。
  8. Google建議使用JSON-LD來標記搜尋框。

小結:

如果上述都做完了,恭喜你,就慢慢等他生效吧,Google 搜尋引擎再次抓到你的網站會判斷是否需要出現搜尋框。所以,不只需要一些時間,最終也會不會有搜索框也不是我們說了算的,不過有做有機會拉~~

JSON-LD範例
<script type="application/ld+json">
{
   "@context": "http://schema.org",
   "@type": "WebSite",
   "url": "https://website.url/",
   "potentialAction": {
     "@type": "SearchAction",
     "target": "https://searchpage.url?&Description={search_term_string}",
     "query-input": "required name=search_term_string"
   }
}
</script>

Google Search sitelink(Microdata)

Microdata是一種結構性的標示,可以用來表示網頁內容的語意。是為了讓搜尋引擎可以順利解析網頁資料的標籤。
例如原本簡單的HTML如下:

<div>
 <h1>Avatar</h1>
 <span>Director: James Cameron (born August 16, 1954)</span>
 <span>Science fiction</span>
 <a href="../movies/avatar-theatrical-trailer.html">Trailer</a>
</div>
 只是以上的HTML並不能讓搜尋引擎知道你在說什麼,因此如果我們改成以下:
<div itemscope itemtype ="http://schema.org/Movie">
  <h1 itemprop="name">Avatar</h1>
  <div itemprop="director" itemscope itemtype="http://schema.org/Person">
  Director: <span itemprop="name">James Cameron</span> (born <span itemprop="birthDate">August 16, 1954)</span>
  </div>
  <span itemprop="genre">Science fiction</span>
  <a href="../movies/avatar-theatrical-trailer.html" itemprop="trailer">Trailer</a>
</div>

以上的itemscope、itemtype、itemprop就是microdata的語法,它可以明白地指出這段內容是關於電影,然後搜尋引擎就可以從裡面的文字"Avatar"去跟電影關聯。然後導演是James Cameron、類型是科幻電影!

Microdata 基礎

Mircodata 主要用到這幾個 attributes 來標注信息:itemscope, itemtype, itemprop,另外還有 itemid 與 itemref。
包含 itemscope 這個 attribute 的 DOM 就是一個事物(item),例如:
<div itemscope>
...
</div>
我們可以給這個事物定義一個類型,比如一本書,這時就需要用到 itemtype,它的值是一個或多個 URL:
<div itemscope itemtype="http://schema.org/Book">
...
</div>
如上的代碼片斷,我們知道它是一本書了,但是它還缺少書的信息,需要用 itemprop 來標示:
<div itemscope itemtype="http://schema.org/Book">
  <div>書名:<span itemprop="name">改革歷程</span></div>
  <div>作者:<span itemprop="author">趙紫陽</span></div>
  ...
</div>
也許因為奇怪的設計方案,也許因為 PM 的無理取鬧,也許就是因為代碼不太好寫,佈局並不能輕易合乎要求,itemprop 可能散落於其它地方,這時可以使用 itemref 將它們連接起來:
<div itemscope itemtype="http://schema.org/Book" itemref="a b">
  <div>作者:<span itemprop="author">趙紫陽</span></div>
  ...
</div>

<!-- 因為某些原因,書名與價格不在 scope 裡面 -->
<div id="a">書名:<span itemprop="name">改革歷程</span></div>
<div id="b">價格:<span itemprop="price">$12</span></div>

至於該如何去定義我所撰寫的內容屬於何種itemtype呢?這種事情當然不是自己說了算,這樣會世界大亂吧,所以Google, Bing, Yandex, Yahoo!合作創建了Schema.org用以統一Microdata。
Schema.org定義了許多類型,也仍在創造更多了類型。


比如我們來賣個車看看:schema.org/Car

<div itemscope itemtype="http://schema.org/Car">
  <h2 itemprop="name">奇瑞QQ</h2>
  <p itemprop="description">中國汽車製造商奇瑞汽車公司於2003年推出的一款微型車(...)</p>
  <img itemprop="image" href="2003_qq.png" />
  <div>
    <strong>顏色</strong>
    <span itemprop="color">黑色</span>
  </div>
  <div>
    <strong>齒輪數</strong>
    <span itemprop="numberOfForwardGears">6</span>
  </div>
  <div itemprop="vehicleEngine" itemscope itemtype="http://schema.org/EngineSpecification">
    <div>
      <strong>引擎</strong>
      <span itemprop="name">1.1 L SQR472F I4 DOHC 16v — 50 kW at 6000 rpm, 90 N·m at 3500 rpm</span>
    </div>
  </div>
  <div>
    <strong>氣囊數</strong>
    <span itemprop="airbags">4</span>
  </div>
</div>

小結:

上面看了這麼多結構化的語法,那實際上有什麼用呢?簡單說就是增加自己的曝光度啦。這也是SEO中重要的一環!
結構化的數據不僅僅能被這些 Web 搜索引擎使用,還能被諸如 Siri, Cortana 等語音設備使用,當然,他們也能被當作搜索引擎。例如我問 Siri 最近有什麼電影可看,於是找到了《土豪大戰外星人》,而你會發現這個內容來自爛番茄(一個國外有名的電影評論網站),因為爛番茄的網頁有使用 Schema.org 的 Microdata 標注,再次詢問 Siri 附近吃飯的地方,返回了大眾點評的數據,查看大眾點評的網頁,亦是使用 Schema.org 的 Microdata 標注的。
結構化的數據在未來相當有吸引力,方便未來的各種設備接入。比如「智能廚房」,當你告訴它你要做什麼菜,於是它從下廚房找到了菜譜,因為下廚房使用了結構化數據,「智能廚房」就能直接列出需要的材料與材料的份量,甚至是烹煮所需要花費的的時間。
Microdata雖然看似是為了機器所設計的一個標準,但最終亦受惠的人依然是使用者和(網站)經營者阿!

2016年12月9日 星期五

如何在forEach中下continue跟break

這只是一篇筆記文,所以也沒什麼前言,只有一點要特別注意,forEach是ES5的語法,所以用IE舊版的朋友,祝你好運。
我們就直接開始吧。

var a = [1,2,3,4,5];

a.forEach(function(x){
    if (x == 2) return; // continue
    if (x == 4) return false; // break
    console.log(x)
})

ouput:
1
3

以上

2016年12月8日 星期四

如何只靠html&css輸出A4的畫面

目標

相信大家在做專案的時候不免會遇到一些要輸出報表,或輸出什麼單什麼單的時候。

然而通常我們用的技術越簡單,對專案開發速度的提升跟交接的難易度也越低。

而這個方法,真的是小編所能找到最簡單的方法了,過程中只有 Html 跟 CSS ,甚至一行 JS 都不需要有!(範例中那行是方便大家輸出跟觀察,直接把print指令寫好了)這對目前是前端工程師的小編來說,根本是逆天的簡單(當然對各位大大來說也是一樣啦 ),而唯一要做的前置作業,就是去查出你要輸出的紙張的size,必要時再去弄一張版型的圖片,當然圖片大小也要調得一樣,這樣可以方便做細部的調整。

廢話不多說我們就直接開始吧。

Html:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
 <title>JS Bin</title>
</head>
<body>
<div class="book" id="app">
    <div class="page">
        <div class="subpage">
          page1/2
      </div>  
    </div>
    <div class="page">
        <div class="subpage">
          page2/2
      </div>  
    </div>
</div>
</body>
<sciript>widnow.print()</script>
</html>

上網查了一下,A4 size =  210mm*297mm,那我們就開始寫CSS吧

CSS:

    body {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
        background-color: #FAFAFA;
        font: 12pt "Tahoma";
    }
    * {
        box-sizing: border-box;
        -moz-box-sizing: border-box;
    }
    .page {
        width: 210mm; /*隨著紙張大小異動*/
        min-height: 297mm;/*隨著紙張大小異動*/
        padding: 20mm;
        margin: 10mm auto;
        border: 1px #D3D3D3 solid;
        border-radius: 5px;
        background: white;
        box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
    }
    .subpage {
        padding: 1cm;
        border: 5px red solid;
        height: 257mm;/*隨著紙張大小異動*/
        outline: 2cm #FFEAEA solid;
    }
 
    @page {
        size: A4;
        margin: 0;
    }
    @media print {
        html, body {
            width: 210mm;/*隨著紙張大小異動*/
            height: 297mm; /*隨著紙張大小異動*/
        }
        .page {
            margin: 0;
            border: initial;
            border-radius: initial;
            width: initial;
            min-height: initial;
            box-shadow: initial;
            background: initial;
            page-break-after: always;
        }
    }


小結

如果有需要,可以直接在page裡面加入 background-image,自行加入要列印單子的版型,這樣可以快速調整到自己想要的位置,甚至可以儲 positon 到資料庫,因應各家不同廠商的單子。光想到就內牛滿面。

小編過足了兩年都過著用 position:releative 去微調每個欄位,然後再列印出來,看到底還差多少,然後硬體不同還會跑版,一整個吐血,無形中不知道浪費了多少紙張,讓北極熊唾棄了幾次,所以後來小編終於受不了良心譴責,離開了那間公司,哈。

不過就這樣短短的程式碼,確實簡單又便利。留起來筆記,也希望可以幫助正在為報表所苦的朋友們,也歡迎大家留言或寄信給我討論

求php攻城屍在面對oracleDB時內心的陰影面積

前言

雖然小編寫php行之有年,而且最近還轉戰前端工程,但我還是沒有放棄php的~~~~(其實只是剛好專案需要)
在完全不知道要怎麼著手,大部分網路神人的教導又沒辦法完全解決問題的時候,真的很苦惱啊.......所以這邊就是我完成了連線並且成功取到資料的一篇筆記,先說小編完全不了解其運作原理,只知道怎麼做到,所以請各位鞭小力一點 = =

經過小編不怕死嘗試,就算library是12c,也一樣可以連線11g的版本

環境設定

所以直接無腦入門了,裝了appserv最新的版本,把php切換到7(appserv裡面有一支PHPVersion Switch可以做到切換,不然預設是跑php5)
(小編因為個人需求所以用appserv會簡單些,如果用習慣xampp或其他的也是可以,可以跑php就好)

切換好PHP版本後,開始修改php.ini,尋找oci,會找到
;extension = php_oci8_12c.dll
;extension = php_pdo_oci.dll
把前面的分號拿掉並儲存

instant client

只靠PHP原生的DLL是無法連線oracl的,需要而外引入dll,所以這時必須上oracle官網載instant client,因為windows的php目前只有32位元版,所以這裡也必須載32位元版,這時看你剛剛修改的php.ini檔內容可以知道你要抓哪個版本的instant client (小編這時是抓12版)。

下載完解壓縮在C:\,會有個資料夾叫 instantclient_12_1,根據網路上的說法,必須把這個資料夾設定在系統環境變數內,並重新啟動電腦。但小編試過,不知道為什麼總是出不來,模組無法讀取,出現錯誤訊息:call undefined function oci_connect()....。最後乾脆把資料夾內的東西全數複製到php\ext & apache\bin兩個資料夾中。然後apache restart就抓到oci_connect function了
(php連線測試程式碼會附在最後)


語系

可以連線後,小編又遇到一個問題,錯誤訊息:
ora-12705: cannot access nls data files or invalid environment specified 
上網看資料大概是語系問題吧,這時你必須知道oracle主機上 NLS_LANG 的設定是什麼
根據網友說法,開啟 regedit.exe 找到 HKEY/SOFTWARE/ORACLE 可以看到字串 NLS_LANG值為NA,直接刪掉他就好了
不知道是不是因為作業系統的關係,小編的路徑是HKEY/SOFTWARE/Wow6432Node/ORACLE,如果都找不到,直接ctrl+F找NLS_LANG也可以,小編也是這樣找到的。另外小編的解決方式是填入跟主機樣的的設定。
設定完之後記得重新啟動apache,然後你就會看到你畫面上出現success!
(主機是linux,只要下指令: unset NLS_LANG 似乎就可以解決,或是 set NLS_LANG=xxxxxxxx)

這時你必須取得host、port(預設1521)、SID(或SERVICE NAME、SERVER),當然還有帳號密碼。
因為要填寫connect string,詳細規格看PHP官網或oracle官網就很清楚了
程式碼如下
<?php
$db = "(DESCRIPTION=
            (ADDRESS_LIST =
                (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.xx.xx)(PORT = 1521))
            )
            (CONNECT_DATA=
                (SID=xxxx)
            )
        )";

$conn = oci_connect("user_id","password",$db);

if(!$conn){
    echo "db access error: ";
    print_r(oci_error());
    exit;
}
else
{
echo "success!";
}
?>

小結

跟網路上許多人隨隨便便就弄上3、5天甚至1、2個禮拜的人來說,小編算花很短的時間了,特別在主機又不是我可以輕易操作的情況下,小編還是活過來了,所以正在為oracle所苦的各位,一定可以撐過來。藉由這次機緣才了解到oracle真的是博大精深阿,據說還有db以外的用法,不過......有機會再來研究拉= =(逃) 還有它真的很貴= =一套破百萬來著,所以各位如果接到用oracle的案子,不要怕,盡量開價吧~~小編我就是沒有做好功課,亂開價嗚嗚~ ><"

另外提個小小的意外,我在取資料時,要輸出json,卻一直無法輸出,原來是json_encode不接受big5格式......所以必須自己寫程式去轉碼。好吧,這種小事情還是自己動手寫一支吧。

<?php
function convertEncoding($data) { 
return is_array($data)?
array_map(function($val){
return convertEncoding($val);
}, $data):
mb_convert_encoding($data, "UTF-8", "BIG-5"); 
}
?>