This commit is contained in:
2026-04-13 11:25:40 +09:00
commit 8a24c92395
67 changed files with 276281 additions and 0 deletions

0
.codex Normal file
View File

6
.env.example Normal file
View File

@@ -0,0 +1,6 @@
BIZMAX_ID=JH1204
BIZMAX_PASSWORD=12345
BIZMAX_RANGE_START=2016-01-01
BIZMAX_BATCH_MONTHS=3
BIZMAX_BASE_URLS=https://hyun.bizmax.net,https://hyun2.bizmax.net
BIZMAX_OUTPUT_DIR=/home/pure13700/project/download/output

144
.gitignore vendored Normal file
View File

@@ -0,0 +1,144 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.*
!.env.example
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
.output
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Sveltekit cache directory
.svelte-kit/
# vitepress build output
**/.vitepress/dist
# vitepress cache directory
**/.vitepress/cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# Firebase cache directory
.firebase/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# pnpm
.pnpm-store
# yarn v3
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# Vite files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
.vite/

View File

@@ -0,0 +1,813 @@
- generic [ref=e1]:
- generic:
- link "상단영역 바로가기" [ref=e2] [cursor=pointer]:
- /url: "#topAsideButton"
- generic [ref=e3]: 상단영역 바로가기
- link "서비스 메뉴 바로가기" [ref=e4] [cursor=pointer]:
- /url: "#shortcutArea"
- generic [ref=e5]: 서비스 메뉴 바로가기
- link "새소식 블록 바로가기" [ref=e6] [cursor=pointer]:
- /url: "#newsstand"
- generic [ref=e7]: 새소식 블록 바로가기
- link "쇼핑 블록 바로가기" [ref=e8] [cursor=pointer]:
- /url: "#shopping"
- generic [ref=e9]: 쇼핑 블록 바로가기
- link "관심사 블록 바로가기" [ref=e10] [cursor=pointer]:
- /url: "#feed"
- generic [ref=e11]: 관심사 블록 바로가기
- link "MY 영역 바로가기" [ref=e12] [cursor=pointer]:
- /url: "#account"
- generic [ref=e13]: MY 영역 바로가기
- link "위젯 보드 바로가기" [ref=e14] [cursor=pointer]:
- /url: "#widgetboard"
- generic [ref=e15]: 위젯 보드 바로가기
- link "보기 설정 바로가기" [ref=e16] [cursor=pointer]:
- /url: "#viewSetting"
- generic [ref=e17]: 보기 설정 바로가기
- generic [ref=e18]:
- complementary "웨일 브라우저 설치 안내" [ref=e20]:
- generic [ref=e21]:
- generic [ref=e22]:
- text: 네이버가 만든 브라우저, 웨일을 설치해보세요
- link "웨일 설치" [ref=e23] [cursor=pointer]:
- /url: https://whale.naver.com/ko?wpid=main_theme1
- button "배너 닫기" [ref=e24] [cursor=pointer]:
- generic [ref=e25]: 배너 닫기
- banner [ref=e26]:
- generic [ref=e27]:
- generic [ref=e31]:
- heading "NAVER NAVER" [level=1] [ref=e32]:
- link "NAVER" [ref=e33] [cursor=pointer]:
- /url: /
- img "NAVER" [ref=e34]
- link "NAVER" [ref=e35] [cursor=pointer]:
- /url: /
- img [ref=e37]
- generic [ref=e39]: NAVER
- search [ref=e40]:
- group "검색" [ref=e41]:
- generic [ref=e42]: 검색
- combobox "검색어를 입력해 주세요." [active] [ref=e44]
- button "검색" [ref=e45] [cursor=pointer]:
- img [ref=e47]
- generic [ref=e49]: 검색
- generic [ref=e50]:
- button [ref=e51] [cursor=pointer]
- button "자동완성/최근검색어펼치기" [ref=e52] [cursor=pointer]:
- generic [ref=e53]: 자동완성/최근검색어펼치기
- navigation "주요 서비스" [ref=e54]:
- list [ref=e55]:
- listitem [ref=e56]:
- link "메일" [ref=e57] [cursor=pointer]:
- /url: https://mail.naver.com
- generic [ref=e59]: 메일
- listitem [ref=e60]:
- link "카페" [ref=e61] [cursor=pointer]:
- /url: https://cafe.naver.com
- generic [ref=e63]: 카페
- listitem [ref=e64]:
- link "블로그" [ref=e65] [cursor=pointer]:
- /url: https://blog.naver.com
- generic [ref=e67]: 블로그
- listitem [ref=e68]:
- link "스토어" [ref=e69] [cursor=pointer]:
- /url: https://shopping.naver.com/ns/home
- generic [ref=e71]: 스토어
- listitem [ref=e72]:
- link "뉴스" [ref=e73] [cursor=pointer]:
- /url: https://news.naver.com/
- generic [ref=e75]: 뉴스
- listitem [ref=e76]:
- link "증권" [ref=e77] [cursor=pointer]:
- /url: https://finance.naver.com/
- generic [ref=e79]: 증권
- listitem [ref=e80]:
- link "부동산" [ref=e81] [cursor=pointer]:
- /url: https://land.naver.com/
- generic [ref=e83]: 부동산
- listitem [ref=e84]:
- link "지도" [ref=e85] [cursor=pointer]:
- /url: https://map.naver.com
- generic [ref=e87]: 지도
- listitem [ref=e88]:
- link "웹툰" [ref=e89] [cursor=pointer]:
- /url: https://comic.naver.com
- generic [ref=e91]: 웹툰
- listitem [ref=e92]:
- link "치지직 치지직 추천" [ref=e93] [cursor=pointer]:
- /url: https://chzzk.naver.com/?tracking_code=home_recommend
- img "치지직" [ref=e95]
- generic [ref=e96]: 치지직
- generic [ref=e98]: 추천
- listitem [ref=e99]:
- button "바로가기 펼침" [ref=e100] [cursor=pointer]:
- generic [ref=e102]: 바로가기 펼침
- button "확장 영역" [ref=e104] [cursor=pointer]:
- generic [ref=e105]: 확장 영역
- link "페이 바로가기" [ref=e107] [cursor=pointer]:
- /url: https://pay.naver.com
- generic [ref=e108]: 페이 바로가기
- button "알림" [ref=e110] [cursor=pointer]:
- generic [ref=e111]: 알림
- link "장바구니 알림" [ref=e113] [cursor=pointer]:
- /url: https://shopping.naver.com/cart
- generic [ref=e114]: 장바구니
- generic [ref=e115]: 알림
- main [ref=e116]:
- generic [ref=e118]:
- generic [ref=e119]:
- generic [ref=e121]:
- generic:
- iframe
- region "뉴스스탠드" [ref=e122]:
- tablist [ref=e125]:
- generic [ref=e126]:
- tab "뉴스스탠드" [selected] [ref=e127] [cursor=pointer]
- tab "언론사편집" [ref=e128] [cursor=pointer]
- tab "엔터" [ref=e129] [cursor=pointer]
- tab "스포츠 LIVE" [ref=e130] [cursor=pointer]:
- text: 스포츠
- generic [ref=e131]: LIVE
- tab "경제" [ref=e132] [cursor=pointer]
- tab "쇼핑투데이" [ref=e133] [cursor=pointer]
- generic [ref=e134]:
- button "전체언론사" [ref=e135] [cursor=pointer]: 전체언론사
- generic [ref=e137]:
- link "연합뉴스" [ref=e138] [cursor=pointer]:
- /url: https://news.naver.com/main/list.naver?mode=LPOD&mid=sec&sid1=001&sid2=140&oid=001&isYeonhapFlash=Y
- link "\"이란, 휴전중 호르무즈 통행 하루 15척 이하로 제한\"" [ref=e143] [cursor=pointer]:
- /url: https://news.naver.com/main/list.naver?mode=LPOD&mid=sec&sid1=001&sid2=140&oid=001&isYeonhapFlash=Y&aid=0016013237
- generic [ref=e144]:
- link "뉴스스탠드" [ref=e145] [cursor=pointer]:
- /url: https://newsstand.naver.com/
- link "뉴스홈" [ref=e146] [cursor=pointer]:
- /url: https://news.naver.com/
- tabpanel [ref=e147]:
- generic [ref=e158]:
- link "MBC" [ref=e160] [cursor=pointer]:
- /url: "#"
- img "MBC" [ref=e161]
- link "OSEN" [ref=e163] [cursor=pointer]:
- /url: "#"
- img "OSEN" [ref=e164]
- link "국민일보" [ref=e166] [cursor=pointer]:
- /url: "#"
- img "국민일보" [ref=e167]
- link "스포츠서울" [ref=e169] [cursor=pointer]:
- /url: "#"
- img "스포츠서울" [ref=e170]
- link "중앙데일리" [ref=e172] [cursor=pointer]:
- /url: "#"
- img "중앙데일리" [ref=e173]
- link "일간스포츠" [ref=e175] [cursor=pointer]:
- /url: "#"
- img "일간스포츠" [ref=e176]
- link "파이낸셜뉴스" [ref=e178] [cursor=pointer]:
- /url: "#"
- img "파이낸셜뉴스" [ref=e179]
- link "노컷뉴스" [ref=e181] [cursor=pointer]:
- /url: "#"
- img "노컷뉴스" [ref=e182]
- link "조선비즈" [ref=e184] [cursor=pointer]:
- /url: "#"
- img "조선비즈" [ref=e185]
- link "연합뉴스TV" [ref=e187] [cursor=pointer]:
- /url: "#"
- img "연합뉴스TV" [ref=e188]
- link "한국일보" [ref=e190] [cursor=pointer]:
- /url: "#"
- img "한국일보" [ref=e191]
- link "아이뉴스24" [ref=e193] [cursor=pointer]:
- /url: "#"
- img "아이뉴스24" [ref=e194]
- link "뉴데일리" [ref=e196] [cursor=pointer]:
- /url: "#"
- img "뉴데일리" [ref=e197]
- link "지디넷코리아" [ref=e199] [cursor=pointer]:
- /url: "#"
- img "지디넷코리아" [ref=e200]
- link "코리아헤럴드" [ref=e202] [cursor=pointer]:
- /url: "#"
- img "코리아헤럴드" [ref=e203]
- link "경인일보" [ref=e205] [cursor=pointer]:
- /url: "#"
- img "경인일보" [ref=e206]
- link "중부일보" [ref=e208] [cursor=pointer]:
- /url: "#"
- img "중부일보" [ref=e209]
- link "기호일보" [ref=e211] [cursor=pointer]:
- /url: "#"
- img "기호일보" [ref=e212]
- link "시사저널이코노미" [ref=e214] [cursor=pointer]:
- /url: "#"
- img "시사저널이코노미" [ref=e215]
- link "월간중앙" [ref=e217] [cursor=pointer]:
- /url: "#"
- img "월간중앙" [ref=e218]
- link "넥스트데일리" [ref=e220] [cursor=pointer]:
- /url: "#"
- img "넥스트데일리" [ref=e221]
- link "쿠키뉴스" [ref=e223] [cursor=pointer]:
- /url: "#"
- img "쿠키뉴스" [ref=e224]
- link "에너지경제" [ref=e226] [cursor=pointer]:
- /url: "#"
- img "에너지경제" [ref=e227]
- link "뉴스토마토" [ref=e229] [cursor=pointer]:
- /url: "#"
- img "뉴스토마토" [ref=e230]
- generic [ref=e231]:
- radiogroup [ref=e232]:
- radio "리스트형" [ref=e233] [cursor=pointer]:
- generic [ref=e234]: 리스트형
- radio "썸네일형" [checked] [ref=e235] [cursor=pointer]:
- generic [ref=e236]: 썸네일형
- button "이전 페이지" [ref=e237] [cursor=pointer]:
- generic [ref=e238]: 이전 페이지
- generic [ref=e239]:
- generic [ref=e240]: 언론사 더보기
- generic [ref=e241]:
- text: "1"
- generic [ref=e242]: 페이지
- generic [ref=e243]:
- generic [ref=e244]: 전체
- text: /4
- button "다음 페이지" [ref=e245] [cursor=pointer]:
- generic [ref=e246]: 다음 페이지
- region "쇼핑" [ref=e247]:
- iframe [ref=e248]:
- tablist [ref=f2e5]:
- tab "쇼핑" [selected] [ref=f2e6] [cursor=pointer]
- tab "맨즈" [ref=f2e7] [cursor=pointer]
- tab "브랜드샵" [ref=f2e8] [cursor=pointer]
- tab "추천핫딜" [ref=f2e9] [cursor=pointer]
- tab "MY추천" [ref=f2e10] [cursor=pointer]
- tab "쇼핑라이브" [ref=f2e11] [cursor=pointer]
- region "추천 관심사" [ref=e249]:
- generic [ref=e251]:
- tablist [ref=e252]:
- tab "추천" [ref=e254] [cursor=pointer]
- tab "카테크" [ref=e255] [cursor=pointer]
- tab "웹툰" [ref=e256] [cursor=pointer]
- tab "패션뷰티" [ref=e257] [cursor=pointer]
- tab "리빙푸드" [ref=e258] [cursor=pointer]
- tab "책방" [ref=e259] [cursor=pointer]
- tab "지식" [ref=e260] [cursor=pointer]
- tab "건강" [ref=e261] [cursor=pointer]
- tab "게임" [ref=e262] [cursor=pointer]
- link "구독홈" [ref=e263] [cursor=pointer]:
- /url: https://m.naver.com/mysubs?mode=pc
- tabpanel [ref=e264]:
- list [ref=e266]:
- listitem [ref=e267]:
- link "노리 후리가케주먹밥 도시락 명란마요 참치마요 주먹밥 만드는법 뵤뵤" [ref=e268] [cursor=pointer]:
- /url: https://in.naver.com/byobyoblog/contents/internal/825269494747008
- generic [ref=e271]:
- strong [ref=e272]: 노리 후리가케주먹밥 도시락 명란마요 참치마요 주먹밥 만드는법
- generic [ref=e274]: 뵤뵤
- listitem [ref=e276]:
- link "닭가슴살볶음밥 만드는법 닭가슴살다이어트요리 다이어트식단 닭가슴살요리 popo" [ref=e277] [cursor=pointer]:
- /url: https://in.naver.com/popo/contents/internal/851094056034976
- generic [ref=e280]:
- strong [ref=e281]: 닭가슴살볶음밥 만드는법 닭가슴살다이어트요리 다이어트식단 닭가슴살요리
- generic [ref=e283]: popo
- listitem [ref=e285]:
- link "양배추당근라페 만들기 다이어트에 좋은 샐러드 슈슈냥" [ref=e286] [cursor=pointer]:
- /url: https://in.naver.com/shushu321/contents/internal/857944040379200
- generic [ref=e289]:
- strong [ref=e290]: 양배추당근라페 만들기 다이어트에 좋은 샐러드
- generic [ref=e292]: 슈슈냥
- listitem [ref=e294]
- listitem [ref=e295]:
- link "성시경 부추당면잡채 만들기 원팬잡채 레시피 냉동 대패삼겹살 잡채밥 요리 쑨여사" [ref=e296] [cursor=pointer]:
- /url: https://in.naver.com/happy4305/contents/internal/857448295249184
- generic [ref=e299]:
- strong [ref=e300]: 성시경 부추당면잡채 만들기 원팬잡채 레시피 냉동 대패삼겹살 잡채밥 요리
- generic [ref=e302]: 쑨여사
- listitem [ref=e304]:
- link "원팬 오일파스타 만들기 새우 알리오 올리오 파스타 소스 레시피 웅이맘" [ref=e305] [cursor=pointer]:
- /url: https://in.naver.com/pingu528/contents/internal/844575116092576
- generic [ref=e308]:
- strong [ref=e309]: 원팬 오일파스타 만들기 새우 알리오 올리오 파스타 소스 레시피
- generic [ref=e311]: 웅이맘
- listitem [ref=e313]:
- link "명란 오일파스타 레시피 원팬 명란파스타 만들기 투우맘" [ref=e314] [cursor=pointer]:
- /url: https://in.naver.com/wookyungmom/contents/internal/852914784916640
- generic [ref=e317]:
- strong [ref=e318]: 명란 오일파스타 레시피 원팬 명란파스타 만들기
- generic [ref=e320]: 투우맘
- listitem [ref=e322]:
- link "양배추참치덮밥 다이어트 양배추요리 참치 양배추덮밥 레시피 라임샴푸" [ref=e323] [cursor=pointer]:
- /url: https://in.naver.com/limeshampoo/contents/internal/842632039113696
- generic [ref=e326]:
- strong [ref=e327]: 양배추참치덮밥 다이어트 양배추요리 참치 양배추덮밥 레시피
- generic [ref=e329]: 라임샴푸
- listitem [ref=e331]:
- link "부추계란볶음 덮밥 만들기 간단한 부추요리 인천서현" [ref=e332] [cursor=pointer]:
- /url: https://in.naver.com/mjh0712/contents/internal/859069732262880
- generic [ref=e335]:
- strong [ref=e336]: 부추계란볶음 덮밥 만들기 간단한 부추요리
- generic [ref=e338]: 인천서현
- generic [ref=e340]:
- strong [ref=e342]: 테마별 레시피
- list [ref=e344]:
- listitem [ref=e345]:
- link "#직장인도시락 직장인 점심도시락메뉴 일주일 샐러드 다이어트식단 간단한 도시락만들기 윌리쥴리맘" [ref=e346] [cursor=pointer]:
- /url: https://in.naver.com/a0910102/contents/internal/856749320614720
- generic [ref=e349]:
- generic [ref=e350]: "#직장인도시락"
- strong [ref=e351]: 직장인 점심도시락메뉴 일주일 샐러드 다이어트식단 간단한 도시락만들기
- generic [ref=e353]: 윌리쥴리맘
- listitem [ref=e355]:
- link "#꽈리고추무침 꽈리고추찜 레시피 꽈리고추 무침 요리 가정식 반찬거리 레즐리" [ref=e356] [cursor=pointer]:
- /url: https://in.naver.com/peace8012/contents/internal/838171749762272
- generic [ref=e359]:
- generic [ref=e360]: "#꽈리고추무침"
- strong [ref=e361]: 꽈리고추찜 레시피 꽈리고추 무침 요리 가정식 반찬거리
- generic [ref=e363]: 레즐리
- listitem [ref=e365]:
- link "#아침밥 버터 간장계란밥 레시피 계란밥 아침밥 레지나" [ref=e366] [cursor=pointer]:
- /url: https://in.naver.com/lalacucina/contents/internal/846385783219360
- generic [ref=e369]:
- generic [ref=e370]: "#아침밥"
- strong [ref=e371]: 버터 간장계란밥 레시피 계란밥 아침밥
- generic [ref=e373]: 레지나
- listitem [ref=e375]:
- link "#스팸감자채볶음 감자채볶음 만들기 레시피 스팸 감자볶음 만드는 법 레즐리" [ref=e376] [cursor=pointer]:
- /url: https://in.naver.com/peace8012/contents/internal/855542825091744
- generic [ref=e379]:
- generic [ref=e380]: "#스팸감자채볶음"
- strong [ref=e381]: 감자채볶음 만들기 레시피 스팸 감자볶음 만드는 법
- generic [ref=e383]: 레즐리
- listitem [ref=e385]:
- link "#꽈리고추무침 꽈리고추찜 레시피 양념 꽈리고추 무침 요리 만드는 법 봉스" [ref=e386] [cursor=pointer]:
- /url: https://in.naver.com/bongsrecipe/contents/internal/855051303388224
- generic [ref=e389]:
- generic [ref=e390]: "#꽈리고추무침"
- strong [ref=e391]: 꽈리고추찜 레시피 양념 꽈리고추 무침 요리 만드는 법
- generic [ref=e393]: 봉스
- listitem [ref=e395]:
- link "#간단한도시락 삼각김밥으로 간단한 도시락 만들기, 간단김밥으로 추천 안녕하다" [ref=e396] [cursor=pointer]:
- /url: https://in.naver.com/annyeong/contents/internal/844365422920480
- generic [ref=e399]:
- generic [ref=e400]: "#간단한도시락"
- strong [ref=e401]: 삼각김밥으로 간단한 도시락 만들기, 간단김밥으로 추천
- generic [ref=e403]: 안녕하다
- listitem [ref=e405]:
- link "#홈베이킹 땅콩버터 요리 땅콩크림빵 만들기 초보 홈베이킹 뚜루" [ref=e406] [cursor=pointer]:
- /url: https://in.naver.com/2min_love/contents/internal/841020666123232
- generic [ref=e409]:
- generic [ref=e410]: "#홈베이킹"
- strong [ref=e411]: 땅콩버터 요리 땅콩크림빵 만들기 초보 홈베이킹
- generic [ref=e413]: 뚜루
- listitem [ref=e415]:
- link "#직장인도시락 직장인남편 점심도시락 만들기 127주차 기록남기기 하니맘" [ref=e416] [cursor=pointer]:
- /url: https://in.naver.com/johan0626/contents/internal/859653992072320
- generic [ref=e419]:
- generic [ref=e420]: "#직장인도시락"
- strong [ref=e421]: 직장인남편 점심도시락 만들기 127주차 기록남기기
- generic [ref=e423]: 하니맘
- listitem [ref=e425]:
- link "#홈베이킹 스모어크래커 만들기ㅣS'more cracker recipeㅣ홈베이킹 효니" [ref=e426] [cursor=pointer]:
- /url: https://in.naver.com/hyuni_525/contents/internal/860060428370752
- generic [ref=e429]:
- generic [ref=e430]: "#홈베이킹"
- strong [ref=e431]: 스모어크래커 만들기ㅣS'more cracker recipeㅣ홈베이킹
- generic [ref=e433]: 효니
- listitem [ref=e435]:
- link "#편스토랑레시피 간단 제육볶음 편스토랑 김강우제육볶음 레시피 앞다리살 양념 심플민" [ref=e436] [cursor=pointer]:
- /url: https://in.naver.com/simplemin_life/contents/internal/859765239596224
- generic [ref=e439]:
- generic [ref=e440]: "#편스토랑레시피"
- strong [ref=e441]: 간단 제육볶음 편스토랑 김강우제육볶음 레시피 앞다리살 양념
- generic [ref=e443]: 심플민
- listitem [ref=e445]:
- link "#아침밥 간단한 아침밥 김가루 주먹밥 레시피 만들기 찬밥 요리 숲 집밥" [ref=e446] [cursor=pointer]:
- /url: https://in.naver.com/forest0405/contents/internal/859643515732768
- generic [ref=e449]:
- generic [ref=e450]: "#아침밥"
- strong [ref=e451]: 간단한 아침밥 김가루 주먹밥 레시피 만들기 찬밥 요리
- generic [ref=e453]: 숲 집밥
- listitem [ref=e455]:
- link "#원팬토스트 간단 토스트 레시피 식빵 계란토스트 원팬으로 간단하게! 리베" [ref=e456] [cursor=pointer]:
- /url: https://in.naver.com/sbmn0313/contents/internal/856973790860256
- generic [ref=e459]:
- generic [ref=e460]: "#원팬토스트"
- strong [ref=e461]: 간단 토스트 레시피 식빵 계란토스트 원팬으로 간단하게!
- generic [ref=e463]: 리베
- listitem [ref=e465]:
- link "#스팸감자채볶음 감자채볶음 입맛 살리는 스팸감자채볶음 만드는 법! 간단한 밑반찬 만들기 미소순이" [ref=e466] [cursor=pointer]:
- /url: https://in.naver.com/misosooni/contents/internal/838175943390016
- generic [ref=e469]:
- generic [ref=e470]: "#스팸감자채볶음"
- strong [ref=e471]: 감자채볶음 입맛 살리는 스팸감자채볶음 만드는 법! 간단한 밑반찬 만들기
- generic [ref=e473]: 미소순이
- listitem [ref=e475]:
- link "#저녁밥상 여름방학 주간밥상 일주일 저녁밥상 집밥메뉴 추천 오늘뭐먹지 윤줌마" [ref=e476] [cursor=pointer]:
- /url: https://in.naver.com/koyunj0723/contents/internal/852943309222880
- generic [ref=e479]:
- generic [ref=e480]: "#저녁밥상"
- strong [ref=e481]: 여름방학 주간밥상 일주일 저녁밥상 집밥메뉴 추천 오늘뭐먹지
- generic [ref=e483]: 윤줌마
- listitem [ref=e485]:
- link "#원팬토스트 원팬 토스트 만들기 통밀 또띠아 다이어트 양배추 요리 꼬마츄츄" [ref=e486] [cursor=pointer]:
- /url: https://in.naver.com/baby0817/contents/internal/856786409456928
- generic [ref=e489]:
- generic [ref=e490]: "#원팬토스트"
- strong [ref=e491]: 원팬 토스트 만들기 통밀 또띠아 다이어트 양배추 요리
- generic [ref=e493]: 꼬마츄츄
- listitem [ref=e495]:
- link "#아침밥 전자레인지 계란밥 레시피 간장계란밥 간단한아침메뉴 뽕림이" [ref=e496] [cursor=pointer]:
- /url: https://in.naver.com/yoririm/contents/internal/856940888498976
- generic [ref=e499]:
- generic [ref=e500]: "#아침밥"
- strong [ref=e501]: 전자레인지 계란밥 레시피 간장계란밥 간단한아침메뉴
- generic [ref=e503]: 뽕림이
- listitem [ref=e505]:
- link "#간단한아침국 코인육수 계란국 끓이는법 간단한 아침국 감자계란국 레시피 맛있는 찰나" [ref=e506] [cursor=pointer]:
- /url: https://in.naver.com/ckfsk07/contents/internal/859975465555232
- generic [ref=e509]:
- generic [ref=e510]: "#간단한아침국"
- strong [ref=e511]: 코인육수 계란국 끓이는법 간단한 아침국 감자계란국 레시피
- generic [ref=e513]: 맛있는 찰나
- listitem [ref=e515]:
- link "#원팬토스트 길거리토스트 만들기 레시피 식빵 계란토스트 원팬 햄치즈 토스트 라임샴푸" [ref=e516] [cursor=pointer]:
- /url: https://in.naver.com/limeshampoo/contents/internal/843746572170400
- generic [ref=e519]:
- generic [ref=e520]: "#원팬토스트"
- strong [ref=e521]: 길거리토스트 만들기 레시피 식빵 계란토스트 원팬 햄치즈 토스트
- generic [ref=e523]: 라임샴푸
- listitem [ref=e525]:
- link "#초간단요리 애호박소고기볶음레시피 애호박덮밥 초간단요리 험나니" [ref=e526] [cursor=pointer]:
- /url: https://in.naver.com/sdhl1004/contents/internal/850117784413376
- generic [ref=e529]:
- generic [ref=e530]: "#초간단요리"
- strong [ref=e531]: 애호박소고기볶음레시피 애호박덮밥 초간단요리
- generic [ref=e533]: 험나니
- listitem [ref=e535]:
- link "#다이어트계란요리 간단한 아침메뉴 빵없는 계란토스트 다이어트 계란요리 제인JANE" [ref=e536] [cursor=pointer]:
- /url: https://blog.naver.com/kyjsp/223979446869
- generic [ref=e539]:
- generic [ref=e540]: "#다이어트계란요리"
- strong [ref=e541]: 간단한 아침메뉴 빵없는 계란토스트 다이어트 계란요리
- generic [ref=e543]: 제인JANE
- button "다음 페이지" [ref=e545] [cursor=pointer]:
- generic [ref=e546]: 다음 페이지
- button "펼쳐보기" [ref=e548] [cursor=pointer]: 펼쳐보기
- generic [ref=e554]:
- region "로그인 정보" [ref=e555]:
- generic [ref=e556]:
- paragraph [ref=e557]: 네이버를 더 안전하고 편리하게 이용하세요
- link "NAVER 로그인" [ref=e558] [cursor=pointer]:
- /url: https://nid.naver.com/nidlogin.login?mode=form&url=https://www.naver.com/
- generic [ref=e560]: NAVER
- text: 로그인
- generic [ref=e561]:
- link "아이디 찾기" [ref=e562] [cursor=pointer]:
- /url: https://nid.naver.com/user2/api/route?m=routeIdInquiry
- link "비밀번호 찾기" [ref=e563] [cursor=pointer]:
- /url: https://nid.naver.com/user2/api/route?m=routePwInquiry
- link "회원가입" [ref=e564] [cursor=pointer]:
- /url: https://nid.naver.com/nidregister.form?url=https%3A%2F%2Fwww.naver.com
- iframe [ref=e570]:
- generic [active]:
- main
- alert [ref=f3e1]
- region "날씨" [ref=e571]:
- generic [ref=e572]:
- generic [ref=e573]:
- link "날씨" [ref=e574] [cursor=pointer]:
- /url: https://weather.naver.com/
- link "예보 비교" [ref=e575] [cursor=pointer]:
- /url: https://weather.naver.com/compare/02285105
- generic [ref=e576]: 고양시 마두동
- generic [ref=e577]:
- generic [ref=e578]:
- link "12.1° 비 최저기온 10° 최고기온 15°" [ref=e579] [cursor=pointer]:
- /url: https://weather.naver.com/today/02285105?cpName=KMA
- generic [ref=e582]:
- text: 12.1°
- generic [ref=e583]:
- generic [ref=e584]:
- generic [ref=e585]:
- generic [ref=e586]: 최저기온
- text: 10°
- generic [ref=e587]:
- generic [ref=e588]: 최고기온
- text: 15°
- link "미세좋음 초미세좋음" [ref=e589] [cursor=pointer]:
- /url: https://weather.naver.com/air/02285105?cpName=KMA
- generic [ref=e590]: 미세좋음
- generic [ref=e591]: 초미세좋음
- link "1시 비 12° 3 구름많음 11° 5 흐림 10° 7 흐림 10° 9 구름많음 10°" [ref=e593] [cursor=pointer]:
- /url: https://weather.naver.com/today/02285105?cpName=KMA
- list [ref=e596]:
- listitem [ref=e597]:
- generic [ref=e598]: 1시
- generic [ref=e600]:
- generic [ref=e601]: 12°
- listitem [ref=e602]:
- generic [ref=e603]: "3"
- generic [ref=e605]: 구름많음
- generic [ref=e606]: 11°
- listitem [ref=e607]:
- generic [ref=e608]: "5"
- generic [ref=e610]: 흐림
- generic [ref=e611]: 10°
- listitem [ref=e612]:
- generic [ref=e613]: "7"
- generic [ref=e615]: 흐림
- generic [ref=e616]: 10°
- listitem [ref=e617]:
- generic [ref=e618]: "9"
- generic [ref=e620]: 구름많음
- generic [ref=e621]: 10°
- region "증시" [ref=e622]:
- generic [ref=e623]:
- generic [ref=e624]:
- link "증시" [ref=e625] [cursor=pointer]:
- /url: https://finance.naver.com/
- button "정보 더보기" [ref=e626] [cursor=pointer]:
- generic [ref=e627]: 정보 더보기
- generic [ref=e628]:
- text: 04.10. 00:43
- button "새로고침" [ref=e629] [cursor=pointer]:
- generic [ref=e630]: 새로고침
- generic [ref=e631]:
- link "다우존스 47,988.88 상승 78.96+0.16%" [ref=e635] [cursor=pointer]:
- /url: https://finance.naver.com/world/sise.nhn?symbol=DJI@DJI
- generic [ref=e636]:
- generic [ref=e637]: 다우존스
- generic [ref=e638]: 47,988.88
- generic [ref=e639]:
- generic [ref=e641]: 상승
- text: "78.96"
- generic [ref=e642]: +0.16%
- list [ref=e645]:
- listitem [ref=e646]:
- link "삼성전자 하락 3.09% 204,000" [ref=e647] [cursor=pointer]:
- /url: https://finance.naver.com/item/main.naver?code=005930
- generic [ref=e648]: 삼성전자
- generic [ref=e649]:
- generic [ref=e651]: 하락
- generic [ref=e652]: 3.09%
- text: 204,000
- listitem [ref=e653]:
- link "SK하이닉스 하락 3.39% 998,000" [ref=e654] [cursor=pointer]:
- /url: https://finance.naver.com/item/main.naver?code=000660
- generic [ref=e655]: SK하이닉스
- generic [ref=e656]:
- generic [ref=e658]: 하락
- generic [ref=e659]: 3.39%
- text: 998,000
- listitem [ref=e660]:
- link "삼천당제약 상승 3.92% 504,000" [ref=e661] [cursor=pointer]:
- /url: https://finance.naver.com/item/main.naver?code=000250
- generic [ref=e662]: 삼천당제약
- generic [ref=e663]:
- generic [ref=e665]: 상승
- generic [ref=e666]: 3.92%
- text: 504,000
- listitem [ref=e667]:
- link "인기종목 더보기" [ref=e668] [cursor=pointer]:
- /url: https://finance.naver.com/sise/lastsearch2.naver
- region "위젯" [ref=e669]:
- generic [ref=e670]: 위젯 보드
- generic [ref=e671]:
- generic [ref=e672]:
- generic [ref=e673]:
- generic [ref=e674]:
- generic [ref=e676]: 캘린더
- generic [ref=e678]:
- text: "4.10"
- generic [ref=e679]:
- link "로그인하기":
- /url: https://nid.naver.com/nidlogin.login?mode=form&url=https://www.naver.com/?to=widget
- generic [ref=e680] [cursor=pointer]: 로그인하기
- link "2026년 4월 캘린더 - 이번 달의 일정을 날짜별로 확인할 수 있는 표입니다. 각 셀에는 날짜와 해당 일정이 포함됩니다." [ref=e681] [cursor=pointer]:
- /url: https://nid.naver.com/nidlogin.login?mode=form&url=https://www.naver.com/?to=widget
- table "2026년 4월 캘린더 - 이번 달의 일정을 날짜별로 확인할 수 있는 표입니다. 각 셀에는 날짜와 해당 일정이 포함됩니다." [ref=e682]:
- caption [ref=e683]: 2026년 4월 캘린더 - 이번 달의 일정을 날짜별로 확인할 수 있는 표입니다. 각 셀에는 날짜와 해당 일정이 포함됩니다.
- rowgroup [ref=e684]:
- row "일 월 화 수 목 금 토" [ref=e685]:
- columnheader "일" [ref=e686]
- columnheader "월" [ref=e687]
- columnheader "화" [ref=e688]
- columnheader "수" [ref=e689]
- columnheader "목" [ref=e690]
- columnheader "금" [ref=e691]
- columnheader "토" [ref=e692]
- rowgroup [ref=e693]:
- row "29 30 31 1 2 3 4" [ref=e694]:
- cell "29" [ref=e695]
- cell "30" [ref=e696]
- cell "31" [ref=e697]
- cell "1" [ref=e698]
- cell "2" [ref=e699]
- cell "3" [ref=e700]
- cell "4" [ref=e701]
- row "5 6 7 8 9 10 11" [ref=e702]:
- cell "5" [ref=e703]
- cell "6" [ref=e704]
- cell "7" [ref=e705]
- cell "8" [ref=e706]
- cell "9" [ref=e707]
- cell "10" [ref=e708]
- cell "11" [ref=e709]
- row "12 13 14 15 16 17 18" [ref=e710]:
- cell "12" [ref=e711]
- cell "13" [ref=e712]
- cell "14" [ref=e713]
- cell "15" [ref=e714]
- cell "16" [ref=e715]
- cell "17" [ref=e716]
- cell "18" [ref=e717]
- row "19 20 21 22 23 24 25" [ref=e718]:
- cell "19" [ref=e719]
- cell "20" [ref=e720]
- cell "21" [ref=e721]
- cell "22" [ref=e722]
- cell "23" [ref=e723]
- cell "24" [ref=e724]
- cell "25" [ref=e725]
- row "26 27 28 29 30 1 2" [ref=e726]:
- cell "26" [ref=e727]
- cell "27" [ref=e728]
- cell "28" [ref=e729]
- cell "29" [ref=e730]
- cell "30" [ref=e731]
- cell "1" [ref=e732]
- cell "2" [ref=e733]
- generic [ref=e734]:
- generic [ref=e735]:
- generic [ref=e737]: VIBE
- generic [ref=e738]: 십자군전쟁이란?
- button "다른 추천 보기" [ref=e739] [cursor=pointer]
- list [ref=e740]:
- listitem [ref=e741]:
- link "1회 - 200년의 종교 전쟁! 지금, 시작합니다. 재생" [ref=e742] [cursor=pointer]:
- /url: https://vibe.naver.com/audio_player?curationId=1027&contentId=CH_4910_EP_405&audioContentId=CH_4910_EP_405
- strong [ref=e744]: 1회 - 200년의 종교 전쟁! 지금, 시작합니다.
- generic [ref=e746]: 재생
- listitem [ref=e747]:
- link "2회 - 균형 잡힌 관점으로 전쟁을 알려면? 이것부터! 재생" [ref=e748] [cursor=pointer]:
- /url: https://vibe.naver.com/audio_player?curationId=1027&contentId=CH_4910_EP_406&audioContentId=CH_4910_EP_406
- strong [ref=e750]: 2회 - 균형 잡힌 관점으로 전쟁을 알려면? 이것부터!
- generic [ref=e752]: 재생
- listitem [ref=e753]:
- 'link "3회 - 셀주크 제국과 동로마의 첫 전투, 결과는? #만지케르트전투 재생" [ref=e754] [cursor=pointer]':
- /url: https://vibe.naver.com/audio_player?curationId=1027&contentId=CH_4910_EP_407&audioContentId=CH_4910_EP_407
- strong [ref=e756]: "3회 - 셀주크 제국과 동로마의 첫 전투, 결과는? #만지케르트전투"
- generic [ref=e758]: 재생
- generic [ref=e759]:
- generic [ref=e760]:
- generic [ref=e761]:
- link "영어사전" [ref=e762] [cursor=pointer]:
- /url: https://en.dict.naver.com/#/mini/main
- link "Its twelve." [ref=e763] [cursor=pointer]:
- /url: https://learn.dict.naver.com/m/endic/today/conversation.dict
- generic [ref=e765]: Its twelve.
- link "단어 검색하기" [ref=e766] [cursor=pointer]:
- /url: https://en.dict.naver.com/#/mini/main
- link "papago 번역하기" [ref=e767] [cursor=pointer]:
- /url: https://papago.naver.com/?sk=ko&st=&tk=en
- generic [ref=e769]: papago
- generic [ref=e770]: 번역하기
- link "메모 로그인하기" [ref=e771] [cursor=pointer]:
- /url: https://nid.naver.com/nidlogin.login?mode=form&url=https://www.naver.com/?to=widget
- generic [ref=e773]: 메모
- list [ref=e775]:
- listitem [ref=e776]
- listitem [ref=e777]
- listitem [ref=e778]:
- generic [ref=e779]: 로그인하기
- button "다음 페이지" [ref=e780] [cursor=pointer]:
- generic [ref=e781]: 다음 페이지
- link "모바일 네이버 메인 열기" [ref=e782] [cursor=pointer]:
- /url: https://m.naver.com/
- strong [ref=e783]: 모바일 네이버 메인
- text: 열기
- generic [ref=e786]:
- button "최상단으로 이동" [ref=e787] [cursor=pointer]:
- generic [ref=e788]: 최상단으로 이동
- button "홈 설정" [ref=e789] [cursor=pointer]:
- generic [ref=e790]: 홈 설정
- contentinfo [ref=e791]:
- generic [ref=e792]:
- generic [ref=e793]:
- iframe [ref=e796]:
- link "광고 이미지 광고 유니세프 한국위원회 의미 있는 4월 어린이를 지키는 약속의 증표 지금 유니세프 팀 팔찌 받기" [ref=f4e3] [cursor=pointer]:
- /url: https://siape.veta.naver.com/fxclick?eu=EU10043568&calp=-&oj=cQgn6aire5MOnC6WBYbmb9tirrR2H6fVusCYsE3LckHGk9JWj3yQyWx%2FhcG054wnCb5%2BPZ4dBW68ikgVu8ae7iGNcbrnFmKn&ac=9267155&src=8067405&br=4921545&evtcd=P901&x_ti=1529&tb=&oid=&sid1=&sid2=&rk=_SS_zRSpH1hCGftVVL7uaw&eltts=uuyZfa5b5lmB3qHjBRDDRQ%3D%3D&brs=Y&
- generic [ref=f4e4]:
- img "광고 이미지" [ref=f4e5]
- generic "광고" [ref=f4e6]
- generic [ref=f4e7]:
- strong [ref=f4e8]: 유니세프 한국위원회
- strong [ref=f4e9]: 의미 있는 4월
- generic [ref=f4e10]: 어린이를 지키는 약속의 증표
- generic [ref=f4e11]: 지금 유니세프 팀 팔찌 받기
- iframe [ref=e799]:
- link "광고 이미지 광고 초록우산 엄마 손 잡고 꼭 다시 걸을거야 재활의 고통보다 엄마의 눈물이 더 아픈 9살 지온이 돕기" [ref=f5e3] [cursor=pointer]:
- /url: https://siape.veta.naver.com/fxclick?eu=EU10043569&calp=-&oj=cQgn6aire5Nx8cfS51TDuttirrR2H6fVusCYsE3LckHGk9JWj3yQyWx%2FhcG054wnCb5%2BPZ4dBW68ikgVu8ae7iGNcbrnFmKn&ac=9265144&src=8060305&br=4918643&evtcd=P901&x_ti=1529&tb=&oid=&sid1=&sid2=&rk=x4HQP1PeLE33AwmjzutSRg&eltts=uuyZfa5b5lmB3qHjBRDDRQ%3D%3D&brs=Y&
- generic [ref=f5e4]:
- img "광고 이미지" [ref=f5e5]
- generic "광고" [ref=f5e6]
- generic [ref=f5e7]:
- strong [ref=f5e8]: 초록우산
- strong [ref=f5e9]: 엄마 손 잡고 꼭 다시 걸을거야
- generic [ref=f5e10]: 재활의 고통보다 엄마의 눈물이
- generic [ref=f5e11]: 더 아픈 9살 지온이 돕기
- iframe [ref=e802]:
- link "광고 이미지 문화체육관광부 수요일 문화혜택 알아보기 문화가 있는 날 확대 매주 수요일은 문화요일" [ref=f6e3] [cursor=pointer]:
- /url: https://siape.veta.naver.com/fxclick?eu=EU10043570&calp=-&oj=9gPJTdEI4gHf40n3ktJ9%2FNtirrR2H6fVusCYsE3LckHGk9JWj3yQyWx%2FhcG054wnCb5%2BPZ4dBW68ikgVu8ae7iGNcbrnFmKn&ac=9264215&src=8057129&br=4917350&evtcd=P901&x_ti=1507&tb=&oid=&sid1=&sid2=&rk=OvrvuDX6wBn974ijpia7Bg&eltts=uuyZfa5b5lmB3qHjBRDDRQ%3D%3D&brs=Y&
- img "광고 이미지" [ref=f6e5]
- generic [ref=f6e6]:
- strong [ref=f6e7]: 문화체육관광부
- strong [ref=f6e8]: 수요일 문화혜택 알아보기
- generic [ref=f6e9]: 문화가 있는 날 확대
- generic [ref=f6e10]: 매주 수요일은 문화요일
- generic [ref=e803]:
- heading "공지사항" [level=3] [ref=e805]:
- link "공지사항" [ref=e806] [cursor=pointer]:
- /url: https://www.naver.com/NOTICE
- link "서비스 전체보기" [ref=e807] [cursor=pointer]:
- /url: /more.html
- generic [ref=e808]:
- generic [ref=e809]:
- generic [ref=e810]:
- heading "Partners" [level=3] [ref=e811]
- link "네이버 임팩트" [ref=e812] [cursor=pointer]:
- /url: https://www.navercorp.com/esg/social
- link "네이버 비즈니스" [ref=e813] [cursor=pointer]:
- /url: https://business.naver.com/
- link "네이버 비즈니스 스쿨" [ref=e814] [cursor=pointer]:
- /url: https://bizschool.naver.com/
- link "네이버 광고 등록" [ref=e815] [cursor=pointer]:
- /url: https://ads.naver.com/
- link "스토어 개설" [ref=e816] [cursor=pointer]:
- /url: https://sell.storefarm.naver.com/#/home/about
- link "지역업체 등록" [ref=e817] [cursor=pointer]:
- /url: https://smartplace.naver.com/
- link "엑스퍼트 등록" [ref=e818] [cursor=pointer]:
- /url: https://expert.naver.com/expert/introduction?tab=guide#join
- generic [ref=e819]:
- heading "Developers" [level=3] [ref=e820]
- link "네이버 개발자 센터" [ref=e821] [cursor=pointer]:
- /url: https://developers.naver.com/
- link "오픈 API" [ref=e822] [cursor=pointer]:
- /url: https://developers.naver.com/docs/common/openapiguide/#/apilist.md
- link "오픈소스" [ref=e823] [cursor=pointer]:
- /url: https://naver.github.io/
- link "네이버 D2" [ref=e824] [cursor=pointer]:
- /url: https://d2.naver.com/
- link "네이버 D2SF" [ref=e825] [cursor=pointer]:
- /url: http://d2startup.com/
- link "네이버 랩스" [ref=e826] [cursor=pointer]:
- /url: https://www.naverlabs.com/
- generic [ref=e827]:
- generic [ref=e828]:
- link "웨일 브라우저" [ref=e829] [cursor=pointer]:
- /url: http://whale.naver.com/
- img "웨일 브라우저" [ref=e830]
- generic [ref=e831]:
- strong [ref=e832]: 웨일 브라우저
- link "이용안내" [ref=e833] [cursor=pointer]:
- /url: http://whale.naver.com/
- generic [ref=e834]:
- link "기업 사이트" [ref=e835] [cursor=pointer]:
- /url: https://www.navercorp.com
- img "기업 사이트" [ref=e836]
- generic [ref=e837]:
- strong [ref=e838]: 기업 사이트
- link "바로가기" [ref=e839] [cursor=pointer]:
- /url: https://www.navercorp.com
- generic [ref=e840]:
- heading "네이버 정책 및 약관" [level=3] [ref=e841]
- list [ref=e842]:
- listitem [ref=e843]:
- link "회사소개" [ref=e844] [cursor=pointer]:
- /url: https://www.navercorp.com/
- listitem [ref=e845]:
- link "인재채용" [ref=e846] [cursor=pointer]:
- /url: https://recruit.navercorp.com/
- listitem [ref=e847]:
- link "제휴제안" [ref=e848] [cursor=pointer]:
- /url: https://www.navercorp.com/company/partnership
- listitem [ref=e849]:
- link "이용약관" [ref=e850] [cursor=pointer]:
- /url: https://www.naver.com/policy/service.html
- listitem [ref=e851]:
- link "개인정보처리방침" [ref=e852] [cursor=pointer]:
- /url: https://www.naver.com/policy/privacy.html
- strong [ref=e853]: 개인정보처리방침
- listitem [ref=e854]:
- link "청소년보호정책" [ref=e855] [cursor=pointer]:
- /url: https://www.naver.com/policy/youthpolicy.html
- listitem [ref=e856]:
- link "네이버 정책" [ref=e857] [cursor=pointer]:
- /url: https://policy.naver.com/policy/service_group.html
- listitem [ref=e858]:
- link "고객센터" [ref=e859] [cursor=pointer]:
- /url: https://help.naver.com/
- link "ⓒ NAVER Corp." [ref=e861] [cursor=pointer]:
- /url: https://www.navercorp.com/

