Sunday, July 1, 2018

PHP連結資料庫以及存放表格規劃

繼續影音網站的自動化....

前面有兩篇提到網址, 網頁都要整理出 pattern , 既然要整理, 就需要有存放資料的地方, 以便撈取.

而整理 pattern 時, 由於需要跨時間與跨不同網站或不同網頁比較, 所以使用資料庫來存放是比較合適的作法.

因為授權關係, PHP 常搭配 mariaDB (由 MySQL 發展出來), 而連線的方式通常有兩種:
1. mysqli : 是由 mysql 改良而來, 傳統 mysql 可能會遇到 SQL Injection 等攻擊, mysqli 提供了"預存結構法", 確保查詢指令與查詢資料是分開的, 而 SQL Injection 就會單純當資料而不容易攻擊.
2. PDO : 由於大部分關聯式資料庫都遵循 ANSI SQL , 基本的挑選(select), 更新(update)功能都是相同的, 但各資料庫服務的語法仍有些許不同, 所以 PHP 提供 PDO 方式, 在 PHP 內保持語法一致, 讓開發者假如跨不同資料庫, 語法不用重寫, 只有剛開始的連線指令不同.

假如是 XAMPP 環境, 基本上就已經裝好 Apache + PHP 擴充 + mariaDB , PHP 程式內寫入連線語法即可, 不過還沒開始用程式分析 pattern , 就先規劃資料表.

學資料庫一定會學到正規化, 不過學正規化跟實作上還是有差別, 主要是實作時, 有些業務上雖然不同, 但規格上是相同的資料, 其實可以放在一起, 舉兩個例子:
1. 為民服務有專案, 群, 服務三層項目, 剛學資料庫可能就會拆三個表, 不過實際上可以用同一張表, 多加一個欄位: 屬於第幾層, 然後再用 view 或程式內篩選.
2. 影音網站是收集 URL , 可是 URL 有包括: a. 由使用者輸入的第一批, b. 依使用者輸入, 實際連線時, 網站會回傳的. c. 根據網站回傳的 URL , 分析 pattern 後, 產生"純"的 URL .

網頁內容因為要分析 pattern 中哪些 div 相同哪些不同, 所以要在不同時間抓取多次來比較, 基本上 div 不同的地方就很可能是廣告, 可以排除, 至於網站內容是否也要產生"純"的網頁內容來當快取, 暫時還沒有想到, 先用 timestamp 多存幾次就好.

這樣基本上就有兩個資料表: 1. URL 2. 網頁內容 ; 而 URL 目前有兩類: a. 使用者輸入 b. 連結時, 網站回傳的 URL , 所以有幾欄: 1. 這個 URL 屬於那一類 2. URL 3. URL 序號 4. URL 是否對應其他序號(假如使用者輸入與網站回傳的 URL 不同時), 5. 抓取的時間  ; 網頁內容則只有 3 欄: 1. 是對應 URL 的序號, 2. 是抓取的時間, 3. 是存放抓取的網頁內容.

這樣的結構, 在 PHP 基本上就有兩個事情:
1. 使用者輸入 URL , 記錄下來.
2. 用 cURL 去抓看看, 然後把抓到的結果存起來.
pattern 分析才開始準備....

Friday, June 29, 2018

初試 JavaScript

(暫時不知道影音網站要寫哪部份, 從最近在練習的 JavaScript 講一下心得.)

JavaScript 剛開始是由 NetScape 開發, 後來發展成 ECMA 通過的共同標準.

而一般網站結構, 會大約分為前端跟後端, 前端就包括:
HTML 負責網頁結構, 基本內容, 提交表格(form)等.
CSS 負責版型, 樣式.
JavaScript 則負責屬於前端的運算, 邏輯判斷.

雖然 JavaScript 也有在伺服器執行的方式, 不過伺服器端有更多競爭者如: PHP / ASP , 近期還有 Python 等, 所以大多應用於前端.

