이 가이드에서는 다음을 다룹니다:
- 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를 사용할 것입니다.
개발자 도구는 브라우저 오른쪽 상단의 ‘추가 도구’에서 접근할 수 있습니다:

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

반대로, 왼쪽 상단의 ‘ ‘ 아이콘을 클릭하고 웹 페이지에서 렌더링된 요소를 선택하면 해당 요소의 원시 HTML로 이동하게 되며, 여기서도 파란색으로 강조 표시됩니다.
이 두 가지 과정만으로도 실습 튜토리얼에 필요한 CSS 디스크립터를 추출할 수 있습니다.
R을 활용한 웹 스크래핑 심층 분석: 튜토리얼
이 섹션에서는 아마존 URL을 웹 스크래핑하여 제품 리뷰를 추출하는 방법을 살펴보겠습니다.
필수 조건
Rstudio 환경에 다음이 설치되어 있는지 확인하십시오:
- R = 4.2.2
- rvest = 1.0.3
- tidyverse = 1.3.2
웹 페이지 대화형 탐색
Chrome의 개발자 도구(DevTools)를 사용하여 URL의 HTML을 탐색하고, 스크래핑하려는 정보(예: 제품 리뷰)가 포함된 HTML 요소의 모든 클래스와 ID 목록을 생성할 수 있습니다:

각 고객 리뷰는 다음과 같은 형식의 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&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&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 디스크립터를 간단히 가져올 수 있습니다:

세 가지 선택자를 다음과 같이 정의할 수 있습니다:
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 > spanfor 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)
출력된 데이터프레임은 다음과 같습니다:

마지막으로, 코드를 함수로 리팩토링하는 것이 좋습니다. 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 등)를 위한 데이터 보호 법적 요건을 이해하는 법률 전문가 팀
웹 콘텐츠는 다양한 형식으로 제공되며, 구조가 완전히 동일한 두 웹사이트를 찾기는 어렵습니다. 웹사이트가 복잡할수록, 스크래핑해야 할 기능과 데이터가 많을수록, 솔루션에 필요한 추가 시간과 자원은 물론, 요구되는 프로그래밍 지식도 더욱 고급스러워집니다.
일반적으로 최소한 다음과 같은 고급 기능을 구현해야 합니다:
- CAPTCHA 및 봇 탐지 가능성 최소화: 간단한 접근법은 웹 서버 과부하와 규칙적인 요청 패턴을 피하기 위해 무작위 sleep()을 추가하는 것입니다. 보다 효과적인 방법은 user_agent 또는 프록시 서버를 사용하여 요청을 서로 다른 IP에 분산시키는 것입니다.
- 자바스크립트 기반 웹사이트 스크래핑: 아마존 예시에서 특정 제품 변형을 선택해도 URL이 변경되지 않습니다. 리뷰는 공유되므로 스크래핑에 적합하지만, 제품 사양 스크래핑에는 부적합합니다. 동적 웹 페이지에서 사용자 상호작용을 모방하려면 RSelenium 같은 도구를 사용해 웹 브라우저 탐색을 자동화할 수 있습니다.
제한된 자원으로 웹 데이터에 접근하거나, 데이터 품질을 보장하거나, 더 고급 사용 사례를 활용하려는 경우 사전 구축된 웹 스크레이퍼가 적합한 선택일 수 있습니다.
Bright Data의 웹 스크레이퍼는 최신 기능을 기반으로 한 다양한 웹사이트용 템플릿을 제공하며, 데모로 소개된 아마존 스크레이퍼보다 훨씬 더 발전된 구현을 포함합니다!
데이터 수집을 직접 처리하고 싶지 않으신가요? 당사의 데이터셋을 확인해 보세요. 무료 샘플을 제공합니다.