掘金里大佬太多了,分享各类牛批技术、代码、经验,看的美滋滋,可是在身为一只菜鸡的我也在思考,你们看了那么多文章,了解了那么多前沿技术,真正落地使用的又有多少?仍是须要实战一下才能知道,何时须要用到什么技术手段,怎么合适的运用各类技术来解决问题。所以本篇文章并非分享哪些技术多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地址