View File

@@ -0,0 +1,323 @@
- generic [active] [ref=e1]:
- generic [ref=e5]:
- link [ref=e6] [cursor=pointer]:
- /url: https://www.coupang.com/mlp/web/package-landing?adType=DA&eventId=epz7WYtFAqoBj6JM
- link [ref=e7] [cursor=pointer]:
- /url: https://www.coupang.com/mlp/web/package-landing?adType=DA&eventId=8k9NwjG8PpaAugUx
- generic [ref=e8]:
- generic [ref=e10]:
- list [ref=e11]:
- listitem [ref=e12]:
- link "판매자 가입" [ref=e13] [cursor=pointer]:
- /url: https://wing.coupang.com/tenants/vendor-signup/signup?utm_source=button_pc_coupang_app_category_3&utm_medium=non_paid&utm_id=onsite_ca&utm_content=20240328
- listitem [ref=e14]:
- link "고객센터" [ref=e15] [cursor=pointer]:
- /url: https://mc.coupang.com/ssr/desktop/contact/faq
- listitem [ref=e16]:
- link "회원가입" [ref=e17] [cursor=pointer]:
- /url: https://login.coupang.com/login/memberJoinFrm.pang
- listitem [ref=e18]:
- link "로그인" [ref=e19] [cursor=pointer]:
- /url: https://login.coupang.com/login/login.pang?rtnUrl=https%3A%2F%2Fwww.coupang.com%2Fnp%2Fpost%2Flogin%3Fr%3Dhttp%253A%252F%252Fwww.coupang.com%252F
- list [ref=e20]:
- listitem [ref=e21]: 즐겨찾기
- listitem [ref=e22]:
- generic [ref=e23] [cursor=pointer]: 입점신청
- generic [ref=e26]:
- generic [ref=e32] [cursor=pointer]: 카테고리
- generic [ref=e33]:
- generic [ref=e34]:
- link "Coupang" [ref=e35] [cursor=pointer]:
- /url: https://www.coupang.com
- img "Coupang" [ref=e36]
- generic [ref=e37]:
- link "전체" [ref=e40] [cursor=pointer]:
- /url: "#"
- textbox "쿠팡 상품 검색" [ref=e42]:
- /placeholder: 찾고 싶은 상품을 검색해보세요!
- button "검색" [ref=e44] [cursor=pointer]
- list [ref=e45]:
- listitem [ref=e46]:
- link "마이쿠팡" [ref=e47] [cursor=pointer]:
- /url: https://mc.coupang.com/ssr/desktop/order/list
- generic [ref=e48]: 마이쿠팡
- listitem [ref=e49]:
- link "0 장바구니" [ref=e50] [cursor=pointer]:
- /url: //cart.coupang.com/cartView.pang
- emphasis [ref=e51]: "0"
- generic [ref=e52]: 장바구니
- list [ref=e57]:
- listitem [ref=e58]:
- link "쿠팡플레이 쿠팡플레이" [ref=e59] [cursor=pointer]:
- /url: https://www.coupangplay.com/catalog
- img "쿠팡플레이" [ref=e60]
- generic [ref=e61]: 쿠팡플레이
- listitem [ref=e62]:
- link "로켓배송 로켓배송" [ref=e63] [cursor=pointer]:
- /url: https://www.coupang.com/np/campaigns/82
- img "로켓배송" [ref=e64]
- generic [ref=e65]: 로켓배송
- listitem [ref=e66]:
- link "로켓프레시 로켓프레시" [ref=e67] [cursor=pointer]:
- /url: https://www.coupang.com/np/categories/393760
- img "로켓프레시" [ref=e68]
- generic [ref=e69]: 로켓프레시
- listitem [ref=e70]:
- link "다시 구매 다시 구매" [ref=e71] [cursor=pointer]:
- /url: https://www.coupang.com/np/fbi?sourceType=home_section_fbi
- img "다시 구매" [ref=e72]
- generic [ref=e73]: 다시 구매
- listitem [ref=e75]:
- link "쿠팡비즈 쿠팡비즈" [ref=e76] [cursor=pointer]:
- /url: https://login.coupang.com/corporation/member/landing-page
- img "쿠팡비즈" [ref=e77]
- generic [ref=e78]: 쿠팡비즈
- listitem [ref=e79]:
- link "로켓직구 로켓직구" [ref=e80] [cursor=pointer]:
- /url: https://www.coupang.com/np/coupangglobal
- img "로켓직구" [ref=e81]
- generic [ref=e82]: 로켓직구
- listitem [ref=e83]:
- link "골드박스 골드박스" [ref=e84] [cursor=pointer]:
- /url: https://pages.coupang.com/p/121237?sourceType=gm_crm_goldbox&subSourceType=gm_crm_gwsrtcut
- img "골드박스" [ref=e85]
- generic [ref=e86]: 골드박스
- listitem [ref=e87]:
- link "이달의신상 이달의신상" [ref=e88] [cursor=pointer]:
- /url: https://pages.coupang.com/p/136938
- img "이달의신상" [ref=e89]
- generic [ref=e90]: 이달의신상
- listitem [ref=e91]:
- link "판매자특가 판매자특가" [ref=e92] [cursor=pointer]:
- /url: https://www.coupang.com/np/omp
- img "판매자특가" [ref=e93]
- generic [ref=e94]: 판매자특가
- listitem [ref=e95]:
- link "와우회원할인 와우회원할인" [ref=e96] [cursor=pointer]:
- /url: https://www.coupang.com/np/campaigns/83
- img "와우회원할인" [ref=e97]
- generic [ref=e98]: 와우회원할인
- listitem [ref=e99]:
- link "이벤트/쿠폰 이벤트/쿠폰" [ref=e100] [cursor=pointer]:
- /url: https://www.coupang.com/np/coupangbenefit
- img "이벤트/쿠폰" [ref=e101]
- generic [ref=e102]: 이벤트/쿠폰
- listitem [ref=e103]:
- link "반품마켓 반품마켓" [ref=e104] [cursor=pointer]:
- /url: https://pages.coupang.com/p/54908
- img "반품마켓" [ref=e105]
- generic [ref=e106]: 반품마켓
- listitem [ref=e107]:
- link "착한상점 착한상점" [ref=e108] [cursor=pointer]:
- /url: https://pages.coupang.com/p/63240
- img "착한상점" [ref=e109]
- generic [ref=e110]: 착한상점
- listitem [ref=e112]:
- link "기획전 기획전" [ref=e113] [cursor=pointer]:
- /url: https://pages.coupang.com/p/bep?sourceType=gm_crm_oms&subSourceType=gm_crm_gwsrtcut
- img "기획전" [ref=e114]
- generic [ref=e115]: 기획전
- listitem [ref=e116]:
- link "쿠팡트래블 쿠팡트래블" [ref=e117] [cursor=pointer]:
- /url: https://trip.coupang.com/?channel=gnb
- img "쿠팡트래블" [ref=e118]
- generic [ref=e119]: 쿠팡트래블
- listitem [ref=e121]:
- link "입점신청 입점신청" [ref=e122] [cursor=pointer]:
- /url: https://wing.coupang.com/tenants/vendor-signup/signup?utm_source=button_pc_coupang_shortcut_1&utm_medium=non_paid&utm_id=onsite_ca&utm_content=202411
- img "입점신청" [ref=e123]
- generic [ref=e124]: 입점신청
- article [ref=e127]:
- list [ref=e128]:
- listitem [ref=e129]:
- link [ref=e130] [cursor=pointer]:
- /url: https://shop.coupang.com/kd1961/487606?source=brandstore_display_ads&adType=DA&eventId=lsKDUCgcKEKkjiRL
- listitem [ref=e131]:
- link [ref=e132] [cursor=pointer]:
- /url: https://shop.coupang.com/woongjinfoods/486609?source=brandstore_display_ads&adType=DA&eventId=BfiMXhBlUxpCwPAx
- listitem [ref=e133]:
- link [ref=e134] [cursor=pointer]:
- /url: https://www.coupang.com/np/campaigns/1440
- listitem [ref=e135]:
- link [ref=e136] [cursor=pointer]:
- /url: https://pages.coupang.com/p/78273
- listitem [ref=e137]:
- link [ref=e138] [cursor=pointer]:
- /url: https://pages.coupang.com/p/164807
- listitem [ref=e139]:
- link [ref=e140] [cursor=pointer]:
- /url: https://pages.coupang.com/p/68594
- listitem [ref=e141]:
- link [ref=e142] [cursor=pointer]:
- /url: https://wing.coupang.com/tenants/vendor-signup/signup?utm_source=button_pc_coupang_web_rignt_wing&utm_medium=non_paid&utm_id=onsite_ca&utm_content=20240516
- generic:
- link "장바구니 0" [ref=e144] [cursor=pointer]:
- /url: //cart.coupang.com/cartView.pang
- generic [ref=e145]: 장바구니
- emphasis [ref=e146]: "0"
- generic [ref=e147]:
- generic [ref=e148]: 최근본상품
- emphasis [ref=e149]: "0"
- generic [ref=e150]:
- list [ref=e151]
- paragraph [ref=e152]:
- generic [ref=e153]:
- strong
- text: /
- emphasis
- generic [ref=e154]:
- link "prev" [ref=e155] [cursor=pointer]:
- /url: 이전 페이지 보기
- link "next" [ref=e156] [cursor=pointer]:
- /url: 다음 페이지 보기
- generic [ref=e157]:
- generic [ref=e160]:
- link [ref=e162] [cursor=pointer]:
- /url: https://shop.coupang.com/A01064449/516623?source=brandstore_display_ads&adType=DA&eventId=0ygW7mjzNMRAMElJ&from=home_C1&traid=home_C1&trcid=11703333
- list [ref=e163]:
- listitem [ref=e165]:
- link [ref=e166] [cursor=pointer]:
- /url: https://shop.coupang.com/A01064449/516623?source=brandstore_display_ads&adType=DA&eventId=0ygW7mjzNMRAMElJ&from=home_C1&traid=home_C1&trcid=11703333
- img [ref=e167]
- listitem [ref=e170]:
- link [ref=e171] [cursor=pointer]:
- /url: https://shop.coupang.com/kleannara/349132?source=brandstore_display_ads&adType=DA&eventId=SH5kfQHMCDwQae4s&from=home_C1&traid=home_C1&trcid=11702264
- img [ref=e172]
- listitem [ref=e175]:
- link [ref=e176] [cursor=pointer]:
- /url: https://shop.coupang.com/A00003619/490614?source=brandstore_display_ads&adType=DA&eventId=fQORb49JQ40ui4l3&from=home_C1&traid=home_C1&trcid=11700357
- img [ref=e177]
- listitem [ref=e180]:
- link [ref=e181] [cursor=pointer]:
- /url: https://pages.coupang.com/p/166727?subSourceType=gm_crm_gwc1mainbnr_p2981_b1004606_c533502&sourceType=gm_crm_oms&from=home_C1&traid=home_C1&trcid=812431103159
- img [ref=e182]
- listitem [ref=e185]:
- link [ref=e186] [cursor=pointer]:
- /url: https://www.coupang.com/mlp/web/package-landing?from=home_C1&traid=home_C1&trcid=812431073637
- img [ref=e187]
- listitem [ref=e190]:
- link [ref=e191] [cursor=pointer]:
- /url: https://login.coupang.com/login/login.pang?rtnUrl=https%3A%2Fmc.coupang.com%2Fssr&from=home_C1&traid=home_C1&trcid=812431097407
- img [ref=e192]
- generic [ref=e195]:
- generic [ref=e196]:
- generic [ref=e197]:
- heading "오늘의 발견" [level=2] [ref=e198]
- heading "오늘 쿠팡이 엄선한 가장 HOT한 상품!" [level=3] [ref=e199]
- list [ref=e201]:
- listitem [ref=e202]:
- link [ref=e203] [cursor=pointer]:
- /url: https://shop.coupang.com/pigeonofficial/280389?source=brandstore_display_ads&adType=DA&eventId=LxgeOlP2rAdYqvnp&from=home_C2&traid=home_C2&trcid=11701308
- img [ref=e206]
- listitem [ref=e209]:
- link [ref=e210] [cursor=pointer]:
- /url: https://shop.coupang.com/ottogi/301273?source=brandstore_display_ads&adType=DA&eventId=hRdXj1hrn9lhzPBM&from=home_C2&traid=home_C2&trcid=11697862
- img [ref=e213]
- listitem [ref=e216]:
- link [ref=e217] [cursor=pointer]:
- /url: https://pages.coupang.com/p/167853?adType=DA&eventId=7utXaPV38Kqx6PIo&from=home_C2&traid=home_C2&trcid=11702945
- img [ref=e220]
- listitem [ref=e223]:
- link [ref=e224] [cursor=pointer]:
- /url: https://shop.coupang.com/missha/490691?source=brandstore_display_ads&adType=DA&eventId=a2fA4jgINBu1UGz0&from=home_C2&traid=home_C2&trcid=11700243
- img [ref=e227]
- listitem [ref=e230]:
- link [ref=e231] [cursor=pointer]:
- /url: https://shop.coupang.com/magicwrap/480423?source=brandstore_display_ads&adType=DA&eventId=nohHc8dlSu2e3XBP&from=home_C2&traid=home_C2&trcid=11704115
- img [ref=e234]
- listitem [ref=e237]:
- link [ref=e238] [cursor=pointer]:
- /url: https://pages.coupang.com/p/167919?adType=DA&eventId=Yv35b3fCpbVgECkq&from=home_C2&traid=home_C2&trcid=11703217
- img [ref=e241]
- listitem [ref=e244]:
- link [ref=e245] [cursor=pointer]:
- /url: https://shop.coupang.com/srise/425294?source=brandstore_display_ads&adType=DA&eventId=dlhxjV4iS5ZKCgt6&from=home_C2&traid=home_C2&trcid=11700789
- img [ref=e248]
- listitem [ref=e251]:
- link [ref=e252] [cursor=pointer]:
- /url: https://pages.coupang.com/p/167989?adType=DA&eventId=K3naQUtxIEtfh9tb&from=home_C2&traid=home_C2&trcid=11703604
- img [ref=e255]
- listitem [ref=e258]:
- link "코멧(로고) 어린이용 일회용 장갑" [ref=e259] [cursor=pointer]:
- /url: /vp/products/7225189423?itemId=18319675609&vendorItemId=80324048129&from=home_C2&traid=home_C2&trcid=4750546
- img "코멧(로고) 어린이용 일회용 장갑" [ref=e260]
- img [ref=e263]
- link [ref=e270] [cursor=pointer]:
- /url: https://shop.coupang.com/cj/418171?source=brandstore_display_ads&adType=DA&eventId=fYJZfC1m0fEwKJas&from=home_event_banner&traid=home_event_banner
- paragraph [ref=e273]: Loading...
- contentinfo [ref=e274]:
- generic [ref=e275]:
- generic [ref=e276]:
- link "회사소개" [ref=e277] [cursor=pointer]:
- /url: https://news.coupang.com/
- link "Investor Relations" [ref=e278] [cursor=pointer]:
- /url: https://ir.aboutcoupang.com/overview/default.aspx
- link "인재채용" [ref=e279] [cursor=pointer]:
- /url: https://rocketyourcareer.kr.coupang.com
- link "입점/제휴문의" [ref=e280] [cursor=pointer]:
- /url: https://marketplace.coupangcorp.com/s/?utm_source=button_pc&utm_medium=non_paid&utm_campaign=onsite_ca&utm_id=coupang_app?inflow=WEB_FOOTER_B
- link "공지사항" [ref=e281] [cursor=pointer]:
- /url: https://mc.coupang.com/ssr/desktop/contact/notice
- link "고객의 소리" [ref=e282] [cursor=pointer]:
- /url: https://mc.coupang.com/ssr/desktop/contact/voc
- link "이용약관" [ref=e283] [cursor=pointer]:
- /url: https://www.coupang.com/np/policies/terms
- link "개인정보 처리방침" [ref=e284] [cursor=pointer]:
- /url: https://privacy.coupang.com/ko/center/coupang
- strong [ref=e285]: 개인정보 처리방침
- link "정보보호/개인정보보호 인증" [ref=e286] [cursor=pointer]:
- /url: https://privacy.coupang.com/ko/security/status
- link "쿠팡페이 이용약관" [ref=e287] [cursor=pointer]:
- /url: https://rocketpay.coupang.com/rocketpay/operationTerms/coupangPcFooter
- link "신뢰관리센터" [ref=e288] [cursor=pointer]:
- /url: https://www.coupang.com/np/safety
- link "제휴마케팅" [ref=e289] [cursor=pointer]:
- /url: https://partners.coupang.com/
- link "광고안내" [ref=e290] [cursor=pointer]:
- /url: https://ads.coupang.com
- paragraph [ref=e294] [cursor=pointer]: Global Site
- generic [ref=e296]:
- generic [ref=e297]:
- link "COUPANG" [ref=e298] [cursor=pointer]:
- /url: https://www.coupang.com/
- generic [ref=e299]:
- text: "상호명 및 호스팅 서비스 제공 : 쿠팡(주)"
- text: "대표이사 : 로저스 해롤드 린(Rogers Harold Lynn)"
- text: 서울시 송파구 송파대로 570
- text: "사업자 등록번호 : 120-88-00767"
- text: "통신판매업신고 : 2017-서울송파-0680"
- link "사업자정보 확인 >" [ref=e300] [cursor=pointer]:
- /url: http://www.ftc.go.kr/info/bizinfo/communicationViewPopup.jsp?wrkr_no=1208800767
- 'link "365고객센터 | 전자금융거래분쟁처리담당 1577-7011 (유료) 서울시 송파구 송파대로 570 email : help@coupang.com" [ref=e302] [cursor=pointer]':
- /url: https://mc.coupang.com/ssr/desktop/contact/inquiry
- strong [ref=e303]: 365고객센터
- text: "| 전자금융거래분쟁처리담당"
- emphasis [ref=e304]: 1577-7011 (유료)
- text: 서울시 송파구 송파대로 570
- text: "email : help@coupang.com"
- paragraph [ref=e305]:
- strong [ref=e306]: 채무지급보증 안내
- generic [ref=e307]:
- text: 당사는 고객님이 현금 결제한 금액에 대해
- text: 채무지급보증 계약을 체결하여
- text: 안전거래를 보장하고 있습니다.
- link "서비스 가입사실 확인 >" [ref=e308] [cursor=pointer]:
- /url: https://www.coupang.com/np/etc/popWooriService
- generic [ref=e314]:
- paragraph [ref=e315]:
- text: 사이버몰 내 판매되는 상품 중에는 쿠팡에 등록한 개별 판매자가 판매하는 마켓플레이스(오픈마켓) 상품이 포함되어 있습니다.
- text: 마켓플레이스(오픈마켓) 상품의 경우 쿠팡은 통신판매중개자이며 통신판매의 당사자가 아닙니다.
- text: 쿠팡은 마켓플레이스(오픈마켓) 상품, 거래정보 및 거래 등에 대하여 책임을 지지 않습니다.
- text: 쿠팡은 소비자 보호와 안전거래를 위해 신뢰관리센터(CM112@coupang.com)를 운영하고 있으며, 관련 분쟁이 발생할 경우 별도의 분쟁 처리절차에 의거 분쟁이 처리됩니다.
- text: Copyright © Coupang Corp. 2010-2025 All Rights Reserved.
- list [ref=e316]:
- listitem [ref=e317]:
- link [ref=e318] [cursor=pointer]:
- /url: https://www.facebook.com/Coupang.korea
- listitem [ref=e319]:
- link [ref=e320] [cursor=pointer]:
- /url: https://news.coupang.com/
- listitem [ref=e321]:
- link [ref=e322] [cursor=pointer]:
- /url: https://www.instagram.com/coupang
- alert [ref=e323]

