R을 활용한 웹 스크래핑 실전 가이드

이 튜토리얼에서는 rvest를 사용한 R에서의 웹 스크래핑에 관련된 모든 단계를 살펴보며, 아마존 웹사이트의 공개적으로 접근 가능한 URL 하나에서 제품 리뷰를 추출하는 것을 목표로 합니다.
2 분 읽기
People coding at computers with data and media icons.

이 가이드에서는 다음을 다룹니다:

  • R을 활용한 웹 스크래핑 시작하기
    • 도구 이해하기
      • 개발 환경 설정
    • 웹 페이지 이해하기
    • 개발자 도구 이해하기
  • R을 활용한 웹 스크래핑 심층 분석: 튜토리얼
    • 필수 조건
    • 웹 페이지 대화형 탐색
      • 웹 스크래핑을 위한 CSS 선택기 vs. XPath
    • 웹 페이지에서 정보를 프로그래밍 방식으로 추출하기
  • 여러 URL로 확장하기
  • 다음 단계: 사전 구축 vs 자체 구축?

R을 이용한 웹 스크래핑 시작하기

이 R 튜토리얼에서 사용할 도구를 이해하는 것이 첫 번째 단계입니다.

도구 이해: R과 rvest

R은 통계 분석 및 데이터 시각화를 위한 풍부하고 사용하기 쉬운 라이브러리로, 데이터 정리 및 동적 타이핑에 유용한 도구를 제공합니다.

rvest(수확을 의미하는 “harvest”에서 유래)웹 스크래핑 기능을 제공하는 가장 인기 있는 R 패키지 중 하나입니다. 매우 사용자 친화적인 인터페이스 덕분에 더욱 그렇습니다. 기본 rvest는 하나의 웹 페이지에서만 데이터를 추출할 수 있게 해주며, 이는 초기 탐색에 완벽합니다. 이후 polite 라이브러리로 확장하여 여러 페이지를 스크래핑할 수 있습니다.

개발 환경 설정

RStudio 내에서 R을 아직 사용하지 않는 경우, 여기에서 설치 지침을 따르세요.

설치가 완료되면 콘솔을 열고 rvest를 설치하세요:

install.packages("rvest")

tidyverse 컬렉션의 일환으로, rvest의 기본 기능을 확장하기 위해 컬렉션 내 다른 패키지(코드 가독성을 위한 magrittr, HTML 및 XML 작업용 xml2 등)를 함께 사용하는 것이 공식적으로 권장됩니다. tidyverse를 직접 설치하여 이를 수행할 수 있습니다:

install.packages("tidyverse")

웹 페이지 이해하기

웹 스크래핑은 규정 준수 자동화 프로세스 내에서 웹사이트로부터 데이터를 추출하는 기술입니다.

이 정의에서 세 가지 중요한 고려 사항이 도출됩니다:

  • 데이터는 다양한 형식으로 제공됩니다.
  • 웹사이트는 정보를 매우 다양한 방식으로 표시합니다.
  • 스크래핑된 데이터는 합법적으로 접근 가능해야 합니다.

URL을 스크래핑하는 방법을 이해하려면 먼저 HTML 마크업 언어와 CSS 시트 스타일 언어를 통해 웹 페이지의 콘텐츠가 어떻게 표시되는지 이해해야 합니다.

HTML은 “태그”로 콘텐츠를 구성하여 웹 브라우저에 로드된 웹 페이지의 콘텐츠와 구조를 제공합니다. 이를 통해 트리 구조의 문서 객체 모델(DOM)을 생성합니다.
태그는 계층적 구조를 가지며, 각 태그는 시작 () 및 종료 () 구문 사이에 포함된 모든 콘텐츠에 특정 기능을 적용합니다:

