Web scraping in practice

library(XML)
library(rvest)
## Loading required package: xml2
## 
## Attaching package: 'rvest'
## The following object is masked from 'package:XML':
## 
##     xml
library(stringr)
page <- readLines("https://news.daum.net/ranking/popular")
page_parsed <- htmlParse(page)

First Headline XPath: ‘//[@id="mArticle"]/div[2]/ul[3]/li[1]/div[2]/strong/a’ Second Headline XPath: ’//[@id="mArticle"]/div[2]/ul[3]/li[2]/div[2]/strong/a’

First Company XPath: ‘//[@id="mArticle"]/div[2]/ul[3]/li[1]/div[2]/strong/span’ Second Company XPath: ’//[@id="mArticle"]/div[2]/ul[3]/li[2]/div[2]/strong/span’

headline_xpath <- '//*[@id="mArticle"]/div[2]/ul[3]/li/div[2]/strong/a'
company_xpath <- '//*[@id="mArticle"]/div[2]/ul[3]/li/div[2]/strong/span'

headlines <- xpathSApply(page_parsed, headline_xpath, xmlValue)
headlines
##  [1] "이런 '대선'은 처음..미국, 분위기가 심상찮다 [2020 미국의 선택]"                  
##  [2] "위스콘신·미시간 등 경합주서 표차 줄어들며 치열한 접전"                          
##  [3] "임용 1년 내 퇴직 공무원 1769명..그들은 왜 '철밥통' 버렸나"                       
##  [4] "[단독] \"김봉현, 기동민에 수천만 원 건네는 것 봤다\""                            
##  [5] "당선자 못정한 미국..결정은 북부 3개 경합주 손에"                                 
##  [6] "바이든, 승리 가능..위스콘신·미시간 잡으면 게임끝"                               
##  [7] "1억 넘긴 사전투표..\"바이든에 유리\" 전망 빗나갔나"                              
##  [8] "저수지에 빠진 할머니..'오리털 점퍼'가 살렸다"                                    
##  [9] "요양병원 투자자 \"윤석열 장모가 실질적 운영자\""                                 
## [10] "'이건희 장례식장' 확진자 발생에 정·재계 줄줄이 코로나 검사"                     
## [11] "베이비박스 1m 옆 버려진 아기..엄마는 \"사망 몰랐다\""                            
## [12] "\"트럼프 만세\" \"미국의 추락\".. 전세계 지도자들의 美 대선 관전평"              
## [13] "[미 대선] 바이든, 경합주 애리조나서 승리..\"트럼프 재선에 타격\""                
## [14] "美언론 \"코로나 '통제 성공' 국가 공통점은?\"..韓 '언급'"                         
## [15] "\"열화상 카메라가 찍는 건 체온 아니라 피부 온도\" 열화상 정확도 논란"            
## [16] "'턱스크' 50대, 지하철 흡연 · 음주..35만 원 내면 끝?"                            
## [17] "고민정 의원은 왜 삼성 이 전무에게 사과했을까"                                    
## [18] "[미 대선] 트럼프 승리 주장에 \"거짓말\"..민주·공화 양당서 비판"                 
## [19] "초접전지 '엎치락뒤치락'..위스콘신·조지아 잡아야 웃는다 [2020 미국의 선택]"      
## [20] "집값 잡겠다는 공시가 현실화.. 전문가들 \"서울·지방 전월세 다 오를 것\""         
## [21] "軍 \"시간 계산하며 철책 넘은 귀순자..침투했을 가능성도\""                        
## [22] "곽상도, 옵티 직원 대화 공개..\"의원되기 전부터 케어\""                           
## [23] "바이든이 펜실베이니아·조지아 가져오면 승리한다"                                 
## [24] "열세 뒤집은 '트럼프의 뒷심'.. 우편투표에 건 '바이든의 운명' [트럼프 승기 잡았다]"
## [25] "20대 가수지망생 극단선택..前 남친 40대 유명가수 성폭행 혐의 입건"                
## [26] "검찰, '무고·명예훼손' 혐의 정봉주 2심도 실형 구형"                              
## [27] "\"유인해서 철사 쪼아\"..한 달 사이 고양이 사체 5마리"                            
## [28] "\"외부 힘에 복부 손상\" 결론..아기 부모 구속영장 검토"                           
## [29] "트럼프, 펜실베이니아 막판추격.. 美언론은 바이든 승리 예측"                       
## [30] "\"일어나! 일어나라고!!\"..깊은 밤 화재에서 주인 구한 앵무새"                     
## [31] "BBK·다스·도곡동 땅..검찰과 특검, 뭐했나?"                                      
## [32] "베트남 서열 1~3위 모두 만난 朴의장..양국 관계 '격상' 성과"                       
## [33] "[단독] 윤석열 장모, 요양병원 '각서 위조' 반박 녹취록 내놔"                       
## [34] "[집중취재]① '뼈 부러졌는데, 파스 발라라?'..평생 후유증 남아"                    
## [35] "230년 역사에 오점으로 남나..법정다툼으로 향하는 美대선"                          
## [36] "학생들 '답안지 유출' 학교 아닌 경찰에 직접 신고한 이유는"                        
## [37] "오늘부터 국내여행 숙소 3만∼4만원 할인쿠폰 100만장 지급(종합)"                   
## [38] "트럼프 \"우리가 승리, 결과 경이롭다\"..백악관서 개표 주시"                       
## [39] "[2020 美선택]트럼프 출구전략 짜던 英 화들짝..\"누가 당선되든 관계 강화\""        
## [40] "[집중취재]① \"출근하지 마\" 경비실 열쇠 바꿔버린 입주민 회장"                   
## [41] "트럼프, 개표 중 '승리 선언'..\"연방대법원 가져갈 것\""                           
## [42] "軍 수색 이틀간 따돌린 귀순자..고성 험준한 지형 영향도"                           
## [43] "1억 명 넘긴 사전투표..\"바이든 유리할 것\" 실제로는?"                            
## [44] "트럼프-바이든, 매직넘버 향해 선거인단수 접전..경합주서 승패"                     
## [45] "[단독] 결혼식 사회도 봐 준 동창 때려 숨지게 한 전직 교사"                        
## [46] "가족 명의 연예기획사 통해 소득세 탈루 유명 연예인 적발"                          
## [47] "\"하루 60건 배달 겨우 19만원 손에 쥐었다 ㅠㅠ\" 부산 '라이더'의 설움! [IT선빵!]" 
## [48] "\"반발하면 바뀐다\"..동학개미들 \"대주주 10억이어 다음은 전면과세\""             
## [49] "[미 대선] \"바이든, 방송사 승리선언 나오면 바로 정권이양 착수\""                 
## [50] "남긴 국물 억지로 벌컥..울산 어린이집, '밥 고문'까지"
companies <- xpathSApply(page_parsed, company_xpath, xmlValue)
companies
##  [1] "경향신문"     "연합뉴스"     "경향신문"     "SBS"          "KBS"         
##  [6] "뉴스1"        "JTBC"         "채널A"        "JTBC"         "연합뉴스"    
## [11] "SBS"          "조선일보"     "연합뉴스"     "뉴스1"        "동아사이언스"
## [16] "SBS"          "뉴스타파"     "연합뉴스"     "경향신문"     "파이낸셜뉴스"
## [21] "뉴시스"       "연합뉴스"     "뉴스1"        "파이낸셜뉴스" "뉴스1"       
## [26] "뉴스1"        "SBS"          "SBS"          "조선일보"     "연합뉴스"    
## [31] "한겨레"       "뉴시스"       "세계일보"     "KBS"          "이데일리"    
## [36] "뉴스1"        "연합뉴스"     "JTBC"         "뉴시스"       "KBS"         
## [41] "SBS"          "뉴시스"       "SBS"          "연합뉴스"     "한국일보"    
## [46] "경향신문"     "헤럴드경제"   "동아일보"     "연합뉴스"     "JTBC"

