목표 : 네이버 로그인 후 메일 목록 크롤링 해오기
단계별 목표
1. 네이버 로그인 성공
2. 메일 첫 페이지의 내용 크롤링
3. 메일 하단 페이지 넘기며 메일 전체 크롤링
개발 환경 : MacBook pro / IntelliJ IDEA / NodeJS / Express
사용할 라이브러리 : pupeteer
참고 사이트 url.
pupeteer : http://magic.wickedmiso.com/138
특정 영역 클릭 : http://magic.wickedmiso.com/144
네이버 로그인 : https://ncube.net/14128
# 전체 코드
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless : false
});
const page = await browser.newPage();
const naver_id = "ID";
const naver_pw = "PW";
await page.goto('https://nid.naver.com/nidlogin.login');
await page.evaluate((id, pw) => {
document.querySelector('#id').value = id;
document.querySelector('#pw').value = pw;
}, naver_id, naver_pw);
await page.click('.btn_global');
await page.waitForNavigation();
await page.goto('https://mail.naver.com');
let data = [];
let lineCount = 30;
const maxCount = 30;
const allMail = await page.$eval("#headTotalNum", (data) => data.textContent);
let pageCount = parseInt(allMail/30 + 1);
for (let index = 1; index <= pageCount; index++) {
await page.goto(`https://mail.naver.com/#%7B%22fClass%22%3A%22list%22%2C%22oParameter%22%3A%7B%22page%22%3A%22${index}%22%2C%22sortField%22%3A%221%22%2C%22sortType%22%3A%220%22%2C%22folderSN%22%3A%220%22%2C%22type%22%3A%22%22%2C%22isUnread%22%3Afalse%7D%7D`);
data.push(await getAll(page, index));
await page.waitFor(1000);
}
console.log(data);
await page.waitFor(1000);
await browser.close();
})();
async function getAll(page, index) {
let data = [];
let lineCount = 30;
const maxCount = 30;
const allMail = await page.$eval("#headTotalNum", (data) => data.textContent);
let pageCount = parseInt(allMail/30 + 1);
if(index === pageCount){
lineCount = (allMail%30);
} else {
lineCount = maxCount;
}
for (let index = 0; index < lineCount; index++) {
data.push(await getOne(page, index + 1));
}
return Promise.resolve(data);
}
async function getOne(page, index) {
let data = {};
data.title = await page.$eval(`#list_for_view > ol > li:nth-child(${index}) > div > div.subject > a > span > strong`, (data) => data.textContent);
data.link = await page.$eval(`#list_for_view > ol > li:nth-child(${index}) > div > div.subject > a`, (data) => data.href);
data.from = await page.$eval(`#list_for_view > ol > li:nth-child(${index}) > div > div > a`, (data) => data.textContent);
return Promise.resolve(data);
}
# 코드 세부 설명.
const puppeteer = require('puppeteer');
pupeteer 라이브러리를 사용하기 위해 선언.
const browser = await puppeteer.launch({
headless : false
});
pupeteer 라이브러리를 이용하여 .launch 내부에 필요한 조건을 넣어서 브라우저를 실행 시킨다.
( headless : false 로 할 경우 실행하는 모습을 모니터로 확인 할 수 있다.
true로 할 경우 보이지 않는 백그라운드에서 실행을 한다.
처음에 프로그래밍 할 때는 어디에서 에러가 발생해서 멈추는지 보기 위해 false로 설정 했다.)
const page = await browser.newPage();
.newPage()의 경우 말 그대로 새로운 페이지(탭)을 여는 것이다.
브라우저 실행 후 새로운 탭을 생성한다.
const naver_id = "ID";
const naver_pw = "PW";
await page.goto('https://nid.naver.com/nidlogin.login');
await page.evaluate((id, pw) => {
document.querySelector('#id').value = id;
document.querySelector('#pw').value = pw;
}, naver_id, naver_pw);
await page.click('.btn_global');
ID와 PW를 변수에 저장한다.
page.goto('');를 이용하여 네이버의 로그인 페이지로 이동한다.
page.evaluate()를 이용하여 #id에 해당하는 태그의 값으로 저장한 ID, PW 값을 넣는다.
page.click() 로 class='.btn_global'에 해당하는 부분을 클릭한다.
~ 여기까지 네이버 로그인 부분.
await page.waitForNavigation();
await page.goto('https://mail.naver.com');
waitForNavigation() 해당 페이지의 로딩이 완료될 때 까지 기다린다.
page.goto() 네이버 메일 서비스 url로 이동한다.
let data = [];
let lineCount = 30;
const maxCount = 30;
사용할 각종 변수를 선언.
const allMail = await page.$eval("#headTotalNum", (data) => data.textContent);
let pageCount = parseInt(allMail/30 + 1);
네이버 메일의 경우 한 페이지의 메일 수가 30개로 고정되어 있지만 마지막 페이지의 메일 수는 체크 해야한다.
이를 알 수 있는 방법이 여러가지 있다.
1. 해당 페이지를 탐색할 때 메일 리스트가 저장되는 ol 태그 자식 태그인 li 태그의 수를 세는 방법.
2. 메일 서비스 페이지 상단의 전체 메일 수로 계산하는 방법이 있다.
둘 중 2번 방법을 사용하였다.
한 페이지에 30개의 메일이 리스트화 되어 생성되니 전체 수에서 30으로 나눈 몫으로 페이지를 알아내고,
마지막 페이지의 메일 수는 전체 수에서 30으로 나눈 나머지 값으로 알아내었다.
1번째 줄은 총 메일 수에 해당하는 태그의 id 값인 'headTotalNum'을 찾아와 allMail에 저장하는 것이다.
2번째 줄에 parseInt 는 나눈 몫이 소수점으로 나왔을때를 대비해 int형태로 변환을 해주었다.
for (let index = 1; index <= pageCount; index++) {
await page.goto(`https://mail.naver.com/#%7B%22fClass%22%3A%22list%22%2C%22oParameter%22%3A%7B%22page%22%3A%22${index}%22%2C%22sortField%22%3A%221%22%2C%22sortType%22%3A%220%22%2C%22folderSN%22%3A%220%22%2C%22type%22%3A%22%22%2C%22isUnread%22%3Afalse%7D%7D`);
data.push(await getAll(page, index));
await page.waitFor(1000);
}
이제 for문을 이용하여 1페이지부터 아까 위에서 계산해서 얻은 마지막 페이지가 될 때 까지 돌아다니며
메일 데이터를 getAll을 불러서 가지고 오도록 시킨다.
page.goto()의 경우 url이 거창하게 길어보이지만.. 별거 없고 중간에 메일 페이지에 따라 바뀌는 숫자 부분만
${변수명}으로 이동하면 된다. ($을 사용할 경우 url을 큰 따옴표, 작은 따옴표가 아닌 백틱(Tab 위에 키)을 써야한다.)
page.waitFor(1000)은 1000ms 를 기다린다.
console.log(data);
await page.waitFor(1000);
await browser.close();
})();
위의 for를 다 돌고나면 저장한 데이터를 출력 한다.
그리고 1000ms 를 기다린 후 브라우저를 종료한다.
~ 여기까지 메인 루프
이제 위에서 불렀던 getAll에 대해 알아보자
async function getAll(page, index) {
let data = [];
let lineCount = 30;
const maxCount = 30;
const allMail = await page.$eval("#headTotalNum", (data) => data.textContent);
let pageCount = parseInt(allMail/30 + 1);
if(index === pageCount){
lineCount = (allMail%30);
} else {
lineCount = maxCount;
}
for (let index = 0; index < lineCount; index++) {
data.push(await getOne(page, index + 1));
}
return Promise.resolve(data);
}
getAll 함수에서도 똑같이 전체 메일 수로 마지막 페이지 인지 확인한다.
전달 받은 index와 pageCount가 같으면 마지막 페이지로 인식하여 남은 메일의 수만큼만 메일을 확인한다.
마지막이 아닐 경우 maxCount인 30을 기준으로 메일을 불러온다.
그리고 한 페이지의 메일 수를 기준으로 getOne 함수를 호출하여 데이터를 push로 저장한다.
한 페이지의 메일을 모두 불러오면 for문이 끝나고 저장한 data 값을 리턴한다.
~여기까지 getAll 함수
바로 위에서 호출된 getOne에 대해 알아보자
async function getOne(page, index) {
let data = {};
data.title = await page.$eval(`#list_for_view > ol > li:nth-child(${index}) > div > div.subject > a > span > strong`, (data) => data.textContent);
data.link = await page.$eval(`#list_for_view > ol > li:nth-child(${index}) > div > div.subject > a`, (data) => data.href);
data.from = await page.$eval(`#list_for_view > ol > li:nth-child(${index}) > div > div > a`, (data) => data.textContent);
return Promise.resolve(data);
}
실질적으로 메일의 제목과 url, 보낸사람의 데이터를 가져오는 부분이다.
여기에서 시간을 많이 보냈다.
보통의 게시글은 td로 되어 클래스 이름으로 구분이 안되어도 nth-child(n)으로 n번째를 선택 할 수 있지만
li도 마찬가지로 nth-child(n)을 사용 할 수 있는지 몰랐기 때문이다.
각각 제목, url, 보낸사람이 저장되어 있는 위치를 지정하여 데이터를 가지고 와서 저장하고 return 한다.
# 출력 결과

끝.
'Program language > NodeJS' 카테고리의 다른 글
| [puppeteer] 강력한 $$eval 사용법. (0) | 2020.03.30 |
|---|---|
| [nodeJS] 브라우저로 서버 일 시키기. (0) | 2020.03.27 |
| 웹에서 버튼을 누르면 크롤링이 되도록 하기. (0) | 2020.03.05 |
| NodeJS 로 크롤링한 데이터 .txt 저장 (0) | 2020.02.28 |
| NodeJS 로 okky.kr 메인의 Q&A 크롤링하기 (0) | 2020.02.28 |