(稍微跳離一下)
JavaScript , PHP , MariaDB 雖然都有字串處理, 基本的迴圈, 邏輯判斷等功能, 不過使用的時機與要處理的資料來源不同, MariaDB 是處理資料庫存放的資料為主, JavaScript 是透過 http(s) 向 Web 取得資料, 然後在 Client 端運作, PHP 則介於兩者之間, 包括存取資料庫, 也包括接受 Client 端的表格(form)或 Request (如: GET/POST ).

所以除了寫程式的語法不同, 適用的資料也不同, 比如:
1. 消費者輸入訂單, 未成立訂單時, 可以在前端用 JavaScript 試算.
2. 查詢消費者累計消費金額時, 可以直接用資料庫的 select sum 算出總值.
3. 因為某些促銷, 依過去的特定消費紀錄, 計算是否符合優惠, 因為優惠不適合放前端, 可能會被直接修改, 或者需要資料庫的資料, 這時候就需要 PHP 這類程式來處理.

雖然之前用 PHP 寫一個統計系統, 因為環境單純, 都在區網速度快, 所以前端只有使用 HTML 的 form , 然後 PHP 整理後重新顯示, 使用一些 Session 變數來判斷目前顯示的內容, 所以還沒有從頭寫過 JavaScript .... XD

(跳回來)

因為寫 PHP 已經有 物件 的概念, 練習 JavaScript 並不算太大的問題, 基本的語法結構, 判斷式寫法先了解, 然後開始了解如何接收 HTML 的資料, 進行加工, 然後學一些字串判斷, 分割字串等.

目前覺得需要經驗累積的, 是 JavaScript 對變數的處理, 因為 JavaScript 支援物件, 陣列, 傳統變數等型態, 而且 JavaScript 會自動判斷目前最適合的型態, 所以做一個動作之後, 要確認回傳的型態是什麼, 要進一步處理時, 要看看 JavaScript 是否會自動轉換, 或假如輸入意外的資料時, 是否會造成意外的錯誤.

所以練習到這邊, 反而在要處理的資料上, 加了好幾道關卡, 以免意外的錯誤發生.

就這樣, 連續幾天等行政程序的空檔, 練習的心得.

Thursday, June 28, 2018

影音網站網址特性....

前一篇有提到要抓網站來分析出 pattern :
http://slimetw.blogspot.com/2018/06/pattern.html

既然要分析, 就要把資料存在合適的地方, 現在關聯式資料庫很容易取得跟安裝, 就分析的網頁結構來說, 不存整部影片的話, 硬碟記憶體空間也很大.

既然要存 資料 , 就要大約知道有哪些資料, 要如何分類, 正規化才能依這些特性進行.

回到原本的主題: 影音網站的自動抓取. 因為不是抓影片本身, 而是抓連結, 再從連結內容分析出 pattern , 再找出可能相關的連結, 排除廣告, 再給程式繼續抓.

所以最關鍵的就是網站連結, 一般會使用 URL 規格, 雖然 URL 基本上不會重複, 但是對影音網站來說, 常常會有配合廣告商, 在連結後面加上廣告商或某些分析用的變數, 例如: /?ref=2397 或 /embedded=xvideo

所以連結可能看起來好幾個, 卻都是同一個影片, 或者同一個業者用了多個 Domain Name , 其實結構都是一樣的, 或者新廠商 A 向廠商 B 致敬, 網址結構相同, 而內容卻不同.

因為這種特性, 所以網址本身必須"純化", 包括幾個步驟:
1. 去除 URL 多餘的變數.
2. 影音網站的網址結構區分:
a. 單一影片.
b. 影片組 ( playlist 或 favorite )
c. 網站整理好的如: 依女優或其他類型列表.
d. 搜尋類型.
3. 再依上面的結構, 分別拆出不同的資料:
a. 影片在該網站內的序號(必存), 影片名稱( 8 成有, 但很多名稱是亂打的), 番號( 3 成有)
b. 通常只有 playlist 名稱或 tag .
c. 這部份不同網站有不同的方式, 有些會編序號, 有些網站只用羅馬拼音, 如果是羅馬拼音就要再加工.
d. 有些網站會故意把影片切短, 然後用影片名稱去搜尋, 屬於影音網站特有的方式.