<!DOCTYPE html>
<html lang="en-gb" class="a-ws a-js a-audio a-video a-canvas a-svg a-drag-drop a-geolocation a-history a-webworker a-autofocus a-input-placeholder a-textarea-placeholder a-local-storage a-gradients a-transform3d -scrolling a-text-shadow a-text-stroke a-box-shadow a-border-radius a-border-image a-opacity a-transform a-transition a-ember" data-19ax5a9jf="dingo" data-aui-build-date="3.22.2-2022-12-01">
▶<head>..</head>
▶<body class="a-aui_72554-c a-aui_accordion_a11y_role_354025-c a-aui_killswitch_csa_logger_372963-c a-aui_launch_2021_ally_fixes_392482-t1 a-aui_pci_risk_banner_210084-c a-aui_preload_261698-c a-aui_rel_noreferrer_noopener_309527-c a-aui_template_weblab_cache_333406-c a-aui_tnr_v2_180836-c a-meter-animate" style="padding-bottom: 0px;">..</body>
</html>

<html> 태그는 모든 웹 페이지의 최소 구성 요소로, 그 안에 <head><body> 태그가 중첩되어 있습니다. <head> 와 <body> 자체도 그 안에 다른 태그들의 “부모” 역할을 하며, <div> (문서 섹션용)와 <p> (단락용)이 가장 흔한 “자식” 태그들입니다.

위 코드 조각에서 각 HTML “요소”에 연관된 “속성”을 확인할 수 있습니다: lang, class, style은 사전 정의된 속성입니다. data-로 시작하는 속성은 아마존의 사용자 정의 속성입니다.

웹 스크래핑에서는 특히class 속성과 id 속성이 중요합니다. 전자는 요소 그룹을, 후자는 특정 요소를 각각 타겟팅할 수 있게 해주며, 원래 CSS 스타일링을 위해 고안되었습니다.

CSS는 웹 페이지의 스타일을 제공합니다. 색상부터 위치 및 크기 조정까지, 어떤 HTML 요소든 선택하여 스타일 속성에 새 값을 할당할 수 있습니다. 위 코드 조각에서 보셨듯이 style 속성을 사용해 HTML 요소 내부에 CSS 스타일을 인라인으로 적용할 수도 있습니다:

<body .. style="padding-bottom: 0px;">

순수 CSS에서는 다음과 같이 작성됩니다:

body {padding-bottom: 0px;}

여기서 body는 “선택자”, padding-bottom은 “속성”, 0px은 “값”입니다.
태그, 클래스, ID는 모두 CSS 선택자로 사용할 수 있습니다.

사용자는 스크립트 태그를 통해 JavaScript 프로그래밍 언어가 제공하는 기능을 활용하여 웹 페이지에 표시된 콘텐츠와 동적으로 상호작용할 수 있습니다. 사용자 상호작용 후 표시된 콘텐츠가 변경되거나 새로운 콘텐츠가 나타날 수 있습니다. 고급 웹 스크레이퍼는 사용자 상호작용을 모방할 수 있으며, 이에 대해서는 후술하겠습니다.

개발자 도구 이해하기

주요 웹 브라우저는 웹 페이지의 기술 정보를 수집하고 실시간으로 업데이트하여 로깅, 디버깅, 테스트 및 성능 분석을 가능하게 하는 내장 개발자 도구를 제공합니다. 이 튜토리얼에서는 Chrome의 DevTools를 사용할 것입니다.

개발자 도구는 브라우저 오른쪽 상단의 ‘추가 도구’에서 접근할 수 있습니다:

Process of opening Developer Tools in Chrome
Chrome에서 개발자 도구 열기 과정

개발자 도구에서 ‘요소’ 탭을 통해 원시 HTML을 스크롤할 수 있습니다. HTML 줄을 스크롤하면 웹 페이지에 렌더링된 해당 요소가 파란색으로 강조 표시됩니다:

Process of accessing rendered elements in the web page from Developer Tools in Chrome
Chrome 개발자 도구에서 웹 페이지의 렌더링된 요소 접근 과정

