Puppeteer配合小程序云开发开发的一款资讯小程序

2,010 阅读4分钟

1. 小程序云开发

小程序云开发的推出,给广大小程序开发者带来了极大的便利性,省去了开发者自行购买服务器费用。虽然推出很久了,一直以来没有使用,趁着空闲,决定开发一款简单的资讯小程序。开发者可以使用云开发开发微信小程序、小游戏,无需搭建服务器。小程序云开发的功能主要包括三点:

  1. 数据库,小程序云开发提供的一个json数据库。
  2. 存储,用来存储一些文件,包含着一些权限。
  3. 云函数,nodejs服务端运行的函数,可以很方便的获取到appid、openid等信息。

小程序云开发文档参考链接 小程序云开发文档

云开发界面图片

2. Puppeteer

Puppeteer是谷歌官方出品的一个通过DevTools协议控制headlessChrome的Node库,其提供了一系列的api文档来进行UI Test或者作为爬虫访问页面来收集数据,可以实现自动化控制chrome浏览器。此次实现的小程序需要的数据就是通过puppeteer进行爬取的。数据的处理流程是这样,通过Puppeteer爬取到数据,保存在本地图片、视频、以及记录的json文件,然后将这些资源导入到对应的小程序云存储、云数据库,最后小程序使用的时候直接从云存储和云数据库取资源。需要注意:

  1. Puppeteer安装时自带一个最新版本的Chromium,安装可能会失败,可以采用淘宝镜像。
  2. 小程序的json文件需要的是jsonline格式,否则导入不能成功。

puppeteer文档参考地址 puppeteer文档参考地址

3.爬取数据代码

  1. 打开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();

})();

  1. 获取详情数据
	 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;
				 })
		 }
     }
     
  1. 读取网络图片到本地

   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;

到这里,我们已经爬取了所有的图片。

  1. 将所有记录写入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('写入失败');
			 }
		 })
	 }

至此我们已经将所有数据获取到了。

  1. 将所有的文件导入到对应的云存储 云数据库。

4.实现小程序代码

小程序云开发的流程和平常不使用云开发的过程类似,无非多了数据库的查询和云函数的操作。

  1. 云函数中我们获取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,
  }
}
  1. 调用云函数的时候,我们需要制定对应的云函数名称。
    // 调用云函数
    wx.cloud.callFunction({
      name: 'login',
      data: {},
      success: res => {
        console.log('成功', res.result.openid);
        // 添加成功的逻辑
      },
      fail: err => {
        console.error('失败', err);
        // 添加失败的的逻辑
      }
    })
    
  1. 从数据库中分页查询资讯列表数据 云数据库提供了增删改查的文档,我们通过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: '查询记录失败'
          })
        }
      })

5. 一切准备就绪,然而,含有资讯视频的小程序微信过审不了,那就换一个主题,搞一个菜谱的分享。

欢饮扫码体验: