요약
리액트 아웃소싱 프로젝트 - 크롤링
kakao map 데이터 크롤링 작업
고민한 내용
처음 기획 단계에서 고민한 내용은 네이버에서 검색한 리스트들을 불러와서 별점과 리뷰수를 보여주는 것이었다.
근데 네이버지도 API, 카카오맵 API 둘 다 별점과 리뷰는 보여주지 않는다는 것이었다.
그래서 내린 결정은 데이터를 크롤링해와서 뿌려주는 걸로 결정을 했다.
네이버지도 같은 경우 리스트들이 iframe으로 하나씩 데이터를 다른데서 불러오기 때문에 크롤링에 어려움이 있었다.
그래서 데이터 긁어오기 용이한 카카오지도를 크롤링 하기로 했다.
1. index.js
일단 리액트로만으론 크롤링이 불가능했다.
정적인 페이지에서 크롤링이 가능한데, 리액트는 동적프레임워크다보니 호환이 되지 않았다.
그래서 백엔드로 node 환경을 구축하기로 결정. 노드 환경에서 데이터를 긁어모아서 프론트로 뿌려주기로 했다.
const compression = require("compression");
const cors = require("cors");
const { router } = require("./src/router");
const express = require("express");
const app = express();
const port = 5001;
// express 미들웨어 설정
// cors 설정
app.use(cors());
// body json 파싱
app.use(express.json());
// HTTP 요청 압축
app.use(compression());
// 라우터 분리
router(app);
app.listen(port, () => {
console.log(`listening on port: ${port}`);
});
서버 포트를 여는 기본적인 index이다
2. index.js
프론트에서 불려질 api들이다.
/mapList는 검색했을 때 리스트의 정보들
/mapDetail은 검색 결과물의 상세페이지의 정보들이다.
const controller = require("./controller.js");
exports.router = function (app) {
app.get("/mapList/:query", controller.crawling); // list
app.get("/mapDetail/:query", controller.crawlingDetail); // detail
};
3. controller.js
크롤링하는 모듈은 처음엔 'cheerio'라는 모듈을 이용해서 할려고했는데,
정적 페이지에 적합한데, 버튼 클릭 후 나오는 걸 긁어오고 그런 동적인 페이지에 적합하지 않아서
puppeteer을 이용했다.
const puppeteer = require("puppeteer");
exports.crawling = async (req, res) => {
const { query } = req.params;
let data = {};
// headless 브라우저 실행
const browser = await puppeteer.launch({ headless: true, slowMo: 30 });
// 새로운 페이지 열기
const page = await browser.newPage();
await page.goto("https://place.map.kakao.com/");
// 대기 후에 검색 입력 필드와 버튼을 찾음
await page.waitForSelector(".box_searchbar");
await page.waitForSelector(".btn_search");
// 찾은 요소에 값을 입력하고 Enter 키를 누름
await page.type(".box_searchbar", query + " 산책로");
await page.keyboard.press("Enter");
// 대기 후에 검색 결과가 나타날 때까지 대기
await page.waitForSelector(".placelist");
````
````
````
data.title = query;
data.total = await total;
data.list = await listData;
console.log("resultList: ", data);
await browser.close();
if (!data.title) {
return await res.send({
isSuccess: false,
code: 400,
message: "크롤링 실패 에러. 관리자에게 문의하세요.",
});
}
return await res.send({
result: data,
isSuccess: true,
code: 200,
});
};
큰 들은 위와 같다. 그리고 안에서 사용되는 기능들은 검색하면서 작업했는데, 굉장히 직관적이고
JQuery와 유사한 것들이 많아서 작업하기엔 용이했다. 데이터를 다 긁은 후 export하면 된다
const resultList = await page.$$eval(
".placelist .PlaceItem",
(elements) => {
return elements.map((element) => {
const titleElement = element.querySelector(
".tit_name .link_name"
);
const subTitleElement = element.querySelector(".subcategory");
const addressElement = element.querySelector(".info_item .addr");
const linkElement = element.querySelector(".info_item .moreview");
const scoreNumElement = element.querySelector(".rating .num");
const scoreTxtElement = element.querySelector(
".rating .numberofscore"
);
const scoreReviewElement =
element.querySelector(".rating .review em");
// titleElement 또는 addressElement가 null이면 해당 값을 빈 문자열로 설정
const title = titleElement ? titleElement.textContent.trim() : "";
const subTitle = subTitleElement
? subTitleElement.textContent.trim()
: "";
const address = addressElement
? addressElement.textContent.trim()
: "";
const linkFilter = linkElement ? linkElement.href.split("/") : "";
const link = linkFilter[3] ? linkFilter[3] : "";
const scoreNum = scoreNumElement
? scoreNumElement.textContent.trim()
: "";
const scoreTxt = scoreTxtElement
? scoreTxtElement.textContent.trim()
: "";
const scoreReview = scoreReviewElement
? scoreReviewElement.textContent.trim()
: "";
return {
title,
subTitle,
address,
link,
scoreNum,
scoreTxt,
scoreReview,
};
});
}
);
안에서 중요 코드.
리스트에서 가져올려고 했던 제목, 주소, 별점 등등 하나씩 찾아내서 긁어서 데이터에 담기만 하면 된다.
결과 화면
회고
가능 큰 후회는 크롤링을 했다는 것 자체다.
개인작업으로 하긴 한거지만 크롤링 자체가 법적으로 문제가 없다곤 해도, 만약 문제가 생긴다면 문제가 생길 수 있는 기능인 것을 알았다. 개인적인 정보들도 맘대로 긁어모을 수가 있기 때문에...
그리고 뭣보다 굳이? 이걸 했어야했나 싶은 느낌이다. 분명이 카카오맵 API 안에서만 해결 할 수 있었을텐데란 생각.
크롤링이 리액트에서 할 수 있다고 생각하고 접근했다보니 이런 문제가 생겼던 것이다.
좀 더 사전지식이 준비된 상태로 시작했어야했고, 빠르게 정보를 얻고 기획을 수정했어야했는데 그게 아쉽다.
또 API를 가져다가 쓴다고해도 사용법이나 기능에서 제공하는게 어느정보 범위까지인지 확실하게 파악할 필요가 있다.
어떤 경우에는 API를 파악하기 위해 하루를 다 쓴다고도 하니, 여러가지로 많은 생각을 얻게된 프로젝트였다.
'TIL' 카테고리의 다른 글
[TIL][23.12.13] Single Responsibility Principle(SRP) (0) | 2023.12.13 |
---|---|
[TIL][23.12.12] 리액트 아웃소싱 프로젝트 - KPT 회고 (0) | 2023.12.12 |
[TIL][23.12.07] 리액트 아웃소싱 프로젝트 S.A (2) | 2023.12.06 |
[TIL][23.12.06] React 팬레터 앱 만들기 - thunk로 리팩토링 (0) | 2023.12.06 |
[TIL][23.12.05] React, Firebase 활용한 팀 프로젝트 (1) | 2023.12.06 |