1. 小程序云开发
小程序云开发的推出,给广大小程序开发者带来了极大的便利性,省去了开发者自行购买服务器费用。虽然推出很久了,一直以来没有使用,趁着空闲,决定开发一款简单的资讯小程序。开发者可以使用云开发开发微信小程序、小游戏,无需搭建服务器。小程序云开发的功能主要包括三点:
- 数据库,小程序云开发提供的一个json数据库。
- 存储,用来存储一些文件,包含着一些权限。
- 云函数,nodejs服务端运行的函数,可以很方便的获取到appid、openid等信息。
小程序云开发文档参考链接 小程序云开发文档。
2. Puppeteer
Puppeteer是谷歌官方出品的一个通过DevTools协议控制headlessChrome的Node库,其提供了一系列的api文档来进行UI Test或者作为爬虫访问页面来收集数据,可以实现自动化控制chrome浏览器。此次实现的小程序需要的数据就是通过puppeteer进行爬取的。数据的处理流程是这样,通过Puppeteer爬取到数据,保存在本地图片、视频、以及记录的json文件,然后将这些资源导入到对应的小程序云存储、云数据库,最后小程序使用的时候直接从云存储和云数据库取资源。需要注意:
- Puppeteer安装时自带一个最新版本的Chromium,安装可能会失败,可以采用淘宝镜像。
- 小程序的json文件需要的是jsonline格式,否则导入不能成功。
puppeteer文档参考地址 puppeteer文档参考地址
3.爬取数据代码
- 打开chrome无头浏览器,爬取资讯列表
const puppeteer = require('puppeteer');
const fs=require('fs');
const url = `XXXXXXXXXXXX`;
const downloadImg=require('./util/index.js');
const devices = require('puppeteer/DeviceDescriptors');
// 模拟iphoe6打开
const iPhone = devices['iPhone 6'];
;(async () => {
console.log('开始执行了')
const browser = await puppeteer.launch({
args: ['--no-sandbox'],
dumpio: false,
//设置超时时间
timeout: 15000,
//如果是访问https页面 此属性会忽略https错误
ignoreHTTPSErrors: true,
// 打开开发者工具, 当此值为true时, headless总为false
devtools: false,
// 关闭headless模式, 不会打开浏览器
headless: false
})
const page = await browser.newPage();
await page.emulate(iPhone);
await page.goto(url, {
waitUntil: 'networkidle2'
});
await page.waitFor(5000);
// 加载列表页
console.log('----------加载列表页---------')
const data=await page.evaluate(()=>{
const items=$('.rn-container');
const results=[];
items.forEach((item,I)=>{
// 有链接并且有title
if($(item).find('.rn-tp13') && $(item).find('.rn-h2').text()){
const images=[];
const originImages=[];
$(item).find('.rn-three-pic-wrap').find('img').each((index,item)=>{
images.push(`${new Date().toLocaleDateString().replace(/\//g,'-')}-${I}-${index}-${Date.now()}.png`);
originImages.push($(item).attr('src'));
});
if(images && images.length==3){
results.push({
url:$(item).find('a').attr('href'),
title:$(item).find('.rn-h2').text(),
domain:$(item).find('.rn-domainName').text(),
images:images,
originImages:originImages
})
}
}
})
return results;
});
browser.close();
})();
复制代码
- 获取详情数据
const getDetaiInfo=async function(){
for(let i=0;i<data.length;i++){
await page.goto(data[i].url, {
waitUntil: 'networkidle2'
});
await page.waitFor(2000);
// 此处接收解决在内部获取不到data的问题。
data[i].detail=await page.evaluate(()=>{
var p=$('.mainContent').find('p');
const content=[];
p.each((index,item)=>{
content.push($(item).text())
});
return content;
})
}
}
复制代码
- 读取网络图片到本地
const loadImg=async function(){
for(let i=0;i<data.length;i++){
for(let k=0;k<data[i].originImages.length;k++){
await downloadImg({
url: data[i].originImages[k],
headers: {
'Referer': url,
}
},'./images/',data[i].images[k]);
}
}
}
// 下载网络图片
const fs=require('fs');
const request=require('request');
function downloadImg(options,path,filename){
if(!fs.existsSync(path)){
fs.mkdirSync(path)
}
console.log(`是否存在文件夹`,fs.existsSync(path))
return new Promise((resolve,reject)=>{
console.log('开始读取图片---------')
request.get(options)
.on('response', (response) => {
console.log("img type:", response.headers['content-type'])
})
.pipe(fs.createWriteStream(`${path}${filename}`))
.on("error", (e) => {
console.log("pipe error", e)
resolve('');
})
.on("finish", () => {
console.log("finish");
resolve("ok");
})
.on("close", () => {
console.log("close");
})
})
}
module.exports=downloadImg;
复制代码
到这里,我们已经爬取了所有的图片。
- 将所有记录写入json文件
const writeJsonFile=async function(){
// 装换成json line形式
let content=JSON.stringify(data).replace(/,{/g,'{');
content=content.substring(1,content.length-1)
await fs.writeFileSync(`./images/${new Date().toLocaleDateString().replace(/\//g,'-')}.json`,content,err=>{
if(err){
console.log('写入失败');
}
})
}
复制代码
至此我们已经将所有数据获取到了。
- 将所有的文件导入到对应的云存储 云数据库。
4.实现小程序代码
小程序云开发的流程和平常不使用云开发的过程类似,无非多了数据库的查询和云函数的操作。
- 云函数中我们获取openid,就非常简单。
exports.main = (event, context) => {
// 获取 WX Context (微信调用上下文),包括 OPENID、APPID、及 UNIONID(需满足 UNIONID 获取条件)
const wxContext = cloud.getWXContext()
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
}
}
复制代码
- 调用云函数的时候,我们需要制定对应的云函数名称。
// 调用云函数
wx.cloud.callFunction({
name: 'login',
data: {},
success: res => {
console.log('成功', res.result.openid);
// 添加成功的逻辑
},
fail: err => {
console.error('失败', err);
// 添加失败的的逻辑
}
})
复制代码
- 从数据库中分页查询资讯列表数据 云数据库提供了增删改查的文档,我们通过wx.cloud.database(),可以拿到数据库实例,指定需要操作的表,就可以使用add,update,datelet,get等方法了。此次分页需要自行计算,借助skip方法,跳过不需要的项,从而查询目标项。
const db = wx.cloud.database();
db.collection('news').skip(this.data.pageOptions.perPage * (number-1)).limit(this.data.pageOptions.perPage)
.get({
success: res => {
const newsList = [...this.data.newsList, ...res.data];
const pageOptions = this.data.pageOptions;
pageOptions.pageNumber+=1;
this.setData({
newsList: newsList,
pageOptions: pageOptions
});
},
fail: error => {
wx.showToast({
icon: 'none',
title: '查询记录失败'
})
}
})
复制代码