1. Requests库的简介与安装
Requests库是Python的第三方库,用于网络请求,是目前爬取网页最好的三方库,其特点是简单、简洁。
requests 的底层实现其实就是 urllib。Requests 继承了urllib的所有特性。Requests支持HTTP连接保持和连接池,支持使用cookie保持会话,支持文件上传,支持自动确定响应内容的编码,支持国际化的 URL 和 POST 数据自动编码。
- 开源地址
- [官方API文档]((http://www.python-requests.org)
安装方法: pip install requests;easy_install requests
作用:模拟浏览器发起请求
爬取流程:
- 指定url
- 发起请求
- 获取响应数据(网页源代码)
- 保存数据
2. Requests请求方式
2.1 GET请求
requests.get(url,params,**kwargs)
- url :请求的网站地址
- params :查询参数,设置动态爬取
- **kwargs :字典形式的变量,可添加headers,一些头部信息等
最基本的GET请求
import requests
url = "http://www.baidu.com"
response = requests.get(url)
# 或者这样写
response = requests.request("get",url)
添加 headers 和 查询参数
import requests
url = "http://www.baidu.com"
kw = {"my":"python"} # 查询参数
# 头部信息,用于模拟浏览器访问
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36"
}
response = requests.get(url=url,params=kw,headers = headers)
2.2 POST请求
requests.post(url, data=None, json=None, **kwargs)
- url:post请求地址
- data:添加的参数,字典形式
- json:(可选)json数据发送到:类的主体
- **kwargs:字典形式,如headers信息等
最基本的POST请求
import requests
url = "http://www.baidu.com"
response = requests.post(url)
添加 headers 和 data参数
import requests
formdata = {"my":"python"}
url = "http://www.baidu.com"
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36"
}
response = requests.post(url, data = formdata, headers = headers)
2.3 获取响应内容
- 使用response.text 时,Requests 会基于 HTTP 响应的文本编码自动解码响应内容,大多数 Unicode 字符集都能被无缝地解码。
- 使用response.content 时,返回的是服务器响应数据的原始二进制字节流,可以用来保存图片等二进制文件。
- 推荐使用response.content.deocde()指定编码
import requests
url = "http://www.baidu.com"
kw = {"my":"python"} # 查询参数
# 头部信息,用于模拟浏览器访问
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36"
}
response = requests.get(url=url,params=kw,headers = headers)
# 查看响应内容,response.text 返回的是Unicode格式的数据
# print (response.text)
# 查看响应内容,response.content返回的字节流数据
# print (response.content)
# 查看完整url地址
print (response.url)
# 查看响应头部字符编码
print (response.encoding)
# 查看响应码
print (response.status_code)
https://m.baidu.com/?my=python&from=844b&vit=fps
utf-8
200
import requests
# 根据协议类型,选择不同的代理
proxies = {
"http": "http://12.34.56.79:9527",
"https": "http://12.34.56.79:9527",
}
response = requests.get("http://www.baidu.com", proxies = proxies)
print( response.text)
3. Requests模块简单的使用
3.1 搭建简易采集器
爬取搜狗首页的页面源代码数据
requests.get()
# 导入requests库
import requests
# 1. 指定url
url = "http://sogou.com/"
# 2. 发起请求,这里用get方法的返回值为响应对象
response = requests.get(url=url)
# 3. 获取响应数据
# .text:返回的是字符串形式的响应数据
result = response.text
# 4. 保存数据
with open('./sougou.html','w',encoding="utf-8") as fp:
fp.write(result)
实现一个简易的网页采集器,基于搜狗针对指定不同的关键字将对应的页面数据进行爬取
- 实现参数动态化:请求的url携带参数,且我们要将携带的参数进行动态化操作,我们就必须:
- 将携带的动态参数以键值对的形式封装在一点字典中;
- 将字典作用在get方法的params参数中即可;
- 需要将原始携带参数的url中携带的参数删除
import requests
# 1. 指定url,利用sogou访问Python
url =" https://www.sogou.com/sogou?query=Python"
# 2. 发起请求,这里用get方法的返回值为响应对象
response = requests.get(url=url)
# 3. 获取响应数据
# .text:返回的是字符串形式的响应数据
result = response.text
# 4. 保存数据
with open('./Python1.html','w',encoding="utf-8") as fp:
fp.write(result)
import requests
# 设置一个输入的值,加载至url中
keyword = input("请输入你要查询的关键字")
# 携带请求参数的url,如果想要爬取不同关键字对应的页面,
# 我们需要将url携带的参数进行动态化
# 实现参数动态化
params = {
"query":keyword
}
url =" https://www.sogou.com/sogou"
# params参数是一个字典采纳数,保存url时携带参数
response = requests.get(url=url,params=params)
# 3. 获取响应数据
# .text:返回的是字符串形式的响应数据
result = response.text
# 4. 保存数据
filename = keyword + ".html"
with open(filename,'w',encoding="utf-8") as fp:
fp.write(result)
print(filename,"爬取完毕")
请输入你要查询的关键字Python
Python.html 爬取完毕
上述简易采集代码出现了问题:乱码问题和数据丢失
解决乱码问题
- result = response.text 这儿的编码格式不对,需要修改编码
import requests
# 设置一个输入的值,加载至url中
keyword = input("请输入你要查询的关键字")
# 携带请求参数的url,如果想要爬取不同关键字对应的页面,
# 我们需要将url携带的参数进行动态化
# 实现参数动态化
params = {
"query":keyword
}
url =" https://www.sogou.com/sogou"
# params参数是一个字典采纳数,保存url时携带参数
response = requests.get(url=url,params=params)
# encoding返回的是响应数据的原始的编码格式,
# 如果给其赋值则表示修改了响应数据的编码格式
response.encoding = "urf-8"
# 3. 获取响应数据
# .text:返回的是字符串形式的响应数据
result = response.text
# 4. 保存数据
filename = keyword + ".html"
with open(filename,'w',encoding="utf-8") as fp:
fp.write(result)
print(filename,"爬取完毕")
请输入你要查询的关键字Python
Python.html 爬取完毕
处理乱码后,页面显示【异常访问请求】导致请求失败
- 异常的访问请求:网站后台检测出该次请求不是通过浏览器发起的请求,而是爬虫请求。不是通过浏览器发起的请求都是异常请求。
- 网站的后台是如何知道请求头中的请求的user-agent判定的
user-agent是什么?
- 请求载体的身份标识。
请求载体:
- 浏览器:身份标识统一固定的,可以从抓包工具获取
- 爬虫程序:身份标识各不相同
反爬机制
- UA检测:网站后台监测请求对应的User-Agent,以判定当前请求是否为异常请求
反反爬策略
- UA伪装:作用于部分网站中,从抓包工具中捕获到某一个基于浏览器请求的User-Agent的值,将其伪装作用到一个字典中,将该字典作用到请求方法(get,post)的headers参数中即可
import requests
# 设置一个输入的值,加载至url中
keyword = input("请输入你要查询的关键字")
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36"
}
# 携带请求参数的url,如果想要爬取不同关键字对应的页面,
# 我们需要将url携带的参数进行动态化
# 实现参数动态化
params = {
"query":keyword
}
url =" https://www.sogou.com/sogou"
# params参数是一个字典采纳数,保存url时携带参数
response = requests.get(url=url,params=params,headers=headers)
# encoding返回的是响应数据的原始的编码格式,
# 如果给其赋值则表示修改了响应数据的编码格式
response.encoding = "urf-8"
# 3. 获取响应数据
# .text:返回的是字符串形式的响应数据
result = response.text
# 4. 保存数据
filename = keyword + ".html"
with open(filename,'w',encoding="utf-8") as fp:
fp.write(result)
print(filename,"爬取完毕")
请输入你要查询的关键字Python
Python.html 爬取完毕
3.2 爬取豆瓣电影中的电影的详情数据
3.2.1 为什么需要通过动态加载数据来进行捕获
为什么需要动态加载
- 我们通过requests模块进行数据爬取无法每次都实现可见即可得。
什么是动态加载数据
- 有些数据是通过非浏览器地址栏中的url请求到的数据,而是其他请求到的数据,那么这些通过其他请求到的数据就是动态加载数据
如何检测网页中存在动态加载数据
- 在网页中打开抓包工具,捕获到地址栏中的url对应的数据包,在该数据包中的response选项卡中搜索我们想要抓取的数据
- 通过抓包工具进行局部搜索,如果搜索到了结果这表示数据不是动态加载的,否则表示数据为动态加载的。
import requests
# 豆瓣电影,动作片top首页
url = "https://movie.douban.com/explore#!type=movie&tag=%E5%8A%A8%E4%BD%9C&sort=rank&page_limit=20&page_start=0"
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36"
}
response = requests.get(url=url,headers=headers)
response.encoding = "utf-8"
result = response.text
with open("./豆瓣电影.html",'w',encoding="utf-8") as fp:
fp.write(result)
print("爬取完毕")
./爬取完毕
目标
结果
分析通过这种利用url获取数据就失效了,不能获取我们想要的数据,其原因可以通过在访问的目标地址中,通过抓包工具中的network查看,可以看到电影数据是动态加载的。因此需要考虑动态加载数据。
3.2.2 捕获动态加载的数据
基于抓包工具进行全局搜索
定位到动态加载对应的数据包,从该数据包中就可以提取出
- 请求的ur
- 请求方式
- 请求的携带参数
- 看到响应数据
url=https://movie.douban.com/j/search_subjects ?type=movie&tag=%E5%8A%A8%E4%BD%9C&sort=rank&page_limit=20&page_start=0
?后含有请求参数,这里我们需要做请求参数的动态化,通过将?后参数部分删除掉,用参数来进行封装
import requests
url = " https://movie.douban.com/j/search_subjects"
# 请求参数
params = {
'type': 'movie',
'tag':'动作',
'sort': 'rank',
'page_limit': '20',
'page_start': '0',
}
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36"
}
response = requests.get(url=url,params=params,headers=headers)
# 通常是获取字符串形式,这里由于是json数据,因此可用.json
# page_text = response.text
# .json()意思为将获取的字符串形式的json数据序列化为字典或者列表
page_text = response.json()
page_text = page_text['subjects']
# 解析出电影名称加评分
for moive in page_text:
name = moive["title"]
score = moive['rate']
print(name,":",score)
这个杀手不太冷 : 9.4
蝙蝠侠:黑暗骑士 : 9.2
指环王3:王者无敌 : 9.2
指环王2:双塔奇兵 : 9.1
指环王1:魔戒再现 : 9.0
黑客帝国 : 9.0
让子弹飞 : 8.8
搏击俱乐部 : 9.0
V字仇杀队 : 8.9
七武士 : 9.2
阿凡达 : 8.7
头号玩家 : 8.7
加勒比海盗 : 8.7
杀人回忆 : 8.8
勇敢的心 : 8.9
蝙蝠侠:黑暗骑士崛起 : 8.8
功夫 : 8.6
复仇者联盟4:终局之战 : 8.5
釜山行 : 8.5
纵横四海 : 8.8
我们通过修改这里面的参数,如我们将’page_start’: ‘0’,为’page_start’: ‘20’,就可以实现想要的数据的获取了。
import requests
url = " https://movie.douban.com/j/search_subjects"
# 请求参数
params = {
'type': 'movie',
'tag':'动作',
'sort': 'rank',
'page_limit': '20',
'page_start': '20',
}
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36"
}
response = requests.get(url=url,params=params,headers=headers)
# 通常是获取字符串形式,这里由于是json数据,因此可用.json
# page_text = response.text
# .json()意思为将获取的字符串形式的json数据序列化为字典或者列表
page_text = response.json()
page_text = page_text['subjects']
# 解析出电影名称加评分
for moive in page_text:
name = moive["title"]
score = moive['rate']
print(name,":",score)
蜘蛛侠:平行宇宙 : 8.6
英雄本色 : 8.7
黑客帝国3:矩阵革命 : 8.7
谍影重重3 : 8.8
被解救的姜戈 : 8.7
东邪西毒 : 8.6
终结者2:审判日 : 8.7
疯狂的麦克斯4:狂暴之路 : 8.6
攻壳机动队 : 9.0
红海行动 : 8.3
谍影重重2 : 8.7
新龙门客栈 : 8.6
小萝莉的猴神大叔 : 8.4
福尔摩斯二世 : 9.5
黑客帝国2:重装上阵 : 8.6
谍影重重 : 8.6
黑鹰坠落 : 8.7
东邪西毒:终极版 : 8.7
无间道2 : 8.5
钢铁侠 : 8.3
由于利用抓包工具进行全局搜索不一定每次都能够搜索到对应的数据包,其原因是如果动态加载的数据是经过加密的密文数据,我们就认为是加密的数据。因此需要进行解密,后面会见。
3.2.3 分页数据的爬取操作
爬取肯德基的餐厅位置数据
- url: http://www.kfc.com.cn/kfccda/storelist/index.aspx
分析步骤
分析
- 通过关键字查询文本框中录入关键字按下搜索按钮,发起的是一个ajax请求。通过搜索关键字可以看出其网址并未发生变化,因此判定当前页面刷新出来的位置信息一定是通过ajax请求到的数据。
- 基于抓包工具可以定位到该ajax请求的数据包,我们可从该数据包中捕获到:
- 请求的url
- 请求方式
- 请求的携带参数
- 看到的响应数据
import requests
url = "http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
}
data = {
'cname':' ',
'pid':' ',
'keyword': '北京',
'pageIndex': '1',
'pageSize': '10',
}
# data参数时post方法中处理参数动态化的参数
response = requests.post(url=url,headers=headers,data=data)
response.status_code
200
response.text
'{"Table":[{"rowcount":0}],"Table1":[]}'
这里没有获取到数据,可能被反爬了。弄了两天了,暂时就这样吧。