Five steps of web scraping

  1. We identify the running mechanism in the URL syntax.
  2. We retrieve links to the running pages.
  3. We download the running pages.
  4. We retrieve links to the entries on the running pages.
  5. We download the single entries.

Manipulating URLs to access multiple pages

baseurl <- "https://news.daum.net/ranking/popular?regDate="
dates <- seq(from=20201025, to=20201031, by=1)
urls <- str_c(baseurl, dates)
urls
## [1] "https://news.daum.net/ranking/popular?regDate=20201025"
## [2] "https://news.daum.net/ranking/popular?regDate=20201026"
## [3] "https://news.daum.net/ranking/popular?regDate=20201027"
## [4] "https://news.daum.net/ranking/popular?regDate=20201028"
## [5] "https://news.daum.net/ranking/popular?regDate=20201029"
## [6] "https://news.daum.net/ranking/popular?regDate=20201030"
## [7] "https://news.daum.net/ranking/popular?regDate=20201031"

What is a function? What is the function, lapply()?

urls
## [1] "https://news.daum.net/ranking/popular?regDate=20201025"
## [2] "https://news.daum.net/ranking/popular?regDate=20201026"
## [3] "https://news.daum.net/ranking/popular?regDate=20201027"
## [4] "https://news.daum.net/ranking/popular?regDate=20201028"
## [5] "https://news.daum.net/ranking/popular?regDate=20201029"
## [6] "https://news.daum.net/ranking/popular?regDate=20201030"
## [7] "https://news.daum.net/ranking/popular?regDate=20201031"
remove_numbers <- function(x){
  y <- str_remove(x, "[[:digit:]]+")
  return(y)
}

?lapply
## starting httpd help server ... done
lapply(urls, remove_numbers)
## [[1]]
## [1] "https://news.daum.net/ranking/popular?regDate="
## 
## [[2]]
## [1] "https://news.daum.net/ranking/popular?regDate="
## 
## [[3]]
## [1] "https://news.daum.net/ranking/popular?regDate="
## 
## [[4]]
## [1] "https://news.daum.net/ranking/popular?regDate="
## 
## [[5]]
## [1] "https://news.daum.net/ranking/popular?regDate="
## 
## [[6]]
## [1] "https://news.daum.net/ranking/popular?regDate="
## 
## [[7]]
## [1] "https://news.daum.net/ranking/popular?regDate="

R Basics for Web Scraping

Creating vectors

A sequence of numbers/integers, characters, or Booleans

c(1,3,5) # Join elements into a vector 
## [1] 1 3 5
1:5 # An integer sequence
## [1] 1 2 3 4 5
seq(1, 5, by=2) # A sequence of integers from 1 to 5, increasing by 2
## [1] 1 3 5
rep(1:5, times=3) # Repeat an integer sequence 1:5 three times
##  [1] 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
rep(1:5, each=3) # Repeat each element of an integer sequence 1:5 three times
##  [1] 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5

Vector functions

# R is an object-oriented programming language; everything can be assigned to an object
headlines
##  [1] "이런 '대선'은 처음..미국, 분위기가 심상찮다 [2020 미국의 선택]"                  
##  [2] "위스콘신·미시간 등 경합주서 표차 줄어들며 치열한 접전"                          
##  [3] "임용 1년 내 퇴직 공무원 1769명..그들은 왜 '철밥통' 버렸나"                       
##  [4] "[단독] \"김봉현, 기동민에 수천만 원 건네는 것 봤다\""                            
##  [5] "당선자 못정한 미국..결정은 북부 3개 경합주 손에"                                 
##  [6] "바이든, 승리 가능..위스콘신·미시간 잡으면 게임끝"                               
##  [7] "1억 넘긴 사전투표..\"바이든에 유리\" 전망 빗나갔나"                              
##  [8] "저수지에 빠진 할머니..'오리털 점퍼'가 살렸다"                                    
##  [9] "요양병원 투자자 \"윤석열 장모가 실질적 운영자\""                                 
## [10] "'이건희 장례식장' 확진자 발생에 정·재계 줄줄이 코로나 검사"                     
## [11] "베이비박스 1m 옆 버려진 아기..엄마는 \"사망 몰랐다\""                            
## [12] "\"트럼프 만세\" \"미국의 추락\".. 전세계 지도자들의 美 대선 관전평"              
## [13] "[미 대선] 바이든, 경합주 애리조나서 승리..\"트럼프 재선에 타격\""                
## [14] "美언론 \"코로나 '통제 성공' 국가 공통점은?\"..韓 '언급'"                         
## [15] "\"열화상 카메라가 찍는 건 체온 아니라 피부 온도\" 열화상 정확도 논란"            
## [16] "'턱스크' 50대, 지하철 흡연 · 음주..35만 원 내면 끝?"                            
## [17] "고민정 의원은 왜 삼성 이 전무에게 사과했을까"                                    
## [18] "[미 대선] 트럼프 승리 주장에 \"거짓말\"..민주·공화 양당서 비판"                 
## [19] "초접전지 '엎치락뒤치락'..위스콘신·조지아 잡아야 웃는다 [2020 미국의 선택]"      
## [20] "집값 잡겠다는 공시가 현실화.. 전문가들 \"서울·지방 전월세 다 오를 것\""         
## [21] "軍 \"시간 계산하며 철책 넘은 귀순자..침투했을 가능성도\""                        
## [22] "곽상도, 옵티 직원 대화 공개..\"의원되기 전부터 케어\""                           
## [23] "바이든이 펜실베이니아·조지아 가져오면 승리한다"                                 
## [24] "열세 뒤집은 '트럼프의 뒷심'.. 우편투표에 건 '바이든의 운명' [트럼프 승기 잡았다]"
## [25] "20대 가수지망생 극단선택..前 남친 40대 유명가수 성폭행 혐의 입건"                
## [26] "검찰, '무고·명예훼손' 혐의 정봉주 2심도 실형 구형"                              
## [27] "\"유인해서 철사 쪼아\"..한 달 사이 고양이 사체 5마리"                            
## [28] "\"외부 힘에 복부 손상\" 결론..아기 부모 구속영장 검토"                           
## [29] "트럼프, 펜실베이니아 막판추격.. 美언론은 바이든 승리 예측"                       
## [30] "\"일어나! 일어나라고!!\"..깊은 밤 화재에서 주인 구한 앵무새"                     
## [31] "BBK·다스·도곡동 땅..검찰과 특검, 뭐했나?"                                      
## [32] "베트남 서열 1~3위 모두 만난 朴의장..양국 관계 '격상' 성과"                       
## [33] "[단독] 윤석열 장모, 요양병원 '각서 위조' 반박 녹취록 내놔"                       
## [34] "[집중취재]① '뼈 부러졌는데, 파스 발라라?'..평생 후유증 남아"                    
## [35] "230년 역사에 오점으로 남나..법정다툼으로 향하는 美대선"                          
## [36] "학생들 '답안지 유출' 학교 아닌 경찰에 직접 신고한 이유는"                        
## [37] "오늘부터 국내여행 숙소 3만∼4만원 할인쿠폰 100만장 지급(종합)"                   
## [38] "트럼프 \"우리가 승리, 결과 경이롭다\"..백악관서 개표 주시"                       
## [39] "[2020 美선택]트럼프 출구전략 짜던 英 화들짝..\"누가 당선되든 관계 강화\""        
## [40] "[집중취재]① \"출근하지 마\" 경비실 열쇠 바꿔버린 입주민 회장"                   
## [41] "트럼프, 개표 중 '승리 선언'..\"연방대법원 가져갈 것\""                           
## [42] "軍 수색 이틀간 따돌린 귀순자..고성 험준한 지형 영향도"                           
## [43] "1억 명 넘긴 사전투표..\"바이든 유리할 것\" 실제로는?"                            
## [44] "트럼프-바이든, 매직넘버 향해 선거인단수 접전..경합주서 승패"                     
## [45] "[단독] 결혼식 사회도 봐 준 동창 때려 숨지게 한 전직 교사"                        
## [46] "가족 명의 연예기획사 통해 소득세 탈루 유명 연예인 적발"                          
## [47] "\"하루 60건 배달 겨우 19만원 손에 쥐었다 ㅠㅠ\" 부산 '라이더'의 설움! [IT선빵!]" 
## [48] "\"반발하면 바뀐다\"..동학개미들 \"대주주 10억이어 다음은 전면과세\""             
## [49] "[미 대선] \"바이든, 방송사 승리선언 나오면 바로 정권이양 착수\""                 
## [50] "남긴 국물 억지로 벌컥..울산 어린이집, '밥 고문'까지"
h_vector <- headlines[1:5]
h_vector
## [1] "이런 '대선'은 처음..미국, 분위기가 심상찮다 [2020 미국의 선택]"
## [2] "위스콘신·미시간 등 경합주서 표차 줄어들며 치열한 접전"        
## [3] "임용 1년 내 퇴직 공무원 1769명..그들은 왜 '철밥통' 버렸나"     
## [4] "[단독] \"김봉현, 기동민에 수천만 원 건네는 것 봤다\""          
## [5] "당선자 못정한 미국..결정은 북부 3개 경합주 손에"
companies
##  [1] "경향신문"     "연합뉴스"     "경향신문"     "SBS"          "KBS"         
##  [6] "뉴스1"        "JTBC"         "채널A"        "JTBC"         "연합뉴스"    
## [11] "SBS"          "조선일보"     "연합뉴스"     "뉴스1"        "동아사이언스"
## [16] "SBS"          "뉴스타파"     "연합뉴스"     "경향신문"     "파이낸셜뉴스"
## [21] "뉴시스"       "연합뉴스"     "뉴스1"        "파이낸셜뉴스" "뉴스1"       
## [26] "뉴스1"        "SBS"          "SBS"          "조선일보"     "연합뉴스"    
## [31] "한겨레"       "뉴시스"       "세계일보"     "KBS"          "이데일리"    
## [36] "뉴스1"        "연합뉴스"     "JTBC"         "뉴시스"       "KBS"         
## [41] "SBS"          "뉴시스"       "SBS"          "연합뉴스"     "한국일보"    
## [46] "경향신문"     "헤럴드경제"   "동아일보"     "연합뉴스"     "JTBC"
c_vector <- companies[1:5]
c_vector
## [1] "경향신문" "연합뉴스" "경향신문" "SBS"      "KBS"
dates
## [1] 20201025 20201026 20201027 20201028 20201029 20201030 20201031
d_vector <- c(rep(dates[1],3),rep(dates[2],2))
d_vector
## [1] 20201025 20201025 20201025 20201026 20201026

