웹 스크래핑(Web Scraping)은 웹 페이지로부터 원하는 정보를 추출하는 기법입니다. 어떤 서비스에서 API가 별도로 제공되고 있지 않지만 웹 페이지로는 정보가 제공되고 있을 때, 웹 스크래핑 기법을 이용하면 원하는 정보를 획득할 수 있습니다.
웹 스크래핑은 흔히 웹 크롤링(Web Crawling)이라고도 많이 불립니다. 물론 엄밀하게 두 단어는 서로 다른 의미입니다. 크롤링은 여러 웹 페이지를 기계적으로 탐색하는 일을 말합니다. 한편 웹 스크래핑은 특정한 하나의 웹 페이지를 탐색하고, 또 소스코드 작성자가 원하는 정보를 콕 집어 얻어낸다는 점에서 크롤링과 차이가 있습니다. 그럼에도 크롤링과 스크래핑은 구현방법이 거의 같기 때문에, 실무에서는 구분없이 많이 불립니다.
이번 포스팅에서는 미국 CNBC 방송의 웹사이트에서 S&P 500 지수의 값을 확인하는 과정을 통해서, 웹 스크래핑의 원리를 PHP 예제코드를 통해 설명합니다.
웹 스크래핑의 원리
위 화면은 미국의 대표적인 경제방송 CNBC의 웹 사이트에 게재되어 있는 S&P 500 지수현황입니다. 스크린 샷에서 2,952.01이라고 표시된 부분이 S&P 500 지수의 현재 값입니다. 이 웹 페이지에서 사람이 S&P 500 지수를 찾는 과정을 거쳐야 하는지 살펴본다면, 다음 3가지 과정으로 정리할 수 있습니다.
- 열기 : 웹 브라우저로 웹 페이지를 엽니다.
- 찾기 : 눈으로 원하는 정보(여기서는 S&P 500 지수)가 표시된 부분을 찾습니다.
- 보기 : 찾은 부분 안에 표시된 지수를 눈으로 보고 확인합니다.
사람이 웹 페이지에서 정보를 찾는 “열고, 찾고, 본다”의 3가지 과정을 소스코드로 그대로 구현해놓은 것이 바로 웹 스크래핑입니다. 다만 사람이 바라보는 것은 웹 브라우저에 표시된 화면이지만, 컴퓨터가 바라보는 것은 HTML 코드라는 것이 다른 점입니다. 따라서 웹 스크래핑으로 정보를 찾을 때에는 아래와 같이 HTML 코드를 헤짚는 과정을 거칩니다.
- 열기 : HTTP 통신으로 웹 페이지를 열어 HTML 코드 전체를 가져옵니다.
- 찾기 : 원하는 정보가 표시된 HTML 태그를 찾습니다.
- 보기 : 찾은 HTML 태그 안에 기재된 정보를 가져옵니다.
이 3가지 과정을 정확하게 소스코드로 구현할 수 있다면, 어떤 프로그래밍 언어로든 웹 스크래핑을 구현할 수 있습니다. 지금부터는 PHP로 S&P 500 지수 현황을 스크래핑을 진행하는 예제코드를 위 3단계에 맞추어서 소개합니다.
PHP에서의 웹 스크래핑 구현
#1 열기 (Loading)
PHP에서 웹 페이지를 열어보기 위해 가장 널리 쓰이는 방법은 cURL 라이브러리를 이용하는 것입니다. cURL 라이브러리는 우분투나 CentOS에서 패키지 관리 명령어로 간단하게 설치를 할 수 있고, 사용법 또한 매우 간결합니다.
cURL에 대한 소개와 사용법은 제가 이전 글에서 정리한 내용이 있으므로 참고하실 수 있습니다. 여기서는 실제 S&P 500 지수 현황 페이지의 HTML 코드를 문자열로 수신하는 예제코드만 소개합니다.
function getWebPage($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, false); $res = curl_exec($ch); curl_close($ch); return $res; } $res = getWebPage('https://www.cnbc.com/quotes/?symbol=.SPX');
위 코드를 실행한 후 변수 $res를 확인하면, 웹 페이지의 HTML 소스코드가 문자열로 고스란히 담겨 있음을 확인할 수 있습니다.
#2 찾기 (Searching)
웹 페이지의 HTML 소스코드를 얻어냈다면, 이제 긴긴 HTML 코드를 해치고 원하는 정보가 어느 태그에 담겨 있는지를 발견해내야 합니다. 가장 손쉬운 방법은 웹 브라우저의 개발자 도구를 이용하는 것입니다.
대표적으로 Chrome 브라우저의 경우에는 F12 단축키를 눌러 개발자 도구를 열 수 있습니다. 개발자 도구는 설정에 따라 새 창으로 열릴 수도 있고, 웹 브라우저 한 켠에 구분된 영역으로 표시될 수도 있습니다.
열린 개발자 도구의 좌측 상단을 보면, 사각형 위에 마우스 포인터가 올려진 듯한 버튼이 있습니다. 이 버튼을 누르고 웹 페이지에서 자신이 확인하고자 하는 부분 위를 클릭하면, 해당 영역의 HTML 태그를 찾을 수 있는 단서를 찾을 수 있습니다. 위 화면의 경우에는 (‘.SPX’,’last’,’original’)라는 부분이 눈에 띕니다.
이제 개발자 도구에서 확인한 단서를 기초로 하여, HTML 소스코드를 열어서 해당 부분의 실제 HTML 태그를 확인합니다. 위 화면의 경우에는 S&P 500 지수의 현재 값이 다음과 같이 <span> 태그로 감싸져 있는 것을 확인할 수 있습니다.
<span class=’last original’ ng-bind=”quoteData[‘.SPX’].lastOutputoriginal | filter:processStripCondition(‘.SPX’,’last’,’original’)”>2991.25</span>
여기서 주목해야 할 점은 (‘.SPX’,’last’,’original’)라는 코드는 이 웹페이지의 HTML 소스코드에서는 다른 어디에서도 나오지 않고, 오직 우리가 원하는 S&P 500 지수를 감싸는 <span> 태그에만 유일하게 등장한다는 점입니다. 따라서 이 코드를 중심으로 HTML 태그를 헤짚는다면, 우리가 원하는 값에 충분히 도달할 수 있을 것입니다.
#3 보기 (Extracting)
이제 우리가 찾은 단서를 활용하여, 웹페이지의 HTML 소스코드 전체에서 원하는 값만 남기고 모두 제거하여 봅시다. 여기에는 여러 가지 방법이 있겠습니다만, 이해하기 쉽고 접근하기도 쉬운 좋은 방법 중 하나는 문자열 함수를 이용하는 것입니다. 아래의 예제처럼 strstr(), substr() 함수 등을 이용하여 차근차근 HTML 소스코드를 양분해가며 범위를 좁혀가면, 어느새 우리가 원하는 값에 도달하게 됩니다.
$res = strstr($res, "('.SPX','last','original')"); $res = strstr($res, ">"); $res = substr($res, 1); $res = strstr($res, "<", true); echo $res;
검출해야 할 HTML 태그가 복잡하지 않다면, 정규표현식을 이용해볼 수도 있습니다. 금번에 예제로 삼은 S&P 500 지수는 양 옆을 감싸고 있는 <span> 태그 하나를 잘 검출해내면 값을 금방 찾아낼 수 있으므로, 정규표현식 또한 아래처럼 간결하게 구성할 수 있습니다.
$regExp = "/\('\.SPX','last','original'\).+>([0-9.]+)</"; preg_match($regExp, $res, $matches); echo $matches[1];
웹 스크래핑의 한계
웹 스크래핑은 API가 지원되지 않거나 서비스 제공사 측의 기술적인 지원이 없는 상황을 타개하고, 원하는 정보를 가져올 수 있는 사실상의 유일한 방법입니다. 그러나 웹 스크래핑에도 한계는 존재합니다.
첫 번째로 웹 페이지가 변경되거나 중단되면 수집 또한 중단된다는 것입니다. 수집 대상이 되는 웹 사이트가 리뉴얼을 단행하거나 도메인 이전이 이루어지는 경우, 또는 장애가 발생한 경우가 대표적인 예입니다. 이러한 일은 보통 예고 없이 발생하게 되므로, 만약 스크래핑으로 획득한 값은 비즈니스에서 핵심적인 역할로 활용하기 어렵습니다.
두 번째로 수집빈도를 높이는 것이 현실적으로 어렵다는 것입니다. 수 분의 간격이라면 문제가 되지 않겠지만, 그 간격이 수 초 단위로 줄어들면 웹서비스 측에서는 이를 악의적인 접속으로 판단하여 차단할지도 모릅니다. 특히 웹 스크래핑을 통해 실시간으로 정보를 수집하려고 시도한다면 그 자체로 DoS 공격과 다르지 않게 되므로, 이러한 시도는 해서는 안될 것입니다.
마지막으로 법적이슈에 취약하다는 것입니다. 빈번한 스크래핑으로 인하여 대상이 되는 웹 서비스에 서버 지연 등의 어려움이 발생하면, 형법상 영업방해죄에 해당할 여지가 있습니다. 또한 수집된 정보의 저작권 정보 역시 민감한 부분이기 때문에, 스크래핑을 비즈니스의 핵심적인 컨셉에 포함하는 계획은 위험성이 있음을 기억해야 합니다.