이전부터 하고 싶은 프로젝트가 있었다. 게임 LostArk의 캐릭터 정보를 얻어와서 뭔가 하는 프로젝트다.
그런데 LostArk는 제공하는 OpenAPI가 없다. 대신 홈페이지에서 캐릭터의 닉네임을 검색해서 캐릭터 정보를 볼 수 있는데, 이 URL을 크롤링해서 캐릭터 정보를 얻을 수 있겠다 싶은 생각이 들었다.
최근에 cheerio 라는 JS 웹 크롤링 라이브러리를 알게 되었고, 이걸로 웹 크롤링을 해 보자는 마음이 들었다.
cheerio는 마크업을 구문 분석해서 결과 데이터 구조를 탐색하고 조작하는 API를 제공하는 라이브러리다. 설명만 들으면 알기 어렵지만, 사용 예를 보면 금방 이해할 수 있다.
이런 API를 사용해 정제된 데이터를 추출해서 사용하는 것이 내 목적이다.
여기 이 웹 사이트를 크롤링 해보자.
npm init
으로 프로젝트를 생성한 다음, npm i axios cheerio
로 axios와 cheerio 라이브러리를 설치한다. 그럼 준비는 끝이다.
우선 axios로 해당 웹 사이트의 URL을 요청해서 응답을 받아오자. 이 응답의 data 프로퍼티에는 해당 URL의 HTML 문서의 마크업이 담겨 있다.
const getHtml = async () => {
try {
return await axios.get('https://lostark.game.onstove.com/Profile/Character/%EB%AA%A8%EC%BD%94%EC%BD%94%EB%B3%BC%EB%94%B0%EA%B5%AC%EB%B9%A0%EB%8A%94%EC%86%8C%EB%A6%AC');
} catch (error) {
console.error(error);
}
};
그 다음 cheerio의 load 메소드의 인자로 응답받은 데이터를 넘겨준다.
getHtml()
.then((html) => {
const $ = cheerio.load(html);
})
이 load 메소드가 뱉어낸 것이 무엇이냐? 이게 바로 CheerioAPI 라는 것이다.
CheerioAPI 명세 를 보면, CheerioAPI는 함수이고 첫 번째 인자로 Selector를 받는다. 이 Selector는 CSS에서 사용하는 Selector와 동일한 것이다.
우선 해당 캐릭터의 장착 아이템 레벨을 얻어보자. 그러려면 이 장착 아이템 레벨을 표시하는 마크업의 Selector가 필요하다.
어떤 요소의 Selector를 얻으려면 개발자 도구에서 Selector 복사를 하기만 하면 된다.
여기 이 녀석의 Selector를 복사해서 CheerioAPI 함수의 인자로 주고 함수를 호출한다. 그리고 함수의 출력값을 log해보자.
그럼 뭔가 DOM 같은 느낌의 데이터가 나온다. 명세에 의하면 이것이 바로 Cheerio의 Main Object 이다. 이 Cheerio의 다양한 메소드를 통해 Cheerio object를 탐색하고 조작하여 원하는 데이터를 얻는 것이다. 우리가 찾은 이 span 안에 캐릭터의 장착 아이템 레벨이 들어있으니, text 메소드를 이용해 마크업 안의 텍스트만 얻어보자.
getHtml()
.then((html) => {
const $ = cheerio.load(html.data);
const data = $('#lostark-wrapper > div > main > div > div.profile-ingame > div.profile-info > div.level-info2 > div.level-info2__expedition > span:nth-child(2)');
const dataText = data.text();
return dataText;
})
.then((res) => log(res));
Cheerio object의 text 메소드를 호출하기만 하면 된다. 반환값을 log해보면?
짠. 장비 아이템 레벨이 나왔다.
명세에 나와있는 Cheerio object의 메소드를 잘 조합하기만 하면 웹 사이트에서 원하는 데이터는 뭐든 얻을 수 있다. 이제 웹 크롤링을 이용해서 캐릭터의 정보가 담긴 데이터를 얻어 제공하는 API들을 직접 만든 뒤, API를 프론트에서 호출해서 뭔가 보여주는 웹을 만들어 볼 예정이다.
이번 프로젝트에선 Vite와 SWR을 써볼 생각이다.