웹 스크래핑을 위한 Cheerio NPM 사용

이 단계별 가이드에서 Cheerio NPM을 사용하여 동적 및 정적 웹사이트를 스크래핑하는 방법을 알아보세요.
1 분 읽기
Cheerio NPM web scraping

Node.js는 웹 스크레이퍼 구축을 위한 강력한 옵션으로 부상했으며, 클라이언트 측과 서버 측 개발 모두에 편의를 제공합니다. 방대한 라이브러리 카탈로그 덕분에 Node.js를 활용한 웹 스크래핑은 매우 간편합니다. 본 문서에서는 cheerio를 집중 조명하고 효율적인 웹 스크래핑을 위한 그 기능을 살펴보겠습니다.

Cheerio는 HTML 및 XML 문서를 파싱하고 조작하기 위한 빠르고 유연한 라이브러리입니다. jQuery 기능의 하위 집합을 구현하므로 jQuery에 익숙한 사람이라면 cheerio의 구문도 쉽게 익힐 수 있습니다. 내부적으로는 HTML 및 XML 문서 파싱을 위해 parse5 라이브러리를 기본으로 사용하고, 선택적으로 htmlparser2 라이브러리도 활용합니다.

이 글에서는 cheerio를 활용한 프로젝트를 만들고, 동적 웹사이트와 정적 웹 페이지에서 데이터를 스크래핑하는 방법을 배울 것입니다.

cheerio를 활용한 웹 스크래핑

이 튜토리얼을 시작하기 전에 시스템에 Node.js가 설치되어 있는지 확인하세요. 아직 설치하지 않았다면 공식 문서를 참고하여 설치할 수 있습니다.

Node.js 설치 후 cheerio-demo 디렉터리를 생성하고 해당 디렉터리로 이동합니다:

  mkdir cheerio-demo u0026u0026 cd cheerio-demon

그런 다음 디렉터리 내에서 npm 프로젝트를 초기화하세요:

  npm init -yn

cheerioAxios 패키지를 설치합니다:

  npm install cheerio axiosn

이 튜토리얼의 코드를 작성할 index.js 파일을 생성하세요. 그런 다음 선호하는 편집기로 이 파일을 열어 시작하세요.

가장 먼저 필요한 모듈을 임포트해야 합니다:

  const axios = require(u0022axiosu0022);nconst cheerio = require(u0022cheeriou0022);n

