내배캠 69일차

·2023년 1월 21일
0

내일배움캠프

목록 보기
74/142
post-thumbnail

사용한 github주소

포켓몬 크롤링 프로그램 만들기

우리가 할 것은 만든 포켓몬 도감 사이트에서 정보를 가져올 것이라서 스크래핑에 더 가까움!

모듈설치

npm init -y

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

app.js

필요 모둘 불러오기

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

포켓몬 사이트를 스크랩 해올 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);
  }
}

스크랩 완료 후 데이터 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));
  • JSON.stringify(data) : 대신 data도 가능 하지만 한글이 들어가있다면 오류가 난다!
  • "utf8" : 안써줘도 되지만 명시해줌.

app.js => app.ts

config.json

//추가
type: "module"

tsconfig.json

{
  // include : 컴파일할 파일 경로를 설정합니다. [ src폴더 하위의 모든 .ts 확장자를 가진 파일 ]
  "include": ["src/**/*.ts"],
  "compilerOptions": {
    "lib": ["ES2021", "DOM"],
    // esModuleInterop 속성이 위의 코드 처럼 true로 설정될 경우, ES6 모듈 사양을 준수하여 CommonJS 모듈을 가져올 수 있게 됩니다.
    // e.g. typescript-test.ts => import express from "express"
    "esModuleInterop": true,
    // 타입스크립트가 모듈을 찾는방법을 지정(import puppeteer때문에)
    "moduleResolution": "NodeNext",
    "target": "ES2021",
    // rootDir : 컴파일될 타입스크립트 코드들의 위치를 명시합니다.
    // "rootDir": "src",
    // outDir : 컴파일 후 생성되는 js파일이 생성될 폴더명
    "outDir": "dist",
    // strictNullChecks
    "strictNullChecks": true,
    // 암시적 any 타입을 허용하지 않는다
    "noImplicitAny": true
  }
}

app.ts

만든 app.js 바탕으로 app.ts를 만들어 볼 것임!

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

interface IPokemon {
  id: string;
  name: string;
  image: string;
  type: string;
}

//성공했을때와 실패했을때 모두 return이 있어야 오류가 안남
async function scrape(): Promise<IPokemon[]> {
  try {
    // 크로미움으로 브라우저를 연다.
    const browser = await puppeteer.launch();

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

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

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

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

    // cards 돌면서 필요한 데이터 수집
    for (const card of cards) {
      const id = await card.$eval(".card--id", (el: Element) => el.textContent);
      const image = await card.$eval(".card--image", (el: Element) =>
        el.getAttribute("src")
      );
      const name = await card.$eval(
        ".card--name",
        (el: Element) => el.textContent
      );
      const type = await card.$eval(
        ".card--details",
        (el: Element) => el.textContent
      );
      // data 배열에 수집한 데이터 등록
      if (id && image && name && type) {
        data.push({ id, image, name, type });
      } else {
        console.log("빈 형식이 들어왔습니다!");
        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: Error) => {
        if (error) {
          console.log("파일 생성 중 에러 발생.");
          return console.log(error);
        }
        console.log("파일 생성 완료!");
      }
    );
  })
  .catch((error) => console.log(error));

1. require => import 로 변경

  • import puppeteer from "puppeteer";
  • import fs from "fs";

2. interface IPokemon생성

  • interface IPokemon { id: string; name: string; image: string; type: string; }

3. type지정

  • async function scrape(): Promise<IPokemon[]>
  • data: Array<IPokemon>
  • el: Element : 안해도 됨.

4. null값 방지

  • const cardId = document.querySelector(".card:last-child .card--id");
    return cardId && cardId.textContent === "#100";
  • if (id && image && name && type)
profile
개발자 꿈나무

0개의 댓글