genерируем xlsx из rss 파일
сперва набросаем пачку зависимостей:
yarn add rss-parser
yarn add --dev exceljs moment posthtml progress request request-promise yargs
정말! impортирую переmenнные и определяю парсер:
const ExcelJS = require('exceljs')
const Parser = require('rss-parser')
const posthtml = require('posthtml')
const rp = require('request-promise')
const moment = require('moment')
const ProgressBar = require('progress')
const yargs = require("yargs")
const parser = new Parser()
описываю обязательные и не очень аrгументы, определяю входную функцию и на ходу рассказываю о прогрессе в терминал:
const options = yargs
.usage(`Usage: -f <rss uri>`)
.option('f', {
alias: 'feed',
describe: 'RSS feed uri',
type: 'string',
demandOption: true
})
.option('a', {
alias: 'amount',
describe: 'Needed RSS feed posts amount',
type: 'string'
})
.option('n', {
alias: 'outputFileName',
describe: 'XLS output file name',
type: 'string'
})
.option('o', {
alias: 'cellOptions',
describe: 'Sheet cell additional options',
type: 'array'
})
.argv
process.stdout.write(`great options, bruh, let's start already!\n`)
entry(options.feed, options.amount, options.outputFileName, options.cellOptions)
главная функция будет принимать на вход обязательный линк на фид и необязательные количество постов, имя таблицы на выходе и набор кастомных настроек для ячеек. назначаю нужные столбцы и х ключи:
let entry = async (rssFeed, amount = 5, outputFileName = 'result', cellOptions = []) => {
process.stdout.write(`parsing your rss feed...\n`)
let feed = await parser.parseURL(rssFeed)
process.stdout.write(`creating excel workbook...\n`)
const workbook = new ExcelJS.Workbook()
const worksheet = workbook.addWorksheet(outputFileName)
worksheet.columns = [{
header: 'text',
key: 'col_text'
},
{
header: 'url',
key: 'col_url'
},
{
header: 'images',
key: 'col_images'
},
{
header: 'time',
key: 'col_time'
}
]
}
приступаю к генерации новых строк и добавляю тикающий прогресс бар:
process.stdout.write(`generating posts from rss feed...\n`)
let generatedRows = await generatePostsMetaFromFeed(feed, amount)
let generatedRowsBar = new ProgressBar('[:bar] :current/:total table rows generated\n', {
incomplete: ' ',
complete: '#',
total: generatedRows.length
})
배경
generatePostsMetaFromFeed
займется пирсингом элементов фида и генерацией набора с нужными таблице полями:let convertFeedToPosts = feed => [...feed.items.map(item => item.link)] // для пагинации по страницам фида понадобятся линки
let generatePostsMetaFromFeed = async (feed, amount) => {
let res = []
let posts = []
let feedLink = feed.link
if (amount > 10) {
process.stdout.write(`wow, so much posts? taking care of it...\n`)
let pages = Math.round(amount / 10) // пагинация для доступа к последующим страницам фида
let pagesLoadingBar = new ProgressBar('[:bar] :current/:total processed\n', {
incomplete: ' ',
complete: '#',
total: pages
})
posts.push(...convertFeedToPosts(feed))
process.stdout.write(`loading needed pages...\n`)
for (let i = 2; i <= pages; i++) {
await rp(encodeURI(`${feedLink}?feed=rss&paged=${i}`))
.then(async rssPage => {
let parsedRSSFeed = await parser.parseString(rssPage)
let isLastPage = i === pages
if (isLastPage) {
let modItems = parsedRSSFeed.items.filter((_, index) => index < amount % 10)
posts.push(...convertFeedToPosts({
items: modItems
}))
} else {
posts.push(...convertFeedToPosts(parsedRSSFeed))
}
pagesLoadingBar.tick()
})
.catch(err => {
console.error('huh, rss pagination failed', err.code)
})
}
} else {
process.stdout.write(`not a lot of posts, gonna be quick!\n`)
posts.push(...convertFeedToPosts({
items: feed.items.slice(0, amount)
}))
}
process.stdout.write(`time to generate some text for our table!\n`)
let postsHandlingBar = new ProgressBar('[:bar] :current/:total posts handled\n', {
incomplete: ' ',
complete: '#',
total: posts.length
})
for (let i = 0; i < posts.length; i++) {
let postLink = posts[i]
let title, description, image
await rp(postLink)
.then(html => {
process.stdout.write(`wuush, working on it...\n`)
posthtml().use(tree => { // парсим дерево и только нужные таблице значения нод
tree.match({
tag: 'title'
}, node => {
title = node.content[0]
})
tree.match({
attrs: {
name: 'description'
},
tag: 'meta'
}, node => {
description = node.attrs.content
})
tree.match({
attrs: {
property: 'og:image'
},
tag: 'meta'
}, node => {
image = node.attrs.content
})
}).process(html)
postsHandlingBar.tick()
})
.catch(err => {
console.error('huh, post parsing failed', err)
})
res.push({
title,
description,
image,
link: postLink
})
}
return res
}
строки сгенерированы, пора вернуться во входную
entry
функцию и прикрутить их к инстансу worksheet
, используя 방법 addRow
:process.stdout.write(`making some rows for your sheet...\n`)
for (let i = 0; i < generatedRows.length; i++) {
let {
title,
description,
image,
link
} = generatedRows[i]
let columnText = `${title}\n\n${description}\n\n${link}`
if (cellOptions.length) {
cellOptions.forEach(cOption => {
if (cOption === 'noImage') {
image = ''
}
if (cOption === 'noOGCard') {
link = ''
}
})
}
worksheet.addRow({
col_text: columnText,
col_url: link,
col_images: image,
col_time: moment().add(i, 'days').format('DD/MM/YYYY hh:mm').toString()
})
generatedRowsBar.tick()
}
이륙! теперь можно отдавать таблицу:
process.stdout.write(`creating your ${outputFileName} file...\n`)
await workbook.xlsx.writeFile(`${outputFileName}.xlsx`)
.then(() => {
process.stdout.write(`${outputFileName} created allright!\n`)
})
.catch((err) => {
process.stdout.write('huh, creating error: ', err)
})
process.stdout.write(`all done, love!\n`)
таблица в karmanе, 이익!
исходный 코드: https://github.com/arkatriymfalnaya/xlsx-from-rss-generator
Reference
이 문제에 관하여(genерируем xlsx из rss 파일), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/scumdograscalhouse/ghienieriruiem-xlsx-iz-rss-fida-4fpo텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)