excel抓取网页动态数据(知晓云云函数导出任务进行管理的流程和流程(一))
优采云 发布时间: 2022-03-07 06:17excel抓取网页动态数据(知晓云云函数导出任务进行管理的流程和流程(一))
在日常工作中,往往需要根据业务需要,对各种格式的数据进行处理和导出。导出后,很多人更喜欢将数据放到excel中进行处理。
一般来说,在处理数据导出时,需要对数据进行一些操作。过去,这是通过在单独的服务器上运行脚本来完成的。
现在有了知云,你不再需要维护服务器,直接写代码,把所有相关的东西都扔给云功能。本文将介绍通过知云功能将数据表导出为excel文件的功能,并使用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个工作日内联系您。一经核实,本站将立即删除侵权内容。这篇文章的链接: