일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- FirebaseAnalytics
- androidId
- uint8array
- Babel standalone
- Three js
- Raycasting
- babel
- Prism.js
- code editor
- typescript
- REST API
- Game js
- methodChannel
- KakaoMap
- Completer
- Three-fiber
- webrtc
- identifierForVender
- swagger-typescript-api
- Image Resize typescript
- Redux
- uint16array
- react
- userevent_tracker
- web track
- node
- Flutter
- RouteObserver
- jszip
- Excel
- Today
- Total
Never give up
Node - XLSX example (feat. scraping) 본문
요즘 엑셀 작업할 일들이 조금 있어서 최대한 자동화를 해보려고 이런저런 삽질(?)을 하다보니..
이런 포스트도 있으면 좋겠다 싶어서 만들어봤습니다
server.js
const express = require('express')
const app = express()
const port = process.env.PORT || 8080
const excel = require('./excel/index')
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use('/excel', excel)
app.listen(port, () => {
console.log(`Started! express server on port ${port}`)
})
기본틀은 그대로 가져가고..
lib/excel/index.js
const express = require('express')
const router = express.Router()
const makeExcel = require('./xlsx/make_excel')
const editExcel = require('./xlsx/edit_excel')
const scrap = require('../scrap/index')
router.get('/make', async (_, res) => {
let blogData = {}
blogData.list = await scrap()
blogData.callback = (fileName) => {
res.download(fileName)
}
makeExcel(blogData)
})
router.get('/edit', async (_, res) => {
try {
await editExcel((fileName) => res.download(fileName))
} catch (e) {
res.send({ e })
}
})
module.exports = router
사용할 api는 2개로 get으로 가져옵니다
/excel/make : scraping 및 엑셀로 만들기
/excel/edit : 이미지 다운로드 및 엑셀 편집(가로, 세로 픽셀 크기 값 추가)
lib/scrap/index.js
const axios = require('axios').default
const cheerio = require('cheerio')
module.exports = async () => {
let list = []
const html = await axios.get('https://devmemory.tistory.com/')
const $ = cheerio.load(html.data)
const $articleList = $('div#mArticle').children('div.list_content')
$articleList.each((_, e) => {
const img = $(e).find('a.thumbnail_post img').attr('src')
const title = $(e).find('a.link_post strong.tit_post').text()
list = [...list, { img: `https:${img}`, title }]
})
return list
}
필자는 cheerio라는 라이브러리를 이용해서 스크래핑을 해봤습니다
(링크 : https://www.npmjs.com/package/cheerio)
사용법은 처음할 때는 조금 삽질했는데 익숙해지면 나름 괜찮아보입니다
cheerio.load('html파일') 형식으로 가져와서 원하는 태그를 선택하려면
$(태그명 + #태그id 혹은 .태그class)로 원하는 태그를 선택하고
children, attr, text등 필요한 정보를 가져옵니다
저는 제 개인블로그로 한번 해봤는데 mArticle의 div.list_content로 일단 포스팅 리스트를 가져와서
이미지와 타이틀을 가져오는데 이미지 같은경우 scheme가 빠져있어서 따로 추가해줬습니다
여기서 return 값이 json으로 변환하기 좋은 형태로 만들어 놓고
lib/excel/xlsx/make_excel.js
const xlsx = require('xlsx')
module.exports = (data) => {
const workBook = xlsx.utils.book_new()
const sheetData = xlsx.utils.json_to_sheet(data.list)
const sheetName = '데이터'
// sheet에 추가
xlsx.utils.book_append_sheet(workBook, sheetData, sheetName)
const excelFileName = 'test.xls'
// excel 파일 생성
xlsx.writeFileAsync(excelFileName, workBook, null, () => {
console.log('Finished writing')
data.callback(excelFileName)
})
}
이후 가져온 데이터를 json_to_sheet로 그대로 붙여놓고
원하는 시트와 파일 명을 만들고 write를 해줍니다
이 부분까지 적용 후 콜을 해보면
만들어진 엑셀파일을 보면 잘 작동된것을 확인할 수 있고
lib/excel/xlsx/edit_excel.js
const axios = require('axios').default
const fs = require('fs')
const sizeUtil = require('image-size')
const xlsx = require('xlsx')
module.exports = async (callback) => {
const excelFile = xlsx.readFile(handler.getExcelFilePath)
const sheetName = excelFile.SheetNames[0]
const sheet = excelFile.Sheets[sheetName]
const jsonData = xlsx.utils.sheet_to_json(sheet)
let imgSizeList = []
for (let i = 0; i < jsonData.length; i++) {
const imageData = await handler.downloadImage(jsonData[i].img)
const imgSize = handler.getImageSize(imageData.filePath)
imgSizeList = [...imgSizeList, [imgSize.width, imgSize.height]]
}
xlsx.utils.sheet_add_aoa(sheet, imgSizeList, { origin: 'C2' })
try {
// xlsx.writeFile(excelFile, handler.getExcelFilePath)
xlsx.writeFileAsync(handler.getExcelFilePath, excelFile, null, () => {
console.log('Finished writing')
callback(handler.getExcelFilePath)
})
} catch (e) {
return { result: e }
}
}
const handler = {
downloadImage: async (url) => {
const urlString = url.split('/')
const filePath = `image/${urlString[urlString.length - 2]}-${urlString[urlString.length - 1]}`
// 파일이 있는지 체크 없으면 다운로드
if (fs.existsSync(filePath)) {
console.log(`[image] existing path : ${filePath}`)
return { filePath, msg: 'already exist', code: -1 }
}
const res = await axios.get(url, { responseType: 'stream' })
const writeStream = fs.createWriteStream(filePath)
res.data.pipe(writeStream)
// 다운로드 완료 blocking용 콜백
async function isFinished() {
return new Promise((res, rej) => {
writeStream.on('finish', () => {
console.log(`[image] download path : ${filePath}`)
res('finished')
})
writeStream.on('error', rej)
})
}
await isFinished()
return { filePath, msg: 'downloaded', code: 1 }
},
getExcelFilePath: 'test.xls',
getImageSize: (filePath) => {
let dimensions
try {
dimensions = sizeUtil(filePath)
} catch (e) {
console.log({ e })
dimensions = { width: 'N/A', height: 'N/A' }
}
return {
width: dimensions.width,
height: dimensions.height
}
}
}
먼저 엑셀파일을 불러오고 sheet이름으로 원하는 sheet를 가져와서
데이터를 json형태로 변환해줍니다
그 후 image들을 특정 폴더에 저장합니다
(필자의 경우 해당 폴더의 image)
그 후 image-size 라이브러리를 이용해서 저장된 이미지 크기를 추출합니다
(링크 : https://www.npmjs.com/package/image-size)
(url로도 가능한데 필자는 다운로드까지 필요한 상황이어서 일단 다운로드 했습니다)
이미지 사이즈들을 다시 엑셀에 저장할 때 sheet_add_aoa로 배열형태로 원하는 위치에 넣어줍니다
필자의 경우 C2부터 밑으로 차례대로 넣어주었습니다
그 후 저장해서 확인해보면
잘 출력된것을 확인할 수 있습니다
(전체 소스 코드 링크 : https://github.com/devmemory/node_xlsx_example)
사실 다른 페이지 스크래핑 및 데이터 추출용으로 만든건데 따로 기록 안해놓으면 까먹을거 같아서 포스팅 해봤습니다
'WEB' 카테고리의 다른 글
React - Without CRA (0) | 2022.05.23 |
---|---|
React - redux toolkit example(createStore is deprecated) (0) | 2022.05.18 |
Node - Nodemailer example (2) | 2022.04.26 |
Node - Google analytics (feat. Chart.js, Excel) (0) | 2022.04.22 |
React, Node - Image upload example (0) | 2022.02.19 |