excel抓取网页动态数据(知晓云云函数导出任务进行管理的流程和流程(一))

优采云 发布时间: 2022-03-07 06:17

  excel抓取网页动态数据(知晓云云函数导出任务进行管理的流程和流程(一))

  在日常工作中,往往需要根据业务需要,对各种格式的数据进行处理和导出。导出后,很多人更喜欢将数据放到excel中进行处理。

  一般来说,在处理数据导出时,需要对数据进行一些操作。过去,这是通过在单独的服务器上运行脚本来完成的。

  现在有了知云,你不再需要维护服务器,直接写代码,把所有相关的东西都扔给云功能。本文将介绍通过知云功能将数据表导出为ex​​cel文件的功能,并使用webpack和mincloud将代码打包上传到知云。

  技术栈:

  一、项目建设

  项目文件结构:

   export-excel-file

├── index.js

├── package.json

├── src

│ └── index.js

├── webpack.config.js

└── yarn.lock

  项目搭建与云功能代码打包示例文档基本一致。项目构建完成后,还需要安装以下依赖(两种安装方式任选其一):

  // 使用 yarn 安装

yarn add node-xlsx mincloud

// 使用 npm 安装

npm install --save node-xlsx minclou

  修改部署脚本如下:

  // package.json

...

"scripts": {

"build": "webpack --mode production",

"predeploy": "npm run build",

"deploy": "mincloud deploy export-excel-file ../"

},

...

  最后我们将使用以下两个命令进行部署和测试:

  npm run deploy // 部署到知晓云

mincloud invoke export-excel-file // 测试已经部署到知晓云上的云函数

  二、导出数据表到excel文件

  我们需要准备两张表:

  order:订单表(新字段:名称,价格)

  export_task:导出任务记录表(新字段:file_download_link)

  

  知云的云函数调用可以是同步的,也可以是异步的。同步调用的最大超时时间为 5 秒,异步调用为 300 秒。

  假设order order表有100000条数据,由于我们知道单次从云端拉取数据的最大限制是1000条,所以需要批量获取数据,可能需要对数据进行处理稍后,这将花费超过 5 秒,因此对云函数的调用将是异步的。这时就需要export_task导出任务记录表来管理导出任务。

  export_task 表按如下方式管理导出任务:

  调用云函数时,在export_task表中创建一条记录A。此时A记录中file_download_link字段的值为空。同时获取记录A的id,将id记录为jobId,查询订单表数据,生成excel文件,上传文件。等待操作,获取文件下载链接后,根据jobId更新第一步创建的记录,将文件下载链接保存在file_download_link字段中,然后在export_task表中获取文件下载链接。

  通过上面的准备和分析,导出excel文件的操作分为以下4个步骤:

  order 订单表数据获取 使用获取的数据在云函数环境中创建excel文件 将创建的excel文件上传到知云 将文件下载链接保存到export_task表中file_download_link字段

  完整代码如下:

  const fs = require('fs')

const xlsx = require('node-xlsx')

const EXPORT_DATA_CATEGORY_ID = '5c711e3119111409cdabe6f2' // 文件上传分类 id

const TABLE_ID = {

order: 66666, // 订单表

export_task: 66667, // 导出任务记录表

}

const TMP_FILE_NAME = '/tmp/result.xlsx' // 本地临时文件路径,以 /tmp 开头,具体请查看:https://doc.minapp.com/support/technical-notes.html (云函数的临时文件存储)

const ROW_NAME = ['name', 'price'] // Excel 文件列名配置

const MAX_CONNECT_LIMIT = 5 // 最大同时请求数

const LIMIT = 1000 // 单次最大拉取数据数

let result = []

/**

* 更新导出记录中的 file_download_link 字段

* @param {*} tableID

* @param {*} recordId

* @param {*} fileLink

*/

function updateExportJobIdRecord(tableID, recordId, fileLink) {

let Schame = new BaaS.TableObject(tableID)

let schame = Schame.getWithoutData(recordId)

schame.set('file_download_link', fileLink)

return schame.update()

}

/**

* 创建数据导出任务

* 设置初始 file_download_link 为空

* 待导出任务执行完毕后将文件下载地址存储到 file_download_link 字段中

* @param {*} tableID

*/

function createExportJobIdRecord(tableID) {

let Schame = new BaaS.TableObject(tableID)

let schame = Schame.create()

return schame.set({file_download_link: ''}).save().then(res => {

return res.data.id

})

}

/**

* 获取总数据条数

* @tableId {*} tableId

*/

function getTotalCount(tableId) {

const Order = new BaaS.TableObject(tableId)

return Order.count()

.then(num => {

console.log('数据总条数:', num)

return num

})

.catch(err => {

console.log('获取数据总条数失败:', err)

throw new Error(err)

})

}

