R爬虫必备——httr+GET请求类爬虫(解螺旋课程)

前期R爬虫必备—httr+ POST请求类爬虫(网易云课堂)这篇推文主要介绍了httr包如何进行POST请求类爬虫,POST请求往往和异步加载组合出现,有关于异步加载在推文R爬虫必备基础—动态异步加载中也做过介绍。今天呢,主要来讲讲GET请求类爬虫。对于这类请求,往往用rvest、httr或RCurl包都可以,但我们主要还是推荐httr和RCurl。上一期 R爬虫必备基础—rvest为什么不用于动态网页?里已经提过,对于网络请求有限制的网页,尽可能不要使用rvest,因为存在风险。除了极少数静态网页对网络请求完全没有限制外,目前基本上接触的大部分网页或多或少对网络请求都设置。当然在很多情况下,对于这类GET请求类的网页,用rvest包其实也可以成功抓取少量内容,但很容易被封。所以呢,保险点,还是用httr/RCurl吧!

今天呢,主要就httr包如何开展GET请求类爬虫做简单介绍!首先,先看下httr这个R包中GET函数的大致用法:GET(url = NULL, config = list(), ..., handle = NULL),这里面比较重要的是config参数(设置请求头、cookies和query)。具体参数解释如下:

  • url :the url of the page to retrieve
  • config:Additional configuration settings such as http authentication (authenticate()), additional headers (add_headers()), cookies (set_cookies()) etc. See config() for full details and list of helpers. Further named parameters, such as query, path, etc, passed on to modify_url(). Unnamed parameters will be combined with config().
  • handle:The handle to use with this request. If not supplied, will be retrieved and reused from the handle_pool() based on the scheme, hostname and port of the url. By default httr requests to the same scheme/host/port combo. This substantially reduces connection time, and ensures that cookies are maintained over multiple requests to the same host. See handle_pool() for more details.

接下来,为了更好理解httr包如何完成一项GET请求类爬虫,下面以解螺旋课程为例做简单介绍!

解螺旋课程案例说明

