第四节:Requests数据抓取
4.1.Requests简介与安装
Requests是python的一个HTTP客户端库,几乎可以解决我们遇到的任何爬虫问题,其强大简洁的API足以让人体会到python的优雅。
作为最强大的python非官方请求库,它能解决90%的爬虫问题
Windows下安装通过pip install requests
Mac下安装通过pip3 install requests
不行的话利用清华镜像:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests_
#注:之后就不一一赘述模块安装方法,安装模块除了特殊的都是这个方法,
今后所言只会说pip install 模块名
要使用requests,首先推荐阅读requests的官方文档
https://2.python-requests.org//zh_CN/latest/user/quickstart.html
基本用法代码:
import requests
def get_html(url): #一个功能写一个函数
html = requests.get(url) #设置get()请求页面
if html.status_code == 200: #判断response返回正常
html.encoding = "utf8" #设置文本解码方式
print(html.text)
else:
print("get error"+html.url)
if __name__ == "__main__":
#当.py文件被直接运行时,if __name__ == '__main__'之下的代码块将被运行;当.py文件以模块形式被导入时,if __name__ == '__main__'之下的代码块不被运行。
url = "https://baidu.com"
get_html(url)
HTTP请求中我们通常只会用GET和POST,requests对于区分了两种不同的请求方式。分别是带参数和不带参数,下边给出实例:
#不带参数
https://www.baidu.com
#带参数
https://www.baidu.com/s?wd=python
判断URL是否带有参数,可以通过对符号?的判断。一般网站URL后边带有?说明带有参数,参数的规则如下
1)?后接参数
2)参数之间用&连接
Requests实现GET请求,对可带参数的URL有两种方式:
import requests
#方法一
html1 = requests.get("https://www.baidu.com/s?wd=python")
#方法二
url = "https://www.baidu.com/s"
paramas = {"wd":'python'}
html2 = requests.get(url,paramas)
print(html1,html2)
两种方法都可以,实际开发中建议第二种,
因为更加简洁优雅,体现了python的语法。
其有如下优点:
方便传输参数
方便维护
方便调试
耦合性低,模块化
POST方法就是我们日常生活中提交表单的方法,比如登录验证之类的。Requests实现的POST需要传递参数data,可以使字典或者json结构,或者元组,列表等。
#提交post
import requests
#字典
data1 = {"name":"python","password":233}
#json
import json
data2 = {"name":'python',"password":233}
data2 = json.dumps(data2)
html = requests.post("https://www.baidu.com",data=data2)
print(html.text)
需要注意的是,
GET请求传递的是params,POST传递的是data,不能混淆二者。
import requests
def post_html(url):
data = {"name":"python","passord":223}
html = requests.post(url,data=data) #post通常用于请求表单 注意其传递的参数是data
if html.status_code == 200:
html.encoding = "utf8"
print(html.text)
else:
print("get error"+html.url)
if __name__=="__main__":
url = "http://baidu.com"
post_html(url)
此外,服务器返回的信息里包含了许多我们需要的数据,比如
Html.status_code 相应状态码
Html.raw 原始响应体
Html.content 字节响应体,需要解码
Html.text 字符串的响应方式
Html.headers 服务器响应头
Html.josn() requests内置的json解码器
Html.cookies 获取请求后的cookies
Html.encoding 获取编码格式
4.2.构建复杂请求
复杂的请求方式包括请求头,代理,证书验证和cookies验证。Requests对此做了简化,将这些功能封装在了requests参数中。
1)session
维持会话:
正常我们浏览器访问页面时,网站会知道这是一系列的操作,
也就是互通的,
而程序访问是单个独立的,服务器认为是两个不同的人访问的。
这样就会出现许多的样板代码,就类似于每次get都需要加headers
这个时候就引入了我们的session,它的好处如下:
- 维持会话
- 自动保存cookies
- 传递参数,Session会自动传递参数,保存cookies和headers
书写方式: 先写入程序:
session=requests.session()
session.headers=headers
再将程序中requests替换为session,并去除headers等
复杂的请求方式包括请求头,代理,证书验证和cookies验证。Requests对此做了简化,将这些功能封装在了requests参数中。
2) 添加请求头:请求头是字典格式
请求头可以从浏览器的 requests headers处获取
书写:
headers = {'Referer': 'https://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=index&fr=&hs=0&xthttps=111111&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=%E7%83%AD%E6%90%9C&oq=%E7%83%AD%E6%90%9C&rsp=-1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3947.100 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'}
2) 使用IP代理(NAT)(访问国外网站的时候使用)
必须是两部分,一是http,二是https,一般加上timeout,以防代理失效
可以利用快代理,西刺代理,都是免费的(但不一定好用)
**
#ip代理
import requests
proxies={
'http':'112.84.72.7',
"https":"112.84.72.7"
}
html = requests.get("http://www.youtube.com/",proxies=proxies,timeout=10)
print(html.status_code)
**
3) https证书验证:
通常是用来关闭的,默认是True
如果需要设置证书文件,则需要传递证书路径
出现下面情况时候使用(ssl error 之类):
import requests
ur1='https://baidu.com '
#关闭证书验证
html1=requests.get(ur1,verify=False)
print(html1.status_code)
#设置证书路径
htm12=requests.get (ur1,verify='D:/path/ mycert')
4) 超时设置,
有时候因为各种因素,我们请求一个网站,在得到响应之前,会等待一段时间。如果不想让程序等待那么长时间,可以自己设置一个等待时间,超时的话会引发一个异常。
5) 使用cookies,
cookies是用来识别用户的,在requests中以字典形式存在。获取方式主要是通过服务器的设置,我们也可以自己伪造。
#使用cookies
import requests
#这是一段取自百度的cookies
cookies='FG=1; pgv_ pvi=5209723904'
cookies_dict=dict()
for i in cookies.split(';'):
value=i.split("=")
cookies_dict[value[0]]=value[1]
html=requests. get('https://www.baidu.com',cookies=cookies_dict)
print(html.status_code)
以上是我们自己传递cookies,那么如何获取服务器的cookies呢?第一种办法是打开浏览器的开发者工具,在network里边寻找(和上述请求头操作一样);第二种方法是通过requests获取
4.3上传与下载
下载文件就是从服务器请求文件,然后保存在本地,以下载图片为例,代码如下:
文件下载本身得到的内容是字节流,以字节方式写入才能实现下载。
基本代码(可封装为一个模块使用)
def download(url):
img = requests.get(url,headers=headers)
with open("imgs/{}.jpg".format(uuid.uuid4()),"wb") as f: #注意后缀格式 填写随意的名称
chunks = img.iter_content(125) #减少cpu压力,一次读取一部分
for c in chunks:
f.write(c)
文件的上传更为复杂一些,是将本地的文件以字节流形式上传到服务器,由服务器接收后做出相应。
基本代码:
#上传文件
import requests
files = {"file":#固定的文件名
("render",open("render.html","rb"), #文件名+文件
"application/html", #文件格式 可以更改如txt等
{"Expires":'0'})} #额外的添加的参数 这里是过期时间
def post_html(url):
html = requests.post(url,files=files)
if html.status_code==200:
print("get error:"+html.url)
if __name__=="__main__":
url ="https://www.csdn.net/"
post_html(url)
4.4.urllib常用参数补充
具体参见https://docs.python.org/zh-cn/3/library/urllib.parse.html#module-urllib.parse
from urllib import parse
Urllib.parse.urlparse: 分解URL(含有解析)
Urllib.parse.urljoin: 将两个URL结合在一起
Urllib.parse.quote: 对字符串转义
Urllib.parse.urlsplit: 分割URL
1.Urllib.parse.urljoin
from urllib import parse
url1 = 'https://bilbil/com/'
url2 = '/img_1'
url = url1 + url2
print("no urllib:"+url)
url = parse.urljoin(url1,url2)
print("urllib:"+url)
输出:
no urllib:https://bilbil/com//img_1(非法url)
urllib:https://bilbil/img_1(合法url)
从上述我们可以看出,urljoin的重要性
2. Urllib.parse.quote
text='%2BKMrOEYbFdQHjteHui6ljLQIHr68g&rqlang=cn&rsv_enter=1&rsv_dl=tb&rsv_sug3=12&rsv_sug1=9&rsv_sug7=100&rsv_sug2=0&inputT=2311&rsv_sug4=5300'
print(parse.unquote(text)) #翻译为我们看的语言
#print(unquote()) #翻译为机器的语言
3. Urllib.parse.urlsplit,Urllib.parse.urlparse
url = 'https://www.baidu.com/s?tn=25017023_6_dg&ch=1&ie=UTF-8&wd=http%3A%2F%2Fhttpbin.org%2Fpost'
print(parse.urlsplit(url)) #分割
print(parse.urlparse(url)) #分析 高级一点
结果:
SplitResult(scheme='https', netloc='www.baidu.com', path='/s', query='tn=25017023_6_dg&ch=1&ie=UTF-8&wd=http%3A%2F%2Fhttpbin.org%2Fpost', fragment='')
ParseResult(scheme='https', netloc='www.baidu.com', path='/s', params='', query='tn=25017023_6_dg&ch=1&ie=UTF-8&wd=http%3A%2F%2Fhttpbin.org%2Fpost', fragment='')
可以看到parse.urlparse是更高级一点的
4.5.利用requests下载图片,音视频
通过上述的介绍,我们初步了解了requests的使用,那么如何利用request获取图片,音视频呢,我们就以获取百度图片的代码作为说明,一个利用session,另一个没有利用,可以作为参考。
#下载图片/视频等 无session
import requests
import json
import uuid
'''https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%E7%83%AD%E6%90%9C&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=©right=&word=%E7%83%AD%E6%90%9C&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=30&rn=30&gsm=1e&1582460979488=
https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%E7%83%AD%E6%90%9C&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=©right=&word=%E7%83%AD%E6%90%9C&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=60&rn=30&gsm=3c&1582460979640=
https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%E7%83%AD%E6%90%9C&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=©right=&word=%E7%83%AD%E6%90%9C&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=90&rn=30&gsm=5a&1582460980025='''
headers = {'Referer': 'https://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=index&fr=&hs=0&xthttps=111111&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=%E7%83%AD%E6%90%9C&oq=%E7%83%AD%E6%90%9C&rsp=-1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3947.100 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'}
#图片视频url下载 都是二进制
def download(url):
img = requests.get(url,headers=headers)
with open("imgs/{}.jpg".format(uuid.uuid4()),"wb") as f: #注意后缀格式 填写随意的名称
chunks = img.iter_content(125) #减少cpu压力,一次读取一部分
for c in chunks:
f.write(c)
def get_html(url):
html = requests.get(url,headers=headers)
try: #并不是所有的都能输出为json
#print(html.json()["data"])
#result = html.json()['data'] #方法一:requests自带简单json
result = json.loads(html.text)["data"] #方法二:python json
for r in result:
if r: #判断非空才输出
print(r["thumbURL"])
download(r["thumbURL"])
except Exception as e:
print(e)
url = ['https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%E7%83%AD%E6%90%9C&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=©right=&word=%E7%83%AD%E6%90%9C&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn={}&rn=30&gsm=1e&1582460979488='.format(i) for i in range(30,100,30)]
if __name__=="__main__":
for u in url:
get_html(u)
#利用session
import requests
import json
import uuid
'''https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%E7%83%AD%E6%90%9C&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=©right=&word=%E7%83%AD%E6%90%9C&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=30&rn=30&gsm=1e&1582460979488=
https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%E7%83%AD%E6%90%9C&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=©right=&word=%E7%83%AD%E6%90%9C&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=60&rn=30&gsm=3c&1582460979640=
https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%E7%83%AD%E6%90%9C&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=©right=&word=%E7%83%AD%E6%90%9C&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=90&rn=30&gsm=5a&1582460980025='''
headers = {'Referer': 'https://image.baidu.com/search/index?tn=baiduimage&ipn=r&ct=201326592&cl=2&lm=-1&st=-1&fm=index&fr=&hs=0&xthttps=111111&sf=1&fmq=&pv=&ic=0&nc=1&z=&se=1&showtab=0&fb=0&width=&height=&face=0&istype=2&ie=utf-8&word=%E7%83%AD%E6%90%9C&oq=%E7%83%AD%E6%90%9C&rsp=-1',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3947.100 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'}
session=requests.session()
session.headers=headers
#图片视频url下载 都是二进制
def download(url):
img = session.get(url)
with open("imgs/{}.jpg".format(uuid.uuid4()),"wb") as f: #注意后缀格式 填写随意的名称
chunks = img.iter_content(125) #减少cpu压力,一次读取一部分
for c in chunks:
f.write(c)
def get_html(url):
html = session.get(url)
try: #并不是所有的都能输出为json
#print(html.json()["data"])
#result = html.json()['data'] #方法一:requests自带简单json
result = json.loads(html.text)["data"] #方法二:python json
for r in result:
if r: #判断非空才输出
print(r["thumbURL"])
download(r["thumbURL"])
except Exception as e:
print(e)
url = ['https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=%E7%83%AD%E6%90%9C&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=©right=&word=%E7%83%AD%E6%90%9C&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn={}&rn=30&gsm=1e&1582460979488='.format(i) for i in range(30,100,30)]
if __name__=="__main__":
for u in url:
get_html(u)
4.6. 拓展模块
requests_html 和request都是一个作者开发的,
我们可以利用requests_html写一个异步爬虫,因为它支持原生异步爬虫
也可以执行js,帮助我们破解js加密
值得注意的是它仅支持python3.6.0版本
安装模块:pip install requests_html
from requests_html import AsyncHTMLSession
import time
#异步是单线程,伪装为多并发
session=AsyncHTMLSession() #异步session
#async标志这是一个异步爬虫函数
#await是等待结果返回
async def get_html():
html = await session.get("https://www.csdn.net/")
return html.status_code
if __name__ == "__main__":
s = time.time()
tasks = [get_html for i in range(100)]
session.run(*tasks)
print(time.time()-s)
运行获取时间的话,我们会发现异步爬虫的速度很快,几乎快于常规的几倍