
上一篇文章 Side Project – beautyptt.cc 表特版看圖工具 提到文章按讚數破百來做一個製作說明。結果短短幾天就破了 300,可見這看圖工具在工程師圈的迴響還是不錯的,今天就來回應自己許下的承諾發個技術文吧。
還沒看過作品點這裡 beautyptt.cc 。
簡單的說,就是爬蟲不斷的定時去爬資料寫到資料庫,還有一些繁瑣的細節設定。
1. PHP 爬蟲外掛 – QueryList
身為一個懶散的自由工作者,有套件就絕對不自己造輪子。使用的第一個工具是強國開發的 QueryList 。這個套件的強大之處在於,它可以使用類似 css selector 的方式選擇要爬的頁面,而且在連線的同時可以保持登入的 cookie。
那該爬哪裡呢? PTT 表特版的網頁版本 :
https://www.ptt.cc/bbs/Beauty/index.html
由於現在表特版已經變成 18 禁版,所以只要沒有已滿 18 歲的 cookie 點擊紀錄就會無法順利的爬文,所以第一件事是必須取得一個有 18 禁的 cookie 連線。方法如下:
use QL\QueryList;
// 想要爬的頁面 URL
$url = 'https://www.ptt.cc/bbs/Beauty/index.html';
// 因為它網址產生的 redirect 是相對路徑所以把前面的 https://www.ptt.cc 拿掉
$url = str_replace("https://www.ptt.cc", "", $url);
//開始一個新的爬蟲
$_ql = QueryList::getInstance();
//是否已滿 18 歲的問答網址
$_18_plus_url = "https://www.ptt.cc/ask/over18";
//使用 post 的方法把重新導向的 URL 放進去,"yes" => "yes" 就是已滿 18 歲的回答。
$_ql->post($_18_plus_url,[
'from' => $url,
'yes' => 'yes'
],[
'timeout' => 30,
'headers' => [
'Accept' => 'application/json',
]
]);
//用這個 $_ql 可以繼續爬文章
$_html = $_ql->getHtml();
$top_bar = $_ql->html($_html)->find('.action-bar .btn-group.btn-group-paging')->html();
$htmls = $_ql->html($_html)->find('.r-ent')->htmls();
就不說太多了,剩下就是依照需求去做爬文章和寫入資料庫的功能。
2. Imgur API
API 位置 : https://apidocs.imgur.com/?version=latest#2078c7e0-c2b8-4bc8-a646-6e544b087d0f
申請方法: https://letswrite.tw/imgur-api-upload-load/
測試了一段時間,發現表特版的文章圖片幾乎都是貼在 imgur 上面,無論是動圖 (gif) 或是一般常見的 .jpg 、 .png ,它的網址常常是 https://imgur.com/ZNH7I9T ,無法從圖片位置判斷檔案格式,而且我也不想整張圖複製到我的主機,這樣太傷流量。所以我申請了一個 Imgur 的帳號,來解析圖片內容。
我先寫了一個 function 來判斷是不是 imgur 的圖片。
如果是 imgur 的圖片就回傳它的 ID,不是則回傳空白字串。
imgur 連結的開頭有這幾種可能: “http://imgur.com/”,”https://imgur.com/”,”http://i.imgur.com/”,”https://i.imgur.com/”,”//imgur.com/”,”//i.imgur.com/”
function is_imgurLink($url){
if(!$url){
return "";
}
if(beauty_functions::is_img($url)){
return "";
}
$check_strings = ["http://imgur.com/","https://imgur.com/","http://i.imgur.com/","https://i.imgur.com/","//imgur.com/","//i.imgur.com/"];
foreach ($check_strings as $key => $check_str) {
$check_str_len = strlen( $check_str );
if(substr( $url , 0, $check_str_len) == $check_str ){
return $id = substr($url, $check_str_len);
}
}
return "";
}
確定是 imgur 的圖片後就用 API 來取得更詳細的資料, function 如下:
$Client_id 帶入申請的 Imgur client id
$imgur_id 帶入網址 route 的 id
function getimgurImgInfo($Client_id ="",$imgur_id=""){
if(!$Client_id || !$imgur_id){
return "";
}
$parts = explode("/", $imgur_id);
if(count($parts) === 1){
$imgur_id = $parts[0];
$api = "https://api.imgur.com/3/image/{$imgur_id}";
}else{
$type = $parts[0];
$imgur_id = $parts[1];
switch ($type) {
case 'a':
$api = "https://api.imgur.com/3/album/{$imgur_id}";
break;
case 'gallery':
$api = "https://api.imgur.com/3/gallery/album/{$imgur_id}";
default:
# code...
break;
}
}
// echo $api;
// $gallery_pic_api = "https://api.imgur.com/3/gallery/{$imgur_id}";
// $album_pic_api = "https://api.imgur.com/3/album/{$imgur_id}";
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $api,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => array(
"Authorization: Client-ID $Client_id"
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
return "";
} else {
$output = json_decode($response,true);
// var_dump($output);
return $output;
}
}
回傳的 json 結果如下:
{
"data": {
"id": "orunSTu",
"title": null,
"description": null,
"datetime": 1495556889,
"type": "image/gif",
"animated": false,
"width": 1,
"height": 1,
"size": 42,
"views": 0,
"bandwidth": 0,
"vote": null,
"favorite": false,
"nsfw": null,
"section": null,
"account_url": null,
"account_id": 0,
"is_ad": false,
"in_most_viral": false,
"tags": [],
"ad_type": 0,
"ad_url": "",
"in_gallery": false,
"deletehash": "x70po4w7BVvSUzZ",
"name": "",
"link": "http://i.imgur.com/orunSTu.gif"
},
"success": true,
"status": 200
}
這樣就可以從 “link” ,來取得圖片的真實資訊。接來我就設定 cronjob 來定時爬文。
這邊一點要注意 imgur 的 API 使用次數限制,上傳圖片 12,50 次 / 日,讀取圖片資訊 12,500 次 / 日。其實正常使用是綽綽有餘的。
一開始為了求快,我設定每 1 分鐘爬一篇文章去追朔以前的貼文,一頁一頁網舊的庫存頁面去爬。結果很快就把單日流量用完。
接下來我開始去看表特版以前庫存的文章,發現大概 2014 年左右圖片都已經失效的差不多了,所以就設定爬蟲抓取到 2014 年的內文就不再繼續往前爬。
新的發文則是每隔 5 分鐘左右到表特版首頁檢查文章的更新和貼文數量。
接下來是一些細節
1. 爬文時設定了文章標題有 [公告]、教學、本文已被刪除 這幾個關鍵字會直接跳過。
2.每隔一段時間自動檢查文章是否被刪除,以免有些人不想上表特或各種原因刪文時在這邊發現庫存頁面。
3.推文數量是負數時,不在前台顯示。
Show 一下目前的收錄內容統計

一周的時間,文章比上次又增加了 100 篇左右,圖片多了快 1000 張。
ReiKuromiya 大大真的很神, 2014 年後的表特文章占比將近 1/3。
小結
呼~ 技術文章反覆檢查真的好吃力。
下一篇解說完這個 side project 的前端,就要來好好增加一下幹話文的數量,好好的暖身來參加朋友舉辦的活動 工程師大腸花 !
畢竟從今年二月開始,我就已經沒有再講過幹話了。
Comments are closed.