list( )

What is a vector object? A collection of ordered elements in the same nature.

Ex) a vector of headlines; a vector of company names; a vector of dates …

What is a list object?

A vector with possible heterogeneous elements. That is, a list is collection of elements which can be of different contents or types.

The elements of a list can include character vectors and numeric vectors at once.

Let say we want to generate a list object that contains the above three vector objects all together.

We can use a function list( ) here.

List1 <- list(headline=h_vector, company=c_vector, date=d_vector) # Generate a list object, List1, to contain all the elements that are named "headline", "company", and "date"
length(List1) # The function, length, calculates how many elements are in any R object (vectors, lists, & factors).
## [1] 3
class(List1) # R is an object-oriented style of programming. The function, class, allows us to know what type an object belongs to. It can be numeric, character, logical, list, and so on...
## [1] "list"
names(List1) # To get or set the names of an object; Here, elements' names are "numbers", "characters", and "Booleans"
## [1] "headline" "company"  "date"
List1[1:2] # returns a new list object that contains the first and the second elements. What is length(List1[1:2])?
## $headline
## [1] "이런 '대선'은 처음..미국, 분위기가 심상찮다 [2020 미국의 선택]"
## [2] "위스콘신·미시간 등 경합주서 표차 줄어들며 치열한 접전"        
## [3] "임용 1년 내 퇴직 공무원 1769명..그들은 왜 '철밥통' 버렸나"     
## [4] "[단독] \"김봉현, 기동민에 수천만 원 건네는 것 봤다\""          
## [5] "당선자 못정한 미국..결정은 북부 3개 경합주 손에"               
## 
## $company
## [1] "경향신문" "연합뉴스" "경향신문" "SBS"      "KBS"
List1[2] # returns a new list object that contains the second element only. What is length(List1[2])?
## $company
## [1] "경향신문" "연합뉴스" "경향신문" "SBS"      "KBS"
List1[[2]] # returns a vector object that contains five elements of company names in the second element of List1. What is length(List1[[2]])? 
## [1] "경향신문" "연합뉴스" "경향신문" "SBS"      "KBS"
List1['headline'] # returns a new list with the element named 'headline' only
## $headline
## [1] "이런 '대선'은 처음..미국, 분위기가 심상찮다 [2020 미국의 선택]"
## [2] "위스콘신·미시간 등 경합주서 표차 줄어들며 치열한 접전"        
## [3] "임용 1년 내 퇴직 공무원 1769명..그들은 왜 '철밥통' 버렸나"     
## [4] "[단독] \"김봉현, 기동민에 수천만 원 건네는 것 봤다\""          
## [5] "당선자 못정한 미국..결정은 북부 3개 경합주 손에"
List1[['headline']] # returns a vector with the elements of the list element named 'headline'
## [1] "이런 '대선'은 처음..미국, 분위기가 심상찮다 [2020 미국의 선택]"
## [2] "위스콘신·미시간 등 경합주서 표차 줄어들며 치열한 접전"        
## [3] "임용 1년 내 퇴직 공무원 1769명..그들은 왜 '철밥통' 버렸나"     
## [4] "[단독] \"김봉현, 기동민에 수천만 원 건네는 것 봤다\""          
## [5] "당선자 못정한 미국..결정은 북부 3개 경합주 손에"