/**

* 分批拉取数据

* @param {*} tableId

* @param {*} offset

* @param {*} limit

*/

function getDataByGroup(tableId, offset = 0, limit = LIMIT) {

let Order = new BaaS.TableObject(tableId)

return Order.limit(limit).offset(offset).find()

.then(res => {

return res.data.objects

})

.catch(err => {

console.log('获取分组数据失败:', err)

throw new Error(err)

})

}

/**

* 创建 Excel 导出文件

* @param {*} sourceData 源数据

*/

function genExportFile(sourceData = []) {

const resultArr = []

const rowArr = []

// 配置列名

rowArr.push(ROW_NAME)

sourceData.forEach(v => {

rowArr.push(

ROW_NAME.map(k => v[k])

)

})

resultArr[0] = {

data: rowArr,

name: 'sheet1', // Excel 工作表名

}

const option = {'!cols': [{wch: 10}, {wch: 20}]} // 自定义列宽度

const buffer = xlsx.build(resultArr, option)

return fs.writeFile(TMP_FILE_NAME, buffer, err => {

if (err) {

console.log('创建 Excel 导出文件失败')

throw new Error(err)

}

})

}

/**

* 上传文件

*/

function uploadFile() {

let MyFile = new BaaS.File()

return MyFile.upload(TMP_FILE_NAME, {category_id: EXPORT_DATA_CATEGORY_ID})

.catch(err => {

console.log('上传文件失败')

throw new Error(err)

})

}

module.exports = async function(event, callback) {

try {

const date = new Date().getTime()

const groupInfoArr = []

const groupInfoSplitArr = []

const [jobId, totalCount] = await Promise.all([createExportJobIdRecord(TABLE_ID.export_task), getTotalCount(TABLE_ID.order)])

const groupSize = Math.ceil(totalCount / LIMIT) || 1

for (let i = 0; i < groupSize; i++) {

groupInfoArr.push({

offset: i * LIMIT,

limit: LIMIT,

})

}

console.log('groupInfoArr:', groupInfoArr)

const length = Math.ceil(groupInfoArr.length / MAX_CONNECT_LIMIT)

for (let i = 0; i < length; i++) {

groupInfoSplitArr.push(groupInfoArr.splice(0, MAX_CONNECT_LIMIT))

}

console.log('groupInfoSplitArr:', groupInfoSplitArr)

const date0 = new Date().getTime()

console.log('处理分组情况耗时:', date0 - date, 'ms')

let num = 0

// 分批获取数据

const getSplitDataList = index => {

return Promise.all(

groupInfoSplitArr[index].map(v => {

return getDataByGroup(TABLE_ID.order, v.offset, v.limit)

})

).then(res => {

++num

result.push(...Array.prototype.concat(...res))

if (num < groupInfoSplitArr.length) {

return getSplitDataList(num)

} else {

return result

}

})

}

Promise.all([getSplitDataList(num)]).then(res => {

const date1 = new Date().getTime()

console.log('结果条数:', result.length)

console.log('分组拉取数据次数:', num)

console.log('拉取数据耗时:', date1 - date0, 'ms')

genExportFile(result)

const date2 = new Date().getTime()

console.log('处理数据耗时:', date2 - date1, 'ms')

uploadFile().then(res => {

const fileLink = res.data.file_link

const date3 = new Date().getTime()

console.log('上传文件耗时:', date3 - date2, 'ms')

console.log('总耗时:', date3 - date, 'ms')

updateExportJobIdRecord(TABLE_ID.export_task, jobId, fileLink)

.then(() => {

const date4 = new Date().getTime()

console.log('保存文件下载地址耗时:', date4 - date3, 'ms')

console.log('总耗时:', date4 - date, 'ms')

callback(null, {

message: '保存文件下载地址成功',

fileLink,

})

})

.catch(err => {

callback(err)

})

}).catch(err => {

console.log('上传文件失败:', err)

throw new Error(err)

})

})

} catch (err)

  三、部署和测试

  和 npm 一样,部署前需要登录。配置请参考文档。

  可以使用以下命令将云功能部署到Know Cloud:

  npm run deploy

  执行结果如下:

  

  使用以下命令进行测试:

  mincloud invoke export-excel-file

  执行结果如下:

  

  export_task 表记录:

  

  上传到知云的excel文件如下:

  

  文件内容:

  

  四、参考文档

  了解云开发文档:

  节点 xlsx 文档:

  五、源码

  仓库地址:

  六、好处

  即日起(3月8日)起,前50名报名并通过网页端公测审核的用户,将在正式接入后获得100元的积分。

  本页面的内容是通过互联网采集和编辑的。所有信息仅供用户参考。本网站没有任何所有权。如果您认为本页内容涉嫌抄袭,请及时联系我们并提供相关证据。5个工作日内联系您。一经核实,本站将立即删除侵权内容。这篇文章的链接:

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线