View File

@@ -0,0 +1,30 @@
- generic [ref=e4]:
- generic [ref=e6]: HYUN
- table [ref=e8]:
- rowgroup [ref=e13]:
- row "ID 로그인" [ref=e14]:
- rowheader "ID" [ref=e15]
- cell [ref=e16]:
- textbox [active] [ref=e17]
- cell "로그인" [ref=e18]:
- button "로그인" [ref=e19] [cursor=pointer]
- row "PW" [ref=e21]:
- rowheader "PW" [ref=e22]
- cell [ref=e23]:
- textbox [ref=e24]
- row "아이디저장" [ref=e25]:
- cell [ref=e26]
- cell "아이디저장" [ref=e27]:
- generic [ref=e28]:
- checkbox "아이디저장" [ref=e29]
- text: 아이디저장
- cell [ref=e30]
- generic [ref=e32]:
- text: "접속 IP : 114.205.211.211"
- text: "접속 웹브라우저 : 크롬(Chrome)"
- link [ref=e35] [cursor=pointer]:
- /url: //www.bizmax.net
- img [ref=e36]
- link "비즈맥스솔루션 BIZMAX.NET" [ref=e39] [cursor=pointer]:
- /url: //www.bizmax.net
- generic [ref=e40]: 전산 프로그램 개설 문의 ☏ 010-2522-9862

