코딩캠프/내일배움캠프

[ TIL ] 01.19(목) 49일차

고랑E 2023. 1. 19. 21:00
728x90

크롤링과 스크래핑 차이

 

크롤링 - URL을 탐색해 반복적으로 링크를 찾고 가져오는 과정

스크래핑 - 우리가 정한 특정 웹 페이지에서 데이터를 추출하는 것

 

웹 크롤링은 불법인가?

  • 웹사이트의 홈 디렉터리에 위치한 robots.txt 파일을 열어보고 해당 사이트의 정책을 준수하지 않는다면 불법
  • 크롤링한 자료를 상업적 용도로 사용하면 불법
  • 비상업적인 용도라 하더라도 원작자에게 불이익을 주면 불법
  • 크롤러를 활용해 고의적으로 Abusing(조작) 하면 불법

 


NodeJs로 간단한 크롤러 만들기

 

npm init 및 모듈 설치

npm init -y

// 크롤링을 도와주는 모듈
npm i puppeteer

app.js

필요 모둘 불러오기

// 크롤링하기위한 puppeteer 모듈 불러오기
const puppeteer = require("puppeteer");
// 데이터를 저장하기위해 fs 모듈 불러오기
const fs = require("fs");

app.js

우리 포켓몬 사이트를 스크랩 해올 scrape 함수 만들기

async function scrape() {
  try {
    // 크로미움으로 브라우저를 연다.
    const browser = await puppeteer.launch();

    // 페이지 열기
    const page = await browser.newPage();

    // 링크 이동
    await page.goto("<http://127.0.0.1:5555>");

    // .card 엘리먼트중에 값이 #100인 .card--id 엘리먼트가 생길때까지 기다림
    await page.waitForFunction(
      () =>
        document.querySelector(".card:last-child .card--id").textContent ===
        "#100",
      { timeout: 5000 }
    );

    // cards 에 모든 카드정보 배열로 저장
    const cards = await page.$$(".card");
    // 100개의 카드가 잘 저장되었는지 확인.
    console.log(cards.length);
    const data = [];

    // cards 돌면서 필요한 데이터 수집
    // data 배열에 수집한 데이터 등록
    for (const card of cards) {
      const id = await card.$eval(".card--id", (el) => el.textContent);
      const image = await card.$eval(".card--image", (el) =>
        el.getAttribute("src")
      );
      const name = await card.$eval(".card--name", (el) => el.textContent);
      const details = await card.$eval(
        ".card--details",
        (el) => el.textContent
      );
      data.push({ id, image, name, details });
    }

    // 페이지와 브라우저 종료
    await page.close();
    await browser.close();

    // data 리턴 => 리턴한 데이터를 받아서 파일로 쓰기 위함.
    return data;
  } catch (error) {
    console.log(error);
  }
}

app.js

스크랩 완료 후 데이터 json파일로 저장

scrape()
  .then((data) => {
    fs.writeFile("pokemon.json", JSON.stringify(data), "utf8", (error) => {
      if (error) {
        console.log("파일 생성 중 에러 발생.");
        return console.log(error);
      }
      console.log("파일 생성 완료!");
    });
  })
  .catch((error) => console.log(error));

pokemon.json 파일에 정보가 잘 들어 왔느지 확인!


js 를 ts로 바꾸기

 

// require => import 로 변경
// 크롤링하기위한 puppeteer 모듈 불러오기
import puppeteer from "puppeteer";
// 데이터를 저장하기위해 fs 모듈 불러오기
import fs from "fs";

// 작성필요 - 인터페이스 내부 값
interface ICard {
  id: string;
  name: string;
  image: string;
  details: string;
}

async function scrape(): Promise<ICard[]> { // 작성필요 - 함수 반환 타입 
  try {
    // 크로미움으로 브라우저를 연다.
    const browser = await puppeteer.launch();

    // 페이지 열기
    const page = await browser.newPage();

    // 링크 이동
    await page.goto("http://127.0.0.1:5500/day3-pokemon/");

    // .card 엘리먼트중에 값이 #100인 .card--id 엘리먼트가 생길때까지 기다림
    await page.waitForFunction(
      () => {
        const cardId = document.querySelector(".card:last-child .card--id");
        return cardId && cardId.textContent === "#100";
      },
      { timeout: 7000 }
    );

    // cards 에 모든 카드정보 배열로 저장
    const cards = await page.$$(".card");
    // 100개의 카드가 잘 저장되었는지 확인.
    console.log(cards.length);
    const data: ICard[] = [] // 작성필요 - data 타입 = [];

    // cards 돌면서 필요한 데이터 수집
    // data 배열에 수집한 데이터 등록
    for (const card of cards) {
      const id = await card.$eval(".card--id", (el) => el.textContent);
      const image = await card.$eval(".card--image", (el) =>
        el.getAttribute("src")
      );
      const name = await card.$eval(".card--name", (el) => el.textContent);
      const details = await card.$eval(
        ".card--details",
        (el) => el.textContent
      );
      // 작성필요 - id, image, name, details 에러처리 필요
      if (id && image && name && details ) {
        data.push({ id, image, name, details });
      } else {
        return [];
      }
    }

    // 페이지와 브라우저 종료
    await page.close();
    await browser.close();

    // data 리턴 => 리턴한 데이터를 받아서 파일로 쓰기 위함.
    return data;
  } catch (error) {
    console.log(error);
    return [];
    // 작성필요 - 리턴값 필요
  }
}

scrape()
  .then((data) => {
    fs.writeFile("pokemon.json", JSON.stringify(data), "utf8", (error) => {
      if (error) {
        console.log("파일 생성 중 에러 발생.");
        return console.log(error);
      }
      console.log("파일 생성 완료!");
    });
  })
  .catch((error) => console.log(error));