Why is list( ) important in web scraping?

Web data are structured at multiple levels. The list( ) function is useful for dealing with hierarchical data.

Ex) DAUM News > Ranking News > Popular News > Top 50 List on Oct/25, Top 50 List on Oct/26, Top 50 List on Oct/27 …

Let say we have a list of ten URLs for popular news at DAUM on different dates

# Example
urls
## [1] "https://news.daum.net/ranking/popular?regDate=20201025"
## [2] "https://news.daum.net/ranking/popular?regDate=20201026"
## [3] "https://news.daum.net/ranking/popular?regDate=20201027"
## [4] "https://news.daum.net/ranking/popular?regDate=20201028"
## [5] "https://news.daum.net/ranking/popular?regDate=20201029"
## [6] "https://news.daum.net/ranking/popular?regDate=20201030"
## [7] "https://news.daum.net/ranking/popular?regDate=20201031"
urls_list <- as.list(urls)
urls_list
## [[1]]
## [1] "https://news.daum.net/ranking/popular?regDate=20201025"
## 
## [[2]]
## [1] "https://news.daum.net/ranking/popular?regDate=20201026"
## 
## [[3]]
## [1] "https://news.daum.net/ranking/popular?regDate=20201027"
## 
## [[4]]
## [1] "https://news.daum.net/ranking/popular?regDate=20201028"
## 
## [[5]]
## [1] "https://news.daum.net/ranking/popular?regDate=20201029"
## 
## [[6]]
## [1] "https://news.daum.net/ranking/popular?regDate=20201030"
## 
## [[7]]
## [1] "https://news.daum.net/ranking/popular?regDate=20201031"
urls_list[[1]] # URL for popular news at DAUM on 10/25
## [1] "https://news.daum.net/ranking/popular?regDate=20201025"
urls_list[[2]] # URL for popular news at DAUM on 10/26
## [1] "https://news.daum.net/ranking/popular?regDate=20201026"