View File

@@ -0,0 +1,34 @@
- generic [ref=e1]:
- generic [ref=e4]:
- generic [ref=e6]: HYUN
- table [ref=e8]:
- rowgroup [ref=e13]:
- row "ID JH_1204 로그인" [ref=e41]:
- rowheader "ID" [ref=e15]
- cell "JH_1204" [ref=e42]:
- textbox [ref=e17]: JH_1204
- cell "로그인" [ref=e18]:
- button "로그인" [active] [ref=e19] [cursor=pointer]
- row "PW 12345" [ref=e43]:
- rowheader "PW" [ref=e22]
- cell "12345" [ref=e44]:
- textbox [ref=e24]: "12345"
- row "아이디저장" [ref=e25]:
- cell [ref=e26]
- cell "아이디저장" [ref=e27]:
- generic [ref=e28]:
- checkbox "아이디저장" [ref=e29]
- text: 아이디저장
- cell [ref=e30]
- generic [ref=e32]:
- text: "접속 IP : 114.205.211.211"
- text: "접속 웹브라우저 : 크롬(Chrome)"
- link [ref=e35] [cursor=pointer]:
- /url: //www.bizmax.net
- img [ref=e36]
- link "비즈맥스솔루션 BIZMAX.NET" [ref=e39] [cursor=pointer]:
- /url: //www.bizmax.net
- generic [ref=e40]: 전산 프로그램 개설 문의 ☏ 010-2522-9862
- generic [ref=e45]:
- button "×" [ref=e46] [cursor=pointer]
- generic [ref=e47]: 아이디 또는 비밀번호를 다시 확인하세요.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
- generic [ref=e4]:
- generic [ref=e6]: HYUN
- table [ref=e8]:
- rowgroup [ref=e13]:
- row "ID 로그인" [ref=e14]:
- rowheader "ID" [ref=e15]
- cell [ref=e16]:
- textbox [active] [ref=e17]
- cell "로그인" [ref=e18]:
- button "로그인" [ref=e19] [cursor=pointer]
- row "PW" [ref=e21]:
- rowheader "PW" [ref=e22]
- cell [ref=e23]:
- textbox [ref=e24]
- row "아이디저장" [ref=e25]:
- cell [ref=e26]
- cell "아이디저장" [ref=e27]:
- generic [ref=e28]:
- checkbox "아이디저장" [ref=e29]
- text: 아이디저장
- cell [ref=e30]
- generic [ref=e32]:
- text: "접속 IP : 114.205.211.211"
- text: "접속 웹브라우저 : 크롬(Chrome)"
- link [ref=e35] [cursor=pointer]:
- /url: //www.bizmax.net
- img [ref=e36]
- link "비즈맥스솔루션 BIZMAX.NET" [ref=e39] [cursor=pointer]:
- /url: //www.bizmax.net
- generic [ref=e40]: 전산 프로그램 개설 문의 ☏ 010-2522-9862

