# 2019-01-04-人民日報CODE
###### tags: `資料科學自學園`
```r=
## 2018-12-4-TITUS
# 抓取人民日報電子檔案,網站連結在下面,有分兩樣:一個是版面,另一個是版面上面的區塊。
# 範圍是從2017/1/1到現在。我覺得最快的抓法是用「動態下載」,但我現在不知道動態下載應
# 該怎麼用才會比較快。傳統的方法應該不大可行,因為從頁面上的特定位置無法抓取到CCS或
# XPath路徑。其他的待研究。
# 第一要務是先研究如何用R來進行動態下載,並查清楚需要哪些套件的配合。
# ---- 2018-07-24
# 這次從2012年開始下載,下載到2017年。可以研究是不是會有更快的方式。
# ---- 2018-11-23
# 抓取人民日報資料庫的資料,連結下面會附上。
# ---- 2018-12-04
rm(list = ls()) # 清除暫存區的資料
# first.date="20120101" # 設定起始日期
# end.date="20130101" # 設定終止日期
library(xml2)
library(httr)
library(tmcn)
library(tm)
library(rvest)
library(magrittr)
#######################################################################
# 抓取邏輯:
# (1) 先抓「版面」
# (2) 再抓「標題」
# (3) 再抓「內文標題」、「日期」、「內文內容」
# (4) 切換到下一個「版面」,重複 (2)) ~ (4)
# (5) 「版面」全部抓完後,切換「下一則新聞」
# (6) 將抓去下來的內容「存檔」。
# (7) 重複 (1) ~ (7)。直到最新貼文抓取完畢為止。
#######################################################################
#
# SETTING FUNCTION
#
## 函式(一):日期函式 (fun_date)
# 設定從first.date到end.date之間的所有日期;並把這些日期放在固定網域的後面。
fun_date = function(){
temp = seq.Date(from = as.Date(first.date,format = "%Y%m%d"), by = "day", to = as.Date(end.date,format = "%Y%m%d"))
temp = gsub("-","",temp)
link = paste0('http://data.people.com.cn/rmrb/',temp,"/")
return(link)}
## 函式(二):版面函式(fun_page)
# 取得每一天新聞的所有版面,並將所有版面的連結存成「pagelist」,最後將pagelist
# 匯出(return)。
fun_page = function(link){
print(sprintf("================ 抓取網頁: %s ================",link))
link_withall = paste0(link,"1?code=1")
# 「read_html(httr::GET(...,timeout(s)))」因為只有在GET函式裡面才能使用
# 「timeout」函數。「timeout」的用法是,如果超過指定的秒數,就會自動判讀讀取網
# 頁失敗。「encoding ' "UTF-8"'」表示網頁編碼使用UTF-8,使R在讀取中文網頁
# 時,不會有亂碼。
# 「tryCatch」若出現錯誤,則回傳「NA」值;並打印錯誤訊息。
html = tryCatch(read_html(httr::GET(link_withall, timeout(10)),
encoding = "UTF-8", options = 'HUGE'),
error = function(msg){
message(paste(msg,Sys.time(),sep = '\n'))
return(NA)},
warning = function(msg){
message(paste(msg,Sys.time(),sep = "\n"))
return(NA)})
pagelist = tryCatch(html_nodes(html,"#UseRmrbPageNum") %>% html_text(),
error = function(msg){
message(paste(msg,Sys.time(),sep = "\n"))
return(NA)},
warning = function(msg){
message(paste(msg,Sys.time(),sep = "\n"))
return(NA)})
pagelist = c(1:as.numeric(pagelist))
print(paste0("finish at ",Sys.time()))
Sys.sleep(runif(2,4,13))
return(list(pagelist))}
## 函式(三):標題函式(fun_title)
# 取得每一版上的標題連結,並將所有標題連結存成「titlelist」。最後將titlelist
# 匯出(return)。
fun_title = function(link,page){
link_withpage = paste0(link,"/",page)
# 這邊的寫法同上。
html_page = tryCatch(read_html(httr::GET(link_withpage, timeout(10)),encoding = "UTF-8",options = 'HUGE'),
error = function(msg){
message(paste(msg,Sys.time(),sep = "\n"))
return(NA)},
warning = function(msg){
message(paste(msg,Sys.time(),sep = "\n"))
return(NA)})
titlelist = tryCatch(html_nodes(html_page,".title_list a") %>% html_attr("href"),
error = function(msg){
message(paste(msg,Sys.time(),sep = "\n"))
return(NA)},
warning = function(msg){
message(paste(msg,Sys.time(),sep = "\n"))
return(NA)})
print(paste0("finish at ",Sys.time()))
return(list(titlelist))}
## 函式(四):內文函式(fun_arti)
# 從標題頁面中抓取:標題、日期、與內文,並存成「arti」。
fun_arti = function(title){
link_withtitle = paste0('http://data.people.com.cn',title)
html_title = tryCatch(read_html(httr::GET(link_withtitle, timeout(10)),encoding = "UTF-8",options = 'HUGE'),
error = function(msg){
message(paste(msg,Sys.time(),sep = "\n"))
return(NA)},
warning = function(msg){
message(paste(msg,Sys.time(),sep = "\n"))
return(NA)})
if (is.na(html_title) == TRUE) {
title = NA
date = NA
ctnt = NA
print("抓取網頁失敗!")} else {
title = html_nodes(html_title,'.title') %>% html_text() #內文標題的CSS
if (length(title)==0) title = NA
author = html_nodes(html_title,'.author') %>% html_text() #文章作者的CSS
if (length(author)==0) author = NA
ctnt = html_nodes(html_title,'#FontZoom') %>% html_text() #內文內容的CSS
if (length(ctnt)==0) ctnt = NA}
Sys.sleep(runif(2,4,13))
arti = list(title,author,ctnt)
message(title)
message(paste0("finish at ",Sys.time()))
return(arti)}
## 函式(五):運行函式(FUN_RUN)
FUN_RUN = function(){
link = fun_date()
date.length = seq.Date(from = as.Date(first.date,format = "%Y%m%d"), by = "day", to = as.Date(end.date,format = "%Y%m%d")) %>% length()
A = lapply(link[1:date.length],fun_page)
DATE = seq.Date(from = as.Date(first.date,format = "%Y%m%d"), by = "day", to = as.Date(end.date,format = "%Y%m%d")) # 另外設置一個DATE資料,以便最後進行資料整併時,作為其中一項變數資料。
for (seed in 1:length(A)){
print(sprintf("================ 抓取 %s 的報紙 ================",DATE[seed]))
B = unlist(A[seed])
E = matrix(ncol = 6) # 設定一個矩陣,寬度為6
colnames(E) = c( # 矩陣的標題分別為這六項。
"NewsDate", # 是哪一天的新聞。
"Page", # 是第幾版。
"Title", # 標題為何。
"Author", # 內文的作者。
"Content", # 內文內容。
"URL") # 該篇文章的連結。
for (pageseed in 1:length(B)){
print(sprintf("第 %d 版的內容",pageseed))
C = fun_title(link[seed],B[pageseed])
D = unlist(C)
for (titleseed in 1:length(D)){
message(paste0("No. ",titleseed,":"))
E_tmp = list(as.character(DATE[seed]),as.character(pageseed),unlist(fun_arti(D[titleseed])),D[titleseed]) # 把新聞日期、新聞版面、新聞內容(共三筆資料)、文章連結存成list「E_tmp」
E = rbind(E,unlist(E_tmp)) # 用指令「rbind」把「E_tmp」資料併在「E」下面(上下列合併)
Sys.sleep(runif(2,4,13))}} # 跑2輪,暫停2 ~ 4秒,以免被鎖IP
E = E[-1,] # 將矩陣第一列的資料(為六項NA值)移除
filename = paste0(DATE[seed],".csv") # 設定檔案名稱「filename」
write.csv(E,file = filename,fileEncoding = "UTF-8") # 把矩陣「E」匯出成CSV檔,檔案名稱為「日期.csv」
# 在匯出成CSV檔後,會在新一輪的Loot中,重製矩陣「E」。這樣就可以確保每一份CSV檔都是那一天的新聞。
print(paste0("================ ","FINISH and write into ",filename,"================"))}}
######################################################################################
#
# RUNNING PROGRAM
YY = 2017
MM = c("01",'02','03','04','05','06','07','08','09','10','11','12')
DD = "01"
##############################################################
repeat{
if (YY > 2018) break
for (mm in 1:12){
if ( mm < 12 ){
period = seq.Date(from = as.Date(paste0(YY,MM[mm],DD),format = "%Y%m%d"), by = "day", to = as.Date(paste0(YY,MM[mm+1],DD),format = "%Y%m%d"))
first.date = period[1]
end.date = period[length(period)-1]
FUN_RUN()
Sys.time(3600)
message("========休息個20秒,看你機器人還抓不抓得到我^___^========")
}
if (mm == 12 ){
period = seq.Date(from = as.Date(paste0(YY,MM[mm],DD),format = "%Y%m%d"), by = "day", to = as.Date(paste0(YY+1,MM[1],DD),format = "%Y%m%d"))
first.date = period[1]
end.date = period[length(period)-1]
FUN_RUN()
Sys.time(3600)
message("========休息個20秒,看你機器人還抓不抓得到我^___^========")
}
}
YY = YY + 1
Sys.time(3600)
message("========休息個10秒,看你機器人還抓不抓得到我^___^========")
}
```