2.2 Python使用Requests库爬取数据
相比于自带的URLlib
库,Requests
更为强大直观语义化。本文为学习崔庆才的gitbook与Requests官网总结输出。
1. 使用Requests抓取网页
强大的Request
只需要直接使用get()
函数就可以获取网页了,在get()
中可以指定各种参数,如proxies与headers等参数。
如:
import urllib.request
import requests
from urllib import request, error
#构造proxy
proxy="http://x.x.x.x:80"
proxies = {
"http": proxy,
"https": proxy,
}
#构造headers
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}
#定义抓取的网站
url = 'https://www.baidu.com/'
try:
#使用get函数直接获取网页
r = requests.get(url=url,headers=headers,proxies=proxies)
print(type(r))
print(r.status_code)
print(type(r.text))
print(r.text)
print(r.cookies)
except error.HTTPError as err:
print(err.reason, err.code, err.headers, sep='\n')
except urllib.error.URLError as err:
print(err.reason)
else:
print('ok.')
2. 使用Requests抓取图片、视频等二进制数据
在上面的例子中,我们抓取的是一个页面,实际上它返回的是一个 HTML 文档,那么如果我们想抓去图片、音频、视频等文件的话应该怎么办呢?
其实,图片、音频、视频这些文件都是本质上由二进制码组成的,由于有特定的保存格式和对应的解析方式,我们才可以看到这些形形色色的多媒体。所以想要抓取他们,那就需要拿到他们的二进制码。
下面我们以 GitHub 的站点图标为例来感受一下:
import requests
r = requests.get("https://github.com/favicon.ico")
print(r.text)
print(r.content)
在这里打印了 Response 对象的两个属性,一个是text,另一个是 content。两个属性有什么区别?前者返回的是字符串str类型,如果返回结果是文本文件,那么用这种方式直接获取其内容即可 。如果返回结果是图片、音频、视频等文件,Requests 会为我们自动解码成第二种属性即bytes 类型,即获取字节流数据。
进一步地,我们可以将刚才提取到的图片保存下来。
import requests
r = requests.get("https://github.com/favicon.ico")
with open('favicon.ico', 'wb') as f:
f.write(r.content)
运行结束之后,可以发现在文件夹中出现了名为 favicon.ico 的图标。
3. 使用Requests判断服务器响应的状态、Hearders、Cookies等
发送 Request 之后,得到的自然就是 Response,在上面的实例中我们使用了 text 和 content 获取了 Response 内容,不过还有很多属性和方法可以获取其他的信息,比如状态码 Status Code、Headers、Cookies 等信息。
下面用一个实例来感受一下:
import requests
r = requests.get('http://www.jianshu.com')
print(type(r.status_code), r.status_code)
print(type(r.headers), r.headers)
print(type(r.cookies), r.cookies)
print(type(r.url), r.url)
print(type(r.history), r.history)
在这里分别打印输出了 status_code 属性得到状态码, headers 属性得到 Response Headers,cookies 属性得到 Cookies,url 属性得到 URL,history 属性得到请求历史。运行结果如下:
$ python test1.py
<class 'int'> 200
<class 'requests.structures.CaseInsensitiveDict'> {'Date': 'Tue, 24 Jul 2018 10:04:37 GMT', 'Server': 'Tengine', 'Content-Type': 'text/html; charset=utf-8', 'Transfer-Encoding': 'chunked', 'X-Frame-Options': 'DENY', 'X-XSS-Protection': '1; mode=block', 'X-Content-Type-Options': 'nosniff', 'ETag': 'W/"c4d8167301c1f4cc2cc64cd7ad75d59d"', 'Cache-Control': 'max-age=0, private, must-revalidate', 'Set-Cookie': 'locale=zh-CN; path=/', 'X-Request-Id': '9a67cc6b-f705-4763-b3eb-5b3904ad0c39', 'X-Runtime': '0.014611', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'Content-Encoding': 'gzip', 'X-Via': '1.1 PSxgHK5hc39:10 (Cdn Cache Server V2.0), 1.1 VMxjpSin1fk45:0 (Cdn Cache Server V2.0), 1.1 VMxjpSIN3qb20:4 (Cdn Cache Server V2.0)', 'Connection': 'keep-alive', 'X-Dscp-Value': '0'}
<class 'requests.cookies.RequestsCookieJar'> <RequestsCookieJar[<Cookie locale=zh-CN for www.jianshu.com/>]>
<class 'str'> https://www.jianshu.com/
<class 'list'> [<Response [301]>]
ok.
Status Code 常用来判断请求是否成功,Requests 还提供了一个内置的 Status Code 查询对象 requests.codes。 如:
import requests
r = requests.get('http://www.jianshu.com')
exit() if not r.status_code == requests.codes.ok else print('Request Successfully')
除了OK码以外,还有以下返回码和相应的查询条件:
# Informational.
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',),
# Redirection.
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
# Client Error.
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',),
# Server Error.
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')
比如如果我们想判断结果是不是 404 状态,可以用 requests.codes.not_found
来比对。
4. 使用Request保持会话
在 Requests 中,我们如果直接利用 get() 或 post() 等方法的确可以做到模拟网页的请求。但是这实际上是相当于不同的会话,即不同的 Session,也就是说相当于你用了两个浏览器打开了不同的页面。
利用 Session 我们可以做到模拟同一个会话,而且不用担心 Cookies 的问题,通常用于模拟登录成功之后再进行下一步的操作。 如:
import requests
#定义变量s,存储会话
s = requests.Session()
#存储会话访问网站
s.get('http://httpbin.org/cookies/set/number/123456789')
#再次访问该网站的另一个网页,查看其cookie是否与前一个一致
r = s.get('http://httpbin.org/cookies')
print(r.text)
5. SSL证书验证
Requests 提供了证书验证的功能,当发送 HTTP 请求的时候,它会检查 SSL 证书,我们可以使用 verify 这个参数来控制是否检查此证书,其实如果不加的话默认是 True,会自动验证,如果设置为False的话,则会跳过证书验证,可以解决大部分SSLError的错误。
提示一个错误,叫做 SSLError,证书验证错误。所以如果我们请求一个 HTTPS 站点,但是证书验证错误的页面时,就会报这样的错误,那么如何避免这个错误呢?很简单,把 verify 这个参数设置为 False 即可。
如:
import requests
response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)
这样,就会打印出请求成功的状态码。
/usr/local/lib/python3.6/site-packages/urllib3/connectionpool.py:852: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
InsecureRequestWarning)
200
6. 使用Requests进行身份认证
在访问网站时,我们可能会遇到认证页面需要填入用户名与密码。这时可以使用 Requests 自带的身份认证功能,实例如下:
import requests
from requests.auth import HTTPBasicAuth
r = requests.get('http://localhost:5000', auth=HTTPBasicAuth('username', 'password'))
print(r.status_code)
如果用户名和密码正确的话,请求时就会自动认证成功,会返回 200 状态码,如果认证失败,则会返回 401 状态码。
当然如果参数都传一个 HTTPBasicAuth 类,就显得有点繁琐了,所以 Requests 提供了一个更简单的写法,可以直接传一个元组,它会默认使用 HTTPBasicAuth 这个类来认证。
所以上面的代码可以直接简写如下:
import requests
r = requests.get('http://localhost:5000', auth=('username', 'password'))
print(r.status_code)
运行效果和上面的是一样的。
Requests 还提供了其他的认证方式,如 OAuth 认证,不过需要安装 oauth 包,命令如下:
pip3 install requests_oauthlib
使用 OAuth1 认证的方法如下:
import requests
from requests_oauthlib import OAuth1
url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET',
'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET')
requests.get(url, auth=auth)
更多详细的功能就可以参考 requests_oauthlib 的官方文档:https://requests-oauthlib.readthedocs.org/
Last updated