200
README-bizmax.md Normal file
View File

@@ -0,0 +1,200 @@
# Bizmax Excel Downloader
이 디렉터리에는 `hyun.bizmax.net`, `hyun2.bizmax.net` 고객목록 엑셀을 기간별로 다운로드하는 스크립트가 들어 있습니다.
## 파일
- `bizmax-download-excel.ts`: 실제 다운로드 로직
- `convert-xlsx-to-csv.ts`: 다운로드된 `.xlsx` 전체를 하나의 `.csv`로 합침
- `compare-csv-phone.ts`: `a.csv`, `b.csv`를 전화번호 기준으로 비교
- `run-bizmax.sh`: `hyun`, `hyun2`, `all` 선택 실행
- `run-hyun.sh`: `hyun.bizmax.net` 전용 실행
- `run-hyun2.sh`: `hyun2.bizmax.net` 전용 실행
- `run-convert-csv.sh`: 다운로드된 `.xlsx` 전체를 하나의 `.csv`로 합침
- `run-compare-csv.sh`: `a.csv`, `b.csv`를 전화번호 기준으로 비교
- `.env.example`: 환경변수 예시
## 1. 환경 설정
`.env.example`를 복사해서 `.env` 파일을 만듭니다.
```bash
cp .env.example .env
```
`.env` 예시:
```env
BIZMAX_ID=JH1204
BIZMAX_PASSWORD=12345
BIZMAX_RANGE_START=2016-01-01
BIZMAX_BATCH_MONTHS=3
BIZMAX_BASE_URLS=https://hyun.bizmax.net,https://hyun2.bizmax.net
BIZMAX_OUTPUT_DIR=/home/pure13700/project/download/output
```
## 2. 의존성 설치
최초 1회:
```bash
npm install
```
현재 `package.json`에는 `axios`, `cheerio`, `xlsx`가 들어 있습니다.
## 3. 실행 방법
두 사이트 모두 실행:
```bash
./run-bizmax.sh all
```
`hyun.bizmax.net`만 실행:
```bash
./run-bizmax.sh hyun
```
또는:
```bash
./run-hyun.sh
```
`hyun2.bizmax.net`만 실행:
```bash
./run-bizmax.sh hyun2
```
또는:
```bash
./run-hyun2.sh
```
직접 TypeScript 파일 실행:
```bash
node --experimental-strip-types ./bizmax-download-excel.ts
```
이 경우 `.env`는 자동 로드되지 않으므로, 셸에서 환경변수를 먼저 export 하거나 `run-*.sh` 사용을 권장합니다.
## 4. 동작 방식
- 로그인
- 고객목록 진입
- `2016-01-01`부터 현재 날짜까지 3개월 단위로 조회
- 각 기간별 조회 결과가 `0건`이면 건너뜀
- `1건 이상`이면 첫 번째 엑셀 다운로드
- 파일명에 사이트명과 기간을 붙여 저장
예시 출력:
```text
[hyun_bizmax_net] SKIP 2016-01-01~2016-03-31 (0건)
/home/pure13700/project/download/output/hyun_bizmax_net_현대생활건강_프로그램_고객목록_2016-04-01_2016-06-30.xlsx
```
## 5. 저장 위치
기본 저장 위치:
```text
/home/pure13700/project/download/output
```
변경하려면 `.env``BIZMAX_OUTPUT_DIR` 값을 수정합니다.
CSV 저장 위치 기본값:
```text
/home/pure13700/project/download/output/csv
```
변경하려면 환경변수 `BIZMAX_CSV_DIR`를 사용합니다.
## 6. XLSX -> CSV 변환
다운로드된 전체 `.xlsx`를 하나의 `.csv`로 합치기:
```bash
./run-convert-csv.sh
```
또는:
```bash
node --experimental-strip-types ./convert-xlsx-to-csv.ts
```
다른 CSV 출력 폴더를 쓰려면:
```bash
BIZMAX_CSV_DIR=/home/pure13700/project/download/output/csv node --experimental-strip-types ./convert-xlsx-to-csv.ts
```
기본 출력 파일:
```text
/home/pure13700/project/download/output/csv/bizmax-all.csv
```
파일명을 바꾸려면:
```bash
BIZMAX_CSV_NAME=my-all.csv node --experimental-strip-types ./convert-xlsx-to-csv.ts
```
동작 방식:
- `output/` 아래의 `.xlsx` 파일들을 찾음
- 각 워크시트를 읽음
- 모든 데이터를 하나의 CSV로 합침
- 앞에 `source_file`, `source_sheet` 컬럼을 추가함
참고:
- `.xlsx` 파싱은 `xlsx` 패키지로 처리합니다.
- 모든 파일의 헤더 구조가 동일하다는 전제를 두는 편이 안전합니다.
## 7. 주의 사항
- 계정이 SMS 추가 인증을 요구하면 `axios`만으로는 완료되지 않습니다.
- 사이트 구조가 바뀌면 hidden input 이름이나 다운로드 파라미터도 같이 바뀔 수 있습니다.
- 장기간 전체 다운로드라 실행 시간이 꽤 걸릴 수 있습니다.
## 8. CSV 전화번호 비교
기본 실행:
```bash
COMPARE_FILE_A=./a.csv COMPARE_FILE_B=./b.csv ./run-compare-csv.sh
```
또는:
```bash
COMPARE_FILE_A=./a.csv COMPARE_FILE_B=./b.csv node --experimental-strip-types ./compare-csv-phone.ts
```
기본값:
- `COMPARE_MODE=intersection`: `a.csv``b.csv`에 모두 있는 전화번호만 출력
- `COMPARE_PHONE_COL_A=phone`
- `COMPARE_PHONE_COL_B=phone`
`a.csv`에만 있는 전화번호만 뽑으려면:
```bash
COMPARE_FILE_A=./a.csv COMPARE_FILE_B=./b.csv COMPARE_MODE=only_a node --experimental-strip-types ./compare-csv-phone.ts
```
출력 파일 기본값:
```text
./compare-intersection.csv
```

