2.2 Python使用Requests库爬取数据

相比于自带的URLlib库,Requests更为强大直观语义化。本文为学习崔庆才的gitbookRequests官网总结输出。

1. 使用Requests抓取网页

强大的Request只需要直接使用get()函数就可以获取网页了,在get()中可以指定各种参数,如proxiesheaders等参数。

如:

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