ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Google Puppeteer를 이용한 네이버 밴드 파싱 프로그램 개발기
    프로그래밍/NodeJS 2019. 4. 1. 19:46
    반응형


    전에 친구들과 카카오아지트를 사용하면서 항상 데이터를 모으고 싶다는 생각은 하곤있었지만 실제로 행하진 못했습니다. 이후에 구버전 카카오아지트가 서비스를 종료하기도 하면서 그 이후론 네이버 밴드를 사용하였습니다.

     

    최근에 빅데이터에 관심이 많아지면서 데이터를 수집하고 정리하여 활용해보는것을 좋아하는데 이를 위해선 어디에선가 데이터를 스크랩해와야하고 저는 그동안 정보가 많이 쌓인 네이버 밴드를 파싱해보기로 하였습니다.

     

    우선 공식적인 방법으로 파싱하는 방법을 찾아보니 네이버 밴드에서 제공하는 Band API가 존재하지만 쿼터가 너무 적은데다가 문서가 너무 제대로 되어있지않기도 하고 구글에 네이버 밴드를 활용한 프로젝트가 너무 적어서.. 다른 방법을 사용해보기로 하였습니다.

     

    NodeJS를 활용해서 파싱해올것이라 여러가지 파싱 모듈들을 찾아보니 Request, CheerIO 조합을 이용한 파싱 방법이 있었지만 이걸 활용해서 인증을 패스하기 너무 어려워 포기하였고 (찾아보니 Header 값에 인증 데이터를 넣으면 가능한듯한데 밴드 인증 쿠키가 너무 많고 수시로 변해서 공통된 값을 찾기 힘들었습니다.) PhantomJS의 경우에는 ES6를 지원하지 않아서 패스.. 그래서 마지막으로 찾게된 모듈은 구글에서 개발한 Puppeteer라는 모듈이였습니다.

     

    우선 구글에서 개발하여 꾸준한 지원도 예상되었고, 아무래도 사용폭도 넓다보니 stackoverflow나 구글링하여 정보찾기도 수월한 편이였기때문입니다.

     

    우선 Puppeteer를 간단히 소개드리면 화면 없는 크롬을 구현하여 일일히 크롬을 조작할수 있는 NodeJS 모듈입니다.

     


     

    제가 파싱 프로그램을 제작하면서 가장 문제가 되었던 인증 부분에서 화면을 일일히 코드로 조작할수 있다보니 문제가 되는 부분을 해결할수 있었습니다.

     

    일단 제가 처음에 목표한 기능은 네이버 특정밴드를 정해주면 전체 글(게시글 및 댓글 포함)을 캡쳐 (하지만 전체 페이지가 아닌 게시글 파트만)하여 저장하는 것이였고 네이버 밴드의 경우 게시글, 댓글의 고유 URL이 비교적 반복성을 띄고 있는 부분을 찾기쉬워 편하게 개발이 가능하였습니다. 처음에 Puppeteer 모듈 자체가 익숙하지 못한 부분이 있고 JavaScript의 Promise, Async 함수등의 적용이 조금 어려웠던 부분이 있었지만 다른 코드들을 보니 조금 이해가 되었습니다.

     

    우선 네이버 밴드 URL은 정말 단순합니다. 다만 이걸 내 글보기에선 따로 알수가 없어서 처음 좀 애먹었습니다.

     

    https://band.us/band/밴드고유번호/post/포스트넘버/ 와 같은 형식으로 URL이 구성되어있습니다.

     

    이 프로그램에 적용시키기 위해서는 밴드 고유번호만 알면됩니다. 포스트넘버의 경우에는 구간별로 파싱해오기 위해서 필요한 값입니다. 1이 밴드에서 가장 처음 생성된 게시글 URL입니다.

     

    우선 전체 코드는 아래와 같습니다. 기능적인 부분은 주석을 전부 달아두었기때문에 확인가능하실겁니다.

    const puppeteer = require('puppeteer'); // Puppeteer 모듈 Load
    (async () => {
        const browser = await puppeteer.launch({    // Puppeteer 모듈을 사용하지 않고 기존의 크롬 사용자 정보를 사용 (Auth 인증을 패스하기 위하여)
            executablePath: 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
            userDataDir: 'C:\\Users\\{본인컴퓨터이름}\\AppData\\Local\\Google\\Chrome\\User Data',   // 설치시 개인 크롬 Directory로 수정하여야함
            headless: true
        });
        const page = await browser.newPage();   // Broswer Open
        await page.setViewport({    // Viewport 설정 가로의 경우 일반적으로 최대 1920, 새로의 경우 예상되는 최대 px를 지정해주면됨
            width: 800,
            height: 6000
        });
        page.on('dialog', async dialog => { // 삭제된 게시글의 경우 Band에서 Dialog를 띄우는데 이를 제거하기 위하여 필요
            console.log(dialog.message());
            await dialog.dismiss(); // Dialog 창 닫음
            postNumber++;   // 삭제된 게시글의 경우 Dialog 창이 닫힌후에 이전 URL로 돌아가므로 postNumber 1증가 시켜줌
            await page.goto(`https://band.us/band/58075840/post/${postNumber}`, {
                waitUntil: 'networkidle2'
            });
        })
        let postNumber = 1;  // 시작되는 PostNumber * 이 부분 부터 시작 *
        while (postNumber <= 3500) {    // PostNumber 끝값 * 이 부분은 마지막 값 *
            await page.goto(`https://band.us/band/58075840/post/${postNumber}`, {
                waitUntil: 'networkidle2'   // 페이지가 완전히 Load된후 작동
            });
            const element = await page.$('.boardList'); // 게시글, 댓글 전체 Class
            const by = await page.evaluate(() => document.getElementsByClassName('text')[0].textContent);   // 게시글 작성자 Text 파싱
            const date = await page.evaluate(() => document.getElementsByClassName('time')[0].textContent); // 게시글 작성일 Text 파싱
            await element.screenshot({  // ScreenShot Function
                path: `./image/${postNumber}-${by}-${date.replace(":","_")}.png`    // 파일 저장 위치 & 파일이름 지정, replace 메소드의 경우 Windows 탐색기에서 :를 파일명으로 지원하지 않기때문
            });
            console.log(`${postNumber}-${by}-${date.replace(":","_")}.png`) // Console.log에서 파일 확인
            postNumber++;   // 최종 성공시 postnumber 증가
        }
        await browser.close();  // 종료
    })();

    참고할 부분은 본인의 크롬데이터를 활용해서 인증부분을 패스하기때문에 크롬에서 밴드로그인이 되어있어야하며 중간의 postNumber 값과 반복문 while안의 끝값을 지정해주어야 구간으로 파싱이 가능합니다.

     

    파일은 image 폴더에 저장되며 글번호-작성자-작성날짜.png의 형식으로 저장되게 되어있습니다. 상단의 Headless 옵션은 자유이나 true일때 원인불명의 에러가 가끔 발생하여 false로 해두는것을 추천합니다.

     

    잘 작동될 경우 아래와 같이 작동합니다. 삭제된 게시글의 경우에는 패스합니다.

     

    해당 최신화 코드는 아래 링크에서도 가능하며 댓글 또는 ISSUE 탭을 활용해서 피드백 & 질문사항을 부탁드립니다.

    반응형

    댓글 2

Designed by Tistory.