掘金裏大佬太多了,分享各類牛批技術、代碼、經驗,看的美滋滋,可是在身爲一隻菜雞的我也在思考,你們看了那麼多文章,瞭解了那麼多前沿技術,真正落地使用的又有多少?仍是須要實戰一下才能知道,何時須要用到什麼技術手段,怎麼合適的運用各類技術來解決問題。所以本篇文章並非分享哪些技術多666,而是跟各位老哥一塊兒根據一個具體需求,完成分析、設計編碼、上線的全流程,造成一個真正可用的產品,以此來造成一種爲了解決問題而選取相應技術的思惟(牛批要先吹好)。php
Screenlapse也算是個比較有意思的產品了,它主要的功能就是幫助用戶按期對指定網頁進行截圖,能夠設置天天、每週、每個月某一個特定時間點去作截圖,截圖隨時預覽,有了這麼個服務你就能夠給你的網站造成一個歷史檔案庫,或者默默的視奸競爭對手網站網頁的變化。html
然而!!它是個PC站沒有移動端,並且他好像被牆了,百度也不索引它 那麼機會來了,咱們本身作一個本土化的移動版的Screenlase,順便把他作到公衆號裏前端
冷靜睿智機敏的分析一下問題vue
這麼一看功能簡單的一批,不就是增刪改查麼,辣雞! 可是定眼一看,發現事情並不簡單。node
相比於小程序,公衆號的微信用戶登錄更麻煩一些,須要經過連接的跳轉來獲取到openid,然而坑並不在這!!!,由於個人公衆號是個弱雞的我的訂閱號!連獲取openid的權限都沒有!!若是是原來,基本上就全劇終了,然而因爲前端事件開通了個PayJs的我的微信支付接口,而裏面恰好有個獲取openid的接口(不知道用了什麼黑科技),所以直接用這個接口來替代官方的接口獲取openidlinux
有了openid,後端直接根據openid在用戶表裏生成一條用戶記錄便可,關於如何生成,路子有不少,各位大佬都是明白人,根據具體狀況選擇合適的方法插一條就行ios
而後驗證的話固然用爛大街的JWT方式,後臺在用戶登錄後驗證完用戶信息,返回個token給前端,前端存着token,每次調用的時候都帶着,老套路了redis
因爲個人後端使用php寫的,所以引伸出了一些問題,vuex
這種事情php雖然有辦法但仍是弱雞一些沒有nodejs使用puppeteer來的方便,既然nodejs能很方便的解決,那麼天然就選擇nodejs來爬取頁面截圖,然而這就會涉及到php的後端和nodejs爬取程序交互的問題,這裏也有不少方式能夠解決,能夠經過接口,也能夠經過其餘方式,可是考慮到爬取任務的特色,在這裏我選擇了用redis作中間層(其餘中間件作任務隊列也都OJBK),利用redis的發佈訂閱模式,php將待爬取的任務放到redis裏,nodejs的程序也做爲訂閱者,獲取到任務,爬取完圖片後經過接口將相關信息返回給php的後臺。chrome
考慮到手上是個乞丐版的服務器,若是把圖片放到服務器上,佔用空間不說,每次客戶端訪問從服務器加載圖片會佔用大量帶寬,影響網站性能,所以確定要找個圖牀啦,個人服務器是騰訊雲的給提供免費50G的對象存儲,有了這個就好說了,puppeteer爬取完圖片後把圖片先暫存到服務器上,而後把圖片上傳到圖牀,得到圖片連接,而後把服務器上的圖片刪掉,給php的後端返回的時候也只是返回一下圖片連接就行啦
系統須要一個定時任務,定時從數據庫中取出須要進行爬取的數據,而後放入redis,然而php在這方面也是比較雞肋,雖然有Swoole這種牛批的框架,可是爲了解決一個小問題,不值當引入這麼大的框架,所以選個這種方案,作個接口,而後利用BT面板配置個linux的定時任務,每隔幾分鐘就訪問一下這個接口,而後接口就去搞一些列操做就完事啦
流程仍是比較簡單的
根據上面的分析大概也知道了,核心功能區就是把用戶須要爬取的連接存下來,按期取出來爬一下,而後把爬取到的圖片連接存一下和用戶的任務關聯上便可,所以設計兩張表就OJBK,簡單的一批
字段名 | 含義 |
---|---|
id | 主鍵 |
user_id | 用戶id |
url | 任務連接 |
start_time | 開始時間 |
next_time | 下次爬取時間 |
period | 週期(以秒來存取) |
period_type | 週期類型 天、周、月 |
status | 狀態 |
create_time | 建立時間 |
update_time | 更新時間 |
more | 擴展屬性 |
字段名 | 含義 |
---|---|
id | 主鍵 |
task_id | 用戶id |
url | 任務連接 |
plan_time | 計劃爬取時間 |
create_time | 建立時間 |
update_time | 更新時間 |
數據表設計完了,如今來識別一下,看看須要哪些接口
各位看官根據狀況自行發揮啦
核心的接口就這些,足以撐起整個業務功能的閉環了
實現分三部分分別實現,移動端、後臺、nodeJS爬取程序
採起的一些工具以下:
都是普通的增刪改查,須要注意的是再添加任務的時候,須要在添加完成後當即生成一個爬取任務,爬一張圖做爲初始圖片
public function add(Request $request){
//驗證輸入
(new TaskAddValidate())->goCheck();
//根據token獲取用戶id
$userId=TokenService::getCurrentVars(TokenService::currentToken(),'id');
$startTime=$request->param('start_time');
$period=$request->param('period');
$periodType=$request->param('period_type',0);
$url=$request->param('url');
//轉換一下時間戳,php裏是10位時間戳,js裏是精確到毫秒的13位
if (strlen($startTime)>10){
$startTime=substr($startTime,0,10);
}
$nextTime=$startTime+$period;
$more=[];
if($request->has('more')){
$more=json_decode(htmlspecialchars_decode($request->param('more')),true);
}
$task=TaskModel::create([
'start_time'=>$startTime,
'next_time'=>$nextTime,
'period'=>$period,
'period_type'=>$periodType,
'url'=>$url,
'user_id'=>$userId,
'more'=>$more
]);
//直接生成一個爬取任務,發佈到redis
RedisService::pushTask('tasks',$task);
return ResultService::success('',$task);
}
複製代碼
爬取並上傳到騰訊雲的對象存儲
const puppeteer = require('puppeteer');
const redis=require('redis');
const redisConfig=require('./redis.config');
var COS = require('cos-nodejs-sdk-v5');
const axios=require('axios')
const fs=require('fs');
const config=require('./config');
//訂閱redis,接收任務
const redisClient=redis.createClient(redisConfig.port,redisConfig.host);
redisClient.auth(redisConfig.password);
redisClient.subscribe('tasks');
redisClient.on('message',(channel,msg)=>{
if(channel=='tasks'){
let task=JSON.parse(msg);
getScreenshot(task);
}
});
//爬取截圖
async function getScreenshot(task){
const browser = await puppeteer.launch({ headless: true,args: ['--no-sandbox', '--disable-setuid-sandbox'] })
const page = await browser.newPage()
page.setViewport({
width: 1280,
height: 960,
});
await page.goto(task.url, {
waitUntil: 'networkidle2' //等待頁面不動了,說明加載完畢了
})
let basePath=config.basePath;
let filenName=`${task.id}-${Date.now()}.png`;
let filePath=basePath+filenName;
await page.screenshot({ path: filePath ,fullPage:true})
//上傳圖片到雲對象存儲
let imgRes=await uploadScreenshot(filePath,filenName);
//將圖片連接回傳給php服務端
let res=await axios.post(config.apiUrl,{
id:task.id,
url:'http://'+imgRes.Location,
plan_time:task.next_time
});
//刪除圖片
fs.unlink(filePath,(err)=>{
if(err){
console.log(`刪除文件不成功,緣由${err}`);
}
});
}
// 使用永久密鑰建立實例
var cos = new COS({
SecretId: '***',
SecretKey: '***'
});
//上傳圖片到雲對象存儲
function uploadScreenshot(filePath,filenName){
return new Promise((resolve,reject)=>{
cos.sliceUploadFile({
Bucket: '***',
Region: '***',
Key: filenName,
FilePath: filePath
},(err,data)=>{
if(!err){
resolve(data);
}
else{
reject(err);
}
});
})
}
複製代碼
用的是vue+vant進行移動端開發,因爲本菜雞沒什麼審美,基本上也就是直接對vant組件進行使用拼拼湊湊就完事了,
項目目錄以下(常規的一批,弱雞)
每一個業務文件夾中的接口均作成promise形式的方便並導出
import http from './../http'
let register=(params=null)=>http.post('/api/front/user/register',params);
let login=(params=null)=>http.post('/api/front/user/login',params);
let checkLogin=()=>http.get('/api/front/user/checkLogin');
let logout=()=>http.post('/api/front/user/logout');
let getUser=()=>http.get('api/front/user/get');
export default {
register,
login,
logout,
checkLogin,
getUser
}
複製代碼
apis則是用來將不一樣業務的接口進行集中,在使用的時候直接引用apis就能夠了
import user from './user'
import portal from './portal'
import screenlapse from './screenlapse'
let exportApi={
getSetting
};
Object.assign(exportApi,user,portal,screenlapse)
export default exportApi
複製代碼
本地測試沒問題,開始上線工做,結合上面的分析,咱們須要對三個部分進行上線
系統是Centos7,使用的是BT面板一鍵搭建lnmp環境,進行平常運維與站點管理
使用bt面板直接建一個站點便可,使用面板自帶的僞靜態配置下隱藏index.php
本地打包完成後,上傳到服務器,利用bt面板新建一個站點,該站點的目錄指向移動端的文件,而後須要配置一下反向代理來解決一下跨域,基本上就是把某一個標識開頭的請求轉發一下
location ^~ /apis/
{
proxy_pass https://api.yizhisamoye.cn/;
}
location ^~ /upload/
{
proxy_pass https://api.yizhisamoye.cn;
}
複製代碼
注意兩點
爬取服務因爲只是一個簡單的小程序,並無使用node的http server相關的東西,所以無法使用pm2來啓動和管理,不過也不要緊,只要讓程序跑起來在後臺一直運行便可,使用命令來啓動便可,定位到爬蟲文件夾,使用命令nohup node spider.js便可,
安裝完相關依賴啓動後,爬蟲報找不到chrome的錯誤,這時候須要對centos系統安裝一下插件和依賴來保障puppeteer的運行
yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 ipa-gothic-fonts xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-utils xorg-x11-fonts-cyrillic xorg-x11-fonts-Type1 xorg-x11-fonts-misc -y
複製代碼
上線完後測試沒什麼問題,至此,完成了一個項目從需求到上線的所有過程,雖然沒有用什麼牛批技術,項目管理也基本沒有,可是畢竟是菜雞啊哈哈哈,主要仍是給一些我的開發者提供一個思路,以解決問題的目標去應用技術,而不是爲了應用技術而應用技術。 最後慣例仍是要放出二維碼供你們直接使用(小弟服務器辣雞,若反應慢了請你們諒解)~稍後整理好後會放出gayhub地址