左右用R右手Pyhon系列——趣直播课程抓取实战

本文将以趣直播课程信息数据抓取为例,展示如何使用RCurl进行结合浏览器抓包操作进行简易数据抓取。

library("RCurl")
livrary("XML")
library("rlist")
library("dplyr")
library("jsonlite")

按照常规的操作步骤,数据抓取首先应该通过浏览器后台确认该目标网页所使用的框架和请求类型,确认网站是否提供了api访问接口,如果可以通过API直接访问数据包,那么抓取工作将会变得极其简单,倘若没有,则才考虑直接请求整个网页并使用xpath、css、正则或者具备路径查询功能的辅助包进行数据提取。

1、查看趣直播网页后台:

url<-"http://m.quzhiboapp.com/#!/lives"

打开该网页之后,按F12键,进入Chrome浏览器开发者后台,定位到xhr栏目,在该栏目的Name请求名称列表里寻找带有参数的项目(可以直接忽略所有.js结尾的请求文件)。

我一眼就看到了一个以listOrderByPlanTs?limit=30命名的请求项目,该项目中含有list关键词、limit关键词,这些关键词很可能是api用于限定信息展示条目的限制参数。

下一步确认这一条是否是我们需要的请求,选中这一条请求并鼠标单击,首先定位到右侧Preview栏目并点开,当你看到排的整整齐齐的josn数据包到的时候,毫无疑问这就是我们需要找的宝藏了。

接下来干什么呢,当时是查看该请求的请求类型,参数提交情况,以及请求参数、返回内容格式了。

从General栏目可以看到该请求是一个GET请求,请求地址是: http://m.quzhiboapp.com/api/lives/listOrderByPlanTs

从Request Headers中可以看到参数提交时优先接受json格式,但同时也可以接受text/plain(纯文本)。 User-Agent是一个重要的请求报头参数,告知目标服务器该请求的客户端设备类型。在该栏目里还有两个不常见的参数——X-Requested-With、X-Session,先不用管如果之后影响请求结果再添加。

从Response Headers中可以得知服务器 返回的数据类型是application/json格式,utf-8编码。这决定着我们使用什么工具来解析返回内容。

最后一个Query String Parameters 项目是GET请求需要提交的参数,本例只有一个limit参数,用于限定单次请求返回的信息条目数,GET请求允许请求参数直接构造在请求URL里面。

2、构造GET请求:

url<-"http://m.quzhiboapp.com/api/lives/listOrderByPlanTs"
header=c(
    "Accept"="application/json, text/plain, */*",
    "User-Agent"="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"
    )
Param<-list("limit"=30)
chandle<-getCurlHandle(debugfunction=debugGatherer()$update,followlocation=TRUE,cookiefile="",verbose = TRUE)
content<-getForm(url,httpheader=header,.params=Param,.encoding="utf-8",curl=chandle)

查看状态码:

getCurlInfo(chandle)$response.code
#[1] 200

OK!!!

打印一下content的内容:

print(content)

完美,接下来解析内容,之前说过返回内容是json,那么这里就需要使用具备json处理能力的包进行处理。

mycontent <- content %>% fromJSON() %>% `[[`(2)

非常规整的数据框,都不用怎么做清洗和缺失值替补,实在是太完美了。

3、封装抓取函数:

接下来肯定会有小伙伴要问了,难倒只能抓这么点数据吗,这种情况下应该如何获取所有课程信息,怎么从网页后台进行检索,确定有多少课程量。

这个问题我也有疑问呢,带着这个疑问 ,我们再次造访Chrome后台。

遗憾呢,真的是看不到总课程量,只能显示30条,怎么办,试一试暴力方法!!!

直接把我们请求的url地址po到浏览器然后访问。

我们直接进入了这个json课程数据包,因为limit设置的30,所以信息自然就是30条喽,不信你用Ctrl+F查找一下关键字,输入一个课程id:liveId,输入框直接显示了该页面一共有多少个liveId数目,没错就是30个。

然后我有个大胆的想法,我们可以篡改url啊我擦~

改成100果然就显示100了哈哈哈,那就索性再大胆一些,改成1000如何???

你会发现改成1000的时候,页面的liveId只增加了12个,是不说一共只有112门课程呢,那我们就多试几次喽,分别改成200、300、500试一试。

可以看到limit参数自200以后始终都没有增加过,一直停留在112,目测一共就这么多了。

当显示出了112门课程的时候,你可以直接Ctrl+S保存该网页为.json文件,直接解析,当然我们还是要做的优雅一点,直接写在请求语句中,然后友好的返回规整的数据框。

url<-"http://m.quzhiboapp.com/api/lives/listOrderByPlanTs"
header=c(
"Accept"="application/json, text/plain, */*",
"User-Agent"="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"
)
Param<-list("limit"=150)
chandle<-getCurlHandle(debugfunction=debugGatherer()$update,followlocation=TRUE,cookiefile="",verbose = TRUE)
content<-getForm(url,httpheader=header,.params=Param,.encoding="utf-8",curl=chandle)
getCurlInfo(chandle)$response.code
[1] 200
mycontent <- content %>% fromJSON() %>% `[[`(2);nrow(mycontent)
[1] 112

啦啦啦,多么完美的结局呀,你看数据框都不用处理缺失值,简直是太神奇了。接下来我们仅需要剔除那些我们不想要的列就可以了。

head(mycontent)
names(mycontent)
 [1] "liveId"          "subject"         "attendanceCount" "needPay"         "coverUrl"        "previewUrl"     
 [7] "amount"          "maxPeople"       "conversationId"  "status"          "planTs"          "beginTs"
[13] "endTs"           "ownerId"         "shareIcon"       "created"         "updated"         "attendanceId"   
[19] "shareId"         "owner"           "topic"           "canJoin"         "realAmount" 
invalid<-c("previewUrl","shareIcon","attendanceCount","shareId","canJoin")
myresult<-setdiff(names(mycontent),invalid) %>% mycontent[.]
head(myresult);dim(myresult);str(myresult)

OK,思路整理完了,现在开始应该封装一个像模像样点儿的自动化小爬虫程序了!

myforce<-function(url){
    header=c(        
    "Accept"="application/json, text/plain, */*",
    "User-Agent"="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"
        )
    Param<-list("limit"=150)
    chandle<-getCurlHandle(debugfunction=debugGatherer()$update,followlocation=TRUE,cookiefile="",verbose = TRUE)
    mycontent<-getForm(url,httpheader=header,.params=Param,.encoding="utf-8",curl=chandle)  %>% fromJSON() %>% `[[`(2) 
    print(getCurlInfo(chandle)$response.code)
    invalid<-c("previewUrl","shareIcon","attendanceCount","shareId","canJoin")
    myresult<-invalid %>% setdiff(names(mycontent),.) %>% mycontent[.]
    print("everything is OK")
}
mydata<-myforce("http://m.quzhiboapp.com/api/lives/listOrderByPlanTs")
[1] 200
[1] "everything is OK"

看到了令人赏心悦目的200,和everything is OK,今天午饭都有食欲啦~~~

来劲了顺便给你个Python版吧~

import json
import pandas as pd
from urllib.parse import urlencode 
from urllib.request import urlopen,Request
def getdata(url):
    #postdata
    values = {'limit':150}
    link=url+'?'+ urlencode(values)
    #headers
    header = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',
    'Accept':'application/json, text/plain, */*'
    }
    response = urlopen(Request(link,headers=header))
    #查看请求状态码
    print(response.getcode())
    #使用json方法解析返回的josn数据包
    myresult=json.loads(response.read())
    print(len(myresult["result"]))
    mydata=pd.DataFrame(myresult['result'])
    invalid=["previewUrl","shareIcon","attendanceCount","shareId","canJoin"]
    full=list(set(list(mydata.columns)).difference(set(invalid)))
    print("everything is OK")
    return(mydata[full])
