自己学习总遇到的爬虫需求不少,新浪、淘宝、瓜子、大众点评、腾讯视频、哔哩哔哩等网站都做过,爬虫程序写的越来越多,准备之后有时间了,把它们分享出来。
最近很多童鞋问我的都是一种类似的爬虫思路,就借其中一位童鞋提供的例子分享一下。(这里只为提高工作效率,禁止恶意攻击网站后台。)
目标网站:两化融合评定管理平台
目标字段:点击表格中企业“证书编码”,获取弹出二级页面中的信息。
先说一说我看到这个任务时的思路:
0、访问这个网站之后,发现二级页面不是打开新标签页的形式,不能直接获取到页面地址。考虑这个二级页面是不是动态获取的方式。
打开开发者工具,点到network选项卡,可以看到每次打开二级页面,都会有相应的请求出现,如下图,而请求的地址就是:
“http://gltxpd.cspiii.com/Credential/GetCredentialInfo?code=”+ “证书编码”
所以考虑先获取所有企业“证书编码”,构造url地址,使用RCurl或者rvest解析网页数据的方法。
1、获取所有企业“证书编码”:总共有4000多条数据,可选择50条每页,看看每页数据的获取方式吧。于是在谷歌开发者工具中可以看到每次点击下一页都会重新请求数据,地址如图:
下拉,看到并且在请求传递的参数有:CredentialNumber, CredentialName, State, ………… PageSize等参数。重点关注PageSize = 50, PageNum = 2这两个参数,我请求的是第二页,每页50行的数据,从而可以确定PageNum和PageSize代表了页数和行数,就是我们需要操控的参数。
2、使用postman尝试通过接口获取数据,主要是看网站有没有其他验证手段
在postman中简单设置参数,发送请求!
可以看出返回了我们想要的答案,网页中看到的表格中的所有信息都通过这个json串返回,自然也包括了企业“证书编码”。这里有一点需要注意,虽然网站上默认只有两个选项:每页10行,每页50行,但是我们可以通过给PageSize赋值100、200、500一次获取更多条(试了一下一次请求完所有的数据,也不报错,但是防止出现问题,后面的程序中一次请求100行)
到这里我们已经可以获取包含所有企业“证书编码”的信息了。有了所有企业的“证书编码”,我们就可以构建二级页面的url, 使用RCurl或者rvest获取需要信息了。
3、验证了方案的可行性之后就是编写程序了
#7/23/2019 Roy Liu
#install.packages(c(“RCurl”,"XML", "rjson", "rvest"))
library(RCurl)
library(XML)
library(rjson)
library(rvest)
#主链接
url1 = 'http://gltxpd.cspiii.com/External/Credentialinfo'
#表格内容接口
url2 = 'http://gltxpd.cspiii.com/Credential/BindData'
#详细信息接口
url3 = 'http://gltxpd.cspiii.com/Credential/GetCredentialInfo?code='
####第一步,构建获取所有表格中认证企业代码的函数
##构造发送表单的函数,该函数需要传递参数index,其意义是爬取第几页的表格信息
postRequest = function(index){
#构建空数据框resultframe ,为存储后面爬取的代码数据
resultframe = data.frame()
##使用RCurl里面的发送表单的函数postForm,使得我们可以模仿浏览器网页发送请求到接口,
##这里请求是post方式,需要传递以下参数,
##其他的参数是什么作用我们不得而知,但是可以看出PageIndex传递了了第几页,PageSize传递了一页多少行的信息
#CredentialNumber='',
#CredentialName='',
#State='1',
#OrgId='-1',
#StartTime='',
#EndTime='',
#AreaId='-1',
#IndustryID='',
#VersionID='-1',
#PageIndex=index,
#PageSize='100',
chartsdata = postForm(url2,
CredentialNumber='',
CredentialName='',
State='1',
OrgId='-1',
StartTime='',
EndTime='',
AreaId='-1',
IndustryID='',
VersionID='-1',
PageIndex=index, #将第几页的信息设置成变量,从而可以根据调用函数时传递的参数,请求不同的页数
PageSize='100',#将每次请求个数设置成100
style = "POST")
#print(chartsdata)
#此处chartsdata是直接从网站服务器端传递的JSON文件,我们使用fromJSON()函数可以将其编程易处理的格式
result = fromJSON(chartsdata)
#result$Rows[[1]]$CredentialNumber
#result[[1]]$Rows$CredentialNumber
#之前PageSize设置成了100,故每次应该传递了100个企业。故利用循环提取出100个认证代码
for (i in 1:100) {
temp = data.frame(num = result$Rows[[i]]$CredentialNumber)
#将认证代码合并入之前设置的空数据框
resultframe = rbind(resultframe ,temp)
}
#将已经合并入100个数据的数据框返回
return(resultframe)
}
####第二步,构建提取企业详细信息的函数,该函数调用需要传递CredentialNumber,代表需要查询信息企业的认证代码
postRequestGetDetails = function(CredentialNumber){
#利用调用函数传递的CredentialNumber参数,构造请求地址,利用paste0(),合并两个字符串即可
reurl = paste0(url3, CredentialNumber)
print(reurl)
#利用rvest请求构造的地址,这一部分的爬虫就是传统的访问网页,利用Xpath等方法,按照节点查找信息
content = read_html(reurl)
#但是这里我是用的html_table()直接读取网页信息,因为该网页就是<tr>等标签组成的比较简单
table = html_table(content)[[1]]
#从读取到的数据中找出需要的字段,保存到temp临时数据框中
temp = data.frame(name = table$X2[2],date = table$X2[5],range = table$X2[6])
#将临时数据框返回
return(temp)
}
###开始第一步爬虫,下载所有企业认证代码
#构建空数据框,用于存放所有的企业认证编码
CredentialNum = data.frame(num = c(""))
#设置获取40页的数据
pageNum = 40
i=1
for (i in 1:pageNum) {
#调用40次之前第一步构建的函数postRequest,并将1到40,分别作为参数,将函数返回的数据框赋值给resultframe
resultframe = postRequest(i)
#将每一返回的数据框,合并到CredentialNum中,这样CredentialNum中就有了需要的所有页的企业认证编码
CredentialNum = rbind(CredentialNum,resultframe)
#打印循环完成的程度
print(paste0("######### Now: ", as.character(round(i/pageNum*100, 3)), "% #########"))
}
#至此所有企业认证编码爬取完毕,存放在CredentialNum中
#存储认证代码到本地CredentialNumber.csv,可在工作路径下查找,或指定存储路径
write.csv(CredentialNum, file = "CredentialNumber.csv")
#读取认证代码
CredentialNum = read.csv("CredentialNumber.csv")
#设置空数据框,用于存放所有企业详细信息
detailFrame = data.frame(name=c("#"), date = c("#"), range=c("#"))
##利用第二步构建的函数爬取企业详细信息
#读取存储企业认证编码CredentialNum的行数作为for循环的范围,每次读取一行的企业认证编码,作为参数
for (i in 2:dim(CredentialNum)[1]) {
#打印即将要爬取的企业认证编码
print(CredentialNum$num[i])
#调用第二步构造的postRequestGetDetails函数,并将传递过来的企业信息赋值给temp
temp = postRequestGetDetails(as.character(CredentialNum$num[i]))
#将temp中的单个企业详细信息合并到detailFrame中存储
detailFrame = rbind(detailFrame, temp)
}
#等待循环执行完毕
#至此已爬完左右企业详细信息
#写详细信息到本地
write.csv(detailFrame, file = "detailFrame.csv")
4、任务完成!