[Node.js] Crawling (feat. Cheerio)
Crawling (크롤링)
웹 사이트를 접속해, 원하는 데이터를 추출하는 것
- Crawling(기어가다, 기다) 이라는 단어처럼, 크롤링의 행위가 마치 웹 사이트를 기어다니며 정보를 수집하는 것과 비슷하다고 표현되기 시작하면서 생겨난 것으로 추측된다.
- 주로 업무 자동화나 많은 양의 데이터를 효율적으로 수집하기 위해 Crawling 방법을 사용한다.
- Crawling은 Python이 유명하지만, JavaScript나 다른 언어로도 가능하다.
- Crawling을 수행하는 프로그램을 'Crawler(크롤러)' 라고 부를 수 있다.
- 해당 글에서는 JavaScript를 통해 [YES24] 베스트 셀러' 페이지 데이터를 Crawling 해보도록 한다.
Crawling 준비
1. 패키지 설치
- 이번 실습에서는 아래 3개의 라이브러리를 통해 Crawling을 실습할 예정이다.
axios
: HTTP 요청을 할 수 있도록 해주는 패키지
cheerio
: 웹 페이지에 있는 데이터를 쉽게 가공할 수 있도록 도와주는 패키지
iconv-lite
: 특정 웹 페이지에서는 한자, 한글, 이모지와 같은 유니코드 문자열이 깨질수 있는데, 문자가 깨지지 않도록 후처리해주는 패키지
- 프로젝트를 생성 후, 프로젝트 폴더에서 아래 명령어를 통해 패키지를 설치한다.
// init
// -y 명령어를 통해, 모두 자동으로 동의하도록 해준다.
npm init -y
// install packages
// 띄어쓰기를 통해 여러 Package를 한번에 설치한다.
npm i axios cheerio iconv-lite
- package.json 파일에 아래 이미지처럼 생성되었다면, 설치가 성공한 것이다.
2. 구조 확인하기
(1) 베스트 셀러 책 수 확인하기
- 우선, 베스트 셀러에서 가져와야 할 책 목록이 몇 개인지부터 살펴본다. 아래 이미지에서 확인할 수 있듯, 총 40권의 책 목록이 제공되고 있다.
(2) selector로 위치 확인하기
-
그럼, 이제 해당 책들을 어떻게 구성하여 보여주고 있는지 html 태그를 확인해보도록 한다. '개발자도구'를 열어 1위에 해당하는 책 영역을 살펴보면, <li>
태그로 감싸져있는 것을 볼 수 있고 해당 태그의 클래스는 1부터 40까지 존재한다. 즉, 1위부터 40위까지의 책이 <li class="num00">
의 형태로 존재하고 있음을 확인할 수 있다.
-
40권의 책에 대한 모든 데이터를 확인하기 위해서 번호 하나씩 모두 확인하는 방법도 있겠지만, 위에서 살펴본 규칙을 통해 한번에 데이터를 가져올 수도 있을 것이다.
-
우선, 1위 책에 대한 html 태그에서 우클릭 후, copy > copy selector 를 클릭해보자. 그리고, 해당 영역에서 cmd+f
키를 눌러, 검색창에 copy된 내용을 붙여넣기하면 아래와 같이 하나의 결과가 찾아짐을 확인할 수 있다.
-
css selector를 살펴보면 li.num1 이라는 것으로 검색을 하고 있는데, 이 것은 <li>
태그 중에서 num1 이라는 클래스를 가진 항목을 찾는다는 의미이다. 우리는 num1부터 num40까지의 값을 모두 가져와야하기 때문에, .num1
을 제거하고 다시 검색해보자.
-
아래 이미지와 같이 총 40개의 검색 결과가 나온 것을 볼 수 있다. 여기서 화살표를 아래로 내려보면, 우리가 원하는대로 1위부터 40위까지의 책 영역이 선택되는 것을 확인할 수 있다.
-
1위부터 40위까지의 책 데이터가 어떤 html 속성으로 구성되어있는지 확인했으므로, 손쉽게 가져올 수 있게 되었다. 이제, 직접 Crawling을 해 볼 차례이다.
원하는 데이터 Crawling 하기
1. axios로 데이터 받아오기
- 우선, axios를 통해 GET 요청을 보낸 후 화면을 구성하는 데이터들을 받아온다.
// index.js
const axios = require('axios');
// axios는 Promise를 반환하기 때문에 then, catch를 통해 chaining 할 수 있다.
axios({
// 크롤링을 원하는 페이지 URL
url: 'http://www.yes24.com/24/Category/BestSeller',
method: 'GET',
})
// 성공했을 경우
.then(response => {
console.log(response.data);
})
// 실패했을 경우
.catch(err => {
console.error(err);
});
- 위와 같이 axios 코드를 실행하면, 아래 이미지처럼 데이터를 받아온다. 하지만, 한글이 깨져보이는 것을 확인할 수 있다.
2. iconv-lite으로 Decoding하기
-
iconv-lite 모듈은 만약 '영문으로만 된 사이트' 이거나, 최근에 만들어진 사이트라면 사용할 필요가 없다. 조금 오래된 웹 사이트 중, Encoding 형식이 'EUC-KR'과 같은 형식일 경우에만 사용하면 되는 모듈이기 때문에, 크롤링 하고자 하는 웹 사이트를 잘 확인해서 사용하면 된다.
-
YES24 웹 사이트의 경우 charset이 UTF-8이 아닌 EUC-KR로 설정되어 있다. 따라서, axios로 데이터를 받아오면 한글이 깨지는 현상이 나타난다. 실제로 YES24 페이지에서 개발자 도구를 통해 확인해보면, charset=euc-kr
로 설정되어있는 것을 볼 수 있다.
-
즉, EUC-KR을 UTF-8로 Decoding 하기 위해 iconv-lite 패키지를 사용하는 것이다.
-
iconv-lite는 ArrayBuffer
라는 형식을 받는데, axios에서 해당 형식으로 데이터를 받을 수 있다.
- axios에서 responseType: 'arraybuffer' 로 지정해준다.
- 성공했을 경우의 response.data를 iconv를 통해 decode 해준다.
- 만약 정상적으로 데이터가 출력되지 않는다면, .toString() 메서드를 통해 string으로 변환한다.
const axios = require('axios');
const iconv = require('iconv-lite');
axios({
// 크롤링을 원하는 페이지 URL
url: 'http://www.yes24.com/24/Category/BestSeller',
method: 'GET',
responseType: 'arraybuffer',
})
// 성공했을 경우
.then(response => {
// 만약 content가 정상적으로 출력되지 않는다면, arraybuffer 타입으로 되어있기 때문일 수 있다.
// 현재는 string으로 반환되지만, 만약 다르게 출력된다면 뒤에 .toString() 메서드를 호출하면 된다.
const content = iconv.decode(response.data, 'EUC-KR');
console.log(content);
})
// 실패했을 경우
.catch(err => {
console.error(err);
});
- 이제 한글이 잘 나오는 것을 확인할 수 있다.
3. cheerio 모듈로 데이터 추출
(1) cheerio 기본 사용법
- 우선, cheerio 모듈을 import하고, 우리가 axios로 받아온 데이터 소스들을 cheerio에 로드 시켜준다. 아래 예시를 통해 간단하게 살펴보자.
const cheerio = require('cheerio');
...
const $ = cheerio.load('<h1 class="title">WebSite</h1>');
$('h1.title').text(); // 'WebSite'
-
위 코드를 조금 살펴보자. cheerio.load() 메서드 안에 html 소스들을 넣고, 그 안에서 찾을 수 있도록 $ 변수에 할당해준다. $()로 표기하는 방식은 JQuery에서 주로 사용하는 형식인데, 크롤링을 할 때에는 이러한 형식을 많이 사용한다.
-
위에서 보면, $('h1.title').text()의 결과로 'WebSite'가 나온 것을 볼 수 있다. 그렇다면 왜 이렇게 되는 것인지, 소스를 뜯어보면 아래와 같다.
$()
: 태그와 클래스를 통해 어떤 데이터의 위치를 선택
h1.title
: h1 태그 중, 클래스가 title 인 것을 의미함
text()
: 해당 태그에서, 'text'를 가져옴
(2) 베스트 셀러 제목 가져오기
-
위의 방법대로 응용해서, 우리가 하고자 하는 YES24 베스트 셀러 40위까지의 제목을 가져와보도록 하자. 위에서 살펴본 방법대로, 개발자 도구에서 copy selector를 통해 '제목'의 위치를 가져온다.
-
제목의 위치는 #bestList > ol > li.num1 > p:nth-child(3) > a
로 확인이 되는데, 마찬가지로 위의 방법대로 li.num1
에서 num1을 제외하면 40개의 제목이 모두 확인되는 것을 알 수 있다. 이를 이용해서, 1위~40위 까지의 제목을 가져와보도록 한다.
const axios = require('axios');
const iconv = require('iconv-lite');
const cheerio = require('cheerio');
axios({
// 크롤링을 원하는 페이지 URL
url: 'http://www.yes24.com/24/Category/BestSeller',
method: 'GET',
responseType: 'arraybuffer',
})
// 성공했을 경우
.then(response => {
// 만약 content가 정상적으로 출력되지 않는다면, arraybuffer 타입으로 되어있기 때문일 수 있다.
// 현재는 string으로 반환되지만, 만약 다르게 출력된다면 뒤에 .toString() 메서드를 호출하면 된다.
const content = iconv.decode(response.data, 'EUC-KR');
const $ = cheerio.load(content);
const titles = $('#bestList > ol > li > p:nth-child(3) > a').text();
console.log(titles);
})
// 실패했을 경우
.catch(err => {
console.error(err);
});
- 위 코드를 실행해보면, 아래와 같이 1위부터 40위까지의 책 제목이 쭉 나열되어있는 것을 볼 수 있다.
(3) each()로 여러 항목 가져오기
- 이렇게 여러 항목들을 효과적으로 가져오기 위해서, cheerio에서 제공하는 'each' 메서드를 사용할 수 있다. cheerio의 each 메서드에 대한 사용법은, cheerio 공식문서 링크에서 확인할 수 있다.
const content = iconv.decode(response.data, 'EUC-KR');
const $ = cheerio.load(content);
const titleSelector = '#bestList > ol > li > p:nth-child(3) > a';
$(titleSelector).each((i, elem) => {
const title = $(elem).text();
console.log(i + 1, title);
});
- 위와같이 코드를 작성해주면, 아래의 결과를 확인할 수 있다. index는 0부터 시작하므로, 순위를 나타내기 위해 +1로 출력했고, $(elem).text()를 통해 제목을 가져왔다.
4. 순위별로 더 많은 데이터 추출하기
-
지금까지는 하나의 항목(제목)을 가져오는 방법을 살펴보았다. 하지만 위의 방법으로는 각 순위별 책의 '가격, 제목, 설명, 책 이미지' 등을 모두 가져오기는 쉽지 않아보인다. 따라서, 각 순위별 책의 항목들을 가져오는 방법을 실습해보도록 한다.
-
우선, 영역이 어떻게 나누어져 있는지 한번 확인해보도록 한다. 아래 이미지처럼, 순위에 맞는 책에 대한 설명, 제목, 저자, 출판사, 가격 등이 모두 하나의 html 태그 안에 포함되어있는 것처럼 보인다.
-
이제 개발자 도구에서 책 하나에 대한 태그를 선택해서 열어보자. 예상대로, 위에서 확인해 본 항목들이 확인된다.
-
이제 우리는 이것을 이용해서 '설명', '제목', '가격', '이미지'를 가져와보도록 하겠다. 이전 코드에서는 1위~40위까지 '제목' 태그만 each() 를 통해 반복했다면, 이제는 '책 한 권'에 대한 태그를 반복하면서 우리가 원하는 각각의 항목들을 출력해보면 될 것이다.
-
먼저, 1위~40위까지는 #bestList > ol > li
임을 위에서 살펴보았다. 이 중에서, 원하는 각 항목에 접근하는 방법을 살펴보도록 하자. 개발자 도구를 통해 '설명'을 클릭하면, 아래처럼 해당 태그로 이동하는 것을 확인할 수 있다. (어떻게 클릭하는지 모르겠다면, 개발자 도구 창의 좌측 상단 '네모와 마우스'가 있는 이미지' 쪽을 클릭하거나, cmd + shift + c
를 입력 후 마우스로 클릭해보면 영역 선택이 된다.)
-
태그 좌측의 화살표를 이용해서 항목을 펼쳐보면, 해당 코드는 <li>
하위에 있는 <p class="copy">
태그의 하위에 <a href=....>{설명}</a>
으로 위치해있다는 것을 알 수 있다. 이 것을 selector로 선택하는 방법을 알아보도록 한다. 아래의 규칙만 알면, 어렵지 않게 선택할 수 있다.
#
: id
.
: class
>
: 하위 태그
attr("attribute name")
: 속성명에 대한 속성
- 우리가 위에서 살펴본 1위~40위까지 각 책에 대한 selector는
#bestList > ol > li
였다. 즉, id가 'bestList'인 것의 하위에 있는 ol 태그를 선택한 후, 그 하위에 있는 li 태그를 찾는 것이다. 그럼, 우리가 가져와야하는 '설명'은 #bestList > ol > li > p.copy > a
가 되고, 그 것의 text를 가져와야 하므로 .text() 메서드를 호출하면 된다.
-
예상대로 설명만 잘 가져오는 것을 확인했으므로, 이제 각 항목을 어떻게 가져오는지 대충 감을 잡았다. 그럼, 1위~40위까지의 selector를 each()로 반복하면서, 각각의 값을 가져와보도록 하겠다. 하위 selector를 찾기 위해서는 .find() 메서드를 사용하면 아주 간단하게 가져올 수 있다. (여기서부터는 selector를 찾는 설명은 생략한다.)
-
#bestList > ol > li
를 통해 각 책에 대한 코드를 반복하면서, 원하는 항목인 제목, 설명, 가격, 이미지 주소를 크롤링 해보았다. 위 설명과 함께 아래의 이미지와 코드를 참고하면 바로 이해를 할 수 있을 것이다.
// 최종 코드
// index.js
const axios = require('axios');
const iconv = require('iconv-lite');
const cheerio = require('cheerio');
axios({
// 크롤링을 원하는 페이지 URL
url: 'http://www.yes24.com/24/Category/BestSeller',
method: 'GET',
responseType: 'arraybuffer',
})
// 성공했을 경우
.then(response => {
// 만약 content가 정상적으로 출력되지 않는다면, arraybuffer 타입으로 되어있기 때문일 수 있다.
// 현재는 string으로 반환되지만, 만약 다르게 출력된다면 뒤에 .toString() 메서드를 호출하면 된다.
const content = iconv.decode(response.data, 'EUC-KR');
const $ = cheerio.load(content);
// 1위~40위까지의 책들에 대한 selector
const booksSelector = '#bestList > ol > li';
$(booksSelector).each((i, elem) => {
const title = $(elem).find('p:nth-child(3) > a').text();
const description = $(elem).find('p.copy > a').text();
const price = $(elem).find('p.price > strong').text();
const imgUrl = $(elem).find('p.image > a > img').attr('src');
console.log(i + 1, {
title,
description,
price,
imgUrl,
});
});
})
// 실패했을 경우
.catch(err => {
console.error(err);
});
Author And Source
이 문제에 관하여([Node.js] Crawling (feat. Cheerio)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@_nine/Node.js-Crawling-feat.-Cheerio
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
웹 사이트를 접속해, 원하는 데이터를 추출하는 것
1. 패키지 설치
- 이번 실습에서는 아래 3개의 라이브러리를 통해 Crawling을 실습할 예정이다.
axios
: HTTP 요청을 할 수 있도록 해주는 패키지cheerio
: 웹 페이지에 있는 데이터를 쉽게 가공할 수 있도록 도와주는 패키지iconv-lite
: 특정 웹 페이지에서는 한자, 한글, 이모지와 같은 유니코드 문자열이 깨질수 있는데, 문자가 깨지지 않도록 후처리해주는 패키지
- 프로젝트를 생성 후, 프로젝트 폴더에서 아래 명령어를 통해 패키지를 설치한다.
// init
// -y 명령어를 통해, 모두 자동으로 동의하도록 해준다.
npm init -y
// install packages
// 띄어쓰기를 통해 여러 Package를 한번에 설치한다.
npm i axios cheerio iconv-lite
- package.json 파일에 아래 이미지처럼 생성되었다면, 설치가 성공한 것이다.
2. 구조 확인하기
(1) 베스트 셀러 책 수 확인하기
- 우선, 베스트 셀러에서 가져와야 할 책 목록이 몇 개인지부터 살펴본다. 아래 이미지에서 확인할 수 있듯, 총 40권의 책 목록이 제공되고 있다.
(2) selector로 위치 확인하기
-
그럼, 이제 해당 책들을 어떻게 구성하여 보여주고 있는지 html 태그를 확인해보도록 한다. '개발자도구'를 열어 1위에 해당하는 책 영역을 살펴보면,
<li>
태그로 감싸져있는 것을 볼 수 있고 해당 태그의 클래스는 1부터 40까지 존재한다. 즉, 1위부터 40위까지의 책이<li class="num00">
의 형태로 존재하고 있음을 확인할 수 있다.
-
40권의 책에 대한 모든 데이터를 확인하기 위해서 번호 하나씩 모두 확인하는 방법도 있겠지만, 위에서 살펴본 규칙을 통해 한번에 데이터를 가져올 수도 있을 것이다.
-
우선, 1위 책에 대한 html 태그에서 우클릭 후, copy > copy selector 를 클릭해보자. 그리고, 해당 영역에서
cmd+f
키를 눌러, 검색창에 copy된 내용을 붙여넣기하면 아래와 같이 하나의 결과가 찾아짐을 확인할 수 있다.
-
css selector를 살펴보면 li.num1 이라는 것으로 검색을 하고 있는데, 이 것은
<li>
태그 중에서 num1 이라는 클래스를 가진 항목을 찾는다는 의미이다. 우리는 num1부터 num40까지의 값을 모두 가져와야하기 때문에,.num1
을 제거하고 다시 검색해보자.
-
아래 이미지와 같이 총 40개의 검색 결과가 나온 것을 볼 수 있다. 여기서 화살표를 아래로 내려보면, 우리가 원하는대로 1위부터 40위까지의 책 영역이 선택되는 것을 확인할 수 있다.
-
1위부터 40위까지의 책 데이터가 어떤 html 속성으로 구성되어있는지 확인했으므로, 손쉽게 가져올 수 있게 되었다. 이제, 직접 Crawling을 해 볼 차례이다.
원하는 데이터 Crawling 하기
1. axios로 데이터 받아오기
- 우선, axios를 통해 GET 요청을 보낸 후 화면을 구성하는 데이터들을 받아온다.
// index.js
const axios = require('axios');
// axios는 Promise를 반환하기 때문에 then, catch를 통해 chaining 할 수 있다.
axios({
// 크롤링을 원하는 페이지 URL
url: 'http://www.yes24.com/24/Category/BestSeller',
method: 'GET',
})
// 성공했을 경우
.then(response => {
console.log(response.data);
})
// 실패했을 경우
.catch(err => {
console.error(err);
});
- 위와 같이 axios 코드를 실행하면, 아래 이미지처럼 데이터를 받아온다. 하지만, 한글이 깨져보이는 것을 확인할 수 있다.
2. iconv-lite으로 Decoding하기
-
iconv-lite 모듈은 만약 '영문으로만 된 사이트' 이거나, 최근에 만들어진 사이트라면 사용할 필요가 없다. 조금 오래된 웹 사이트 중, Encoding 형식이 'EUC-KR'과 같은 형식일 경우에만 사용하면 되는 모듈이기 때문에, 크롤링 하고자 하는 웹 사이트를 잘 확인해서 사용하면 된다.
-
YES24 웹 사이트의 경우 charset이 UTF-8이 아닌 EUC-KR로 설정되어 있다. 따라서, axios로 데이터를 받아오면 한글이 깨지는 현상이 나타난다. 실제로 YES24 페이지에서 개발자 도구를 통해 확인해보면, charset=euc-kr
로 설정되어있는 것을 볼 수 있다.
-
즉, EUC-KR을 UTF-8로 Decoding 하기 위해 iconv-lite 패키지를 사용하는 것이다.
-
iconv-lite는 ArrayBuffer
라는 형식을 받는데, axios에서 해당 형식으로 데이터를 받을 수 있다.
- axios에서 responseType: 'arraybuffer' 로 지정해준다.
- 성공했을 경우의 response.data를 iconv를 통해 decode 해준다.
- 만약 정상적으로 데이터가 출력되지 않는다면, .toString() 메서드를 통해 string으로 변환한다.
const axios = require('axios');
const iconv = require('iconv-lite');
axios({
// 크롤링을 원하는 페이지 URL
url: 'http://www.yes24.com/24/Category/BestSeller',
method: 'GET',
responseType: 'arraybuffer',
})
// 성공했을 경우
.then(response => {
// 만약 content가 정상적으로 출력되지 않는다면, arraybuffer 타입으로 되어있기 때문일 수 있다.
// 현재는 string으로 반환되지만, 만약 다르게 출력된다면 뒤에 .toString() 메서드를 호출하면 된다.
const content = iconv.decode(response.data, 'EUC-KR');
console.log(content);
})
// 실패했을 경우
.catch(err => {
console.error(err);
});
- 이제 한글이 잘 나오는 것을 확인할 수 있다.
3. cheerio 모듈로 데이터 추출
(1) cheerio 기본 사용법
- 우선, cheerio 모듈을 import하고, 우리가 axios로 받아온 데이터 소스들을 cheerio에 로드 시켜준다. 아래 예시를 통해 간단하게 살펴보자.
const cheerio = require('cheerio');
...
const $ = cheerio.load('<h1 class="title">WebSite</h1>');
$('h1.title').text(); // 'WebSite'
-
위 코드를 조금 살펴보자. cheerio.load() 메서드 안에 html 소스들을 넣고, 그 안에서 찾을 수 있도록 $ 변수에 할당해준다. $()로 표기하는 방식은 JQuery에서 주로 사용하는 형식인데, 크롤링을 할 때에는 이러한 형식을 많이 사용한다.
-
위에서 보면, $('h1.title').text()의 결과로 'WebSite'가 나온 것을 볼 수 있다. 그렇다면 왜 이렇게 되는 것인지, 소스를 뜯어보면 아래와 같다.
$()
: 태그와 클래스를 통해 어떤 데이터의 위치를 선택
h1.title
: h1 태그 중, 클래스가 title 인 것을 의미함
text()
: 해당 태그에서, 'text'를 가져옴
(2) 베스트 셀러 제목 가져오기
-
위의 방법대로 응용해서, 우리가 하고자 하는 YES24 베스트 셀러 40위까지의 제목을 가져와보도록 하자. 위에서 살펴본 방법대로, 개발자 도구에서 copy selector를 통해 '제목'의 위치를 가져온다.
-
제목의 위치는 #bestList > ol > li.num1 > p:nth-child(3) > a
로 확인이 되는데, 마찬가지로 위의 방법대로 li.num1
에서 num1을 제외하면 40개의 제목이 모두 확인되는 것을 알 수 있다. 이를 이용해서, 1위~40위 까지의 제목을 가져와보도록 한다.
const axios = require('axios');
const iconv = require('iconv-lite');
const cheerio = require('cheerio');
axios({
// 크롤링을 원하는 페이지 URL
url: 'http://www.yes24.com/24/Category/BestSeller',
method: 'GET',
responseType: 'arraybuffer',
})
// 성공했을 경우
.then(response => {
// 만약 content가 정상적으로 출력되지 않는다면, arraybuffer 타입으로 되어있기 때문일 수 있다.
// 현재는 string으로 반환되지만, 만약 다르게 출력된다면 뒤에 .toString() 메서드를 호출하면 된다.
const content = iconv.decode(response.data, 'EUC-KR');
const $ = cheerio.load(content);
const titles = $('#bestList > ol > li > p:nth-child(3) > a').text();
console.log(titles);
})
// 실패했을 경우
.catch(err => {
console.error(err);
});
- 위 코드를 실행해보면, 아래와 같이 1위부터 40위까지의 책 제목이 쭉 나열되어있는 것을 볼 수 있다.
(3) each()로 여러 항목 가져오기
- 이렇게 여러 항목들을 효과적으로 가져오기 위해서, cheerio에서 제공하는 'each' 메서드를 사용할 수 있다. cheerio의 each 메서드에 대한 사용법은, cheerio 공식문서 링크에서 확인할 수 있다.
const content = iconv.decode(response.data, 'EUC-KR');
const $ = cheerio.load(content);
const titleSelector = '#bestList > ol > li > p:nth-child(3) > a';
$(titleSelector).each((i, elem) => {
const title = $(elem).text();
console.log(i + 1, title);
});
- 위와같이 코드를 작성해주면, 아래의 결과를 확인할 수 있다. index는 0부터 시작하므로, 순위를 나타내기 위해 +1로 출력했고, $(elem).text()를 통해 제목을 가져왔다.
4. 순위별로 더 많은 데이터 추출하기
-
지금까지는 하나의 항목(제목)을 가져오는 방법을 살펴보았다. 하지만 위의 방법으로는 각 순위별 책의 '가격, 제목, 설명, 책 이미지' 등을 모두 가져오기는 쉽지 않아보인다. 따라서, 각 순위별 책의 항목들을 가져오는 방법을 실습해보도록 한다.
-
우선, 영역이 어떻게 나누어져 있는지 한번 확인해보도록 한다. 아래 이미지처럼, 순위에 맞는 책에 대한 설명, 제목, 저자, 출판사, 가격 등이 모두 하나의 html 태그 안에 포함되어있는 것처럼 보인다.
-
이제 개발자 도구에서 책 하나에 대한 태그를 선택해서 열어보자. 예상대로, 위에서 확인해 본 항목들이 확인된다.
-
이제 우리는 이것을 이용해서 '설명', '제목', '가격', '이미지'를 가져와보도록 하겠다. 이전 코드에서는 1위~40위까지 '제목' 태그만 each() 를 통해 반복했다면, 이제는 '책 한 권'에 대한 태그를 반복하면서 우리가 원하는 각각의 항목들을 출력해보면 될 것이다.
-
먼저, 1위~40위까지는 #bestList > ol > li
임을 위에서 살펴보았다. 이 중에서, 원하는 각 항목에 접근하는 방법을 살펴보도록 하자. 개발자 도구를 통해 '설명'을 클릭하면, 아래처럼 해당 태그로 이동하는 것을 확인할 수 있다. (어떻게 클릭하는지 모르겠다면, 개발자 도구 창의 좌측 상단 '네모와 마우스'가 있는 이미지' 쪽을 클릭하거나, cmd + shift + c
를 입력 후 마우스로 클릭해보면 영역 선택이 된다.)
-
태그 좌측의 화살표를 이용해서 항목을 펼쳐보면, 해당 코드는 <li>
하위에 있는 <p class="copy">
태그의 하위에 <a href=....>{설명}</a>
으로 위치해있다는 것을 알 수 있다. 이 것을 selector로 선택하는 방법을 알아보도록 한다. 아래의 규칙만 알면, 어렵지 않게 선택할 수 있다.
#
: id
.
: class
>
: 하위 태그
attr("attribute name")
: 속성명에 대한 속성
- 우리가 위에서 살펴본 1위~40위까지 각 책에 대한 selector는
#bestList > ol > li
였다. 즉, id가 'bestList'인 것의 하위에 있는 ol 태그를 선택한 후, 그 하위에 있는 li 태그를 찾는 것이다. 그럼, 우리가 가져와야하는 '설명'은 #bestList > ol > li > p.copy > a
가 되고, 그 것의 text를 가져와야 하므로 .text() 메서드를 호출하면 된다.
-
예상대로 설명만 잘 가져오는 것을 확인했으므로, 이제 각 항목을 어떻게 가져오는지 대충 감을 잡았다. 그럼, 1위~40위까지의 selector를 each()로 반복하면서, 각각의 값을 가져와보도록 하겠다. 하위 selector를 찾기 위해서는 .find() 메서드를 사용하면 아주 간단하게 가져올 수 있다. (여기서부터는 selector를 찾는 설명은 생략한다.)
-
#bestList > ol > li
를 통해 각 책에 대한 코드를 반복하면서, 원하는 항목인 제목, 설명, 가격, 이미지 주소를 크롤링 해보았다. 위 설명과 함께 아래의 이미지와 코드를 참고하면 바로 이해를 할 수 있을 것이다.
// 최종 코드
// index.js
const axios = require('axios');
const iconv = require('iconv-lite');
const cheerio = require('cheerio');
axios({
// 크롤링을 원하는 페이지 URL
url: 'http://www.yes24.com/24/Category/BestSeller',
method: 'GET',
responseType: 'arraybuffer',
})
// 성공했을 경우
.then(response => {
// 만약 content가 정상적으로 출력되지 않는다면, arraybuffer 타입으로 되어있기 때문일 수 있다.
// 현재는 string으로 반환되지만, 만약 다르게 출력된다면 뒤에 .toString() 메서드를 호출하면 된다.
const content = iconv.decode(response.data, 'EUC-KR');
const $ = cheerio.load(content);
// 1위~40위까지의 책들에 대한 selector
const booksSelector = '#bestList > ol > li';
$(booksSelector).each((i, elem) => {
const title = $(elem).find('p:nth-child(3) > a').text();
const description = $(elem).find('p.copy > a').text();
const price = $(elem).find('p.price > strong').text();
const imgUrl = $(elem).find('p.image > a > img').attr('src');
console.log(i + 1, {
title,
description,
price,
imgUrl,
});
});
})
// 실패했을 경우
.catch(err => {
console.error(err);
});
Author And Source
이 문제에 관하여([Node.js] Crawling (feat. Cheerio)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@_nine/Node.js-Crawling-feat.-Cheerio
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
// index.js
const axios = require('axios');
// axios는 Promise를 반환하기 때문에 then, catch를 통해 chaining 할 수 있다.
axios({
// 크롤링을 원하는 페이지 URL
url: 'http://www.yes24.com/24/Category/BestSeller',
method: 'GET',
})
// 성공했을 경우
.then(response => {
console.log(response.data);
})
// 실패했을 경우
.catch(err => {
console.error(err);
});
iconv-lite 모듈은 만약 '영문으로만 된 사이트' 이거나, 최근에 만들어진 사이트라면 사용할 필요가 없다. 조금 오래된 웹 사이트 중, Encoding 형식이 'EUC-KR'과 같은 형식일 경우에만 사용하면 되는 모듈이기 때문에, 크롤링 하고자 하는 웹 사이트를 잘 확인해서 사용하면 된다.
YES24 웹 사이트의 경우 charset이 UTF-8이 아닌 EUC-KR로 설정되어 있다. 따라서, axios로 데이터를 받아오면 한글이 깨지는 현상이 나타난다. 실제로 YES24 페이지에서 개발자 도구를 통해 확인해보면, charset=euc-kr
로 설정되어있는 것을 볼 수 있다.
즉, EUC-KR을 UTF-8로 Decoding 하기 위해 iconv-lite 패키지를 사용하는 것이다.
iconv-lite는 ArrayBuffer
라는 형식을 받는데, axios에서 해당 형식으로 데이터를 받을 수 있다.
- axios에서 responseType: 'arraybuffer' 로 지정해준다.
- 성공했을 경우의 response.data를 iconv를 통해 decode 해준다.
- 만약 정상적으로 데이터가 출력되지 않는다면, .toString() 메서드를 통해 string으로 변환한다.
const axios = require('axios');
const iconv = require('iconv-lite');
axios({
// 크롤링을 원하는 페이지 URL
url: 'http://www.yes24.com/24/Category/BestSeller',
method: 'GET',
responseType: 'arraybuffer',
})
// 성공했을 경우
.then(response => {
// 만약 content가 정상적으로 출력되지 않는다면, arraybuffer 타입으로 되어있기 때문일 수 있다.
// 현재는 string으로 반환되지만, 만약 다르게 출력된다면 뒤에 .toString() 메서드를 호출하면 된다.
const content = iconv.decode(response.data, 'EUC-KR');
console.log(content);
})
// 실패했을 경우
.catch(err => {
console.error(err);
});
const cheerio = require('cheerio');
...
const $ = cheerio.load('<h1 class="title">WebSite</h1>');
$('h1.title').text(); // 'WebSite'
위 코드를 조금 살펴보자. cheerio.load() 메서드 안에 html 소스들을 넣고, 그 안에서 찾을 수 있도록 $ 변수에 할당해준다. $()로 표기하는 방식은 JQuery에서 주로 사용하는 형식인데, 크롤링을 할 때에는 이러한 형식을 많이 사용한다.
위에서 보면, $('h1.title').text()의 결과로 'WebSite'가 나온 것을 볼 수 있다. 그렇다면 왜 이렇게 되는 것인지, 소스를 뜯어보면 아래와 같다.
$()
: 태그와 클래스를 통해 어떤 데이터의 위치를 선택
h1.title
: h1 태그 중, 클래스가 title 인 것을 의미함
text()
: 해당 태그에서, 'text'를 가져옴
위의 방법대로 응용해서, 우리가 하고자 하는 YES24 베스트 셀러 40위까지의 제목을 가져와보도록 하자. 위에서 살펴본 방법대로, 개발자 도구에서 copy selector를 통해 '제목'의 위치를 가져온다.
제목의 위치는 #bestList > ol > li.num1 > p:nth-child(3) > a
로 확인이 되는데, 마찬가지로 위의 방법대로 li.num1
에서 num1을 제외하면 40개의 제목이 모두 확인되는 것을 알 수 있다. 이를 이용해서, 1위~40위 까지의 제목을 가져와보도록 한다.
const axios = require('axios');
const iconv = require('iconv-lite');
const cheerio = require('cheerio');
axios({
// 크롤링을 원하는 페이지 URL
url: 'http://www.yes24.com/24/Category/BestSeller',
method: 'GET',
responseType: 'arraybuffer',
})
// 성공했을 경우
.then(response => {
// 만약 content가 정상적으로 출력되지 않는다면, arraybuffer 타입으로 되어있기 때문일 수 있다.
// 현재는 string으로 반환되지만, 만약 다르게 출력된다면 뒤에 .toString() 메서드를 호출하면 된다.
const content = iconv.decode(response.data, 'EUC-KR');
const $ = cheerio.load(content);
const titles = $('#bestList > ol > li > p:nth-child(3) > a').text();
console.log(titles);
})
// 실패했을 경우
.catch(err => {
console.error(err);
});
const content = iconv.decode(response.data, 'EUC-KR');
const $ = cheerio.load(content);
const titleSelector = '#bestList > ol > li > p:nth-child(3) > a';
$(titleSelector).each((i, elem) => {
const title = $(elem).text();
console.log(i + 1, title);
});
지금까지는 하나의 항목(제목)을 가져오는 방법을 살펴보았다. 하지만 위의 방법으로는 각 순위별 책의 '가격, 제목, 설명, 책 이미지' 등을 모두 가져오기는 쉽지 않아보인다. 따라서, 각 순위별 책의 항목들을 가져오는 방법을 실습해보도록 한다.
우선, 영역이 어떻게 나누어져 있는지 한번 확인해보도록 한다. 아래 이미지처럼, 순위에 맞는 책에 대한 설명, 제목, 저자, 출판사, 가격 등이 모두 하나의 html 태그 안에 포함되어있는 것처럼 보인다.
이제 개발자 도구에서 책 하나에 대한 태그를 선택해서 열어보자. 예상대로, 위에서 확인해 본 항목들이 확인된다.
이제 우리는 이것을 이용해서 '설명', '제목', '가격', '이미지'를 가져와보도록 하겠다. 이전 코드에서는 1위~40위까지 '제목' 태그만 each() 를 통해 반복했다면, 이제는 '책 한 권'에 대한 태그를 반복하면서 우리가 원하는 각각의 항목들을 출력해보면 될 것이다.
먼저, 1위~40위까지는 #bestList > ol > li
임을 위에서 살펴보았다. 이 중에서, 원하는 각 항목에 접근하는 방법을 살펴보도록 하자. 개발자 도구를 통해 '설명'을 클릭하면, 아래처럼 해당 태그로 이동하는 것을 확인할 수 있다. (어떻게 클릭하는지 모르겠다면, 개발자 도구 창의 좌측 상단 '네모와 마우스'가 있는 이미지' 쪽을 클릭하거나, cmd + shift + c
를 입력 후 마우스로 클릭해보면 영역 선택이 된다.)
태그 좌측의 화살표를 이용해서 항목을 펼쳐보면, 해당 코드는 <li>
하위에 있는 <p class="copy">
태그의 하위에 <a href=....>{설명}</a>
으로 위치해있다는 것을 알 수 있다. 이 것을 selector로 선택하는 방법을 알아보도록 한다. 아래의 규칙만 알면, 어렵지 않게 선택할 수 있다.
#
: id
.
: class
>
: 하위 태그
attr("attribute name")
: 속성명에 대한 속성
#bestList > ol > li
였다. 즉, id가 'bestList'인 것의 하위에 있는 ol 태그를 선택한 후, 그 하위에 있는 li 태그를 찾는 것이다. 그럼, 우리가 가져와야하는 '설명'은 #bestList > ol > li > p.copy > a
가 되고, 그 것의 text를 가져와야 하므로 .text() 메서드를 호출하면 된다.예상대로 설명만 잘 가져오는 것을 확인했으므로, 이제 각 항목을 어떻게 가져오는지 대충 감을 잡았다. 그럼, 1위~40위까지의 selector를 each()로 반복하면서, 각각의 값을 가져와보도록 하겠다. 하위 selector를 찾기 위해서는 .find() 메서드를 사용하면 아주 간단하게 가져올 수 있다. (여기서부터는 selector를 찾는 설명은 생략한다.)
#bestList > ol > li
를 통해 각 책에 대한 코드를 반복하면서, 원하는 항목인 제목, 설명, 가격, 이미지 주소를 크롤링 해보았다. 위 설명과 함께 아래의 이미지와 코드를 참고하면 바로 이해를 할 수 있을 것이다.
// 최종 코드
// index.js
const axios = require('axios');
const iconv = require('iconv-lite');
const cheerio = require('cheerio');
axios({
// 크롤링을 원하는 페이지 URL
url: 'http://www.yes24.com/24/Category/BestSeller',
method: 'GET',
responseType: 'arraybuffer',
})
// 성공했을 경우
.then(response => {
// 만약 content가 정상적으로 출력되지 않는다면, arraybuffer 타입으로 되어있기 때문일 수 있다.
// 현재는 string으로 반환되지만, 만약 다르게 출력된다면 뒤에 .toString() 메서드를 호출하면 된다.
const content = iconv.decode(response.data, 'EUC-KR');
const $ = cheerio.load(content);
// 1위~40위까지의 책들에 대한 selector
const booksSelector = '#bestList > ol > li';
$(booksSelector).each((i, elem) => {
const title = $(elem).find('p:nth-child(3) > a').text();
const description = $(elem).find('p.copy > a').text();
const price = $(elem).find('p.price > strong').text();
const imgUrl = $(elem).find('p.image > a > img').attr('src');
console.log(i + 1, {
title,
description,
price,
imgUrl,
});
});
})
// 실패했을 경우
.catch(err => {
console.error(err);
});
Author And Source
이 문제에 관하여([Node.js] Crawling (feat. Cheerio)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@_nine/Node.js-Crawling-feat.-Cheerio저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)