반대로, 왼쪽 상단의 ‘ ‘ 아이콘을 클릭하고 웹 페이지에서 렌더링된 요소를 선택하면 해당 요소의 원시 HTML로 이동하게 되며, 여기서도 파란색으로 강조 표시됩니다.

이 두 가지 과정만으로도 실습 튜토리얼에 필요한 CSS 디스크립터를 추출할 수 있습니다.

R을 활용한 웹 스크래핑 심층 분석: 튜토리얼

이 섹션에서는 아마존 URL을 웹 스크래핑하여 제품 리뷰를 추출하는 방법을 살펴보겠습니다.

필수 조건

Rstudio 환경에 다음이 설치되어 있는지 확인하십시오:

  • R = 4.2.2
  • rvest = 1.0.3
  • tidyverse = 1.3.2

웹 페이지 대화형 탐색

Chrome의 개발자 도구(DevTools)를 사용하여 URL의 HTML을 탐색하고, 스크래핑하려는 정보(예: 제품 리뷰)가 포함된 HTML 요소의 모든 클래스와 ID 목록을 생성할 수 있습니다:

Customer review from the Amazon product page selected with DevTools in Chrome
Chrome의 DevTools로 선택한 Amazon 제품 페이지의 고객 리뷰

각 고객 리뷰는 다음과 같은 형식의 ID를 가진 div 에 속합니다:

customer_review_$INTERNAL_ID.

위 스크린샷에서 고객 리뷰에 해당하는 div의 HTML 내용은 다음과 같습니다:

<div id="customer_review-R2U9LWUSIPY0GS" class="a-section celwidget" data-csa-c-id="kj23dv-axnw47-69iej3-apdvzi" data-cel-widget="customer_review-R2U9LWUSIPY0GS">
<div data-hook="genome-widget" class="a-row a-spacing-mini">..</div>
<div class="a-row">
<a class="a-link-normal" title="별점 5점 만점에 4.0점" href="https://www.amazon.co.uk/gp/customer-reviews/R2U9LWUSIPY0GS/ref=cm_cr_dp_d_rvw_ttl?ie=UTF8&amp;ASIN=B07SR4R8K1">
<i data-hook="review-star-rating" class="a-icon a-icon-star a-star-4 review-rating">
<span class="a-icon-alt">5점 만점에 4.0점</span>
</i>
</a>
<span class="a-letter-space"></span>
<a data-hook="review-title" class="a-size-base a-link-normal review-title a-color-base review-title-content a-text-bold" href="https://www.amazon.co.uk/gp/customer-reviews/R2U9LWUSIPY0GS/ref=cm_cr_dp_d_rvw_ttl?ie=UTF8&amp;ASIN=B07SR4R8K1">
         <span>가격이 조금 비싸긴 하지만 아주 좋은 컨트롤러입니다</span>
       </a>
</div>
<span data-hook="review-date" class="a-size-base a-color-secondary review-date">..</span>
<div class="a-row a-spacing-mini review-data review-format-strip">..</div>
<div class="a-row a-spacing-small review-data">
<span data-hook="review-body" class="a-size-base review-text">
<div data-a-expander-name="review_text_read_more" data-a-expander-collapsed-height="300" class="a-expander-collapsed-height a-row a-expander-container a-expander-partial-collapse-container" style="max-height:300px">
<div data-hook="review-collapsed" aria-expanded="false" class="a-expander-content reviewText review-text-content a-expander-partial-collapse-content">
<span>솔직히 말해서 가격이 왜 이렇게 높은지 잘 모르겠네요 ….</span>
</div>
                …</div>
            …</span>
        …</div>
    …</div>

고객 리뷰에서 관심 있는 각 콘텐츠 조각에는 고유한 클래스가 있습니다: 제목은 review-title-content, 본문은 review-text-content, 평점은 review-rating입니다.

