генерируем xlsx из rss фида
source link: https://dev.to/scumdograscalhous/ghienieriruiem-xlsx-iz-rss-fida-4fpo
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Posted on Mar 27
генерируем xlsx из rss фида
нагулял задачу генерации таблиц из rss фида, парсеры выходят на арену. план такой: из терминала кормим скрипт набором аргументов с линком к rss фиду и его настройками, добавляем прогресс бары для отзывчивости, на выход отдаем сгенерированную таблицу.
сперва набросаем пачку зависимостей:
yarn add rss-parser
yarn add --dev exceljs moment posthtml progress request request-promise yargs
Enter fullscreen mode
Exit fullscreen mode
за дело! импортирую переменные и определяю парсер:
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()
Enter fullscreen mode
Exit fullscreen mode
описываю обязательные и не очень аргументы, определяю входную функцию и на ходу рассказываю о прогрессе в терминал:
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)
Enter fullscreen mode
Exit fullscreen mode
главная функция будет принимать на вход обязательный линк на фид и необязательные количество постов, имя таблицы на выходе и набор кастомных настроек для ячеек. назначаю нужные столбцы и их ключи:
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'
}
]
}
Enter fullscreen mode
Exit fullscreen mode
приступаю к генерации новых строк и добавляю тикающий прогресс бар:
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
})
Enter fullscreen mode
Exit fullscreen mode
функция 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
}
Enter fullscreen mode
Exit fullscreen mode
строки сгенерированы, пора вернуться во входную 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()
}
Enter fullscreen mode
Exit fullscreen mode
lift off! теперь можно отдавать таблицу:
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`)
Enter fullscreen mode
Exit fullscreen mode
таблица в кармане, profit!
исходный код: https://github.com/arkatriymfalnaya/xlsx-from-rss-generator
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK