-
-
Save listly-io/dcca6c4540cedc849fa984ad88ab12d4 to your computer and use it in GitHub Desktop.
daiso_review_2026-05-29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // test page: https://www.daisomall.co.kr/pd/pdr/SCR_PDR_0001?pdNo=B202505133965&recmYn=N | |
| // ============================================================ | |
| // ▶ 사용자 설정 영역 (필요에 따라 수정하세요) | |
| // ============================================================ | |
| const MAX_PAGE_COUNT = 10; // 수집할 페이지 수 (1 = 현재 페이지만) | |
| const WAIT_AFTER_CLICK = 5000; // 다음 페이지 클릭 후 대기 시간 (ms) | |
| const WAIT_BEFORE_CLICK = 1000; // 스크롤 후 클릭 전 대기 시간 (ms) | |
| const REVIEW_LIST_SELECTOR = 'ul.review-list > li.review-detail'; // 개별 리뷰 항목 셀렉터 | |
| const PAGINATION_SELECTOR = '.el-pagination'; // 페이지네이션 영역 셀렉터 | |
| const NEXT_BTN_SELECTOR = `${PAGINATION_SELECTOR} .btn-next`; // 다음 버튼 셀렉터 | |
| const CONTAINER_ID = '__review_collector'; // 수집 컨테이너 ID | |
| // ============================================================ | |
| // ▶ 내부 로직 (수정 불필요) | |
| // ============================================================ | |
| (async () => { | |
| const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); | |
| // 1. 수집 컨테이너 생성 (이미 있으면 재사용) | |
| let container = document.getElementById(CONTAINER_ID); | |
| if (!container) { | |
| container = document.createElement('div'); | |
| container.id = CONTAINER_ID; | |
| Object.assign(container.style, { | |
| position: 'fixed', | |
| right: '200px', | |
| bottom: '200px', | |
| zIndex: '999999', | |
| background: 'rgba(0, 0, 0, 0.85)', | |
| color: '#0f0', | |
| fontSize: '18px', | |
| fontWeight: 'bold', | |
| fontFamily: 'monospace', | |
| padding: '16px 24px', | |
| borderRadius: '12px', | |
| border: '2px solid #0f0', | |
| boxShadow: '0 4px 20px rgba(0,255,0,.3)', | |
| cursor: 'default', | |
| userSelect: 'none', | |
| overflow: 'hidden', | |
| }); | |
| document.body.appendChild(container); | |
| } | |
| // 수집 저장소 (숨김 영역) — 컨테이너 내부에 display:none 인 래퍼 | |
| let store = container.querySelector('#__review_store'); | |
| if (!store) { | |
| store = document.createElement('div'); | |
| store.id = '__review_store'; | |
| store.style.display = 'none'; | |
| container.appendChild(store); | |
| } | |
| // 카운트 표시 라벨 | |
| let label = container.querySelector('#__review_label'); | |
| if (!label) { | |
| label = document.createElement('div'); | |
| label.id = '__review_label'; | |
| container.appendChild(label); | |
| } | |
| const updateLabel = () => { | |
| const count = store.children.length; | |
| label.textContent = `수집된 리뷰: ${count}개`; | |
| }; | |
| // 2. 현재 페이지 리뷰 수집 함수 | |
| const collectCurrentPage = () => { | |
| const reviews = document.querySelectorAll( | |
| `${REVIEW_LIST_SELECTOR}:not(#${CONTAINER_ID} *)` | |
| ); | |
| let added = 0; | |
| reviews.forEach((el) => { | |
| const clone = el.cloneNode(true); | |
| store.appendChild(clone); | |
| added++; | |
| }); | |
| console.log(`[Scraper] ${added}개 리뷰 수집 (누적 ${store.children.length}개)`); | |
| updateLabel(); | |
| }; | |
| // 3. 다음 페이지 버튼 클릭 함수 (스크롤 → 대기 → 클릭) | |
| // 반환: true = 클릭 성공, false = 버튼 없음/비활성 | |
| const isDisabled = (b) => | |
| !b || | |
| b.disabled || | |
| b.classList.contains('is-disabled') || | |
| b.getAttribute('aria-disabled') === 'true'; | |
| const goNextPage = async () => { | |
| const btn = document.querySelector(NEXT_BTN_SELECTOR); | |
| if (isDisabled(btn)) return false; | |
| // 페이지네이션 영역으로 스크롤 | |
| const paginationArea = document.querySelector(PAGINATION_SELECTOR); | |
| if (paginationArea) { | |
| paginationArea.scrollIntoView({ behavior: 'smooth', block: 'center' }); | |
| } | |
| await sleep(WAIT_BEFORE_CLICK); | |
| // 다시 한번 버튼 상태 확인 (스크롤 중 상태 변경 가능) | |
| const btnAgain = document.querySelector(NEXT_BTN_SELECTOR); | |
| if (isDisabled(btnAgain)) return false; | |
| btnAgain.click(); | |
| return true; | |
| }; | |
| // 4. 메인 루프 | |
| console.log(`[Scraper] 수집 시작 — 최대 ${MAX_PAGE_COUNT}페이지`); | |
| for (let page = 1; page <= MAX_PAGE_COUNT; page++) { | |
| // 현재 페이지 수집 | |
| collectCurrentPage(); | |
| console.log(`[Scraper] ${page}/${MAX_PAGE_COUNT} 페이지 수집 완료`); | |
| // 마지막 페이지면 종료 | |
| if (page === MAX_PAGE_COUNT) break; | |
| // 다음 페이지로 이동 | |
| const moved = await goNextPage(); | |
| if (!moved) { | |
| console.log('[Scraper] 다음 페이지 없음 — 수집 종료'); | |
| break; | |
| } | |
| // 페이지 로딩 대기 | |
| await sleep(WAIT_AFTER_CLICK); | |
| } | |
| console.log(`[Scraper] 수집 완료! 총 ${store.children.length}개 리뷰`); | |
| updateLabel(); | |
| // ▶ 나중에 수집된 HTML을 꺼내려면 콘솔에서: | |
| // document.getElementById('__review_store').innerHTML | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment