React + express + Puppeteer 로 크롤링 만들면서 공부하기

이명진·2024년 9월 24일
0

web. 웹 공부

목록 보기
9/9

크롤링에 대해서 공부하다가 파이썬 으로 많이 만들어진것 봤는데 React로도 만들수 있을까 생각하다가
공부도 할겸 찾아보며 실습하게 되었다.

크롤링이 무조건 나쁠까? 에 대해서는 찾아보니 공개적인 게시글에서는 크롤링이 가능하지만
이것을 재배포 하면 문제가 될수 있다 라는 것을 알게 되었다. 그러므로 크롤링은 문제될것은 없지만
이것을 긁어다가 어떻게 사용할 것인가가 문제가 된다. 나는 공부하는 목적이어서 따로 재배포 하거나 따로 데이터를 사용하지 않았음을 미리 밝힌다.

결과적으로 말하자면 React로만 단순히 크롤링을 진행할수 없다.

cheerio

이건 서버 없이 라이브러리 만 사용해서 가져올수 있는 방법이다.
이전 게시물들이 많아서 방법을 알아봤는데
axios 를 이용해서 get 을 사용해 데이터 html 긁어오기 ,
그리고 cheerio를 사용해서 html 을 json 파싱해서 원하는 정보를 긁어오는 방법이다.

간단히 사용이 가능하잖아? 라고 생각하면서 써봤는데 첫번째 시도는 CROS 에러로 중단되었다.
CORS 로는 날 막을순 없다 라고 하며 Proxy 를 구축해서 Get 200 으로 가져올수는 있었는데

head 는 긁어왔는데 body가 비어있었다.
요즘에는 SPA 를 많이 쓰다보니 돔이 그려지기 전에 페이지를 긁어와서 이런 문제가 생기는 것이었다.

정적인 파일로 한번에 그려진 홈페이지들은 이런 방법으로 OK다.

하지만 요즘은 SPA가 기본이다 보니 이런 방식은 적합하지 않았다.

Puppeteer

이거는 서버가 필요하다. 하지만 결과적으로 성공할수 있던 방식이었다.

React + vite 를 사용했어서 간단하게 만들어 보게 되었다.

서버를 구축해야 하기 때문에 React를 개설해주고 파일을 서버와 클라이언트로 나눠주자

1. React 구축

npm create vite@latest web-scraping --template react-ts

이를 통해서 vite + React를 만들어 주었다.

그리고 폴더 구조를 나눠준다.

2. 폴더 구조

내 폴더 구조는 이렇게 나눠주었다. 서버에서 다 작동하기 때문에 세팅해두고 디자인은 건들지 않았다.

/root 
 .gitignore
 package.json  // 공용 패키지 
 package-lock.json
 README.md 
  /client
	index.html
	package.json   // client 패키지 
	tsconfig.app.json
	tsconfig.json
	tsconfig.node.json
	vite.config.ts
	/node_modules
	/src
		App.tsx
		eslint.config.js
		index.css
		main.tsx
		cite-env.d.ts
 /server 
	/node_modules
	index.js
	package.json  //  server 패키지

3.필요 패키지 설치

필수 패키지가 필요하다.

서버에서는 express , nodemon,puppeteer을 설치해준다.

cd server 
npm install express nodemon puppeteer

클라이언트에서는 그냥 vite 그대로 설정

cd client
npm install

루트에서는 concurrently 가 필요한데 서버와 클라이언트 동시에 작동하는 명령어를 만들어주기 때문에 설치한다.

cd ..
npm install concurrently --save-dev

4. package.json 명령어 구성

1.server/package.json (Express 관련):

{
  "name": "server",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "nodemon server.js"
  },
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "nodemon": "^2.0.22"
  }
}

2. client/package.json (React + Vite 관련):

{
  "name": "client",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "vite": "^4.0.0"
  }
}

3. 루트 package.json (공통):

{
  "name": "fullstack-app",
  "version": "1.0.0",
  "scripts": {
    "start": "concurrently \"npm run server --prefix server\" \"npm run client --prefix client\"",
    "server": "npm start --prefix server",
    "client": "npm run dev --prefix client"
  },
  "devDependencies": {
    "concurrently": "^7.0.0"
  }
}

5. vite 프록시 설정

client/vite.config.ts 에서 서버 설정을 해준다.

 server: {
    proxy: {
      "/api/data": {
        target: "http://localhost:5000",
        changeOrigin: true,
      },
    },
  },

서버는 로컬 5000 으로 만들것이기 때문에 5000 으로 해주고 오리진 변경을 설정해준다.

6. 서버 세팅

그냥 모든게 서버에서 진행된다. 서버에서 api를 만들어서 client에서 찔러주는건데
서버에서 크롤링이 진행된다.

puppeteer

Puppeteer는 Google Chrome 개발팀에서 직접 개발한 Chrome(혹은 Chromium) 렌더링 엔진을 이용하는 node.js 라이브러리이다. Puppeteer는 headless 모드를 지원하며, 이는 브라우저를 띄우지 않고 렌더링 작업을 가상으로 진행하고 실제 브라우저와 동일하게 동작한다. Puppeteer는 다양한 기능을 가지고 있으며 아래와 같은 기능들이 있다.

  • 웹페이지의 스크린샷과 PDF를 생성한다.
  • SPA(단일 페이지)를 크롤링하고 미리 렌더링된 콘텐츠(예: SSR)를 생성한다.
  • 폼 입력, UI 테스트, 키보드 입력 등을 자동화 할 수 있다.
  • 최신 자바스크립트 및 브라우저 기능을 이용해 최신버전의 크롬에서 직접 테스트할 수 있다.
  • 사이트의 Timeline Trace를 기록하여 성능이나 문제를 진단할 수 있다.
  • 크롬 확장 프로그램을 테스트 할 수 있다.

서버 코드

// express 모듈 불러오기
const express = require("express");

// express 객체 생성
const app = express();

// 기본 포트를 app 객체에 설정
const port = process.env.PORT || 5000;
app.listen(port);

// JSON 본문 파싱을 위한 미들웨어 추가
app.use(express.json());

// 미들웨어 함수를 특정 경로에 등록
// post로 url을 변경해주기 위해서 post로 만들었다. 
app.post("/api/data", async function (req, res) {
  const { url } = req.body;
  console.log("Received request for URL:", url); // 요청 URL 로그 출력
  if (!url) {
    return res.status(400).json({ error: "URL is required." });
  }
  try {
    const Text = await openBrowser(url);
    return res.json({ Text }); // Text를 클라이언트에 반환
  } catch (error) {
    console.error(error);
    return res.status(500).json({ error: "Something went wrong!" });
  }
});
const puppeteer = require("puppeteer");
async function openBrowser(url) {
  try {
    const browser = await puppeteer.launch({ headless: false }); //headless false 로 해주면 가상브라우져가 켜지는게 보인다. 
    const page = await browser.newPage(); // 새로운 페이지 만들기 

    await page.goto(url, {
      waitUntil: "networkidle2",
    }); // url로 이동시키기, waitUntil 설정이 있는데 내가 설정한것은 네트워크가 2칸 이상이기를 기다리기 라고 한다.(부채모양 말하는듯) 



    // // 페이지 내 특정 요소 추출

    try {
      await page.waitForSelector(`#need-text`, {
        timeout: 10000,
      }); //page.waitForSelector 명령어를 쓰면 원하는 태그를 찾을때까지 기다려라 라는 말이다. 
    // 페이지의 전체 HTML 가져오기  이것을 사용했는데 너무 많이 긁어와서 다르게 썼다. 이 명령어는 전체 가져온다. 
      //   const html = await page.content();
    
      const pageText = await page.$eval(
        '#need-text',
        (el) => el.textContent
      );
      return pageText;
    } catch (error) {
      console.log("Error waiting for selector:", error);
      await browser.close(); // 에러 발생 시 브라우저 닫기
      throw error;
    }

    // // 브라우저 닫기
    // await browser.close();
  } catch (error) {
    console.error("Error in openBrowser:", error); // 에러를 로그에 출력
    throw error; // 에러를 호출한 쪽으로 전달
  }
}

이렇게 서버를 구축해주고 클라이언트에서 호출하면 된다 .

클라이언트 호출

호출은 간단하다 axios를 이용했는데

axios.post(‘/api/data“,{url:’원하는 url’} )

이렇게 실행해주면 된다.

useEffect로 실행해서 가져왔다.

이렇게 해서 성공할수 있었다.

가상 크롬이 실행되는데 해킹영화처럼 막 자동으로 페이지가 켜지면서 긁어오는게 신기하기도하며 재밌기도 했다.
공부할겸 코드를 작성했는데 재미있는 시간이 되었던것 같다.
서버에서 대해서 잘 몰라서 그냥 코드를 쓰고 GPT 에게 도움을 많이 받았다.

profile
프론트엔드 개발자 초보에서 고수까지!

0개의 댓글