首先进入解螺旋官网(https://www.helixlife.cn/),点击精品课程,然后右击-检查打开浏览器开发工具,点击Network面板,然后ctrl+R刷新,点击第一条信息,发现该网页是个GET请求类。想获取这页信息,对https://www.helixlife.cn/courses/boutique网址走GET请求即可!

image

但我们发现精品课程共计4页,想要获取所有的课程信息应该怎么办? 首先,需要分析每个页面的网址,有没有规律,当我们逐条点击不同页面时,发现浏览器开发后台的General中Request URL的变化规律如下:

https://www.helixlife.cn/courses/boutique?page=1
https://www.helixlife.cn/courses/boutique?page=2
https://www.helixlife.cn/courses/boutique?page=3
https://www.helixlife.cn/courses/boutique?page=4

同时,Query String Parameters的变化规律如下:

page: 1
page: 2
page: 3
page: 4

在正式爬取之前,需要对下面爬虫主要涉及的参数做下介绍:General里面的Request URL、Request Method、Status Code;Response Headers里面的Content-Type;Request Headers 里面的 Accept、Content-Type、Cookie、Referer、User-Agent等以及最后Query String Parameters里面的所有参数。

  • General里面的Request URL和Request Method方法即是即决定访问的资源对象和使用的技术手段。
  • Response Headers里面的Content-Type决定着你获得的数据以什么样的编码格式返回。
  • Request Headers 里面的 Accept、Content-Type、Cookie、Referer、User-Agent等是你客户端的浏览器信息,其中Cookie是你浏览器登录后缓存在本地的登录状态信息,使用Cookie登入可以避免爬虫程序被频繁拒绝。这其中的参数不一定全部需要提交。
  • Query String Parameters也很重要,是GET请求必备的定位信息,该到哪一页了。
如何实际操作?

1. 加载所需要的R包,没安装的提前安装。

rm(list=ls())
library("httr") 
library("magrittr")
library("rvest")
library("xml2")
library("stringr")

2. 首先构造第一页的url。

url <- c('https://www.helixlife.cn/courses/boutique?page=1')

3. 构造请求提交信息,根据Request headers的内容填写,如下图所示。

mycookie <- 'Hm_lvt_4b46c1065cade82ef3fa0c6e05cb0f7a=1592799906; XSRF-TOKEN=eyJpdiI6IkEzcWcwZWlhWkJtOGlIWTZWSzZ3V3c9PSIsInZhbHVlIjoiMkxXV3pLbEx3R3lwY2x0SW1tdEh2VGxpSXNjejdDVTVGeFMwV1NrdHpZbkMxSlowcXlnc1J6cVFJdUV2V3dpQiIsIm1hYyI6ImEwZjhjOGZlNTBiMGZmNzdkNmViYTNkNzc4OTM3YzBmZWZlNmFhOTQ0OTAwN2JlNzYzNDQzNjY3MmMzODJmY2YifQ%3D%3D; _session=eyJpdiI6IktaMzFDYTRaOHh6UWM4RmFHYU9yM2c9PSIsInZhbHVlIjoielEzVmRzK0toVzE2Q1dWVjFwSWZZTXRKUEszd3Y5UU9vWFFxV0xUTXNPNk56WkNFcEZ5SHpHTFN0Zk9SNnFGTCIsIm1hYyI6ImU2Mjc2YWI2MGMxY2FkZDA2N2E4NGMwYmRiOTUzOGYwZjQ3NWY1MTg1ZjMyMzk0Zjg0Mjk5OGY1NGU5NTVkODMifQ%3D%3D; SERVERID=3f56386521b609ab6e34c0e5ca694901|1592802688|1592802016; Hm_lpvt_4b46c1065cade82ef3fa0c6e05cb0f7a=1592802695'
myheaders <- c('accept' ='text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
               'accept-encoding' = 'gzip, deflate, br',
               'accept-language' = 'zh-CN,zh;q=0.9',
               'user-agent' = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
               'cookie' = mycookie)
image

4. 构造请求头参数信息,根据Query String Parameters内容填写。以下代码是第一页的Query String Parameters参数。

mypayload <- list("page"= 1)
image.gif

5. 执行第一页,httr的GET函数,这里用什么函数是由General里的Request Method参数决定的。

GET(url , add_headers(.headers = 待爬取网页的头部信息),
    set_cookies(.cookies =自己的cookie,
    query = Query String Parameters(构建成特定格式),
    timeout(最大请求时长/秒),
    use_proxy(代理IP)...)

根据GET函数用法,正式进行网络请求,服务器响应返回包含有json格式课程的数据。

response <- GET(url = url, add_headers(.headers = myheaders), timeout(10), query = mypayload)

这时候通过httr包的GET函数成功请求并获取到网页信息,接下来,交给rvest包进行下游的解析和提取,这方面rvest包更强大。

url <- c('https://www.helixlife.cn/courses/boutique?page=1')
  mycookie <- 'Hm_lvt_4b46c1065cade82ef3fa0c6e05cb0f7a=1592799906; XSRF-TOKEN=eyJpdiI6IkEzcWcwZWlhWkJtOGlIWTZWSzZ3V3c9PSIsInZhbHVlIjoiMkxXV3pLbEx3R3lwY2x0SW1tdEh2VGxpSXNjejdDVTVGeFMwV1NrdHpZbkMxSlowcXlnc1J6cVFJdUV2V3dpQiIsIm1hYyI6ImEwZjhjOGZlNTBiMGZmNzdkNmViYTNkNzc4OTM3YzBmZWZlNmFhOTQ0OTAwN2JlNzYzNDQzNjY3MmMzODJmY2YifQ%3D%3D; _session=eyJpdiI6IktaMzFDYTRaOHh6UWM4RmFHYU9yM2c9PSIsInZhbHVlIjoielEzVmRzK0toVzE2Q1dWVjFwSWZZTXRKUEszd3Y5UU9vWFFxV0xUTXNPNk56WkNFcEZ5SHpHTFN0Zk9SNnFGTCIsIm1hYyI6ImU2Mjc2YWI2MGMxY2FkZDA2N2E4NGMwYmRiOTUzOGYwZjQ3NWY1MTg1ZjMyMzk0Zjg0Mjk5OGY1NGU5NTVkODMifQ%3D%3D; SERVERID=3f56386521b609ab6e34c0e5ca694901|1592802688|1592802016; Hm_lpvt_4b46c1065cade82ef3fa0c6e05cb0f7a=1592802695'
  myheaders <- c('accept' ='text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
                 'accept-encoding' = 'gzip, deflate, br',
                 'accept-language' = 'zh-CN,zh;q=0.9',
                 'user-agent' = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
                 'cookie' = mycookie)
  mypayload <- list("page"= 1)
  response <- GET(url = url, add_headers(.headers = myheaders),query = mypayload, encode="raw")
  #read_html()函数读入并解析网页内容
  web <- read_html(response, encoding ="utf-8")
  #提取课程名称
  course_names <- web %>% html_nodes("div.course-info-container a") %>% html_text()
  #提取课程级别
  course_class <- web %>% html_nodes("span.course-rank") %>% html_text() %>% str_replace_all(" ","") %>% str_replace_all("\n","")
  #提取观看人数
  course_people <- web %>% html_nodes("div.lean-num img") %>% html_attr("alt") 
  #提取课程评分
  course_grade <- web %>% html_nodes("div.course-info-text span.f-l") %>% html_text() %>% str_replace_all(" ","") %>% str_replace_all("\n","") %>% str_replace_all("评分:","")
  course_grade <- course_grade[seq(2,length(course_grade),2)]
  #提取课程价格
  course_price <- web %>% html_nodes("div.course-price") %>% html_text() %>% str_replace_all("¥","")
  #创建数据框存储以上信息
  course <- data.frame(course_names,course_class,course_people,course_grade,course_price)
  course_inf <- rbind(course_inf,course)
  1. 执行获取所有页面信息,共4页,利用循环抓取。
i=1
course_inf <- data.frame()
for (i in 1:4){
  url <- c(paste0('https://www.helixlife.cn/courses/boutique?page=',i))
  mycookie <- 'Hm_lvt_4b46c1065cade82ef3fa0c6e05cb0f7a=1592799906; XSRF-TOKEN=eyJpdiI6IkEzcWcwZWlhWkJtOGlIWTZWSzZ3V3c9PSIsInZhbHVlIjoiMkxXV3pLbEx3R3lwY2x0SW1tdEh2VGxpSXNjejdDVTVGeFMwV1NrdHpZbkMxSlowcXlnc1J6cVFJdUV2V3dpQiIsIm1hYyI6ImEwZjhjOGZlNTBiMGZmNzdkNmViYTNkNzc4OTM3YzBmZWZlNmFhOTQ0OTAwN2JlNzYzNDQzNjY3MmMzODJmY2YifQ%3D%3D; _session=eyJpdiI6IktaMzFDYTRaOHh6UWM4RmFHYU9yM2c9PSIsInZhbHVlIjoielEzVmRzK0toVzE2Q1dWVjFwSWZZTXRKUEszd3Y5UU9vWFFxV0xUTXNPNk56WkNFcEZ5SHpHTFN0Zk9SNnFGTCIsIm1hYyI6ImU2Mjc2YWI2MGMxY2FkZDA2N2E4NGMwYmRiOTUzOGYwZjQ3NWY1MTg1ZjMyMzk0Zjg0Mjk5OGY1NGU5NTVkODMifQ%3D%3D; SERVERID=3f56386521b609ab6e34c0e5ca694901|1592802688|1592802016; Hm_lpvt_4b46c1065cade82ef3fa0c6e05cb0f7a=1592802695'
  myheaders <- c('accept' ='text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
                 'accept-encoding' = 'gzip, deflate, br',
                 'accept-language' = 'zh-CN,zh;q=0.9',
                 'user-agent' = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
                 'cookie' = mycookie)
  mypayload <- list("page"= i)
  response <- GET(url = url, add_headers(.headers = myheaders),query = mypayload, )
  #读入并解析网页内容
  web <- read_html(response, encoding ="utf-8")
  #提取课程名称
  course_names <- web %>% html_nodes("div.course-info-container a") %>% html_text()
  #提取课程级别
  course_class <- web %>% html_nodes("span.course-rank") %>% html_text() %>% str_replace_all(" ","") %>% str_replace_all("\n","")
  #提取观看人数
  course_people <- web %>% html_nodes("div.lean-num img") %>% html_attr("alt") 
  #提取课程评分
  course_grade <- web %>% html_nodes("div.course-info-text span.f-l") %>% html_text() %>% str_replace_all(" ","") %>% str_replace_all("\n","") %>% str_replace_all("评分:","")
  course_grade <- course_grade[seq(2,length(course_grade),2)]
  #提取课程价格
  course_price <- web %>% html_nodes("div.course-price") %>% html_text() %>% str_replace_all("¥","")
  #创建数据框存储以上信息
  course <- data.frame(course_names,course_class,course_people,course_grade,course_price)
  course_inf <- rbind(course_inf,course)
}
#将数据写入csv文档
write.csv(course_inf, file="course_inf.csv")

最终爬取结果如下,解螺旋官网上的精品课程共计52门。

image.png

下面简单对这部分课程信息做进一步分析,比如想知道这些课程在不同级别上的分布?经分析发现,共计四种课程分级:入门、初级、中级和高级,其中初级类课程最多,高级类课程最少。可见解螺旋的课程设置偏向小白用户,致力于入门和打基础。

rm(list=ls())
library("readx")
course_inf <- read.csv("course_inf.csv", header = T,stringsAsFactors = F)

##整理数据,修改数据格式
course_inf$course_people <- as.numeric(as.character(course_inf$course_people))
course_inf$course_grade <- as.numeric(as.character(course_inf$course_grade))
course_inf$course_price <- as.numeric(as.character(course_inf$course_price))
str(course_inf)

#课程在不同级别上的分布
course_class <- as.data.frame(sort(table(course_inf$course_class),decreasing = T))
library(ggplot2)
ggplot(course_class,aes(Var1,Freq)) + geom_bar(stat = "identity") + 
  labs(x = "课程分类", y = "课程数量") +
  theme(panel.background=element_rect(fill='transparent')) + 
  geom_text(mapping = aes(label = Freq),size=4,vjust=-1,color = "black")
image

往往官网的课程设置也是有讲究的,一般适用群体范围较大的课程会花更多时间和精力去打造,从上述分析中偏基础入门类的课程多达41门,占所有课程的80%。那是不是这部分课程的确受用户偏爱呢?如下图,入门类课程的观看人数多达26000多人,占据份额的一大半。从这些数据不难看出,解螺旋的精品课程中入门类课程设置最多,且也是用户最偏爱的一类课程。

rumen <- sum(course_inf[course_inf$course_class=="入门","course_people"])
chuji <- sum(course_inf[course_inf$course_class=="初级","course_people"])
zhongji <- sum(course_inf[course_inf$course_class=="中级","course_people"])
gaoji <- sum(course_inf[course_inf$course_class=="高级","course_people"])
tmp1 <- data.frame("入门"=rumen,"初级"= chuji,"中级" = zhongji, "高级" = gaoji)
tmp1 <- as.data.frame(t(tmp1))
tmp1$V2 <- factor(rownames(tmp1),levels = c("入门", "初级" , "中级",   "高级"))
  
ggplot(tmp1,aes(V2,V1)) + geom_bar(stat = "identity") + 
  labs(x = "课程分类", y = "观看人数") +
  theme(panel.background=element_rect(fill='transparent')) + 
  geom_text(mapping = aes(label = V1),size=4,vjust=-1,color = "black")
image

以上只是做了些简单分析,有兴趣的小伙伴,可以根据自己需求,个性化的进行分析。此外,若有小伙伴想重复此代码,需要更新自己的cookies,否则容易运行失败。

往期回顾
R爬虫在工作中的一点妙用
R爬虫必备基础——HTML和CSS初识
R爬虫必备基础——静态网页+动态网页
R爬虫必备——rvest包的使用
R爬虫必备基础——CSS+SelectorGadget
R爬虫必备基础—Chrome开发者工具(F12)
R爬虫必备基础—HTTP协议
R爬虫必备—httr+POST请求类爬虫(网易云课堂)
R爬虫必备基础—rvest为什么不用于动态网页?

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342