문서 내에서 해당 클래스가 고유한지 확인한 후 “단순 선택자”를 직접 사용할 수 있습니다. 보다 안전한 방법은 CSS 디스크립터를 사용하는 것으로, 향후 새 요소에 클래스가 할당되더라도 고유성을 유지합니다.

개발자 도구에서 요소를 마우스 오른쪽 버튼으로 클릭하고 ‘선택자 복사’를 선택하면 CSS 디스크립터를 간단히 가져올 수 있습니다:

Process of copying the CSS selector for an element from Developer Tools in Chrome
Chrome 개발자 도구에서 요소 CSS 선택기 복사 과정

세 가지 선택자를 다음과 같이 정의할 수 있습니다:

  • customer_review-R2U9LWUSIPY0GS > div:nth-child(2) > a.a-size-base.a-link-normal.review-title.a-color-base.review-title-content.a-text-bold > span (제목용)
  • customer_review-R2U9LWUSIPY0GS > div.a-row.a-spacing-small.review-data > span > div > div.a-expander-content.reviewText.review-text-content.a-expander-partial-collapse-content > span (본문용)
  • customer_review-R2U9LWUSIPY0GS > div:nth-child(2) > a:nth-child(1) > i.review-rating > span for the rating

    일관성 향상을 위해*.review-rating을 수동으로 추가했습니다.

웹 스크래핑을 위한 CSS 선택자 vs. XPath

이 튜토리얼에서는 웹 스크래핑을 위한 요소 식별에 CSS 선택자를 사용하기로 결정했습니다. 다른 일반적인 접근 방식은 XPath(XML 경로)를 사용하는 것으로, DOM 내 완전한 경로를 통해 요소를 식별합니다.

CSS 선택자와 동일한 절차로 전체 XPath를 추출할 수 있습니다. 예를 들어, 리뷰 제목은 다음과 같습니다:

/html/body/div[2]/div[3]/div[6]/div[32]/div/div/div[2]/div/div[2]/span[2]/div/div/div[3]/div[3]/div/div[1]/div/div/div[2]/a[2]/span 

CSS 선택기는 약간 더 빠르지만, XPath는 약간 더 나은 하위 호환성을 가집니다. 이러한 사소한 차이점을 제외하면, 어느 쪽을 선택할지는 기술적 함의보다는 개인적 선호도에 더 많이 좌우됩니다.

웹 페이지에서 정보를 프로그래밍 방식으로 추출하기

콘솔을 직접 사용해 URL 웹 스크래핑 방법을 탐색할 수도 있지만, 추적성과 재현성을 위해 스크립트를 작성한 후 콘솔의 source() 명령어로 실행하겠습니다.

스크립트 생성 후 첫 단계는 설치된 라이브러리를 로드하는 것입니다:

library(”rvest”)
library(”tidyverse”)

그런 다음 다음과 같이 관심 있는 콘텐츠를 프로그래밍 방식으로 추출할 수 있습니다. 먼저 검색할 URL을 저장할 변수를 생성합니다:

HtmlLink <- "https://www.amazon.co.uk/Xbox-Elite-Wireless-Controller-2/dp/B07SR4R8K1/ref=sr_1_1_sspa?crid=3F4M36E0LDQF3"

다음으로 URL에서 아마존 표준 식별 번호(ASIN)를 추출하여 고유한 제품 ID로 사용합니다:

ASIN <- str_match(HtmlLink, "/dp/([A-Za-z0-9]+)/")[,2]

웹 스크래핑으로 추출한 텍스트를 정규 표현식으로 정리하는 것은 데이터 품질 보장을 위해 흔히 사용되며 권장되는 방법입니다.

이제 웹 페이지의 HTML 콘텐츠를 다운로드합니다:

HTMLContent <- read_html(HtmlLink)

read_html() 함수는 xml2 패키지의 일부입니다.