Binary file not shown.

451
bizmax-download-excel.ts Normal file
View File

@@ -0,0 +1,451 @@
import axios, {
type AxiosRequestConfig,
type AxiosResponse,
type RawAxiosResponseHeaders,
} from "axios";
import * as cheerio from "cheerio";
import { createHash } from "node:crypto";
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
const DEFAULT_BASE_URLS = ["https://hyun.bizmax.net", "https://hyun2.bizmax.net"];
const BASE_URLS = (
process.env.BIZMAX_BASE_URLS ??
process.env.BIZMAX_BASE_URL ??
DEFAULT_BASE_URLS.join(",")
)
.split(",")
.map((value) => value.trim())
.filter(Boolean);
const LOGIN_ID = process.env.BIZMAX_ID;
const LOGIN_PASSWORD = process.env.BIZMAX_PASSWORD;
const OUTPUT_DIR = process.env.BIZMAX_OUTPUT_DIR ?? path.resolve(process.cwd(), "output");
const CUSTOMER_LIST_PATH = "/customer/tpl/customer_list.php";
const RANGE_START = process.env.BIZMAX_RANGE_START ?? "2016-01-01";
const BATCH_MONTHS = Number(process.env.BIZMAX_BATCH_MONTHS ?? "3");
type LoginResponse = {
ret_code?: number;
ret_msg?: string;
login_succ?: string | number;
u_sms_a?: string;
f_url?: string;
lt?: string;
};
class SimpleCookieJar {
private readonly cookies = new Map<string, string>();
addFromHeaders(setCookie: string[] = []): void {
for (const cookieLine of setCookie) {
const [pair] = cookieLine.split(";", 1);
if (!pair) {
continue;
}
const eqIndex = pair.indexOf("=");
if (eqIndex <= 0) {
continue;
}
const name = pair.slice(0, eqIndex).trim();
const value = pair.slice(eqIndex + 1).trim();
this.cookies.set(name, value);
}
}
toHeader(): string {
return Array.from(this.cookies.entries())
.map(([name, value]) => `${name}=${value}`)
.join("; ");
}
}
function md5(value: string): string {
return createHash("md5").update(value).digest("hex");
}
function getRequiredEnv(name: string, value: string | undefined): string {
if (!value) {
throw new Error(`Missing required environment variable: ${name}`);
}
return value;
}
function getSetCookieHeaders(headers: RawAxiosResponseHeaders | Record<string, unknown>): string[] {
const headerValue = headers["set-cookie"];
if (Array.isArray(headerValue)) {
return headerValue.filter((item): item is string => typeof item === "string");
}
return [];
}
async function request<T = unknown>(
jar: SimpleCookieJar,
config: AxiosRequestConfig,
): Promise<AxiosResponse<T>> {
const headers: Record<string, string> = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36",
...(config.headers as Record<string, string> | undefined),
};
const cookieHeader = jar.toHeader();
if (cookieHeader) {
headers.Cookie = cookieHeader;
}
const response = await axios<T>({
...config,
headers,
validateStatus: () => true,
maxRedirects: 0,
});
jar.addFromHeaders(getSetCookieHeaders(response.headers));
if (response.status >= 400) {
throw new Error(`Request failed: ${config.method ?? "GET"} ${config.url} -> ${response.status}`);
}
return response;
}
function absoluteUrl(baseUrl: string, urlOrPath: string): string {
return new URL(urlOrPath, baseUrl).toString();
}
function parseLoginPage(html: string): { appName: string; authStep: string } {
const $ = cheerio.load(html);
const appName = $('input[name="app_name"]').val()?.toString() ?? "Chrome";
const authStep = $('input[name="auth_step"]').val()?.toString() ?? "1";
return { appName, authStep };
}
function parseExcelForm(html: string): { action: string; fields: Record<string, string> } {
const $ = cheerio.load(html);
const form = $('form[name="excel_form"]');
if (!form.length) {
throw new Error("excel_form was not found in the customer list page");
}
const action =
form.attr("action") ||
(() => {
const tpl = form.attr("ctm_tpl_excel") || "customer_excel.php";
return `/customer/tpl/${tpl}`;
})();
if (!action) {
throw new Error("excel_form action was not found");
}
const fields: Record<string, string> = {};
form.find("input[name]").each((_, element) => {
const input = $(element);
const name = input.attr("name");
if (!name) {
return;
}
fields[name] = input.val()?.toString() ?? "";
});
return { action, fields };
}
function parseTotalCount(html: string): number {
const $ = cheerio.load(html);
const explicitValue =
$('form[name="excel_form"] input[name="ctm_list_total_cnt"]').val()?.toString() ??
$('form[name="search_form"] input[name="ctm_list_total_cnt"]').val()?.toString();
if (explicitValue) {
const parsed = Number(explicitValue.replace(/,/g, ""));
if (!Number.isNaN(parsed)) {
return parsed;
}
}
const bodyText = $.text();
const match = bodyText.match(/Total\s*[:]\s*([\d,]+)건/i);
if (match) {
const parsed = Number(match[1].replace(/,/g, ""));
if (!Number.isNaN(parsed)) {
return parsed;
}
}
return 0;
}
function parseSearchForm(html: string): Record<string, string> {
const $ = cheerio.load(html);
const form = $('form[name="search_form"]');
if (!form.length) {
throw new Error("search_form was not found in the customer list page");
}
const fields: Record<string, string> = {};
form.find("input[name], select[name], textarea[name]").each((_, element) => {
const input = $(element);
const name = input.attr("name");
if (!name) {
return;
}
const tagName = element.tagName.toLowerCase();
const type = (input.attr("type") ?? "").toLowerCase();
if (type === "checkbox" || type === "radio") {
if (!input.is(":checked")) {
return;
}
fields[name] = input.val()?.toString() ?? "on";
return;
}
if (tagName === "select") {
fields[name] = input.find("option[selected]").val()?.toString() ?? input.val()?.toString() ?? "";
return;
}
fields[name] = input.val()?.toString() ?? "";
});
return fields;
}
function buildFormBody(fields: Record<string, string>): URLSearchParams {
const params = new URLSearchParams();
for (const [name, value] of Object.entries(fields)) {
params.set(name, value);
}
return params;
}
function getFileNameFromDisposition(contentDisposition: string | undefined): string | null {
if (!contentDisposition) {
return null;
}
const decodeFileName = (value: string): string => {
try {
return decodeURIComponent(value);
} catch {
return value;
}
};
const utf8Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
if (utf8Match) {
return decodeFileName(utf8Match[1]);
}
const basicMatch = contentDisposition.match(/filename="?([^"]+)"?/i);
if (basicMatch) {
return decodeFileName(basicMatch[1]);
}
return null;
}
function toYmd(date: Date): string {
return date.toISOString().slice(0, 10);
}
function addMonths(date: Date, months: number): Date {
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + months, date.getUTCDate()));
}
function addDays(date: Date, days: number): Date {
return new Date(date.getTime() + days * 24 * 60 * 60 * 1000);
}
function minDate(left: Date, right: Date): Date {
return left.getTime() <= right.getTime() ? left : right;
}
function buildQuarterRanges(startYmd: string, batchMonths: number): Array<{ start: string; end: string }> {
const startDate = new Date(`${startYmd}T00:00:00Z`);
const today = new Date();
const endDate = new Date(Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()));
if (Number.isNaN(startDate.getTime())) {
throw new Error(`Invalid BIZMAX_RANGE_START: ${startYmd}`);
}
if (!Number.isInteger(batchMonths) || batchMonths <= 0) {
throw new Error(`Invalid BIZMAX_BATCH_MONTHS: ${batchMonths}`);
}
const ranges: Array<{ start: string; end: string }> = [];
let rangeStart = startDate;
while (rangeStart.getTime() <= endDate.getTime()) {
const nextRangeStart = addMonths(rangeStart, batchMonths);
const rangeEnd = minDate(addDays(nextRangeStart, -1), endDate);
ranges.push({ start: toYmd(rangeStart), end: toYmd(rangeEnd) });
rangeStart = nextRangeStart;
}
return ranges;
}
async function main(): Promise<void> {
const loginId = getRequiredEnv("BIZMAX_ID", LOGIN_ID);
const loginPassword = getRequiredEnv("BIZMAX_PASSWORD", LOGIN_PASSWORD);
await mkdir(OUTPUT_DIR, { recursive: true });
const ranges = buildQuarterRanges(RANGE_START, BATCH_MONTHS);
for (const baseUrl of BASE_URLS) {
const jar = new SimpleCookieJar();
const siteSlug = new URL(baseUrl).hostname.replace(/\./g, "_");
const loginPageResponse = await request<string>(jar, {
method: "GET",
url: absoluteUrl(baseUrl, "/"),
responseType: "text",
});
const { appName, authStep } = parseLoginPage(loginPageResponse.data);
const loginBody = new URLSearchParams({
from: "pc",
nhoj: loginId,
mluap: md5(loginPassword),
app_name: appName,
captcha_str: "",
auth_step: authStep,
force_sms: "n",
});
const loginResponse = await request<LoginResponse>(jar, {
method: "POST",
url: absoluteUrl(baseUrl, "/login_pcs_jr.php"),
data: loginBody.toString(),
responseType: "json",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
Accept: "application/json, text/javascript, */*; q=0.01",
Referer: absoluteUrl(baseUrl, "/"),
Origin: baseUrl,
"X-Requested-With": "XMLHttpRequest",
},
});
const loginData = loginResponse.data;
if (loginData.u_sms_a === "y") {
throw new Error(`[${baseUrl}] This account requires SMS verification. Axios-only automation cannot finish that step.`);
}
if (`${loginData.login_succ ?? ""}` !== "1" || !loginData.f_url) {
throw new Error(`[${baseUrl}] Login failed: ${loginData.ret_msg ?? "unknown error"}`);
}
const mainPageUrl = absoluteUrl(baseUrl, loginData.f_url);
await request<string>(jar, {
method: "GET",
url: mainPageUrl,
responseType: "text",
headers: {
Referer: absoluteUrl(baseUrl, "/"),
},
});
const customerListResponse = await request<string>(jar, {
method: "POST",
url: absoluteUrl(baseUrl, CUSTOMER_LIST_PATH),
data: new URLSearchParams({
_ihr: "n",
_ihl: "n",
_content_only: "n",
site_gubun: "pc",
}).toString(),
responseType: "text",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
Referer: mainPageUrl,
Origin: baseUrl,
"X-Requested-With": "XMLHttpRequest",
},
});
const baseSearchFields = parseSearchForm(customerListResponse.data);
for (const range of ranges) {
const searchFields = {
...baseSearchFields,
_ihr: "n",
_ihl: "n",
_content_only: "n",
site_gubun: "pc",
mode: "search",
page: "",
use_date_1: "a.reg_date",
use_date_1_src: "a.reg_date",
use_date_1_dsp: "- 접수일자",
start_date_1: range.start,
end_date_1: range.end,
};
const rangedCustomerListResponse = await request<string>(jar, {
method: "POST",
url: absoluteUrl(baseUrl, CUSTOMER_LIST_PATH),
data: buildFormBody(searchFields).toString(),
responseType: "text",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
Referer: mainPageUrl,
Origin: baseUrl,
"X-Requested-With": "XMLHttpRequest",
},
});
const totalCount = parseTotalCount(rangedCustomerListResponse.data);
if (totalCount === 0) {
process.stdout.write(`[${siteSlug}] SKIP ${range.start}~${range.end} (0건)\n`);
continue;
}
const { action, fields } = parseExcelForm(rangedCustomerListResponse.data);
fields.e_type = "1";
const excelResponse = await request<ArrayBuffer>(jar, {
method: "POST",
url: absoluteUrl(baseUrl, action),
data: buildFormBody(fields).toString(),
responseType: "arraybuffer",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
Referer: absoluteUrl(baseUrl, CUSTOMER_LIST_PATH),
Origin: baseUrl,
},
});
const contentDisposition =
typeof excelResponse.headers["content-disposition"] === "string"
? excelResponse.headers["content-disposition"]
: undefined;
const fileName =
getFileNameFromDisposition(contentDisposition) ??
`bizmax-customer-list-${range.start}-${range.end}.xlsx`;
const ext = path.extname(fileName);
const baseName = ext ? fileName.slice(0, -ext.length) : fileName;
const rangedFileName = `${siteSlug}_${baseName}_${range.start}_${range.end}${ext || ".xlsx"}`;
const outputPath = path.join(OUTPUT_DIR, rangedFileName);
await writeFile(outputPath, Buffer.from(excelResponse.data));
process.stdout.write(`${outputPath}\n`);
}
}
}
main().catch((error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
process.stderr.write(`${message}\n`);
process.exitCode = 1;
});

