掌握了一些基础的Http原理之后,我们就要想办法去获取网页当中的内容,最基础的便是模拟浏览器向服务器发送请求;Python强大的第三方库中已经为我们提供了最直接以及最有效的方法,来让我们模拟向指定网站发送请求,并且拿到想要的数据
基本库之request
我是用的Python版本是基于Anaconda集成的,里面已经安装了许多的第三方库,所以我这里所用到的第三方库除了没有的,就不再提供下载
先来看一个Demo
import requests
# 利用request发起get请求去访问百度首页,获取百度首页的源代码
r = requests.get('https://www.baidu.com')
print(r.text)
# 抓取二进制数据(图片、音频、视频等)
re = requests.get('https://www.baidu.com/favicon.ico')
# print(re.text) 这里会因为解析问题而产生乱码
print(re.content)
# 抓取的二进制写入文件(第一个参数: 文件名,第二个参数: 二进制打开)
with open('favicon.ico', 'wb') as f:
f.write(r.content)
通过Demo中的代码,我们就可以获得百度网站的源码以及百度的图标并将图标保存在本地,这就是Python当中最基础也是最常见的Requests库
1.1 请求
请求我们之前在基础部分也提到了,其中最常见的就是Get请求和Post请求,其他的请求理论上都可以通过这两个请求来完成,所以这里只来大概说一下这两个请求的使用
1.1.1 Get请求
在上面的Demo中我们已经展示了获取两种不同格式的网页内容的Get方法,这里我们通过一个新的渠道让你看到你所发起的请求是Get请求而不是其他的,同时,在认识一个网页返回内容的特殊且最常见的格式JSON格式数据,我们也打印一下返回的内容的格式
import requests
# request发起Get请求,去访问httpbin.org
r = requests.get('http://httpbin.org/get')
print(r.text)
print(type(r.text))
# <class 'str'> 即String格式
print(type(r.json()))
# <class 'dict'> 即字典格式,也就是说打印的内容可以通过JSON转化
格式的问题我就直接标记在上面的代码当中了,那么我们还是仔细来看一下print(r.text)这条语句打印出来的内容(出于信息保护我就抹掉了一些信息)
返回结果[格式为JSON格式]
{
"args": {}, // 用于展示参数
"headers": {
"Accept": "*/*", // 可接受的数据格式,*/*代表不受限
"Accept-Encoding": "gzip, deflate", // 允许的数据格式
"Host": "httpbin.org", // 访问主页
"User-Agent": "python-requests/2.24.0", // 访问的请求头
"X-Amzn-Trace-Id": "Root=1-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx" // 负载均衡器标头
},
"origin": "xxx.xxx.xxx.xxx", // 访问的IP地址
"url": "http://httpbin.org/get" // URL地址
}
是不是第一眼看过去很乱,别着急,后面会一点点去拆解这个json字符串
这里我们就看整个返回结果的url里面的内容,我们自己的代码里面是发起了一个Get请求去访问这个地址,然后返回的结果就会给我们返回一个以get结尾的地址,之就说明我们的请求发送成功了,并且确实是一个get请求
又说了,那有的时候我想访问网站的时候带上一部分的参数,这样子要怎么去发送Get请求,也就是发起带有参数的Get请求;
下面的代码里面我提供两种最基本的方式来实现这个请求
import requests
# request发起get请求时带有参数
# 方法一: 直接在get路径后面加?key=value&key=value的方式来添加参数
r = requests.get('http://httpbin.org/get?name=xiren&age=24')
print(r.text)
# 方法二: 利用params来传递参数
data = {
'name': 'xiren',
'age': 24
}
r = requests.get('http://httpbin.org/get', params=data)
print(r.text)
'''
返回结果(两种方式请求的结果是一样的)
{
"args": {
"age": "24",
"name": "xiren"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.24.0",
"X-Amzn-Trace-Id": "Root=1-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx"
},
"origin": "xxx.xxx.xxx.xxx",
"url": "http://httpbin.org/get?name=xiren&age=24" // 生成的请求路径带有参数
}
'''
从返回结果中我们看出来,这两种方法没有任何的区别,方法二其实就是相当于将拼接的对象做了一下简单的封装,变成了更加便于使用的传参形式;
我们再来看整个的返回结果,会注意到其中有一项’User-Agent’,叫做请求头,这个里面现在是python-requests/2.24.0,也就是说我们现在在访问这些地址的时候,这些地址的服务器接收到的访问时由python-requests/2.24.0这个库来发出的,而不是一个浏览器发出来的,大家应该知道,现在信息时代,每个公司都对数据有着严密的保护,大部分的公司对自己的网站都有着各式各样的反爬措施,所以这样一个不是一个正常的浏览器发起的请求,在请求一些有反爬机制的网站的时候,会返回异常结果,抓取失败,那么为了避免这个问题,我们都会给我们的请求去设置一个请求头,其代码如下:
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.x.xxxx.xxx Safari/537.36'
}
r = requests.get('http://httpbin.org/get', headers=headers)
print(r.text)
'''
返回结果
很明显,User-Agent那一栏有了不同的请求头,也就是我们上面设置的
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.x.xxxx.xxx Safari/537.36",
"X-Amzn-Trace-Id": "Root=1-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx"
},
"origin": "xxx.xxx.xxx.xxx",
"url": "http://httpbin.org/get"
}
'''
这里我就来解释一下User-Agent里面的各个部分以及怎么查看自己本地浏览器的User-Agent
以谷歌浏览器示例,随意打开一个网页,在页面里面右键检查或者按快捷键F12,打开检查工具,然后如下图所示,如果点开没有内容请刷新网页
在下面随意选择一条,不一定跟我标记的一样,在里面Headers选项卡里面找到Request Header,这个里面有一个User-Agent,就是你本地浏览器的请求头,我的是在整个Headers选项卡的最下方,如下图所示
1. Mozilla/5.0 引擎版本 浏览器版本号(历史原因,基本上是默认值)
2. (Macintosh; Intel Mac OS X 10_11_4)这一部分分为三个系统,Windows,Linux和Mac OS,每个之间略有不同
Windows:
(Windows NT 10.0; Win64; x64)
Windows NT 5.0 // 如 Windows 2000
Windows NT 5.1 // 如 Windows XP
Windows NT 6.0 // 如 Windows Vista
Windows NT 6.1 // 如 Windows 7
Windows NT 6.2 // 如 Windows 8
Windows NT 6.3 // 如 Windows 8.1
Windows NT 10.0 // 如 Windows 10
Win64; x64 // Win64 on x64
WOW64 // Win32 on x64(32位兼容系统,有的ie会是这样子)
Linux:
X11; Linux i686 // Linux桌面,i686版本
X11; Linux x86_64 // Linux桌面,x86_64版本
X11; Linux i686 on x86_64 // Linux桌面,运行在x86_64的i686版本
Mac OS:
Macintosh; Intel Mac OS X 10_11_4 // Intel x86或者x86_64
Macintosh; PPC Mac OS X 10_9_0 // PowerPC
Macintosh; Intel Mac OS X 10.12; // 不用下划线,用点
3. AppleWebKit/537.36 (KHTML, like Gecko) 引擎版本,早期是各厂商之间的疯狂伪装,现在基本没变化
4. Chrome/xx.x.xxxx.xxx Safari/537.36" 显示的是浏览器内核,后边的依次是大版本,小版本,补丁,最后的Sa..也是默认值,
有一些浏览器会在结尾再添加浏览器信息,比如Microsoft Edge后缀会有Safari/537.36 Edg/xx.x.xxx.xx
1.1.2 Post请求
Post请求的方法和Get请求的方法很相似,但是Post请求带参数不能直接利用?的形式去拼接,这是处于对数据的保护
import requests
# request发起Post请求,带参数去访问httpbin.org
data = {'name': 'xiren', 'age': '24'}
rp = requests.post("http://httpbin.org/post", data=data)
print(rp.text)
'''
返回结果
{
"args": {},
"data": "",
"files": {},
"form": { // 提交的表单数据
"age": "24",
"name": "xiren"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Content-Length": "18",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.24.0",
"X-Amzn-Trace-Id": "Root=1-xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx"
},
"json": null,
"origin": "xxx.xxx.xxx.xxx",
"url": "http://httpbin.org/post"
}
'''
还有不太一样的就是post默认是用来提交表单数据的,所以会显示在form里面,这样最基本的两个请求方式也就差不多讲完了
1.2 响应
当然我们不能是只发送请求,我们更加需要服务器对我们的请求做出响应,并返回给我们,方便我们从中选取我们所需要的数据,这里我就仅列举几个我们常用的几个想用的参数
import requests
r = requests.get('https://www.baidu.com/')
# 响应代码
print(type(r.status_code), r.status_code)
# <class 'int'> 200
# 头部信息
print(type(r.headers), r.headers)
# <class 'requests.structures.CaseInsensitiveDict'>
# {'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Connection': 'keep-alive',
# 'Content-Encoding': 'gzip', 'Content-Type': 'text/html', 'Date': 'Wed, 27 Jan 2021 07:08:41 GMT',
# 'Last-Modified': 'Mon, 23 Jan 2017 13:24:45 GMT', 'Pragma': 'no-cache', 'Server': 'bfe/1.0.8.18',
# 'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/', 'Transfer-Encoding': 'chunked'}
# Cookie信息
print(type(r.cookies), r.cookies)
# <class 'requests.cookies.RequestsCookieJar'>
# <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
# Url信息
print(type(r.url), r.url)
# <class 'str'> https://www.baidu.com/
# 历史信息
print(type(r.history), r.history)
# <class 'list'> []
其中更加需要掌握的就是状态码,虽然之前在基础中有提到过,这里再来写一下(最常用的我都加黑处理了)
# 信息性状态码
100: ('continue',),
101: ('switching_protocols',),
102: ('processing',),
103: ('checkpoint',),
122: ('uri_too_long', 'request_uri_too_long'),
# 成功状态码
**200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '✓'),**
201: ('created',),
**202: ('accepted',),**
203: ('non_authoritative_info', 'non_authoritative_information'),
204: ('no_content',),
205: ('reset_content', 'reset'),
206: ('partial_content', 'partial'),
207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'),
208: ('already_reported',),
226: ('im_used',),
# 重定向状态码
300: ('multiple_choices',),
301: ('moved_permanently', 'moved', '\\o-'),
**302: ('found',),**
303: ('see_other', 'other'),
304: ('not_modified',),
305: ('use_proxy',),
306: ('switch_proxy',),
307: ('temporary_redirect', 'temporary_moved', 'temporary'),
308: ('permanent_redirect',
'resume_incomplete', 'resume',), # These 2 to be removed in 3.0
# 客户端错误状态码
**400: ('bad_request', 'bad'),
401: ('unauthorized',),
402: ('payment_required', 'payment'),
403: ('forbidden',),
404: ('not_found', '-o-'),
405: ('method_not_allowed', 'not_allowed'),**
406: ('not_acceptable',),
407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'),
408: ('request_timeout', 'timeout'),
409: ('conflict',),
410: ('gone',),
411: ('length_required',),
412: ('precondition_failed', 'precondition'),
413: ('request_entity_too_large',),
414: ('request_uri_too_large',),
415: ('unsupported_media_type', 'unsupported_media', 'media_type'),
416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'),
417: ('expectation_failed',),
418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'),
421: ('misdirected_request',),
422: ('unprocessable_entity', 'unprocessable'),
423: ('locked',),
424: ('failed_dependency', 'dependency'),
425: ('unordered_collection', 'unordered'),
426: ('upgrade_required', 'upgrade'),
428: ('precondition_required', 'precondition'),
429: ('too_many_requests', 'too_many'),
431: ('header_fields_too_large', 'fields_too_large'),
444: ('no_response', 'none'),
449: ('retry_with', 'retry'),
450: ('blocked_by_windows_parental_controls', 'parental_controls'),
451: ('unavailable_for_legal_reasons', 'legal_reasons'),
499: ('client_closed_request',),
# 服务端错误状态码
**500: ('internal_server_error', 'server_error', '/o\\', '✗'),**
501: ('not_implemented',),
**502: ('bad_gateway',),**
503: ('service_unavailable', 'unavailable'),
504: ('gateway_timeout',),
505: ('http_version_not_supported', 'http_version'),
506: ('variant_also_negotiates',),
507: ('insufficient_storage',),
509: ('bandwidth_limit_exceeded', 'bandwidth'),
510: ('not_extended',),
511: ('network_authentication_required', 'network_auth', 'network_authentication')
这样基本的响应也就差不多了,那么我们就来看一看一些网页访问的时候各式各样的信息要怎么处理和设置
1.3 高级使用
1.3.1 Cookies&Session
import requests
r = requests.get('http://www.baidu.com')
print(r.cookies)
# <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
for key, value in r.cookies.items():
print(key + '=' + value)
# 结果为:BDORZ=27315
# 在这里我们就可以把一些自己在登录之后保留在网页上面的Cookies信息,设置在我们自己的请求头当中
# 如何查找自己的cookie信息
cookies = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
# cookies信息的获取如同上面的User-Agent一样,上面被我打了马赛克的那一大段就是你对应的cookie信息
jar = requests.cookies.RequestsCookieJar()
# 这个是设置cookie的格式
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.xxx.xxxx.xx Safari/537.36'
}
for cookie in cookies.split(';'):
key, value = cookie.split('=', 1)
jar.set(key, value)
r = requests.get('https://baidu.com/', cookies=jar, headers=headers)
print(r.text)
# Session维持
# 在这里你不能单纯的去设置cookie,否在在下一次再去访问的时候便会失效,你就需要再去设置
s = requests.Session()
s.get('http://httpbin.org/cookies/set/number/123456789')
r = s.get('http://httpbin.org/cookies')
print(r.text)
'''
返回结果
{
"cookies": {
"number": "123456789"
}
}
'''
1.3.2 SSL证书
高能预警:接下来的*网站有着很强力的反爬措施,建议不要测试,会被封ip,解封大概四个小时
在之前的基础部分,有提到过HTTP和HTTPS之间的差别,也就是差一个S,这个S就是SSL证书
当一个网站缺少ssl信息的时候,在谷歌浏览器上便会显示不安全
import requests
response = requests.get('https://rsj.beijing.gov.cn/')
print(response.status_code)
'''
返回结果
requests.exceptions.SSLError:
HTTPSConnectionPool(host='rsj.beijing.gov.cn', port=443):
Max retries exceeded with url:
/ (Caused by SSLError(SSLCertVerificationError(1,
'[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:
self signed certificate (_ssl.c:1123)')))
'''
这个返回结果的报错是因为对应的网站是HTTP协议的网站,所以我们请求的证书是无效的,就会报出SSL的错误,所以利用verify参数来设置不验证证书
response = requests.get('https://rsj.beijing.gov.cn/', verify=False)
print(response.status_code)
'''
返回结果
InsecureRequestWarning: Unverified HTTPS request is being made to host 'rsj.beijing.gov.cn'.
Adding certificate verification is strongly advised.
See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
warnings.warn(
200(第一次访问)/403(尝试访问被反爬机制阻挡)
'''
这里的返回结果就不在是报错了,而且能够打印出来访问码,200表示着访问成功了,403是因为之前在访问的时候被列为了风险访问,然后后面的访问就被拒绝了,然后就给你报403。这里还是会有一个Warning(警告): 这个警告是说建议在访问这种网站的时候还是要去给它指定一个证书,我们一般用以下两种设置去忽略这个警告
import requests
# 无论哪个方法上面那个import都要有
# 方法一
# 从requests的包中导入urllib3
from requests.packages import urllib3
# urllib3设置不显示警告
urllib3.disable_warnings()
response = requests.get('https://rsj.beijing.gov.cn/', verify=False)
print(response.status_code)
# 方法二
# 导入logging日志库
import logging
# 利用logging来捕获警告并忽略
logging.captureWarnings(True)
response = requests.get('https://rsj.beijing.gov.cn/', verify=False)
print(response.status_code)
当然你也可以去设置SSL证书来避免这个问题,利用cert参数来设置,但是本地路径的crt和key文件路径要写对,且私有证书key要是解密状态,其代码如下
response = requests.get('https://rsj.beijing.gov.cn/', cert=('/path/server.crt', '/path/server.key'))
1.3.3 超时时间
我们在设置好一切后,就可以去请求网页,但是有些网站服务器比较慢,或者其他一些网络问题,访问迟迟得不到回应,而我们又不可能无限等下去,这个时候我们就要引入超时时间这个参数,来设置我们访问一个网页所能接受的最长时间,当超过超时时间后,程序就会直接抛出异常
import requests
# timeout 单位为秒
r = requests.get('https://httpbin.org/get', timeout=1)
print(r.status_code)
1.3.4 身份认证
有一部分的网站会在搭建的时候就要求访问带有基本身份认证(HTTP Basic Access Authentication),这种认证是一种用来允许网页浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证的一种登录验证方式,常见的认证比如说用户名:密码这种,或者是一串特定的字符串传入authentication header,再或者就是现在较多使用的各种token方式,这个时候我们就要想办法在访问的时候去带上这些所需要的参数,方法我这里提供一种,再提供一种参考方法
import requests
# 从requests.auth库中导入HTTP基础Auth
from requests.auth import HTTPBasicAuth
r = requests.get('http://httpbin.org/', auth=HTTPBasicAuth('admin', 'admin'))
print(r.status_code)
另外一种参考方法就是利用OAuth认证,这个方法需要另外下载导入requests_oauth库
下载命令: pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests_oauthlib (使用清华源)
其参考官网示例具体使用: https://requests-oauthlib.readthedocs.io/en/latest/examples/examples.html
1.3.5 代理设置
前面在对http去进行访问的时候正好遇到了这个问题
无论是因为请求不是模仿浏览器,还是大规模的频繁访问一个网站,对于一些设置了反爬技术的网站都会有一些措施,例如验证码,人机测试选择图片,甚至是封禁ip,为了解决这个问题,可以通过设置代理来解决问题,也有叫做ip池
方法一,通过自带的proxies参数(内部的ip我是随便写的)
import requests
proxies = {
'http': 'http://10.10.10.1:1090',
'https': 'http://10.10.10.1:1180',
# 'https': 'http://user:password@10.10.10.10:1080/'(这种为请求中需要带身份验证的)
}
requests.get('https://httpbin.org/get', proxies=proxies)
# 下载命令: pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests[socks](使用清华源)
# proxies = {
# 'http': 'socks5://user:password@host:port',
# 'https': 'socks5://user:password@host:port'
# }
# requests.get('https://httpbin.org/get', proxies=proxies)
方法二: 利用socks库(proxies参数还是示例)
这个socks库被集成在了Anaconda里面了,如果没有的还是根据下面的下载命令去下载
下载命令: pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests[socks](使用清华源)
下载完成后,导入这个库
import requests
proxies = {
'http': 'socks5://user:password@host:port',
'https': 'socks5://user:password@host:port'
}
requests.get('https://httpbin.org/get', proxies=proxies)
这样子就可以去设置ip池,其中这两种方法中的ip地址,你可以考虑利用虚拟机搭建本地集群,也可以去网站上面去寻找一些免费的ip池来进行填充
到这里,基本库requests就基本说完了,还有说的不够细的地方大家再去看看别的资料,有些地方实验的需谨慎;
祝各位码上无ERROR,键盘无BUG!!