How to turn a list into a vector

#unlist( ) is a function to turn a list object into a vector object
#be cautious about using the unlist function
class(urls_list)
## [1] "list"
unlist(urls_list) # turn a list object into a vector object
## [1] "https://news.daum.net/ranking/popular?regDate=20201025"
## [2] "https://news.daum.net/ranking/popular?regDate=20201026"
## [3] "https://news.daum.net/ranking/popular?regDate=20201027"
## [4] "https://news.daum.net/ranking/popular?regDate=20201028"
## [5] "https://news.daum.net/ranking/popular?regDate=20201029"
## [6] "https://news.daum.net/ranking/popular?regDate=20201030"
## [7] "https://news.daum.net/ranking/popular?regDate=20201031"
class(unlist(urls_list))
## [1] "character"

unlist( ) allows us to combine a list object’s elements into a vector object

The apply() Family

apply() and its derivative functions allow crossing the data in a number of ways and avoid explicit use of loop constructs. They act on an input list, matrix or array and apply a named function with one or several optional arguments.

lapply( ): Applies a function to a list and returns a list object

lapply( ) applies a specified function to each element of a list and returns a new list object of the same length as the input list object. Each element of which is the result of applying a function to the corresponding element of the input list.

Function

Function

l in lapply( ) stands for list

List2 <- list(1:3,4:6,7:9)
List2
## [[1]]
## [1] 1 2 3
## 
## [[2]]
## [1] 4 5 6
## 
## [[3]]
## [1] 7 8 9
add_two <- function(x){
  y <- x + 2
  return(y)
}

lapply(List2, add_two) # returns a list object of results from applying a function "mean" to each element of List2 
## [[1]]
## [1] 3 4 5
## 
## [[2]]
## [1] 6 7 8
## 
## [[3]]
## [1]  9 10 11

sapply( ): Applies a function to a list and returns a vector (or matrix) object

sapply( ) applies a specified function to each element of a list and returns a vector object when possible. It is the same as applying the function unlist( ) to the result of lapply( ).