所以要分析網址, 就要先知道這些分類, 網址的 pattern 才有意義.

Wednesday, June 27, 2018

影片網站pattern初步分析

要做網站分析, 網站就要分析出結構, 找到 pattern .

而從單純到複雜的程度, 大約是:
1. 政府網站: 基本上不會有廣告, 結構也最單純.
2. 製造業的公司網站: 產品可能會變化, 但基本上不會有廣告, 結構也不太會變.
3. 遊戲網站: 雖然動畫可能很多, 廣告則大多是自家的.
4. Blog 等互動網站: 廣告很多, 大多是別人家的, 結構反而單純.
5. 影音網站: 別人家的廣告多, 自家的廣告也多, 彈跳視窗多.

既然要分析網站的 pattern , 抓取網站內容才能分析, 不過要分析之前, 就要先思考網站裡面有什麼東西.

以影音網站而言, 最重要的影片只會有一個, 就是正準備播放的影片.
但是超多廣告, 所以廣告也要分類, 如果連結是同一個網站, 那可能是自家的.
連結是不同網站則可能是別人家的.

但是這樣並不容易找到影片的本體, 所以還是要試著分開, 例如:

標題: 影片 A ; 影片 ; 影片簡介或分組 g1 ; 廣告(自家) * n1 ; 廣告(他家) * n2
標題: 影片 B ; 影片 ; 影片簡介或分組 g2 ; 廣告(自家) * n3 ; 廣告(他家) * n4

因為廣告可能有數量上的不同, 但仍有一些共通的特性, 就是標題通常是唯一且跟影片最接近的, 所以拆 pattern 時, 就可以先從標題下手, 找出標題大約被包在哪個 div 內.

而影音網站的廣告(自家), 往往就是其他的影片連結, 所以這邊抓出 pattern 後, 就有機會透過程式自動去把所有的連結找出來.

Tuesday, June 26, 2018

PHP + cURL 爬蟲基本語法

寫程式常常會聽到: 不要重複發明輪子! 或 站在巨人的肩膀上.
意思是已經有很多工具可以用的時候, 學習使用工具就好. (當然授權也要注意.)

如果要做一個網站分析的程式, 首要工作就是把網站的內容抓下來.
抓網站內容就有一個好工具: cURL , 基本上沒有授權問題, 而且 PHP 內已經有相關語法可以直接使用(註1).

在 PHP 內使用 cURL 的步驟, 大約是:
1. curl_init() 初始化一個連線.
2. curl_setopt() 指定相關參數.
3. curl_exec() 連線抓取網站資料.
4. curl_close() 關閉連線.

這樣好像不知道要抓什麼? 其實全部都在 curl_setopt() 內設定, 以常用的功能來看:

$ch = curl_init() ;
$url = 'https://slimetw.blogspot.com/' ;
curl_setopt( $ch , CURLOPT_URL , $url ) ;
curl_setopt( $ch , CURLOPT_HEADER , true ) ;
curl_setopt( $ch , CURLOPT_RETURNTRANSFER , true ) ;
curl_setopt( $ch , CURLOPT_FOLLOWLOCATION , true ) ;
$temp = curl_exec( $ch ) ;
curl_close( $ch ) ;
echo strlen( $temp ) ;

指定一個網址, 然後抓取時連同 header 都抓(註2), 用 CURLOPT_FOLLOWLOCATION 設定允許轉址, 然後用 CURLOPT_RETURNTRANSFER 設定回傳的資料不是直接顯示, 而是可以轉給其他地方用.