106
compare-csv-phone.ts Normal file
View File

@@ -0,0 +1,106 @@
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { parse } from "csv-parse/sync";
const FILE_A = process.env.COMPARE_FILE_A ?? path.resolve(process.cwd(), "a.csv");
const FILE_B = process.env.COMPARE_FILE_B ?? path.resolve(process.cwd(), "b.csv");
const PHONE_COL_A = process.env.COMPARE_PHONE_COL_A ?? "phone";
const PHONE_COL_B = process.env.COMPARE_PHONE_COL_B ?? "phone";
const MODE = process.env.COMPARE_MODE ?? "intersection";
const OUTPUT_FILE =
process.env.COMPARE_OUTPUT_FILE ?? path.resolve(process.cwd(), `compare-${MODE}.csv`);
type CsvRow = Record<string, string>;
function normalizePhone(value: string): string {
return value.replace(/\D+/g, "");
}
function toCsv(rows: CsvRow[]): string {
if (rows.length === 0) {
return "";
}
const headers = Array.from(
rows.reduce((set, row) => {
for (const key of Object.keys(row)) {
set.add(key);
}
return set;
}, new Set<string>()),
);
const escape = (value: string): string => {
if (/[",\n\r]/.test(value)) {
return `"${value.replace(/"/g, "\"\"")}"`;
}
return value;
};
const lines = [headers.join(",")];
for (const row of rows) {
lines.push(headers.map((header) => escape(row[header] ?? "")).join(","));
}
return `${lines.join("\n")}\n`;
}
async function readCsv(filePath: string): Promise<CsvRow[]> {
const content = await readFile(filePath, "utf8");
return parse(content, {
columns: true,
skip_empty_lines: true,
}) as CsvRow[];
}
async function main(): Promise<void> {
const [rowsA, rowsB] = await Promise.all([readCsv(FILE_A), readCsv(FILE_B)]);
const phonesB = new Set(
rowsB.map((row) => normalizePhone(row[PHONE_COL_B] ?? "")).filter(Boolean),
);
const matches: CsvRow[] = [];
const onlyA: CsvRow[] = [];
for (const row of rowsA) {
const normalized = normalizePhone(row[PHONE_COL_A] ?? "");
if (!normalized) {
continue;
}
if (phonesB.has(normalized)) {
matches.push({
source: "a",
normalized_phone: normalized,
...row,
});
} else {
onlyA.push({
source: "a",
normalized_phone: normalized,
...row,
});
}
}
let outputRows: CsvRow[];
switch (MODE) {
case "intersection":
outputRows = matches;
break;
case "only_a":
outputRows = onlyA;
break;
default:
throw new Error(`Unsupported COMPARE_MODE: ${MODE}. Use intersection or only_a.`);
}
await writeFile(OUTPUT_FILE, toCsv(outputRows), "utf8");
process.stdout.write(`${OUTPUT_FILE}\n`);
}
main().catch((error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
process.stderr.write(`${message}\n`);
process.exitCode = 1;
});

91
convert-xlsx-to-csv.ts Normal file
View File

@@ -0,0 +1,91 @@
import XLSX from "xlsx";
import { mkdir, readdir, writeFile } from "node:fs/promises";
import path from "node:path";
const INPUT_DIR = process.env.BIZMAX_OUTPUT_DIR ?? path.resolve(process.cwd(), "output");
const CSV_DIR = process.env.BIZMAX_CSV_DIR ?? path.join(INPUT_DIR, "csv");
const CSV_NAME = process.env.BIZMAX_CSV_NAME ?? "bizmax-all.csv";
function toText(value: unknown): string {
if (value == null) {
return "";
}
return String(value);
}
function escapeCsv(value: string): string {
if (/[",\n\r]/.test(value)) {
return `"${value.replace(/"/g, "\"\"")}"`;
}
return value;
}
function rowToCsv(row: string[]): string {
return row.map(escapeCsv).join(",");
}
async function main(): Promise<void> {
const entries = await readdir(INPUT_DIR, { withFileTypes: true });
const xlsxFiles = entries
.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".xlsx"))
.map((entry) => path.join(INPUT_DIR, entry.name))
.sort();
if (xlsxFiles.length === 0) {
throw new Error(`No .xlsx files found in ${INPUT_DIR}`);
}
const outputRows: string[] = [];
let wroteHeader = false;
for (const xlsxFile of xlsxFiles) {
const workbook = XLSX.readFile(xlsxFile, { cellDates: false });
for (const sheetName of workbook.SheetNames) {
const worksheet = workbook.Sheets[sheetName];
const rows = XLSX.utils.sheet_to_json<unknown[]>(worksheet, {
header: 1,
raw: false,
defval: "",
});
if (rows.length === 0) {
continue;
}
const headerRow = (rows[0] ?? []).map(toText);
if (!wroteHeader) {
outputRows.push(
rowToCsv(["source_file", "source_sheet", ...headerRow]),
);
wroteHeader = true;
}
for (const row of rows.slice(1)) {
const values = (row as unknown[]).map(toText);
outputRows.push(
rowToCsv([
path.basename(xlsxFile),
sheetName,
...values,
]),
);
}
}
}
if (!wroteHeader) {
throw new Error(`No worksheet rows found in ${INPUT_DIR}`);
}
await mkdir(CSV_DIR, { recursive: true });
const outputPath = path.join(CSV_DIR, CSV_NAME);
await writeFile(outputPath, `${outputRows.join("\n")}\n`, "utf8");
process.stdout.write(`${outputPath}\n`);
}
main().catch((error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
process.stderr.write(`${message}\n`);
process.exitCode = 1;
});