List2
## [[1]]
## [1] 1 2 3
## 
## [[2]]
## [1] 4 5 6
## 
## [[3]]
## [1] 7 8 9
sapply(List2, add_two) # applies a function "add_two" to elements as vectors in List2 as a list
##      [,1] [,2] [,3]
## [1,]    3    6    9
## [2,]    4    7   10
## [3,]    5    8   11

sapply( ) is also applicable to elements in different formats.

List1
## $headline
## [1] "이런 '대선'은 처음..미국, 분위기가 심상찮다 [2020 미국의 선택]"
## [2] "위스콘신·미시간 등 경합주서 표차 줄어들며 치열한 접전"        
## [3] "임용 1년 내 퇴직 공무원 1769명..그들은 왜 '철밥통' 버렸나"     
## [4] "[단독] \"김봉현, 기동민에 수천만 원 건네는 것 봤다\""          
## [5] "당선자 못정한 미국..결정은 북부 3개 경합주 손에"               
## 
## $company
## [1] "경향신문" "연합뉴스" "경향신문" "SBS"      "KBS"     
## 
## $date
## [1] 20201025 20201025 20201025 20201026 20201026
sapply(List1, str_length)
##      headline company date
## [1,]       40       4    8
## [2,]       30       4    8
## [3,]       37       4    8
## [4,]       31       3    8
## [5,]       28       3    8

tapply( ): Useful in applying to table

tapply( ) is used for applying a specified function to each element of a vector, grouped by another vector.

library(tidyverse)
## -- Attaching packages ------------------------------------------ tidyverse 1.3.0 --
## √ ggplot2 3.3.0     √ purrr   0.3.4
## √ tibble  3.0.0     √ dplyr   1.0.1
## √ tidyr   1.0.2     √ forcats 0.5.0
## √ readr   1.3.1
## -- Conflicts --------------------------------------------- tidyverse_conflicts() --
## x dplyr::filter()         masks stats::filter()
## x readr::guess_encoding() masks rvest::guess_encoding()
## x dplyr::lag()            masks stats::lag()
## x purrr::pluck()          masks rvest::pluck()
## x rvest::xml()            masks XML::xml()
mytable <- tibble(headline=h_vector, company=c_vector, date=d_vector) # Create a tibble object (dataframe) with three variables
mytable # returns a table with the three vectors
## # A tibble: 5 x 3
##   headline                                                      company     date
##   <chr>                                                         <chr>      <dbl>
## 1 "이런 '대선'은 처음..미국, 분위기가 심상찮다 [2020 미국의 선택]"~ 경향신문  2.02e7
## 2 "위스콘신·미시간 등 경합주서 표차 줄어들며 치열한 접전"       연합뉴스  2.02e7
## 3 "임용 1년 내 퇴직 공무원 1769명..그들은 왜 '철밥통' 버렸나"   경향신문  2.02e7
## 4 "[단독] \"김봉현, 기동민에 수천만 원 건네는 것 봤다\""        SBS       2.02e7
## 5 "당선자 못정한 미국..결정은 북부 3개 경합주 손에"             KBS       2.02e7
mytable$headline # $ sign selects a column of a data frame named "headline"
## [1] "이런 '대선'은 처음..미국, 분위기가 심상찮다 [2020 미국의 선택]"
## [2] "위스콘신·미시간 등 경합주서 표차 줄어들며 치열한 접전"        
## [3] "임용 1년 내 퇴직 공무원 1769명..그들은 왜 '철밥통' 버렸나"     
## [4] "[단독] \"김봉현, 기동민에 수천만 원 건네는 것 봤다\""          
## [5] "당선자 못정한 미국..결정은 북부 3개 경합주 손에"
mytable$company
## [1] "경향신문" "연합뉴스" "경향신문" "SBS"      "KBS"
mytable$date
## [1] 20201025 20201025 20201025 20201026 20201026

Say we want to count how many times each word appears in doc1 and doc2. Here each word can be grouped by the variable wordlist. tapply(x,y,length): Applies the function length() to x as a vector of frequency in each doc, grouped by y as another vector of words.

tapply(mytable$headline, mytable$date, str_length) # applies the function length to mytable$headline, grouped by mytable$date, and returns the result: the number of characters in each headline
## $`20201025`
## [1] 40 30 37
## 
## $`20201026`
## [1] 31 28

tapply(x,y,length): Applies the function str_length() to x as a vector of character length in each headline, grouped by y as another vector of dates.

