NodeJS 웹 크롤링 Ch02

수박·2020년 9월 30일
0

nodejs

목록 보기
16/19

노드 웹 크롤링 Ch02


공식문서

2.1 puppeteer 시작하기

퍼펫티어를 사용하는 이유

  • 1초에 10번씩 클릭하면 안되므로 사람인척 해야함.

  • userAgent : 내 브라우저가 크롬인지, 파폭인지 나타내는 문자열 접속할 때 bot은 bot이라고 티가남. 따라서 정상적인 브라우저로 속이기 위해서


설치

  • npm i puppeteer
  • 크롬 브라우저의 기반인 Chromium 브라우저를 같이 받으므로 서버용량이 1기가정도 여유가 되어야함

구현 예제

  • 페이지 열고, 기다리고하는 작업들은 비동기작업이라서 퍼펫티어 명령어는 앞에 await를 붙이는 비동기 처리를 해야한다.

  • const crawler = async() => {
        try{
        const browse = await puppeteer.launch({ headless: false});
        const page = await brower.newPage();
        await page.goto('secho.com'); //페이지이동
        await page.waitFor(3000); //로딩 후 3초뒤 종료시키기
        await page.close();// 페이지 닫기
        await browser.close(); // 브라우저 닫기   
        } catch(e){
            console.error(e);
        }
    }


2.2 headless 옵션 이해

위의 예제에서 headless 옵션을 사용했다

headless

  • 화면을 사용자가 볼 수 있는지에 대한 유무를 정함
  • true: 볼 수없음
  • false: 볼 수있음
  • 그럼 화면이 필요없는 브라우저가 있나?
    • 크롤러는 서버에게 명령을 하는데 서버는 주로 CLI형태로 사용하므로 배포할 때는 true를 적용
    • headless: process.env.NODE_ENV === 'production' : 환경변수 사용으로 배포일 때만 적용하도록 사용을 많이함

Promise.all

  • 동시에 , 순서가 보장되지 않을 때 사용 -> 속도가 빠름
  • 아래 예제처럼 하나씩 페이지를 동작할 때 기다려야하므로 시간낭비
await page.goto(url);
await page2.goto(url);
await page3.goto(url);

  • Promise.all을 사용해서 동시에 진행되도록 할 수 있음.
const [page, page2, page3] = await Promise.all([
    browser.newPage(),
    browser.newPage(),
    browser.newPage()
]);
await Promise.all([
    await page.goto(url);
    await page2.goto(url);
    await page3.goto(url);
]);


2.3 첫 puppeteer 크롤링

  • 실제 크롤링 해보기
await Promise.all(records.map(async (r,i) => {
	const page = await browser.newPage();
	await page.goto(r[1]);
	const scoreEl = await page.$('.score.score_left .star_score');// $가 태그찾는 메소드
    if(scoreEl){//태그를 제대로 찾았는지 확인해야함
    	const text = await page.evaluate(tag => tag.textContent, scoreEl); // scoreEl 태그에서 tag.textContent를 반환한다.  
    }
}));
  • 크롤링을 차단하는 페이지는 page.waitFor(ms)을 조금씩 늘려보면서 사용해보자

  • evaluate : JS코드를 사용할 수 있게 해주는 함수

    •    	await page.evaluate(( { magicId, magicPw } ) => {
             	document.getElementById( "loginId" ).value = magicId;
             	document.getElementById( "loginPw" ).value = magicPw;
      // document.getElementsByClassName( "btn_login" ).disabled = false;
      	}, { magicId, magicPw } );

      출처

UnhandledPromiseRejectionWaring 에러가 발생했을 때

  • async 함수 내부에 try catch로 감싸 에러를 잡아야한다.
  • async함수당 try catch를 최소 하나이상씩 넣어주어야한다.


2.4 csv에 출력하기

npm i csv-stringify

  • parse의 반댓말 = stringify

  • const result = [];
    result.push([r[0], r[1], text.trim()]);//2차원 배열 push
    ...
    const str = stringify(result); //2차원 배열을 문자열로
    fs.writeFileSync('csv/result.csv', str);//문자열을 csv파일로 쓰기

순서도 보장하면서 속도도 빠르게 하는방법? for of 말고

크롤링 못지않게 중요한 결과순서 맞추기

result.push([r[0], r[1], text.trim()]); -> 배열 순서 보장 X

  • 인덱스를 사용한다.

    • result[i] = [r[0], r[1], text.trim()];


2.5 page.evaluate 사용하기

evaluate 사용법

가져와야하는 정보가 많아질수록 코드가 지저분해질 수있어서 다음과 같은 방법으로 사용

하나의 evaluate로 DOM에 접근할수있고 원하는 결과를 가져올 수 있음.

//const 태그핸들러 = await page.$(선택자); 
const text = await page.evaluate(() => {
   //evaluate안에서 쓸 수있는 document조작
    document.querySelector('.score.score_left .star_score');
    if (score){
        return score.textContent;
        //태그가 많아지는 경우
        return {
            score: score.textContent,
            score2: score2.textConten;t
        }
    }
});

제일 중요한 것!! :smile:

DOM API를 쓰려면 무조건 page.evaluate안에서 쓰고 결과물은 return해서 받는다!

퍼펫티어 다양한 예제사이트



2.6 userAgent와 한 탭으로 크롤링

봇을 검사하는 사이트를 피하기 위해 사람을 흉내내야함 이 챕터는 흉내내기 위한 방법을 소개

크롤러를 막는 기본적인 방법 userAgent

  • 퍼펫티어말고 axios에서도 userAgent 변경해서 보내는 것도 좋음

  • Postman도 userAgent에 적혀있어서 막힐수도있음

  • navigator.userAgent : 내가 무슨 브라우저를 쓰는지 알려줌

  • page.setUserAgent('브라우저정보') 로 bot인것 속이기

순차적으로 페이지 검사

await page.setUserAgent('Mozilla/5.0....');
//사람은 동시에 검색을 못하므로 페이지 하나만 사용
const page = await browser.newPage();
for (const [i, r] of records.entries()){
    await page.goto(r[1]);
    //userAgent적용되었는지 검사
    console.log(await page.evaluate('navigator.userAgent'));
    const text = await page.evaluate(() => {
        ...
        //DOM에서 데이터 추출
        return 
    });
    //사람 흉내 -> 3초 기다렸다가 반복
    await page.waitFor(3000);
}

=> 동시성을 잃기때문에 웹서버가 차단을 하는지에 대한 테스트를 잘해보아야함

=> 클라우드로 서버 여러대를 만들어서 동시에 가져오도록 하는 방법도 있음


0개의 댓글