이 내용을 print() 하면, 이전에 분석한 원시 HTML 구조와 일치하는 것을 확인할 수 있습니다:

{html_document}
<html lang="en-gb" class="a-no-js" data-19ax5a9jf="dingo">
[1] <head>n<meta http-equiv="Content-Type" content="text/ht ...
[2] <body class="a-aui_72554-c a-aui_accordion_a11y_role_354 ...

이제 페이지의 모든 제품 리뷰에 대해 관심 있는 세 개의 노드를 추출할 수 있습니다. Chrome 개발자 도구가 제공하는 CSS 설명자를 사용하되, 문자열에서 특정 고객 리뷰 식별자 #customer_review-R2U9LWUSIPY0GS와> ” 연결자를 제거하도록 수정하십시오. 또한 rvest의 html_nodes() 및 html_text() 기능을 활용하여 HTML 콘텐츠를 별도의 객체로 저장할 수도 있습니다.

다음 명령어는 리뷰 제목을 추출합니다:

review_title <- HTMLContent %>%
html_nodes("div:nth-child(2) a.a-size-base.a-link-normal.review-title.a-color-base.review-title-content.a-text-bold span") %>%
html_text()

review_title의 항목 예시는 “가격이 조금 비싸긴 하지만 아주 좋은 컨트롤러입니다.”입니다.

아래 코드는 리뷰 본문을 추출합니다:

review_body <- HTMLContent %>%
html_nodes("div.a-row.a-spacing-small.review-data span div div.a-expander-content.reviewText.review-text-content.a-expander-partial-collapse-content span") %>%
html_text()

review_body 항목의 예시는 “솔직히 말해서, 가격이 왜 이렇게 비싼지 모르겠네요…”로 시작합니다.

리뷰 평점을 추출하려면 다음 명령어를 사용할 수 있습니다:

review_rating <- HTMLContent %>%
html_nodes("div:nth-child(2) a:nth-child(1) i.review-rating span") %>%
html_text()

review_rating 항목의 예시는 “4.0 out of 5 stars”입니다.

이 변수의 품질을 개선하려면 평점 “4.0”만 추출하여 정수로 변환하세요:

review_rating <- substr(review_rating, 1, 3) %>% as.integer()

파이프 기능 %>%는 magrittr 툴킷에서 제공합니다.

이제 데이터 분석을 위해 스크랩한 내용을 tibble로 내보낼 차례입니다.
tibble은 tidyverse 컬렉션에 속하는 R 패키지로, 데이터 프레임을 조작하고 출력하는 데 사용됩니다.

df <- tibble(review_title, review_body, review_rating)

출력된 데이터프레임은 다음과 같습니다:

The scraped data as outputted by tibble
tibble로 출력된 스크랩된 데이터

마지막으로, 코드를 함수로 리팩토링하는 것이 좋습니다. scrape_amazon <- function(HtmlLink) 이렇게 하면 모범 사례를 따르고, 여러 URL로 확장할 수 있도록 코드를 준비하는 데 가장 적합합니다.

여러 URL로 확장하기

웹 스크래핑 템플릿이 생성된 후, 웹 크롤링 및 스크래핑을 통해 아마존에서 모든 주요 경쟁사 제품의 URL 목록을 생성할 수 있습니다.

솔루션을 프로덕션화하기 위해 여러 URL로 확장할 때는 애플리케이션의 기술적 요구 사항을 개괄적으로 설명해야 합니다.

명확하게 정의된 기술적 요구 사항을 통해 비즈니스 요구 사항을 올바르게 지원하고 기존 시스템과 원활하게 통합할 수 있습니다.

특정 기술 요구 사항에 따라 스크래핑 기능을 업데이트하여 다음 조합을 지원해야 합니다:

  • 실시간 또는 배치 처리
  • JSON, NDJSON, CSV 또는 XLSX와 같은 출력 형식
  • 이메일, API, 웹훅 또는 클라우드 스토리지와 같은 출력 대상

