1탄에서 이어지는 내용입니다.
(2) 개발자도구에서 추출할 elements 확인하기
- 크롬(이나 혹은 다른 브라우저 대부분)에서 F12를 누르면 개발자모드가 활성화되고 그 중 Elements 탭에서 페이지를 구성하는 요소들을 확인할 수 있다는 사실은 이 글을 보고 있는 대부분이 알 것이다.
- 많은 요소들 중 필요한 데이터만을 추출하기 위해 Elements 탭 왼쪽의 선택 도구를 클릭하고 원하는 영역을 확인한다.
- 나는 각 뉴스(사진에서 li 태그)들의 기사 제목, 썸네일, 링크가 필요했다.
- 영역을 확인했으면 구분 가능한 id에서부터 시작해서 내가 필요한 태그까지 안쪽으로 타고타고 들어간다.
- 무슨 말이냐면 사진의 저 영역은 content라는 id 아래의 news-list라는 div 아래의 div 태그 아래의 ul 태그 아래의 li 태그이다.
- 주의할 점은 ul 태그 아래의 li 태그들에 반복문을 사용할거라 일단 ul 태그까지만 들어갔는데, 내가 필요한 정보가 모든 li 태그들에 있다면 li 태그까지 들어가는게 맞았다. 대신 li 태그가 총 15개(한 페이지에 기사가 15개씩 페이지네이션되어 있었음)이기 때문에 toArray( ) 를 사용해 배열 형태로 만들어준다.
- 그리고 두 번째 div 처럼 div 태그가 여러개인 경우에는 . 으로 클래스 이름을 특정해주어야 한다. 이를 표현하면 다음과 같다.
#content > div.news-list > div > ul > li
// cheerio를 활용하여 body에서 데이터 추출
const $ = cheerio.load(body);
const list_news = $(
"#content > div.news-list > div > ul > li"
).toArray(); // 한 페이지는 뉴스 15개
(3) 추출할 데이터 디테일하게 접근
- 여기서 내가 결국 응답값으로 얻고 싶은 데이터의 형식을 생각해보면, 나는 배열 안에 각 기사들의 url, 썸네일, 제목이 객체값으로 들어가길 바랐다.
- 따라서 result 라는 배열을 하나 만들어주고 list_news에 li 태그를 기준으로 forEach문을 사용했다.
- 그리고 이제 find( ) 와 attr( ) 함수만 쓰면 모든 데이터에 접근할 수 있다!
- a 태그에 접근하기 위해 const aTag = $(li).find("a")
- 그리고 그 안의 href 속성에 접근하기 위해 const path = aTag.attr("href")
- 이런식으로 필요한 데이터에 접근하고 변수에 할당해주면 된다.
- 나는 이게 result 라는 배열 안에 객체값으로 들어가길 바랐으니까 result.push({ url, thumbnail, title }) 로 넣어주었다.
- 그리고 나는 Promise 객체를 return하게 만들었기 때문에 res 값에 result를 넣어주었다. 그러면 전체 코드는 다음과 같다.
import request from "request";
import cheerio from "cheerio";
const OutsideApi = {
// 온라인: "O" | PC: "P" | 비디오: "V" | 웹게임: "W" | 모바일: "M"
getNews: async (category) => {
return new Promise((res, rej) => {
request(
{
url: `https://www.gamemeca.com/news.php?ca=${category}`,
method: "GET",
},
(error, response, body) => {
if (error) {
console.error(error);
rej(new Error("error"));
}
if (response.statusCode === 200) {
// cheerio를 활용하여 body에서 데이터 추출
const $ = cheerio.load(body);
const list_news = $(
"#content > div.news-list > div > ul > li"
).toArray(); // 한 페이지는 뉴스 15개
const result = [];
list_news.forEach((li) => {
// result에 1. 뉴스글 url / 2. 뉴스 썸네일 / 3. 뉴스제목
const aTag = $(li).find("a");
const path = aTag.attr("href"); // 첫번째 <a> 태그 href
const url = `https://www.gamemeca.com/${path}`; // 도메인을 붙인 url 주소
const thumbnail = aTag.find("img").attr("src"); // 썸네일 url
const divTag = $(li).find("div").first();
const title = divTag.find("strong").find("a").text().trim();
result.push({ url, thumbnail, title });
});
res(result);
}
}
);
});
}
}
export { OutsideApi };
[04 잘 응답하는지 확인 ]
(1) Postman 테스트
- Postman으로 요청을 할 수 있도록 router 계층에서의 코드도 완성해준다.
import { redisClient, DEFAULT_EXPIRATION } from "../db";
import { Router } from "express";
import { OutsideApi } from "../common/OutsideApi";
const OutsideApiRouter = Router();
OutsideApiRouter.get("/gameNews", async (req, res, next) => {
try {
const category = req.query.category;
// redis 서버에서 캐시 확인
const cache = await redisClient.GET(`gameNews?${category}`);
if (cache) {
// 캐시가 있으면
res.status(200).json(JSON.parse(cache));
} else {
// 캐시가 없으면
const gameNews = await OutsideApi.getNews(category);
await redisClient.SETEX(
`gameNews?${category}`,
DEFAULT_EXPIRATION,
JSON.stringify(gameNews)
);
res.status(200).json(gameNews);
}
} catch (error) {
next(error);
}
});
- 코드를 완성하고 postman으로 요청을 보내보면 정상적으로 응답이 돌아오는 것을 확인할 수 있다.
- 나는 시행착오를 겪은 과정을 뺐지만 태그가 많다면 당연히 중간 중간 console.log( ) 로 잘 접근 중인지 원하는 데이터가 맞는지 확인하며 진행하는 것을 추천한다.
(2) Front에서 적용한 모습
[05 느낀점 ]
- 보통 크롤링을 파이썬으로 많이 하기 때문에 한 번은 해봐야지 했지만 막상 해 보지 않았었는데 프로젝트를 계기로 외뷰 뉴스를 내 웹서비스에 넣어보니 신기하고 재밌었다.
- 그리고 이번건 실제로 했을 땐 별거 아니었는데 글로 작성하는게 더 어려웠다... 그래도 다음에 또 사용할 때를 대비해서 또 복습을 위해 기록해두기!
'엘리스 AI트랙 4기 > 프로젝트' 카테고리의 다른 글
AWS EC2에 프로젝트 배포하기_2탄 (0) | 2022.05.30 |
---|---|
AWS EC2에 프로젝트 배포하기_1탄 (0) | 2022.05.30 |
Youtube search API 사용해서 검색 목록 이용하기 (0) | 2022.05.16 |
express 웹서비스 프로젝트에 뉴스 크롤링 추가하기_1탄 (0) | 2022.05.11 |
Redis 윈도우 설치 / nodejs 코드에 적용하는 법 / redis-cli 명령어 / 캐시 서버 사용전후 응답속도 차이 (0) | 2022.05.10 |