271919
output/csv/bizmax-all.csv Normal file

File diff suppressed because it is too large Load Diff

696
package-lock.json generated Normal file
View File

@@ -0,0 +1,696 @@
{
"name": "download",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"axios": "^1.15.0",
"cheerio": "^1.2.0",
"xlsx": "^0.18.5"
}
},
"node_modules/adler-32": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz",
"integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"proxy-from-env": "^2.1.0"
}
},
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"license": "ISC"
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/cfb": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
"license": "Apache-2.0",
"dependencies": {
"adler-32": "~1.3.0",
"crc-32": "~1.2.0"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/cheerio": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz",
"integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==",
"license": "MIT",
"dependencies": {
"cheerio-select": "^2.1.0",
"dom-serializer": "^2.0.0",
"domhandler": "^5.0.3",
"domutils": "^3.2.2",
"encoding-sniffer": "^0.2.1",
"htmlparser2": "^10.1.0",
"parse5": "^7.3.0",
"parse5-htmlparser2-tree-adapter": "^7.1.0",
"parse5-parser-stream": "^7.1.2",
"undici": "^7.19.0",
"whatwg-mimetype": "^4.0.0"
},
"engines": {
"node": ">=20.18.1"
},
"funding": {
"url": "https://github.com/cheeriojs/cheerio?sponsor=1"
}
},
"node_modules/cheerio-select": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
"integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-select": "^5.1.0",
"css-what": "^6.1.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/codepage": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"license": "Apache-2.0",
"bin": {
"crc32": "bin/crc32.njs"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/css-select": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
"integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
"domhandler": "^5.0.2",
"domutils": "^3.0.1",
"nth-check": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/css-what": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
"integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"license": "MIT",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/domelementtype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "BSD-2-Clause"
},
"node_modules/domhandler": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^2.3.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domutils": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
"license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/encoding-sniffer": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz",
"integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==",
"license": "MIT",
"dependencies": {
"iconv-lite": "^0.6.3",
"whatwg-encoding": "^3.1.1"
},
"funding": {
"url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/frac": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/htmlparser2": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
"integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "MIT",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.2.2",
"entities": "^7.0.1"
}
},
"node_modules/htmlparser2/node_modules/entities": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0"
},
"funding": {
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
"node_modules/parse5": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
"license": "MIT",
"dependencies": {
"entities": "^6.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5-htmlparser2-tree-adapter": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
"integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
"license": "MIT",
"dependencies": {
"domhandler": "^5.0.3",
"parse5": "^7.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5-parser-stream": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
"integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
"license": "MIT",
"dependencies": {
"parse5": "^7.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5/node_modules/entities": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/proxy-from-env": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/ssf": {
"version": "0.11.2",
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
"license": "Apache-2.0",
"dependencies": {
"frac": "~1.1.2"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/undici": {
"version": "7.24.7",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.24.7.tgz",
"integrity": "sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
}
},
"node_modules/whatwg-encoding": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
"deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
"license": "MIT",
"dependencies": {
"iconv-lite": "0.6.3"
},
"engines": {
"node": ">=18"
}
},
"node_modules/whatwg-mimetype": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/wmf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/word": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.8"
}
},
"node_modules/xlsx": {
"version": "0.18.5",
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
"license": "Apache-2.0",
"dependencies": {
"adler-32": "~1.3.0",
"cfb": "~1.2.1",
"codepage": "~1.15.0",
"crc-32": "~1.2.1",
"ssf": "~0.11.2",
"wmf": "~1.0.1",
"word": "~0.3.0"
},
"bin": {
"xlsx": "bin/xlsx.njs"
},
"engines": {
"node": ">=0.8"
}
}
}
}

9
package.json Normal file
View File

@@ -0,0 +1,9 @@
{
"type": "module",
"dependencies": {
"axios": "^1.15.0",
"cheerio": "^1.2.0",
"csv-parse": "^5.5.6",
"xlsx": "^0.18.5"
}
}

37
run-bizmax.sh Executable file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${BIZMAX_ENV_FILE:-$SCRIPT_DIR/.env}"
TARGET="${1:-all}"
if [[ -f "$ENV_FILE" ]]; then
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
fi
case "$TARGET" in
hyun)
BASE_URLS="https://hyun.bizmax.net"
;;
hyun2)
BASE_URLS="https://hyun2.bizmax.net"
;;
all)
BASE_URLS="https://hyun.bizmax.net,https://hyun2.bizmax.net"
;;
*)
echo "Usage: $0 [hyun|hyun2|all]" >&2
exit 1
;;
esac
if [[ -z "${BIZMAX_ID:-}" || -z "${BIZMAX_PASSWORD:-}" ]]; then
echo "Set BIZMAX_ID and BIZMAX_PASSWORD before running." >&2
exit 1
fi
BIZMAX_BASE_URLS="$BASE_URLS" \
node --experimental-strip-types "$SCRIPT_DIR/bizmax-download-excel.ts"

14
run-compare-csv.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${BIZMAX_ENV_FILE:-$SCRIPT_DIR/.env}"
if [[ -f "$ENV_FILE" ]]; then
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
fi
node --experimental-strip-types "$SCRIPT_DIR/compare-csv-phone.ts"

14
run-convert-csv.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${BIZMAX_ENV_FILE:-$SCRIPT_DIR/.env}"
if [[ -f "$ENV_FILE" ]]; then
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
fi
node --experimental-strip-types "$SCRIPT_DIR/convert-xlsx-to-csv.ts"

20
run-hyun.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${BIZMAX_ENV_FILE:-$SCRIPT_DIR/.env}"
if [[ -f "$ENV_FILE" ]]; then
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
fi
if [[ -z "${BIZMAX_ID:-}" || -z "${BIZMAX_PASSWORD:-}" ]]; then
echo "Set BIZMAX_ID and BIZMAX_PASSWORD before running." >&2
exit 1
fi
BIZMAX_BASE_URLS="https://hyun.bizmax.net" \
node --experimental-strip-types "$SCRIPT_DIR/bizmax-download-excel.ts"

20
run-hyun2.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${BIZMAX_ENV_FILE:-$SCRIPT_DIR/.env}"
if [[ -f "$ENV_FILE" ]]; then
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
fi
if [[ -z "${BIZMAX_ID:-}" || -z "${BIZMAX_PASSWORD:-}" ]]; then
echo "Set BIZMAX_ID and BIZMAX_PASSWORD before running." >&2
exit 1
fi
BIZMAX_BASE_URLS="https://hyun2.bizmax.net" \
node --experimental-strip-types "$SCRIPT_DIR/bizmax-download-excel.ts"