#提供URL,运行抓取程序获取数据:
url="http://m.quzhiboapp.com/api/lives/listOrderByPlanTs"
mydata=getdata(url)
200
112
everything is OK
mydata.head(10)
mydata.info()

由于mydata里面的owner和topic字段仍然是嵌套字典,没有铺平,接下来我们使用列表表达式铺平嵌套字典。

mydata['avatarUrl']=[avatarUrl['avatarUrl'] for avatarUrl in mydata.owner]
mydata['userId']=[avatarUrl['userId'] for avatarUrl in mydata.owner]
mydata['username']=[avatarUrl['username'] for avatarUrl in mydata.owner]
mydata['name']=[avatarUrl['name'] for avatarUrl in mydata.topic]
mydata['topicId']=[avatarUrl['topicId'] for avatarUrl in mydata.topic]

删除那两列嵌套字典:

del mydata["owner"]
del mydata["topic"]
mydata.shape
(112, 21)
mydata.head()

最后就可以分析或者入库啦!!!

往期案例数据请移步本人GitHub: https://github.com/ljtyduyu/DataWarehouse/tree/master/File

原文发布于微信公众号 - 数据小魔方(datamofang)

原文发表时间:2017-10-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏知晓程序

晓实战 | 这样编辑小程序富文本,又快又方便!

我们会定期邀请优秀的开发者,从实践的角度,亲自分享他们使用知晓云(cloud.minaapp.com)开发的实战经验,如果你也想分享你的小程序实战经验,欢迎加入...

13930
来自专栏李成熙heyli

性能优化三部曲之二——通用纯前端优化首屏时间

背景: 对构建的改造已经完成,目前构建的能力可以较为灵活地支撑进一步的优化 希望进一步减少首屏时间,将首屏时间控制在2秒以内 页面情况: 优化之前,并没有上报...

38590
来自专栏更流畅、简洁的软件开发方式

【自然框架】——思路、结构、特点的介绍(初稿,欢迎大家多提意见)

开场白   面向过程:面向过程是“写代码”,根据客户提出来的需求来写代码,包括函数。一步一步的写,都写完了,功能也就实现了。 面向对象:面向对象是“做设计”...

25470
来自专栏木子昭的博客

Chrome与vim双神器融合, vimium完全攻略

77940
来自专栏纯洁的微笑

谈谈我与 Intellij IDEA 的故事

说到这款 IDEA,很早就想写一篇关于它的文章了,可不知从何处写起,刚好一位师弟问起我来,正好写一篇入门文章,本文的涉及的快捷键主要针对于 Windows系统「...

11020
来自专栏JAVA高级架构

网站性能优化实战——从12.67s到1.06s的故事

30130
来自专栏程序员的知识天地

如何阅读大型前端开源项目的源码,授人以鱼不如授人以渔

目前网上有很多「XX源码分析」这样的文章,不过这些文章分析源码的范围有限,有时候讲的内容不是读者最关心的。同时我也注意到,源码是在不断更新的,文章里写的源码往往...

50510
来自专栏linux、Python学习

小白请上车 | Python抓取花瓣网高清美图

嘀嘀嘀,上车请刷卡。昨天看到了不错的图片分享网—— 花瓣 ,里面的图片质量还不错,所以利用selenium+xpath我把它的妹子的栏目下爬取了下来,以图片栏目...

12100
来自专栏iOSDevLog

Xcode 10

Xcode 10包含为所有Apple平台创建出色应用所需的一切。现在Xcode和Instruments在macOS Mojave上的新Dark Mode中看起来...

16320
来自专栏北京马哥教育

小白请上车 | Python抓取花瓣网高清美图

? 一:前言 嘀嘀嘀,上车请刷卡。昨天看到了不错的图片分享网—— 花瓣 ,里面的图片质量还不错,所以利用selenium+xpath我把它的妹子的栏目下爬取了...

29530

扫码关注云+社区

领取腾讯云代金券