爬虫请求响应以及提取数据
回顾:
网页给客户端响应数据, 有哪些写法(在爬虫入门之爬虫原理以及请求响应这篇博客咯嘛有提到)?
1.响应对象.text(获取网页数据的时候会用到)
2.响应对象.content(将图片, 音频或视频等数据存放到文件的时候会用到)
那这一篇文章, 介绍一个新的写法:
响应对象.json(), 这个方法在获取网页数据所对应的请求数据时会用到
什么情况下会使用
当再浏览器对应的响应中看到的数据结构有点像python中的字典或者列表(因为浏览器中不叫字典或者列
表,只是说数据格式类似)
我们通过代码的案例, 来讲解响应对象.json()的写法。
代码:
import requests
url = 'https://careers.tencent.com/tencentcareer/api/post/Query?
timestamp=1726832959000&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&
attrId=1&keyword=&pageIndex=1&pageSize=10&language=zh-cn&area=cn'
res = requests.get(url)
# print(type(res.text)) # 如果使用响应对象.text 那么得到的数据永远都是字符串类型
# print(type(res.json()))# 如果使用响应对象.text 那么得到的数据都是字典类型
data = res.json() # 使用data变量存储字典数据 字典更方便取值
# 结合循环编写解析的代码
count = 1 # 输出的序号
for i in data['Data']['Posts']:
# 岗位名称
RecruitPostName = i['RecruitPostName']
# 所在城市
LocationName = i['LocationName']
# 岗位内容
Responsibility = i['Responsibility']
print(count,RecruitPostName,LocationName,Responsibility)
count += 1
这个url怎么来的, 首先打开腾讯招聘
接着点击技术类那个框, 跳转到如下页面:
然后再请求中找到Query……, 这个就是我们需要爬取的数据请求。
每个网站, 需要爬取的数据请求都不一样, 这个需要自己找。
我们双击那个请求, 可以发现, 后台返回了数据。
所以url就是https://careers.tencent.com/tencentcareer/api/post/Query?
timestamp=1726832959000&countryId=&cityId=&bgIds=&productId=&categoryId=&parentCategoryId=&
attrId=1&keyword=&pageIndex=1&pageSize=10&language=zh-cn&area=cn, 也就是请求。
说明:
data = res.json()这句话, 使用data变量存储字典数据, 因为字典更方便取值
我们可以发现请求里面, 有个Posts的列表(数组), 所以我们先要获取到那个列表再循环, 使用for i in data[‘Data’][‘Posts’]这句话进行循环, 因为Posts列表再Data里面, 所以要data[‘Data’][‘Posts’]这样写。然后在循环里面, 有三行代码, 获取岗位名称, RecruitPostName = i[‘RecruitPostName’], 获取所在城市, LocationName = i[‘LocationName’], 获取岗位内容, Responsibility = i[‘Responsibility’]。因为岗位名称、所在城市、岗位内容这三个东西都在Posts列表里面, 而i就是每一次遍历出来的列表里面的一条数据, 所以可以用i[‘字段名’]这么写。
伪装
url = 'https://movie.douban.com/j/chart/top_list?
type=10&interval_id=100%3A90&action=&start=0&limit=20'
# 响应的数据格式:[{},{}] 列表嵌套字典
res = requests.get(url)
data = res.json()
print(data) # 报错
# 得到的响应数据不是一个纯列表或者纯字典的时候 就会报如下错误
# requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
# 改响应对象.text输出
print(res.text) # 当前获取到的数据是空字符串 为什么浏览器可以正常得到数据,而爬虫得不到
结果:
为什么会报错?
原因: 对方识别了当前的客户端是爬虫, 所以不给数据
如何解决: 我们需要用到伪装
如何实现伪装: 在请求头当中带上请求头(从浏览器复制过来)
修改过后的代码:
# 使用字典保存请求头信息
url = 'https://movie.douban.com/j/chart/top_list?
type=10&interval_id=100%3A90&action=&start=0&limit=20'
# 响应的数据格式:[{},{}] 列表嵌套字典
headers = {
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/128.0.0.0 Safari/537.36'
}
# 携带请求头的方式:headers参数传参
res = requests.get(url,headers=headers)
data = res.json()
print(data)
print(text)
此时此刻, 我们就能够获取到数据啦!
我们看一个完整的一个案例, 爬取豆瓣电影的数据:
import requests
url = 'https://movie.douban.com/j/chart/top_list?type=10&interval_id=100%3A90&action=&start=0&limit=20'
# user-agent对应的是客户端的版本信息Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
}
response = requests.get(url, headers=headers)
# print(response.text)
# print(type(response.json()))
data = response.json()
count = 1
for i in data:
# i = 第一次循环取出的是 列表种的第一个字典 对应的是一个电影信息
# 电影名
title = i['title']
# 演员表
actors = i['actors']
# 评分
score = i['score']
# 上映时间
release_date = i['release_date']
print(count, "电影标题:", title, ", 演员列表:", actors, ", 评分", score, ", 上映时间", release_date)
count += 1
运行结果:
由于控制台的数据太多, 无法完全展示。
我们可以看到有电影名, 演员列表, 电影的评分以及电影的上映时间。
基于分页的爬虫
分页爬虫, 是非常常用的爬虫手段, 因为非常多的网站, 它不只是只有当前的一页数据, 它可以有n页的数据, 而我们却不知道这个网站有多少页数据的时候(因为网站会更新, 页数会变化), 我们就可以用分页查询法。
我们还是爬取豆瓣电影的网站
在写代码之前, 我们需要研究一下url, 也就是请求。
https://movie.douban.com/j/chart/top_list?type=10&interval_id=100%3A90&action=&start=0&limit=20
这个代表从第一条数据开始, 查询20条数据
start = 0代表从第一条数据开始, limit = 20代表查询20条数据
https://movie.douban.com/j/chart/top_list?type=10&interval_id=100%3A90&action=&start=20&limit=20
https://movie.douban.com/j/chart/top_list?type=10&interval_id=100%3A90&action=&start=40&limit=20
https://movie.douban.com/j/chart/top_list?type=10&interval_id=100%3A90&action=&start=60&limit=20
https://movie.douban.com/j/chart/top_list?type=10&interval_id=100%3A90&action=&start=80&limit=20
……
通过观察, 我们可以发现只有start参数会变
那如何实现0 20 40 60 80这样多个url呢?
我们需要用到循环
可以用一个变量代表start的值
然后每一次循环, 变量值+20
代码:
import requests
# 如果循环次数不确定, 就应该写死循环
# 定义页码变量(就是url里面的start)
page = 0
count = 1
# 因为我们不知道什么时候数据才能爬完, 也就是说我们不知道什么时候结束, 所以我们这里会用死循环, 之后根据观察当没有数据的时候, 控制台会返回什么信息, 再做决定如何跳出循环。
while True:
# url会变, 用刚才定义好的page参数代表url里面的start
url = f'https://movie.douban.com/j/chart/top_list?type=10&interval_id=100%3A90&action=&start={page}&limit=20'
# 请求头
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
}
response = requests.get(url, headers=headers)
data = response.json()
for i in data:
# i = 第一次循环取出的是 列表种的第一个字典 对应的是一个电影信息
# 电影名
title = i['title']
# 演员表
actors = i['actors']
# 评分
score = i['score']
# 上映时间
release_date = i['release_date']
print(count, "电影标题:", title, ", 演员列表:", actors, ", 评分", score, ", 上映时间", release_date)
print(f"第{count}页数据已经全部提取完毕")
count += 1
page += 20
# 无限循环什么时候结束? 数据没有了就应该结束
if len(data) == 0:
print(f"第{count}页数据已经全部提取完毕")
break
结果:
这里要说明一下, if len(data) == 0这句话是判断响应的数据, 是否是空列表, 也就是说判断数据是否已经爬完了。当然, 每个网站, 判断是否结束爬虫的判断方法, 都是不同的, 这个得看实际情况来判断。
这里我们为什么要用if len(data) == 0这样的判断, 我们再把刚才的代码运行一遍的同时再注释小判断结束的语句, 再加上一个print(data)语句, 我们看一看结果会如何。
代码:
import requests
# 如果循环次数不确定, 就应该写死循环
# 定义页码变量(就是url里面的start)
page = 0
count = 1
# 因为我们不知道什么时候数据才能爬完, 也就是说我们不知道什么时候结束, 所以我们这里会用死循环, 之后根据观察当没有数据的时候, 控制台会返回什么信息, 再做决定如何跳出循环。
while True:
# url会变, 用刚才定义好的page参数代表url里面的start
url = f'https://movie.douban.com/j/chart/top_list?type=10&interval_id=100%3A90&action=&start={page}&limit=20'
# 请求头
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
}
response = requests.get(url, headers=headers)
data = response.json()
for i in data:
# i = 第一次循环取出的是 列表种的第一个字典 对应的是一个电影信息
# 电影名
title = i['title']
# 演员表
actors = i['actors']
# 评分
score = i['score']
# 上映时间
release_date = i['release_date']
print(count, "电影标题:", title, ", 演员列表:", actors, ", 评分", score, ", 上映时间", release_date)
print(f"第{count}页数据已经全部提取完毕")
count += 1
page += 20
print(data)
# 无限循环什么时候结束? 数据没有了就应该结束
# if len(data) == 0:
# print(f"第{count}页数据已经全部提取完毕")
# break
结果:
我们可以看到, 在265条数据以后, 都返回了空列表, 这就意味着数据到265条就结束了, 而且空列表的判断, 是判断data的长度是否为0(data的数据是列表里面套着字典,也就是[{},{}]这样的格式), 所以判断方法是if len(data) == 0。
同样的, 我们再看一个案例, 这个案例的判断爬虫是否结束的方法和上面的不一样了。
腾讯招聘网站分页:
代码:
import requests
"""
pageIndex: 查看的数据是第几页数据
"""
page = 1
# 获取所有数据
while True:
# 获取1到10页数据
count = 1
url = f"https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1726832959000&countryId=&cityId=&bgIds=&productId=&category=&parentCategory=&attrId=1&keyword=&pageIndex={page}&pageSize=10&language=zh-cn&area=cn"
res = requests.get(url)
# 打印每一条响应的数据, 目的是需要观察当响应不到数据的时候, 在控制台返回的是什么。
print(res.text)
data = res.json()
# TypeError: 'NoneType' object is not iterable
# {"Code":200,"Data":{"Count":0,"Posts":null}}
# 根据数据响应的结果, 进行结束循环的判断
if data["Data"]['Posts'] is None:
break
for i in data["Data"]['Posts']:
# 岗位名称
RecruitPostName = i['RecruitPostName']
# 工作地点
LocationName = i['LocationName']
# 工作内容
Responsibility = i['Responsibility']
print(count, RecruitPostName, LocationName, Responsibility)
count += 1
print(f"第{page}页数据加载完毕")
page += 1
结果:
我们可以发现, 利用分页爬虫可以在腾讯招聘网里面爬取246条数据。同时我们可以在控制台的最下方, 看到有{“Code”:200,“Data”:{“Count”:0,“Posts”:null}}, 我们看到了Posts是null, 那我们就知道, 判断爬虫是否结束, 需要怎么去判断它了, 那这里, 我们需要用到
if data[“Data”][‘Posts’] is None, 判断data[“Data”][‘Posts’]是否为空作为判断爬虫是否结束。
将爬虫到的数据保存
这里, 我们先将数据保存到文本里面, 至于如何将数据保存到数据库或者excel等操作, 这些我们之后再讲。
其实, 把数据保存到文本里面非常的简单, 思路也很简单, 我们可以创建一个字符串变量, 那个变量就是用于存储爬虫的数据的, 最后我们将数据全部写入文件中。我们就那腾讯招聘的代码为例子。
代码:
import requests
"""
pageIndex: 查看的数据是第几页数据
"""
page = 1 # 页码
count = 1 # 输出的数据序号
content = '' # 为了存储爬虫的数据所用的变量
# 获取所有数据
while True:
# 获取1到10页数据
url = f"https://careers.tencent.com/tencentcareer/api/post/Query?timestamp=1726832959000&countryId=&cityId=&bgIds=&productId=&category=&parentCategory=&attrId=1&keyword=&pageIndex={page}&pageSize=10&language=zh-cn&area=cn"
res = requests.get(url)
print(res.text)
data = res.json()
# TypeError: 'NoneType' object is not iterable
# {"Code":200,"Data":{"Count":0,"Posts":null}}
# 根据数据响应的结果, 进行结束循环的判断
if data["Data"]['Posts'] is None:
break
for i in data["Data"]['Posts']:
# 岗位名称
RecruitPostName = i['RecruitPostName']
# 工作地点
LocationName = i['LocationName']
# 工作内容
Responsibility = i['Responsibility']
print(count, RecruitPostName, LocationName, Responsibility)
# 调用操作系统, 打开文件, 往文件中写入数据。
# 循环多少次, 就打开文件多少次, 效率不高
# with open("腾讯招聘.txt", "a", encoding="utf-8") as f:
# f.write(f"{count}, {RecruitPostName}, {LocationName}, {Responsibility} \n")
content += f"{count}, {RecruitPostName}, {LocationName}, {Responsibility} \n"
count += 1
print(f"第{page}页数据加载完毕")
page += 1
# 将爬虫爬取到的数据都写到文件里面
with open("腾讯招聘.txt", mode="a", encoding="utf-8") as f:
f.write(content)
注意:存储数据里面有些细节需要注意一下, 在for循环中, 有一句content += f"{count}, {RecruitPostName}, {LocationName}, {Responsibility} \n", 这句话的content后面必须是+=,而不是=, 这里面我们需要追加数据, 如果是=的话, 那就变成赋值了, 用=会导致只保存最后一条数据, 在最后面\n是换行, 为了保存的数据美观一些而不是挤在一起。还有一点就是, with open(“腾讯招聘.txt”, mode=“a”, encoding=“utf-8”) as f这句话, open函数的mode的值为a, w虽然有写入的模式, 但是会覆盖, 而a代表追加的意思, 这里建议写a。
运行结果:
我们打开腾讯招聘.txt文件
我们发现, 我们成功将爬虫爬取到的数据存入腾讯招聘.txt的文本中了。
实战:
给你一个url, 在那个网站上面进行爬虫
url = https://quote.eastmoney.com/ztb/detail#type=ztgc
找到这个网站数据对应的url发起请求 获取响应数据(解析的字段不限定,随意获取)
注意!!请求参数有一个cb=callbackdataxx 不要携带
提示:
找到网页当中的请求:
1.打开网站(https://quote.eastmoney.com/ztb/detail#type=ztgc), 然后再打开开发者工具f12, 找到网络。
我们发现, 这里面的请求太多了, 根本不能一下子找出来哪个是请求文件, 我们可以用搜索框来搜索我们想要爬取的数据, 然后它就会显示出哪个请求里面会有这个数据。我们随便在网站上面找一个数据进行搜索。
比如我们搜索一个002016, 就是网站的表格当中的第13条数据的其中一个数据。
点击搜索按钮, 输入002016, 按回车, 我们可以发现下面有数据。
点击下面返回的数据, 在右边选择响应。
我们可以发现, 请求就在这个里面。
我们再双击打开这个请求。
我们可以发现返回的是一些后端的数据, 这些后端的数据, 正是我们需要的响应数据。但是我们在最左上面可以发现有个callbackdata7124215, ()里面才是我们想要的数据, 那怎么办呢?
这个很简单, 在题目当中, 就已经告诉我们怎么取操作啦。
所以原本我们的请求url是https://push2ex.eastmoney.com/getTopicZTPool?cb=callbackdata7124215&ut=7eea3edcaed734bea9cbfc24409ed989&dpt=wz.ztzt&Pageindex=0&pagesize=20&sort=fbt%3Aasc&date=20240924&=1727160924310
但是我们要把cb=callbackdataxxx这个参数去掉, 所以我们的请求最终的url是https://push2ex.eastmoney.com/getTopicZTPool?ut=7eea3edcaed734bea9cbfc24409ed989&dpt=wz.ztzt&Pageindex=0&pagesize=20&sort=fbt%3Aasc&date=20240924&=1727160924310
先不要马上看答案, 尝试自己做一做哦。
参考答案:
import requests
page = 0
content = ""
while True:
url = f'https://push2ex.eastmoney.com/getTopicZTPool?ut=7eea3edcaed734bea9cbfc24409ed989&dpt=wz.ztzt&Pageindex={page}&pagesize=20&sort=fbt%3Aasc&date=20240923&_=1727104055700'
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"}
res = requests.get(url, headers=headers)
data = res.json()
if len(data['data']['pool']) == 0:
break
for i in data['data']['pool']:
# 代码
code = i['c']
# 名称
n = i['n']
# 涨跌幅
zdp = i['zdp']
zdp_float = str(round(zdp, 2)) + "%"
# 最新价
p = i['p']
p_f = p / 1000
p_number = round(p_f, 2)
# 成交额
amount = i['amount']
str_amount = ""
if amount >= 10 ** 8:
amount /= 10 ** 8
string = str(round(amount, 2)) + "亿"
str_amount += string
else:
amount /= 10 ** 4
string = str(round(amount)) + "万"
str_amount += string
# 流通市值
ltsz = i['ltsz']
str_ltsz = ""
ltsz /= 10 ** 8
str_ltsz += str(round(ltsz, 2)) + "亿"
# 总价值
tshare = i['tshare']
str_tshare = ""
tshare /= 10 ** 8
str_tshare += str(round(tshare, 2)) + "亿"
# 换手率
hs = i['hs']
str_hs = str(round(hs, 2)) + "%"
# 封板资金
fund = i['fund']
str_fund = ""
if fund >= 10 ** 8:
fund /= 10 ** 8
string = str(round(fund, 2)) + "亿"
str_fund += string
else:
fund /= 10 ** 4
string = str(round(fund, 1)) + "万"
str_fund += string
# 首次封板时间
fbt = i['fbt']
str_fbt = ""
if fbt >= 100000:
string = str(fbt)
str_fbt += string[0:2] + ":" + string[2:4] + ":" + string[4:6]
else:
string = str(fbt)
str_fbt += string[0:1] + ":" + string[1:3] + ":" + string[3:5]
# 最后封板时间
lbt = i['lbt']
str_lbt = ""
if lbt >= 100000:
string = str(lbt)
str_lbt += string[0:2] + ":" + string[2:4] + ":" + string[4:6]
else:
string = str(lbt)
str_lbt += string[0:1] + ":" + string[1:3] + ":" + string[3:5]
# 炸板次数
zbc = i['zbc']
str_zbc = str(zbc) + "次"
# 涨停统计
zttj = str(i['zttj']['ct']) + "/" + str(i['zttj']['days'])
# 连版数
lbc = i['lbc']
str_lbc = ""
if lbc == 1:
string = "首版"
str_lbc += string
else:
str_lbc = str(lbc) + "连扳"
# 所属行业
hybk = i['hybk']
content += "代码:" + str(code) + ",名称:" + n + ",涨跌幅:" + str(zdp_float) + ",最新价(千):" + str(p_number) + ",成交额:" + str_amount + ",流通市值:" + str_ltsz + ",总价值:" + str_tshare + ",换手率:" + str_hs + ",封板资金:" + str_fund + ",首次封板时间:" + str_fbt + ",最后封板时间:" + str_lbt + ",炸板次数:" + str_zbc + ",涨停统计:" + zttj + ",连版数:" + str_lbc + ",所属行业:" + hybk + "\n"
print("代码:", code, ",名称:", n, ",涨跌幅:", zdp_float, ",最新价(千):", p_number, ",成交额:", str_amount,
",流通市值:", str_ltsz, ",总价值:", str_tshare, ",换手率:", str_hs, ",封板资金:", str_fund,
",首次封板时间:", str_fbt, ",最后封板时间:", str_lbt, ",炸板次数:", str_zbc, ",涨停统计:", zttj,
",连版数:", str_lbc, ",所属行业:", hybk)
# print(i)
page += 1
with open("涨停股池.txt", "a", encoding="utf-8") as f:
f.write(content)
这里面的代码, 将爬虫爬取到的数据先格式化了, 数据格式化的效果和网站上表格的数据是一样的, 然后再把爬虫爬取到的数据存入txt文件里面去()。
你写出来了吗?如果写出来的话, 给自己一个掌声哦。
以上就是爬虫请求响应以及提取数据的所有内容了, 如果有哪里不懂的地方,可以把问题打在评论区, 欢迎大家在评论区交流!!!
如果我有写错的地方, 望大家指正, 也可以联系我, 让我们一起努力, 继续不断的进步.
学习是个漫长的过程, 需要我们不断的去学习并掌握消化知识点, 有不懂或概念模糊不理解的情况下,一定要赶紧的解决问题, 否则问题只会越来越多, 漏洞也就越老越大.
人生路漫漫, 白鹭常相伴!!!
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 爬虫请求响应以及提取数据
发表评论 取消回复