Let’s practice on tapply( )

Let’s calculate how many headlines are in each company and how many characters are in the headlines, using the function tapply( ).

headline_df <- tibble(headlines, companies) # creates a data frame including two vectors as columns
headline_df
## # A tibble: 50 x 2
##    headlines                                                        companies
##    <chr>                                                            <chr>    
##  1 "이런 '대선'은 처음..미국, 분위기가 심상찮다 [2020 미국의 선택]" 경향신문 
##  2 "위스콘신·미시간 등 경합주서 표차 줄어들며 치열한 접전"          연합뉴스 
##  3 "임용 1년 내 퇴직 공무원 1769명..그들은 왜 '철밥통' 버렸나"      경향신문 
##  4 "[단독] \"김봉현, 기동민에 수천만 원 건네는 것 봤다\""           SBS      
##  5 "당선자 못정한 미국..결정은 북부 3개 경합주 손에"                KBS      
##  6 "바이든, 승리 가능..위스콘신·미시간 잡으면 게임끝"               뉴스1    
##  7 "1억 넘긴 사전투표..\"바이든에 유리\" 전망 빗나갔나"             JTBC     
##  8 "저수지에 빠진 할머니..'오리털 점퍼'가 살렸다"                   채널A    
##  9 "요양병원 투자자 \"윤석열 장모가 실질적 운영자\""                JTBC     
## 10 "'이건희 장례식장' 확진자 발생에 정·재계 줄줄이 코로나 검사"     연합뉴스 
## # ... with 40 more rows
# length() & str_length() apply to headline_df$headlines, grouped by headline_df$companies
tapply(headline_df$headlines, headline_df$companies, length) # returns a table
##         JTBC          KBS          SBS     경향신문        뉴스1     뉴스타파 
##            4            3            7            4            6            1 
##       뉴시스 동아사이언스     동아일보     세계일보     연합뉴스     이데일리 
##            4            1            1            1            9            1 
##     조선일보        채널A 파이낸셜뉴스       한겨레     한국일보   헤럴드경제 
##            2            1            2            1            1            1
tapply(headline_df$headlines, headline_df$companies, str_length)
## $JTBC
## [1] 29 26 33 31
## 
## $KBS
## [1] 28 37 35
## 
## $SBS
## [1] 31 31 35 31 31 32 31
## 
## $경향신문
## [1] 40 37 45 31
## 
## $뉴스1
## [1] 28 34 25 39 30 33
## 
## $뉴스타파
## [1] 25
## 
## $뉴시스
## [1] 32 36 44 31
## 
## $동아사이언스
## [1] 39
## 
## $동아일보
## [1] 38
## 
## $세계일보
## [1] 35
## 
## $연합뉴스
## [1] 30 34 38 37 31 35 37 34 37
## 
## $이데일리
## [1] 32
## 
## $조선일보
## [1] 38 33
## 
## $채널A
## [1] 26
## 
## $파이낸셜뉴스
## [1] 41 49
## 
## $한겨레
## [1] 26
## 
## $한국일보
## [1] 34
## 
## $헤럴드경제
## [1] 51

Let’s apply the function for extracting headlines and companies from the URLs

urls
## [1] "https://news.daum.net/ranking/popular?regDate=20201025"
## [2] "https://news.daum.net/ranking/popular?regDate=20201026"
## [3] "https://news.daum.net/ranking/popular?regDate=20201027"
## [4] "https://news.daum.net/ranking/popular?regDate=20201028"
## [5] "https://news.daum.net/ranking/popular?regDate=20201029"
## [6] "https://news.daum.net/ranking/popular?regDate=20201030"
## [7] "https://news.daum.net/ranking/popular?regDate=20201031"
headline_extractor <- function(pageurl) {
  page <- readLines(pageurl)
  page_parsed <- htmlParse(page)
  headlines <- xpathSApply(page_parsed, headline_xpath, xmlValue)
  return(headlines)
}

headline_list <- lapply(urls, headline_extractor)
class(headline_list)
## [1] "list"
length(headline_list)
## [1] 7
company_extractor <- function(pageurl) {
  page <- readLines(pageurl)
  page_parsed <- htmlParse(page)
  companies <- xpathSApply(page_parsed, company_xpath, xmlValue)
  return(companies)
}

company_list <- lapply(urls, company_extractor)
length(company_list)
## [1] 7