살다 보면 가끔 인터넷에 있는 자료를 통째로 가져와서 데이터를 뽑아내고 싶다는 생각이 들 때가 있습니다.
이런 작업은 크롤링(crawling)이라고 부르기도 하고 (웹) 스크래이핑(scraping)이라고 부르기도 합니다.
전 세계에서 승객이 가장 많았던 공항 50군데를 시각화하면서 맛배기로 크롤링을 소개해 드렸던 적이 있습니다.
이번에는 한번 신문 기사를 긁어오는 과정을 알아보겠습니다.
이번에도 사용할 도구는 역시 R입니다. R 공식 홈페이지는 "R는 통계 계산과 그래픽에 활용하는 무료 소프트웨어 환경(R is a free software environment for statistical computing and graphics)"이라고 밝혀두고 있습니다.
러니 누구든 공짜로 이 프로그램을 사용할 수 있습니다. 아직 컴퓨터에 R가 없다면 이 다운로드 페이지에서 내려받아 설치하시면 됩니다. 그냥 다른 프로그램을 설치하실 때하고 똑같은 과정입니다.
아, 준비물이 하나 더 있습니다. 웹 브라우저입니다. 웹페이지 소스를 볼 수 있는 기능이 들어 있는 브라우저라면 어떤 것이든 관계 없습니다. 보통은 웹 브라우저에 저런 기능이 다 들어 있습니다.
저는 구글 크롬을 사용할 예정입니다. 역시 혹시나 크롬이 필요한데 설치하지 않으셨다면 이 링크를 찾아가시면 됩니다. (제가 추천하는 크롬 확장 프로그램을 같이 설치하셔도 좋습니다.)
신문 기사를 긁어올 때는 △원하는 기사를 상세하게 찾아서 △검색 결과에 나온 링크를 정리한 뒤 △해당 페이지에서 기사 내용을 긁어오는 세 단계를 거치는 게 일반적입니다.
넓은 의미에서 '인터넷 게시판'이 어떻게 생겼는지 떠올려 보면 각종 커뮤니티 사이트나 블로그, 인터넷 카페도 비슷한 과정을 거치면 된다는 걸 짐작할 수 있습니다. 한번 차근차근 천천히 해보겠습니다.
이번에 목표로 세운 건 제가 여태 쓴 '베이스볼 비키니' 모두 가져오기.
원하는 자료만 골라 오려면 정밀하게 검색하는 것도 중요합니다. 저는 이 칼럼을 쓸 때 기자가 누군지 알려주는 '바이라인'에 e메일 주소 대신 페이스북 계정 주소(fb.com/bigkini)를 넣었습니다. 다른 기자가 기사에 'bigkini'라는 낱말을 쓸 확률은 사실상 제로에 가까울 터.
동아닷컴(www.donga.com)에서 'bigkini'로 검색했습니다. 그다음 신문에 나온 기사만 보여주도록 선택했습니다
검색을 마치셨으면 인터넷 주소(URL)를 한번 분석할 필요가 있습니다. 위 화면이 나왔을 때 제 주소창에는 이렇게 떠 있었습니다.
http://news.donga.com/search?check_news=1&more=1&sorting=1&range=3&search_date=&query=bigkini
주소를 자세히 보시면 '&낱말=숫자 또는 낱말' 구조가 이어지는 게 눈에 띕니다.
기사를 찾을 때는 검색 결과가 한 페이지로 끝나는 일이 드물죠?
맨 아래로 내려서 검색 결과 2 페이지를 찾아 보겠습니다. URL은 다시 이렇게 나옵니다.
http://news.donga.com/search?p=16&query=bigkini&check_news=1&more=1&sorting=1&search_date=1&v1=&v2=&range=3
뭐가 뭔지 잘 모르겠으니 다시 1 페이지로 돌아오겠습니다. URL이 이렇게 바뀐 걸 알 수 있습니다.
http://news.donga.com/search?p=1&query=bigkini&check_news=1&more=1&sorting=1&search_date=1&v1=&v2=&range=3
맨 처음에 쓴 URL하고 보여주는 결과는 똑같은데 구조가 다릅니다. 검색 결과가 여러 페이지로 나올 때는 페이지 사이를 왕복해 보면 좋습니다. URL 구조가 좀더 분명히 들어오기 때문입니다.
현재 주소에서는 '&낱말=숫자 또는 낱말' 구조를 하나씩 지우고 다시 페이지를 불러 보시면 각 단위가 어떤 구실을 하는지 알 수 있습니다. 눈치가 빠른 분이라면 '&query=bigkini'는 검색한 낱말이 'bigkini'라는 걸 알려주고 있다는 걸 알아채셨을 겁니다.
또 맨 처음에는 &query=bigkini가 URL 맨 끝에 있었는데 다시 불러왔을 때는 앞으로 자리를 옮겼네요? 이건 이 단위끼리 자리를 바꿔도 검색 결과에 영향을 끼치지 않는다는 증거입니다.
이것 저것 지우고 옮겨 보니 아래처럼 URL을 바꿔도 처음 본 검색 결과하고 똑같은 결과가 나온다는 걸 알 수 있었습니다.
http://news.donga.com/search?query=bigkini&more=1&range=3&p=1
이 주소로 페이지를 불러온 상태에서 2페이지로 가봤더니 URL이 이렇게 나왔습니다.
http://news.donga.com/search?p=16&query=bigkini&check_news=1|2|3|6|7|8|9|12|14&more=1&sorting=1&search_date=1&v1=&v2=&range=3
처음에도 2페이지에 갔을 때 'p=16이 눈에 띄었는데 이번에도 그러네요. 줄이고 줄인 URL 맨 끝에 있는 p만 16으로 바꿔 주소창에 넣어 봤습니다. 예상대로 2페이지하고 같은 결과가 나옵니다.
좀더 확인해 보시면 3페이지는 p=31, 4페이지는 p=46, 5페이지는 p=61로 숫자가 15씩 늘어난다는 걸 알 수 있습니다. 한번에 보여주는 검색 결과가 15개거든요.
여기까지 오셨으면 이 글을 처음 시작할 때 말씀 드린 세 단계 중에서 첫 단계는 끝내신 겁니다. 수고하셨습니다.
아직은 별로 어려운 게 없었습니다. 아마 앞으로도 그럴 겁니다. 그래도 혹시 모르니 심호흡 한번 하시고 스크롤을 내려보겠습니다.
이제부터는 컴퓨터 프로그래밍이라고 부르기도 하고 코딩(coding)이라고 부르기도 하는 과정을 시작할 겁니다. 내가 이러저러한 게 하고 싶다고 컴퓨터에게 알려주는 게 바로 프로그래밍입니다.
예전에 썼던 글에서 그림을 가져오면 이렇게 print("Hello, World")라고 치는 것만으로 (한번도 코딩을 해보지 않으셨다면) 여러분은 생애 첫 프로그램을 완성하실 수 있습니다.
자, 우리는 여기서 URL을 가지고 뭔가 해보려고 합니다. 일단은 아래처럼 URL 여섯 줄을 만들어 내야 합니다.
http://news.donga.com/search?query=bigkini&more=1&range=3&p=1
http://news.donga.com/search?query=bigkini&more=1&range=3&p=16
http://news.donga.com/search?query=bigkini&more=1&range=3&p=31
http://news.donga.com/search?query=bigkini&more=1&range=3&p=46
http://news.donga.com/search?query=bigkini&more=1&range=3&p=61
http://news.donga.com/search?query=bigkini&more=1&range=3&p=76
맨 끝에 나온 숫자만 빼고는 전부 똑같은 구조입니다. 앞 부분은 놔두고 맨 끝만 반복적으로 갈아끼우면 되겠죠?
이럴 때 쓰라고 있는 게 문자 그대로 반복문입니다. 우리는 1, 16, 31, 46, 61, 76을 반복적으로 만들어야 합니다. 이걸 수식으로 표현하면 어떻게 될까요?
1 = 0 × 15 + 1 16 = 1 × 15 + 1 31 = 2 × 15 + 1…
처럼 표현할 수 있습니다. 역시 맨 앞에 숫자만 하나씩 커지고 나머지는 변화가 없습니다.
R에서는 for가 반복문을 만드는 대표적인 명령어(함수)입니다.
아래처럼 써서 R에 입력하면 우리가 원하는 값을 얻을 수 있습니다. x, y는 그냥 수학 방정식에 등장하는 것처럼 대표적인 문자 기호일 뿐 다른 의미가 있는 건 아닙니다.
for(x in 0:5){
y = x * 15 + 1
print(y)
}
이 반복문 실행이 모두 끝나고 나서 print(y) 또는 y라고 입력하시면 76이 나옵니다. 맨 마지막 값만 저장하고 있는 겁니다. 같은 방식으로 x를 치면 5가 나옵니다.우리는 저 링크 6개를 전부 저장해 두고 싶습니다. 이럴 때는 미리 그릇을 하나 만들어 놓고 거기 담으면 됩니다. 이런 그릇을 '변수'라고 합니다. 위에 나온 x, y도 변수입니다.
한번 x <- c(1, 2, 3, 4, 5) 라고 입력하신 다음 x를 치시면 저 숫자가 차례로 나올 겁니다. 여기서 c는 묶는다(concatenate)는 뜻입니다.
만약 x[1]이라고 치면 어떻게 될까요? 이때는 1이 나옵니다. x 중에서 첫 번째 값을 불러오라는 뜻이거든요. x[3]은? 네, 3이 나옵니다.
여기까지 이해하셨을 걸로 믿고 링크 6개를 저장하는 코드를 만들어 보겠습니다.
basic_url <- 'http://news.donga.com/search?query=bigkini&more=1&range=3&p='
urls <- NULL
for(x in 0:5){
urls[x+1] <- paste0(basic_url, x*15+1)
}
맨 첫 줄은 변하지 않는 부분을 컴퓨터에게 알려주는 겁니다. R에서 '<-' 또는 '='는 어떤 변수에 어떤 값을 넣으라는 뜻입니다.
다음 줄은 urls라는 방에 NULL을 넣으라는 뜻일 터. NULL은 빈 방이라는 뜻입니다. 미리 이런 변수를 만들어주지 않은 채로 '손님을 받으라'고 하면 R가 그 방을 못 찾아서 에러를 내거든요.
urls[x+1]은 urls 중에서 몇 번째 칸인지 정하는 겁니다. x는 0에서 5까지 움직이는데 R에서 변수에는 0번째 칸이 없습니다. 그래서 하나씩 자리를 키워주는 겁니다.
다음 나오는 paste0는 '붙여넣기'입니다. 이 변수와 저 변수를 붙이라는 뜻입니다. 그냥 paste를 쓰면 변수 사이에 한 칸을 띄웁니다. 여기서는 그러면 안 뇌니까 paste0을 써서 변수 사이를 붙였습니다.
결과를 확인해 볼까요?
urls
[1] "http://news.donga.com/search?query=bigkini&more=1&range=3&p=1" [2] "http://news.donga.com/search?query=bigkini&more=1&range=3&p=16" [3] "http://news.donga.com/search?query=bigkini&more=1&range=3&p=31" [4] "http://news.donga.com/search?query=bigkini&more=1&range=3&p=46" [5] "http://news.donga.com/search?query=bigkini&more=1&range=3&p=61" [6] "http://news.donga.com/search?query=bigkini&more=1&range=3&p=76"
생각처럼 잘 나왔죠? 이제 여러분은 한 걸음 더 크롤러(크롤링을 하는 프로그램) 완성에 가까이 다가가신 겁니다.
저 URL 여섯 개는 검색 결과 페이지를 찾아가는 주소입니다. 우리가 필요한 건 기사 그 자체로 가는 URL입니다. 이제 그 주소를 찾아볼 겁니다.
인터넷 페이지는 보통 하이퍼텍스트 마크업 언어(HTML) 문서로 만듭니다.
그렇다고 HTML이 뭔지 당장 배우실 필요는 없습니다. 지금은 HTML 문서 특징만 이해하시면 됩니다.
먼저 HTML 문서는 보통 트리(tree) 구조로 돼 있습니다.
만약 윈도 탐색기로 '내 음악' 폴더에 들어 있는 어떤 음악 파일을 재생한다고 해보겠습니다. 이러려면 사용자는 내 문서 → 내 음악 → 해당 음악 파일 순서로 찾아갈 겁니다. 이게 바로 트리 구조입니다.
또 한 가지 특징은 HTML은 태그(tag)라는 녀석을 한 데 합친 결과물이라는 점입니다. 아래는 이 블로그에 쓴 'WWW의 스물 다섯 번째 생일'에서 태그 하나를 가져온 겁니다.
<a href="http://info.cern.ch/hypertext/WWW/TheProject.html" target="_blank" class="tx-link">세계 최초 웹페이지</a>
맨 앞에 있는 'a'가 바로 태그입니다. a는 링크를 만드는 구실을 합니다.
그다음에 있는 href, target, class 같은 녀석은 속성(attribute)입니다. 이 링크를 어떻게 보여줘야 할지 설정을 해주는 것.
'세계 최초 웹페이지'는 실제로 화면에 나오는 텍스트(text)입니다.
HTML에서는 태그를 끝낼 때 시작한 태그로 닫아줘야 합니다. </a>가 들어간 이유입니다.
구체적으로 href는 실제로 가야 할 URL을 담고 있습니다.
target="_blank"는 이 링크를 새 창에서 띄우라는 뜻입니다.
클래스(class)는 게시물에서 이 부분을 "tx-link"라고 미리 정한 모양대로 보여주라는 뜻입니다.
이어 나온 '세계 최초 웹페이지'라는 일곱 글자를 표시할 때 글자색은 어떻게 하고, 밑줄은 어떻게 치라는 의미입니다.
한번 실제로 HTML 문서를 열어볼까요?
검색 결과 맨 처음에 나온 페이지에서 F12(크롬 기준)를 눌러 소스를 보겠습니다.
문서가 너무 길어 보기 불편할 때는 소스 보기 창 맨 위에 있는 화살표 모양을 눌러 놓고 웹 페이지에서 원하는 부분을 클릭하면 그 부분이 나와 있는 코드로 이동합니다.
우리가 찾으려는 건 실제 기사로 이동하는 링크, 그러니까 a 태그입니다.
이걸 찾아 보니까 searchCont라는 클래스가 붙은 디비전(divisodn) 아래 링크가 들어 있다는 걸 알 수 있습니다.
이제 이 링크를 전부 긁어 오면 스크래이핑 두 번째 단계를 끝낼 수 있습니다.
R로 크롤링을 할 때 가장 많이 쓰는 패키지는 rvest입니다.
아직 이 패키지를 설치하지 않으셨대도 걱정하실 거 없습니다. 그냥 install.packages("rvest")라고 R 입력창(콘솔)에 쳐넣기만 하면 됩니다.
그러고 나면 미러 선택창이 나오는데 아무 거나 고르셔도 됩니다.
R에서는 패키지를 설치만 했다고 바로 쓸 수 있는 건 아닙니다. 패키지를 메모리에 불러오는 과정이 필요합니다.
이번에도 쉽습니다. library('rvest')라고 입력하면 끝입니다.
install.packages('rvest')
library(rvest)
이제 R에 HTML을 불러와야겠죠? 이때 쓰는 함수는 read_html입니다. 한번 첫 번째 검색 페이지를 불러와 보면:
read_html(urls[1])
{xml_document} <html lang="ko"> [1] <head>\n<title>동아닷컴</title>\n<meta name="keywords" content="뉴스, 기사, 속보 ... [2] <body>\r\n<div class="skip"><a href="#contents">본문바로가기</a></div
잘 됩니다. 왜 urls[1]이라고 썼는지 모르는 분은 아니 계시겠죠? 그냥 두면 날아가니까 이 결과를 html이라는 변수에 넣어놓겠습니다.
html <- read_html(urls[1])
이제 기사로 가는 링크를 찾을 차례. rvest에서 특정 태그를 찾을 때는 html_node(s)를 쓰면 됩니다. s가 붙으면 같은 태그를 전부 찾고, 빼면 맨 처음에 나오는 하나만 찾습니다.
우리가 제일 먼저 찾아야 하는 건 'searchCont'라는 class가 붙은 div 태그입니다. 이건 이런 식으로 입력하면 됩니다.
html_nodes(html, '.searchCont')
'searchCont' 앞에 점(.)을 찍은 건 찾고자 하는 게 class라는 걸 알려주는 기능을 합니다.
실제로 크롤링할 때는 태그 속성 중에 class나 id를 이용할 때가 많습니다. class는 앞에 .을 찍고 id는 #을 붙여주면 그만입니다.
'html2 <- html_nodes(html, '.searchCont')'를 써서 이 결과를 html2라는 변수에 넣겠습니다.
html2에서 우리가 찾아야 하는 건 a 태그. 이번에는 html3에 저장합니다.
html3 <- html_nodes(html2, 'a')
이번에는 태그를 찾는 거니까 .이나 #을 쓰지 않았습니다. 결과는?
html3
{xml_nodeset (55)} [1] <a href="http://news.donga.com/3/all/20170202/82676578/1" target="_bl ... [2] <a href="http://news.donga.com/3/all/20170202/82676578/1" target="_bl ... [3] <a href="http://web.donga.com/pdf/pdf_viewer.php?vcid=2017020245A2601 ... [4] <a href="http://news.donga.com/3/all/20170202/82676578/1" target="_bl ... [5] <a href="http://news.donga.com/3/all/20170126/82600548/1" target="_bl ... (이하 생략)
뭔가 2% 부족합니다. 우리가 진짜 찾으려는 건 맨 처음 검색 결과 주소를 저장했을 때처럼 저 href 속성 안에 들어 있는 URL이거든요.
이번에도 문제가 없습니다. rvest에는 속성을 가져오는 html_attr 함수도 있으니까요. links라는 변수에 URL만 담아 보겠습니다.
links <- html_attr(html3, 'href')
links
[1] "http://news.donga.com/3/all/20170202/82676578/1" [2] "http://news.donga.com/3/all/20170202/82676578/1" [3] "http://web.donga.com/pdf/pdf_viewer.php?vcid=2017020245A2601" [4] "http://news.donga.com/3/all/20170202/82676578/1" [5] "http://news.donga.com/3/all/20170126/82600548/1" (이하 생략)
원하는 결과가 나왔습니다. 그런데 어차피 html → html2 → html3 → links 순서로 자료를 이동할 건데 이렇게 한 줄, 한 줄 치면 좀 피곤하지 않은가요?
그래서 세상에 나온 게 '파이프(pipe)'라는 녀석입니다.
현실 세계에서 파이프가 한 곳에서 다른 곳으로 물 같은 액체를 보낼 수 있게 도와주는 것처럼 이 녀석도 함수에서 다른 함수로 자료를 손쉽게 보내주는 구실을 합니다.
게다가 HTML이 트리 구조로 돼 있기 때문에 파이프를 쓰면 직관적으로 이해하기도 쉽습니다.
R에서 파이프를 쓰려면 dplyr라는 패키기자 필요합니다. 마찬가지로 설치하고 불러줍니다.
install.packages('dplyr')
library('dplyr')
deplyr를 설치하고 나면 HTML 문서를 읽어서 links에 저장하는 데 이 한 줄이면 충분합니다. R에서 파이프는 '%>%'로 표시합니다.
links <- html %>% html_nodes('.searchCont') %>% html_nodes('a') %>% html_attr('href')
여러 번에 나눠쳤던 걸 한 줄에 몰아 넣은 구조입니다.
파이프는 이 방 저 방 만들지 않고 한번에 끝낼 수 있다는 게 역시 제일 큰 장점입니다. 또 머릿속에 HTML 문서 구조가 있으면 입력하기도 훨씬 수월합니다.
사실 우리가 links라는 방에 넣어둔 URL은 여전히 2% 부족합니다. 다시 볼까요?
links
[1] "http://news.donga.com/3/all/20170202/82676578/1" [2] "http://news.donga.com/3/all/20170202/82676578/1" [3] "http://web.donga.com/pdf/pdf_viewer.php?vcid=2017020245A2601" [4] "http://news.donga.com/3/all/20170202/82676578/1" [5] "http://news.donga.com/3/all/20170126/82600548/1"(이하 생략)
[1], [2]가 똑같고 [4], [5]도 마찬가지입니다. 이건 기사 제목에만 링크가 들어 있는 게 아니라 대표 이미지(섬네일)에도 링크가 있는데 이를 거르지 않고 한번에 크롤링해서 생긴 일입니다.
이번에도 걱정하실 거 없습니다. R에서는 중복 자료를 정리해 보여주는 unique라는 함수가 있거든요.
처음부터 파이프를 써서 'links <- html %>% html_nodes('.searchCont') %>% html_nodes('a') %>% html_attr('href') %>% unique()'라고 입력했어도 아래하고 같은 결과가 나왔을 겁니다.
links <- unique(links)
links
[1] "http://news.donga.com/3/all/20170202/82676578/1" [2] "http://web.donga.com/pdf/pdf_viewer.php?vcid=2017020245A2601" [3] "http://news.donga.com/3/all/20170126/82600548/1" [4] "http://web.donga.com/pdf/pdf_viewer.php?vcid=2017012645A2601" [5] "http://news.donga.com/3/all/20170120/82482150/1"(이하 생략)
이번에도 좀 이상합니다. [1]하고 [2]가 스타일이 달라 보입니다. 실제로 저 주소를 브라우저에 입력해 보면 [1] 같은 형태는 인터넷 기사로 가는 반면 [2]는 지면 PDF로 연결한다는 걸 확인할 수 있습니다. 저런 건 빼야겠죠?
그때 도와주는 게 grep이라는 함수입니다. 딱 봐도 주소에 pdf가 들어가 있을 때 빼면 됩니다.
그러면 먼저 pdf가 들어간 걸 찾아 보겠습니다.
grep("pdf", links)
[1] 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
이상하게(?) 숫자가 나옵니다. 이 숫자는 pdf가 들어간 행을 나타냅니다. 위를 보시면 정말 [2], [4]에 pdf가 들어있네요
grep은 특정한 문자(열)가 들어간 자료 순서를 알려준다는 사실을 알 수 있습니다. 만약 links[grep("pdf", links)]라고 입력하면 어떤 결과가 나올까요?
links[grep("pdf", links)]
[1] "http://web.donga.com/pdf/pdf_viewer.php?vcid=2017020245A2601" [2] "http://web.donga.com/pdf/pdf_viewer.php?vcid=2017012645A2601" [3] "http://web.donga.com/pdf/pdf_viewer.php?vcid=2017012045A2601" [4] "http://web.donga.com/pdf/pdf_viewer.php?vcid=2016122845A3001" [5] "http://web.donga.com/pdf/pdf_viewer.php?vcid=2016122245A3101"(이하 생략)
pdf가 들어간 링크만 골라 보여줍니다. 위에서 변수 이름 옆에 []를 치면 그 순서에 맞는 자료만 보여준다는 걸 확인했습니다.
이번에는 [grep("pdf", links)]라고 입력했으니까 그 결과에 따라 순서를 골라 보여준 겁니다.
우리가 원하는 pdf가 들어간 자료만 남기는 게 아니라 빼는 겁니다. 특정 자료만 뺄 때는 []사이에 빼기(-) 표시를 하면 됩니다.
links[-grep("pdf", links)]처럼 말입니다.
links[-grep("pdf", links)]
[1] "http://news.donga.com/3/all/20170202/82676578/1" [2] "http://news.donga.com/3/all/20170126/82600548/1" [3] "http://news.donga.com/3/all/20170120/82482150/1" [4] "http://news.donga.com/3/all/20161228/82065527/1" [5] "http://news.donga.com/3/all/20161222/81980312/1"(이하 생략)
좋습니다. 이제 이걸 links에 담으면 되니까 links <- links[-grep("pdf", links)]를 치면 끝입니다.
(이 꼭지 제목은 사실과 다릅니다. URL은 Uniform Resource Locator를 줄인 말입니다.)
이제 겨우 딱 한 페이지에서 기사로 가는 URL을 찾았습니다. 여섯 페이지에서 똑같은 작업을 반복해야 하죠. 뭘 써야 할까요?
네, 정답은 반복문입니다.
이때는 이미 urls에 URL 여섯 개가 들어 있는 상태이기 때문에 x 같은 변수를 따로 지정할 필요는 없습니다. 그냥 '변수 in 변수' 형태로 표현하면 그만입니다.
우리는 urls라는 변수에 URL을 담아 놓았으니 'url in urls'라고 표현해 보겠습니다.
아, 뭐 잊은 게 없냐고 묻는 분이 계시네요. 맞습니다. 칸이 있는 방(변수)을 쓰려면 미리 빈 방이라고 한번 선언을 해줘야 합니다.
links <- NULL
for(url in urls){ html <- read_html(url) links <- c(links, html %>% html_nodes('.searchCont') %>% html_nodes('a') %>% html_attr('href') %>% unique()) }
낯선 표현이 하나 있습니다. c(links, 블라블라) 형태네요. c는 자료를 묶는다는 뜻이라고 위에서 확인했습니다.
이 줄은 links라는 변수에 자료를 계속 더하라는 뜻입니다.
예를 들어:
x <- 1
x <- 2
x
[1] 2
이렇게 치고 나서 x를 확인해 보면 2가 나옵니다. 맨 마지막에 입력한 값이 나오는 것.
이때 1과 2를 모두 가지고 있는 변수를 만들고 싶다면 아래처럼 치면 됩니다.
x <- 1
x <- c(x, 2)
x
[1] 1 2
아직 PDF 링크는 그대로 남아 있으니까 grep까지 적용하고 나서 확인해 보시면 links안에 URL이 88개 들어 있는 걸 확인하실 수 있습니다. 처음에 검색 결과에 나왔던 바로 그 숫자입니다.
length(links)
[1] 88
length는 이름 그대로 자료 길이(숫자)를 알려주는 함수입니다. length(urls)를 치면? 처음에 링크 6개를 넣어 뒀으니까 6이 나옵니다. 이제 슬슬 목적지가 보입니다.
URL이 있으니까 HTML 문서를 열어봐야겠죠?
개별 기사 소스를 확인하니까 'article_txt'라는 클래스로 된 div 안에 기사 본문이 들어 있는 게 보입니다.
클래스로 찾을 때는 .을 찍는다는 것만 기억하시면 어렵지 않습니다. 바로 앞에 했던 작업과 다른 건 본문 텍스트를 긁어 오는 거니까 html_text 함수를 써야 한다는 것뿐입니다.
그러면 이렇게 정리가 끝납니다.
txts <- NULL
for(link in links){
html <- read_html(link)
txts <- c(txts, html %>% html_nodes('.article_txt') %>% html_text())
}
txts
[1] "\n 심심풀이로 봤습니다, 프로야구 감독들 [2] "\n \n “차우찬이 95억 원씩이나 받 [3] "\n 현실 안맞는 일률 보상규정 때문에 충분히 [4] "\n 11개 종합지 올 시즌 기사 3742건 분석\n [5] "\n \n 올해 가장 비효율적으로 돈을(이하 생략)
축하드립니다. 여러분은 지금 생애 첫 크롤러(스크래이퍼)를 완성하셨습니다.
이제 이 프로그램을 잘 보완하시면 (이론적으로는) 구글이나 네이버 같은 검색 엔진도 만들 수 있습니다.
이렇게 얻은 결과를 나중에 또는 다른 프로그램에 활용하고 싶을 수도 있으니까 저장만 하면 끝입니다.
이럴 때는 보통 CSV(Comma Separated Values)라는 형태를 많이 씁니다. 이 형식은 문자 그대로 쉼표(comma)로 구분한 값이라는 뜻인데요, 그냥 쉽게 이해하시려면 텍스트(.txt) 파일 한 형태라고 보시면 됩니다.
write.csv(txts, "text.csv")
이렇게 입력하시면 여러분이 지정한 R 작업 폴더(윈도 기본은 '내 문서')에 text.csv 파일이 생긴 걸 확인하실 수 있습니다.
클로링한 자료는 여러분이 활용하시기 나름입니다. txts[4]에 등장하는 건 이 기사입니다.
텍스트 데이터로 할 수 있는 가장 기본이라고 할 수 있는 낱말 구름(word cloud)을 그렸습니다.
이 때는 R가 아니라 파이선(python)이라는 프로그래밍 언어를 썼는데 방식은 R하고 100% 똑같다고 해도 과언이 아닙니다. 말(언어)이 다르니 문법이 조금 다를 뿐이었습니다.
이 글은 크롤링 혹은 웹 스크래이퍼가 뭔지 전혀 모르지만 한번 그런 걸 만들어 보고 싶어하는 분들께 도움이 될까 해서 써봤습니다.
사실 저도 이런 건 만들어 보고 싶은 마음은 퍽 예전부터 있었는데, 코딩은 ABC도 잘 모르다 보니 어디서부터 시작해야 할지 막막했거든요.
이런 분들께 도움을 드리려고 최대한 자세하게 쓰다 보니 글이 너무 길어졌습니다.
모쪼록 도움을 받는 분이 계시길 바라며 코드를 정리해 남깁니다.
아래 코드를 그냥 가져다 R 콘솔에 붙이기만 하셔도 똑같은 결과는 얻으실 수 있습니다.
install.packages("rvest")
install.packages("dplyr")
library(rvest)
library(dplyr)
basic_url <- 'http://news.donga.com/search?query=bigkini&more=1&range=3&p='
urls <- NULL
for(x in 0:5){
urls[x+1] <- paste0(basic_url, as.character(x*15+1))
}
links <- NULL
for(url in urls){
html <- read_html(url)
links <- c(links, html %>% html_nodes('.searchCont') %>% html_nodes('a') %>% html_attr('href') %>% unique())
}
links <- links[-grep("pdf", links)]
txts <- NULL
for(link in links){
html <- read_html(link)
txts <- c(txts, html %>% html_nodes('.article_txt') %>% html_text())
}
write.csv(txts, "text.csv")
댓글,