여러 웹 페이지를 스크래핑하기 위해 polite를 사용하여 rvest를 확장할 수 있다는 점은 이미 언급한 바 있습니다. polite는 웹 호스트의 robots.txt 파일을 완전히 준수하고 내장된 속도 제한 및 응답 캐싱 기능을 통해 세 가지 주요 기능을 사용하여 웹 수집 세션을 생성하고 관리합니다:

  • bow() 는 특정 URL에 대한 스크래핑 세션을 생성합니다. 즉, 웹 호스트에 접근하여 스크래핑 허가를 요청합니다.
  • scrape() 는 URL의 HTML에 접근합니다. rvest의 html_nodes()html_text() 함수로 파이프하여 특정 콘텐츠를 추출할 수 있습니다.
  • nod()는 세션을 재작성할 필요 없이 세션의 URL을 다음 페이지로 업데이트합니다.

웹사이트에서 직접 인용하면, “예의 바른 세션의 세 가지 원칙은 허락 구하기, 천천히 진행하기, 두 번 묻지 않기입니다.”

다음 단계: 사전 구축 vs. 자체 구축?

비즈니스에 유용한 데이터를 추출할 수 있는 최첨단 웹 스크레이퍼를 개발하려면 다음과 같은 역량이 필요합니다:

  • 웹 데이터 추출 전문성을 갖춘 데이터 전문가 팀
  • 프록시 관리 및 봇 방지 우회 기술에 능숙한 DevOps 엔지니어 팀 (CAPTCHA 통과 및 비공개 웹사이트 접근 가능)
  • 실시간 및 배치 데이터 추출을 위한 인프라 구축 전문성을 갖춘 데이터 엔지니어 팀
  • 개인정보 보호(GDPR, CCPA 등)를 위한 데이터 보호 법적 요건을 이해하는 법률 전문가 팀

웹 콘텐츠는 다양한 형식으로 제공되며, 구조가 완전히 동일한 두 웹사이트를 찾기는 어렵습니다. 웹사이트가 복잡할수록, 스크래핑해야 할 기능과 데이터가 많을수록, 솔루션에 필요한 추가 시간과 자원은 물론, 요구되는 프로그래밍 지식도 더욱 고급스러워집니다.

일반적으로 최소한 다음과 같은 고급 기능을 구현해야 합니다:

  1. CAPTCHA 및 봇 탐지 가능성 최소화: 간단한 접근법은 웹 서버 과부하와 규칙적인 요청 패턴을 피하기 위해 무작위 sleep()을 추가하는 것입니다. 보다 효과적인 방법은 user_agent 또는 프록시 서버를 사용하여 요청을 서로 다른 IP에 분산시키는 것입니다.
  2. 자바스크립트 기반 웹사이트 스크래핑: 아마존 예시에서 특정 제품 변형을 선택해도 URL이 변경되지 않습니다. 리뷰는 공유되므로 스크래핑에 적합하지만, 제품 사양 스크래핑에는 부적합합니다. 동적 웹 페이지에서 사용자 상호작용을 모방하려면 RSelenium 같은 도구를 사용해 웹 브라우저 탐색을 자동화할 수 있습니다.

제한된 자원으로 웹 데이터에 접근하거나, 데이터 품질을 보장하거나, 더 고급 사용 사례를 활용하려는 경우 사전 구축된 웹 스크레이퍼가 적합한 선택일 수 있습니다.

Bright Data의 웹 스크레이퍼는 최신 기능을 기반으로 한 다양한 웹사이트용 템플릿을 제공하며, 데모로 소개된 아마존 스크레이퍼보다 훨씬 더 발전된 구현을 포함합니다!

데이터 수집을 직접 처리하고 싶지 않으신가요? 당사의 데이터셋을 확인해 보세요. 무료 샘플을 제공합니다.