이 튜토리얼에서는 웹 스크레이퍼 테스트용 공개 샌드박스인 Books to Scrape 페이지를 스크레이핑합니다. 먼저 Axios를 사용하여 다음 코드로 웹 페이지에 GET 요청을 보냅니다:

  axios.get(u0022https://books.toscrape.com/u0022).then((response) =u003e {n    n});n

콜백 함수의 응답 객체에는 data 속성에 웹 페이지의 HTML 코드가 포함됩니다. 이 HTML은 cheerio 모듈의 load 함수에 전달되어야 합니다. 이 함수는 CheerioAPI 인스턴스를 반환하며, 이후 코드에서 DOM에 접근하고 조작하는 데 사용됩니다. CheerioAPI 인스턴스는 $라는 변수에 저장되는데, 이는 jQuery 구문을 연상시키는 것입니다:

  axios.get(u0022https://books.toscrape.com/u0022).then((response) =u003e {n    const $ = cheerio.load(response.data);n});n

요소 찾기

cheerio는 페이지에서 요소를 선택하기 위해 CSS 및 XPath 선택자를 지원합니다. jQuery를 사용해 본 적이 있다면 구문이 익숙할 것입니다—CSS 선택자를 $() 함수에 전달하세요. 이 구문을 사용하여 Books to Scrape 웹사이트의 첫 페이지에서 정보를 찾고 추출해 보세요.

https://books.toscrape.com/을 방문하여 개발자 콘솔을 엽니다. ‘요소 검사’ 탭을 검색하면 페이지의 HTML 구조에 대해 자세히 알 수 있습니다. 이 경우, 책에 대한 모든 정보가 product-pod 클래스를 가진 article 태그에 포함되어 있음을 확인할 수 있습니다:

Inspect element

책을 선택하려면 다음과 같이 article.product_pod CSS 선택자를 사용해야 합니다:

  $(u0022article.product_podu0022);n

이 함수는 선택자와 일치하는 모든 요소의 목록을 반환합니다. each 메서드를 사용해 목록을 반복 처리할 수 있습니다:

  $(u0022article.product_podu0022).each( (i, element) =u003e {nn});n

루프 내부에서 element 변수를 사용해 데이터를 추출할 수 있습니다.

첫 페이지의 책 제목을 추출해 보세요. 요소 검사 콘솔로 돌아가면 제목이 어떻게 저장되어 있는지 확인할 수 있습니다:

inspect title elements

element 변수의 자식인 h3 요소를 찾아야 함을 알 수 있습니다. h3 내부에는 책 제목을 포함하는 a 요소가 있습니다. CSS 선택자와 함께 find 메서드를 사용해 요소의 자식 요소를 찾을 수 있지만, 먼저 element를 $를 통해 Cheerio 인스턴스로 변환해야 합니다:

  $(u0022article.product_podu0022).each( (i, element) =u003e {n    const titleH3 = $(element).find(u0022h3u0022);nn});n

이제 titleH3 내부의 a 를 찾을 수 있습니다:

  $(u0022article.product_podu0022).each( (i, element) =u003e {n    const titleH3 = $(element).find(u0022h3u0022);n    const title = titleH3.find(u0022au0022);n});n

참고: titleH3는 이미 Cheerio 인스턴스이므로 $를 통해 전달할 필요가 없습니다.

텍스트 추출

요소를 선택한 후 text 메서드를 사용해 해당 요소의 텍스트를 가져올 수 있습니다.

이전 예제를 수정하여 find 메서드 결과에 text 메서드를 호출해 책 제목을 추출합니다:

  $(u0022article.product_podu0022).each( (i, element) =u003e {n    const titleH3 = $(element).find(u0022h3u0022);n    const title = titleH3.find(u0022au0022).text();nn    console.log(title);n});n

완성된 코드는 다음과 같아야 합니다:

  const axios = require(u0022axiosu0022);nconst cheerio = require(u0022cheeriou0022);nnaxios.get(u0022https://books.toscrape.com/u0022).then((response) =u003e {n    const $ = cheerio.load(response.data);nn    $(u0022article.product_podu0022).each( (i, element) =u003e {n        const titleH3 = $(element).find(u0022h3u0022);n        const title = titleH3.find(u0022au0022).text();nn        console.log(title);n    });n});n

node index.js로 코드를 실행하면 다음과 같은 출력이 표시됩니다:

  A Light in the ...nTipping the VelvetnSoumissionnSharp ObjectsnSapiens: A Brief History ...nThe Requiem RednThe Dirty Little Secrets ...nThe Coming Woman: A ...nThe Boys in the ...nThe Black MarianStarving Hearts (Triangular Trade ...nShakespeare's SonnetsnSet Me FreenScott Pilgrim's Precious Little ...nRip it Up and ...nOur Band Could Be ...nOlionMesaerion: The Best Science ...nLibertarianism for BeginnersnIt's Only the Himalayasn

DOM 탐색: 자식 요소 및 형제 요소 찾기

제목을 추출한 후에는 각 책의 가격과 재고 상태를 추출할 차례입니다. Inspect Element를 통해 가격과 재고 상태가 모두 product_price 클래스의 div에 저장되어 있음을 확인할 수 있습니다. 이 div를 .product_price CSS 선택자로 선택할 수 있지만, CSS 선택자에 대해서는 이미 다루었으므로 다음에서는 다른 방법을 설명하겠습니다:

Finding children and siblings code

참고:div는 이전에 선택한 titleH3의 형제 요소입니다. titleH3의 next 메서드를 호출하면 다음 형제 요소를 선택할 수 있습니다:

  const priceDiv = titleH3.next();n

find 메서드를 사용해 CSS 선택자에 기반한 요소의 자식 요소를 찾을 수 있다는 점은 이미 확인하셨습니다. children 메서드로 모든 자식 요소를 선택한 후 eq 메서드를 사용해 특정 자식 요소를 선택할 수도 있습니다. 이는 nth-child CSS 선택자와 동일합니다.

이 경우 price는 priceDiv의 첫 번째 자식이고, availability는 priceDiv의 두 번째 자식입니다. 즉, 각각 priceDiv.children().eq(0)priceDiv.children().eq(1)으로 선택할 수 있습니다. 이렇게 선택한 후 가격과 재고 상태를 출력해 보세요:

  $(u0022article.product_podu0022).each( (i, element) =u003e {n    const titleH3 = $(element).find(u0022h3u0022);n    const title = titleH3.find(u0022au0022).text();nnn    const priceDiv = titleH3.next();n    const price = priceDiv.children().eq(0).text().trim();n    const availability = priceDiv.children().eq(1).text().trim();n    console.log(title, price, availability);n});n

이제 코드를 실행하면 다음과 같은 결과가 출력됩니다:

  A Light in the ... £51.77 In stocknTipping the Velvet £53.74 In stocknSoumission £50.10 In stocknSharp Objects £47.82 In stocknSapiens: A Brief History ... £54.23 In stocknThe Requiem Red £22.65 In stocknThe Dirty Little Secrets ... £33.34 In stocknThe Coming Woman: A ... £17.93 In stocknThe Boys in the ... £22.60 In stocknThe Black Maria £52.15 In stocknStarving Hearts (Triangular Trade ... £13.99 In stocknShakespeare's Sonnets £20.66 In stocknSet Me Free £17.46 In stocknScott Pilgrim's Precious Little ... £52.29 In stocknRip it Up and ... £35.02 In stocknOur Band Could Be ... £57.25 In stocknOlio £23.88 In stocknMesaerion: The Best Science ... £37.59 In stocknLibertarianism for Beginners £51.33 In stocknIt's Only the Himalayas £45.17 In stockn

속성 접근

지금까지 DOM을 탐색하고 요소에서 텍스트를 추출하는 방법을 살펴보았습니다. cheerio를 사용하면 요소에서 속성도 추출할 수 있으며, 이번 섹션에서는 이를 수행할 것입니다. 여기서는 요소의 class 목록을 읽어 책의 평점을 추출할 것입니다.

책의 평점은 흥미로운 구조를 가집니다. 평점은 p 태그 안에 포함되어 있습니다. 각 p 태그에는 정확히 다섯 개의 별이 있지만, 별은 p 요소의 클래스 이름을 기반으로 CSS를 사용하여 색상이 지정됩니다. 예를 들어, star-rating.Four 클래스를 가진 p에서는 처음 네 개의 별이 노란색으로 표시되어 4점 평점을 나타냅니다:

Star ratings code

책의 평점을 추출하려면 p 요소의 클래스 이름을 추출해야 합니다. 첫 번째 단계는 평점이 포함된 단락을 찾는 것입니다:

  const ratingP = $(element).find(u0022p.star-ratingu0022);n

attr 메서드에 속성 이름을 전달하면 요소의 속성을 읽을 수 있습니다. 이 경우 클래스 목록을 읽어야 하며, 다음 코드에서 이를 보여줍니다:

  const starRating = ratingP.attr('class');n

클래스 목록은 다음과 같은 형식입니다: star-rating X, 여기서 X는 One, Two, Three, Four, Five 중 하나입니다. 즉, 클래스 목록을 공백으로 분할하고 두 번째 요소를 가져와야 합니다. 다음 코드는 이를 수행하고 텍스트 등급을 숫자 등급으로 변환합니다:

  const rating = { One: 1, Two: 2, Three: 3, Four: 4, Five: 5 }[starRating.split(u0022 u0022)[1]];n

모든 것을 합치면 코드는 다음과 같습니다:

  $(u0022article.product_podu0022).each( (i, element) =u003e {n    const titleH3 = $(element).find(u0022h3u0022);n    const title = titleH3.find(u0022au0022).text();nnn    const priceDiv = titleH3.next();n    const price = priceDiv.children().eq(0).text().trim();n    const availability = priceDiv.children().eq(1).text().trim();nn    const ratingP = $(element).find(u0022p.star-ratingu0022);n    const starRating = ratingP.attr('class');n    const rating = { One: 1, Two: 2, Three: 3, Four: 4, Five: 5 }[starRating.split(u0022 u0022)[1]];nn    console.log(title, price, availability, rating);n});n

출력 결과는 다음과 같습니다:

  A Light in the ... £51.77 In stock 3nTipping the Velvet £53.74 In stock 1nSoumission £50.10 In stock 1nSharp Objects £47.82 In stock 4nSapiens: A Brief History ... £54.23 In stock 5nThe Requiem Red £22.65 In stock 1nThe Dirty Little Secrets ... £33.34 In stock 4nThe Coming Woman: A ... £17.93 In stock 3nThe Boys in the ... £22.60 In stock 4nThe Black Maria £52.15 In stock 1nStarving Hearts (Triangular Trade ... £13.99 In stock 2nShakespeare's Sonnets £20.66 In stock 4nSet Me Free £17.46 In stock 5nScott Pilgrim's Precious Little ... £52.29 In stock 5nRip it Up and ... £35.02 In stock 5nOur Band Could Be ... £57.25 In stock 3nOlio £23.88 In stock 1nMesaerion: The Best Science ... £37.59 In stock 1nLibertarianism for Beginners £51.33 In stock 2nIt's Only the Himalayas £45.17 In stock 2n

데이터 저장

웹 페이지에서 데이터를 스크래핑한 후에는 일반적으로 이를 저장하고 싶을 것입니다. 파일 저장, 데이터베이스 저장, 데이터 처리 파이프라인으로 전송하는 등 여러 방법이 있습니다. 이 섹션에서는 가장 간단한 방법인 CSV 파일로 데이터 저장하는 법을 배웁니다.

이를 위해 node-csv 패키지를 설치하세요:

  npm install csvn

index.js에서 fscsv-stringify 모듈을 임포트하세요:

  const fs = require(u0022fsu0022);nconst { stringify } = require(u0022csv-stringifyu0022);n

로컬 파일을 작성하려면 WriteStream을 생성해야 합니다:

  const filename = u0022scraped_data.csvu0022;nconst writableStream = fs.createWriteStream(filename);n

CSV 파일에 헤더로 추가될 열 이름을 선언합니다:

  const columns = [n  u0022titleu0022,n  u0022ratingu0022,n  u0022priceu0022,n  u0022availabilityu0022n];n

열 이름을 사용해 stringifier를 생성합니다:

  const stringifier = stringify({ header: true, columns: columns });n

each 함수 내부에서 stringifier를 사용하여 데이터를 작성합니다:

  $(u0022article.product_podu0022).each( (i, element) =u003e {n    ...nn    const data = { title, rating, price, availability };n    stringifier.write(data);nn});n

마지막으로 each 함수 외부에서 stringifier의 내용을 writableStream 변수에 작성해야 합니다:

  stringifier.pipe(writableStream);n

이 시점에서 코드는 다음과 같아야 합니다:

  const axios = require(u0022axiosu0022);nconst cheerio = require(u0022cheeriou0022);nconst fs = require(u0022fsu0022);nconst { stringify } = require(u0022csv-stringifyu0022);nnconst filename = u0022scraped_data.csvu0022;nconst writableStream = fs.createWriteStream(filename);nnconst columns = [n  u0022titleu0022,n  u0022ratingu0022,n  u0022priceu0022,n  u0022availabilityu0022n];nconst stringifier = stringify({ header: true, columns: columns });nnaxios.get(u0022https://books.toscrape.com/u0022).then((response) =u003e {n    const $ = cheerio.load(response.data);nn    $(u0022article.product_podu0022).each( (i, element) =u003e {n        const titleH3 = $(element).find(u0022h3u0022);n        const title = titleH3.find(u0022au0022).text();n    n        const priceDiv = titleH3.next();n        const price = priceDiv.children().eq(0).text().trim();n        const availability = priceDiv.children().eq(1).text().trim();n        const ratingP = $(element).find(u0022p.star-ratingu0022);n        const starRating = ratingP.attr('class');n        const rating = { One: 1, Two: 2, Three: 3, Four: 4, Five: 5 }[starRating.split(u0022 u0022)[1]];nn        console.log(title, price, availability, rating);nn        const data = { title, rating, price, availability };n        stringifier.write(data);nn    });nn    stringifier.pipe(writableStream);nn});n

코드를 실행하면 스크랩된 데이터가 포함된 scraped_data.csv 파일이 생성됩니다:

  title,rating,price,availabilitynA Light in the ...,3,£51.77,In stocknTipping the Velvet,1,£53.74,In stocknSoumission,1,£50.10,In stocknSharp Objects,4,£47.82,In stocknSapiens: A Brief History ...,5,£54.23,In stocknThe Requiem Red,1,£22.65,In stocknThe Dirty Little Secrets ...,4,£33.34,In stocknThe Coming Woman: A ...,3,£17.93,In stocknThe Boys in the ...,4,£22.60,In stocknThe Black Maria,1,£52.15,In stocknStarving Hearts (Triangular Trade ...,2,£13.99,In stocknShakespeare's Sonnets,4,£20.66,In stocknSet Me Free,5,£17.46,In stocknScott Pilgrim's Precious Little ...,5,£52.29,In stocknRip it Up and ...,5,£35.02,In stocknOur Band Could Be ...,3,£57.25,In stocknOlio,1,£23.88,In stocknMesaerion: The Best Science ...,1,£37.59,In stocknLibertarianism for Beginners,2,£51.33,In stocknIt's Only the Himalayas,2,£45.17,In stockn

결론

여기서 보셨듯이, cheerio 라이브러리는 jQuery와 유사한 구문과 매우 빠른 작동으로 웹 스크래핑을 쉽게 만들어 줍니다. 이 글에서는 다음을 수행하는 방법을 배웠습니다:

  • cheerio로 HTML 웹 페이지 로드 및 파싱
  • CSS 선택자로 요소 찾기
  • 요소에서 데이터 추출하기
  • DOM 탐색하기
  • 스크래핑한 데이터를 로컬 파일 저장소에 저장하기

전체 코드는 GitHub에서 확인할 수 있습니다.

그러나 cheerio는 단순한 HTML 파서이므로 자바스크립트 코드를 실행할 수 없습니다. 즉, 동적 웹 페이지나 단일 페이지 애플리케이션(SPA) 스크래핑에는 사용할 수 없습니다. 이러한 페이지를 스크래핑하려면 cheerio를 넘어 Selenium이나 Playwright 같은 복잡한 도구를 살펴봐야 합니다. 바로 여기에 Bright Data가 필요한 이유입니다. Bright Data의 방대한 웹 스크래핑 솔루션에는 Selenium 스크래핑 브라우저와 Playwright 스크래핑 브라우저가 포함됩니다. 제품에 대한 자세한 내용은 스크래핑 브라우저 문서를 참조하세요.