執行這個 PHP 之後, 就會把網站本身的內容長度顯示出來, 這當然不是主要的功能, 而是連線測試, 以後只有網址跟存放的地方會換掉, 就可以寫成函式(function)多次呼叫使用.

註:
1. XAMPP 7.2.6 包含的 PHP 已經內建.
2. header 是由網站主機提供給瀏覽器用的參考資料, 一般不會呈現給使用者看, 不過假如以後要分析可能會用到, 所以先抓.

Monday, June 25, 2018

用 MariaDB 切 URL 文字

字串的處理往往是因為應用上的需要,
而在"網站爬蟲"時, URL 常常是要分解的字串,
所以對 URL 的處理就有幾個部份:

A. 判斷是否為 URL :
以一般網站而言, 是 http:// 或 https:// 開頭, 後面至少有一個 / ,
所以可以用 正規表示式 來判斷, where url regexp '^https?://.*/.*$'

B. 如果是網頁 URL , 取出協定 :
因為預設 URL 是有 :// 字串, 所以可以用 position( in ) 來找到字串內的位置, 再用 left 剪裁.
比如: position( '://' in 'https://' ) = 6 表示 :// 從第 6 位開始(註1)
但是如果用 left( 'https://' , position( '://' in 'https://' ) ) 結果是 https:
雖然也是可以理解, 不過一般來說協定只要紀錄成 https , 所以長度要減 1 ,
就用 left( 'https://' , position( '://' in 'https://' ) -1 ) , 取得協定是 http 或 https

(註2)

另外的方法也可以用 regexp 判斷開頭是 https:// 就直接寫入 https 到指定欄位等作法.

C. 為了分析不同網站的 pattern , 所以取得 hostname 紀錄也是必要的,
比如: https://hostname/A/B/C 使用 / 區隔找出 hostname , 但左邊開頭有 // , 可能造成誤判,
所以分兩個階段, 先切成 https:// , hostname/A/B/C , 再取出 hostname ,
第一階段類似前一個步驟取協定的方式,
position( '://' in 'https://hostname/A/B/C' ) = 6
要取得 hostname/A/B/C , 原字串從右側取出總長度減去左側不要的字串長度
所以語法是: right( url , length( url ) - position( '://' in 'https://hostname/A/B/C' ) - 2 )
要 -2 的原因是搜尋 :// 時, 位置在 : , 如果沒有 -2 , 剩下的字串還會有 // , 盡量精簡就不留這個.

取得 hostname/A/B/C 之後, 再判斷一次 / , 就用
left(
right( url , length( url ) - position( '://' in 'https://hostname/A/B/C' ) - 2 ),
position(
'/' in
right( url , length( url ) - position( '://' in 'https://hostname/A/B/C' ) - 2 ) - 1
)
)

就可以取得 hostname , 存起來.
註1: MariaDB 字串位置從 1 開始, 有些軟體或資料庫字串會用 0 開始.
註2: 雖然大部分網址會用小寫字母, 不過有時候複製或轉換常有首字變大寫, 特別是經過 Office 軟體複製貼上, 所以比對時最好加上 lower() 處理.

Wednesday, April 25, 2018

上週五到昨天在練習QGIS,
然後主管提到地段率, 嗯....

大概兩種作法:
1. 用地段率資料->所有地址或宗地->依地籍圖找座標
2. 用地段率資料->道路座標->分段畫出來

不過地籍圖資料跟道路資料就要找地政單位或養工單位了....


雖然想到要硬幹....

從業務端的地址資料庫, 撈出該路段分奇偶數的第一個跟最後一個
例如:
柏油路1號, 柏油路99號
柏油路2號, 柏油路100號

然後丟給Google地圖轉成座標, 再把座標拉回QGIS當基準點, 手動畫直線.
不過Google地圖回傳的座標系可能不同, 或使用建物中心點的座標,
把座標拉到QGIS後實在對不起來....先想想其他辦法.