Python爬虫-爬取库的使用介绍

使用urllib

  • Python2中,有urllib和urllib2两个库来实现请求的发送,在Python3中,统一为urllib,官方文档链接为:https://docs.python.org/3/library/urllib.html
  • urllib是Python的内置模块,所以不需要额外安装。
  • urllib包含4个模块:
    • request:它是最基本的HTTP请求模块,可以用来模拟发送请求。就像在浏览器里输入网址然后回车一样,只需要给库方法传入URL以及额外的参数,就可以模拟实现这个过程了。
    • error:异常处理模块,如果出现请求错误,我们可以捕获这些异常,然后进行重试或其他操作以保证程序不会意外终止。
    • parse:一个工具模块,提供很多URL处理方法,比如拆分、解析、合并等。
    • robotparser:主要是用来识别网站的robots.txt文件,然后判断哪些网站可以爬,哪些网站不可以爬,它其实用的比较少。

rquest模块

urlopen()

  • urllib.request模块提供了基本的构造HTTP请求的方法,利用它可以模拟浏览器的请求发起过程,同时它还带有处理授权验证(authenticaton)、重定向(redirection)、浏览器Cookies以及其他内容。

  • 抓取网页实例

    import urllib.request
    
    res = urllib.request.urlopen('https://www.baidu.com')
    #利用type()方法输出响应的类型
    print(type(res))
    #status属性可以得到返回结果的状态码,如:200代表请求成功,404代表网页未找到等
    print(res.status)
    #输出响应的头信息
    print(res.getheaders())
    #调用getheader()方法并传递一个参数Server获取了响应头中的Server值,结果是BWS/1.1
    #意思是服务器使用BWS/1.1搭建的
    print(res.getheader('Server'))
    #read()方法可以得到返回的网页内容
    print(res.read().decode('utf-8'))
    

    运行结果如下:

    <class 'http.client.HTTPResponse'>
    200
    [('Accept-Ranges', 'bytes'), ('Cache-Control', 'no-cache'), ('Content-Length', '227'), ('Content-Type', 'text/html'), ('Date', 'Mon, 24 Dec 2018 22:32:50 GMT'), ('Etag', '"5c1a1790-e3"'), ('Last-Modified', 'Wed, 19 Dec 2018 10:04:00 GMT'), ('P3p', 'CP=" OTI DSP COR IVA OUR IND COM "'), ('Pragma', 'no-cache'), ('Server', 'BWS/1.1'), ('Set-Cookie', 'BD_NOT_HTTPS=1; path=/; Max-Age=300'), ('Set-Cookie', 'BIDUPSID=F496065066021BD3CEC67DF2678EAA77; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Set-Cookie', 'PSTM=1545690770; expires=Thu, 31-Dec-37 23:55:55 GMT; max-age=2147483647; path=/; domain=.baidu.com'), ('Strict-Transport-Security', 'max-age=0'), ('X-Ua-Compatible', 'IE=Edge,chrome=1'), ('Connection', 'close')]
    BWS/1.1
    <html>
    <head>
    	<script>
    		location.replace(location.href.replace("https://","http://"));
    	</script>
    </head>
    <body>
    	<noscript><meta http-equiv="refresh" content="0;url=http://www.baidu.com/"></noscript>
    </body>
    </html>
    
  • 可以发现,它是一个HTTPResponse类型的对象,主要包含read()、readinto()、getheader(name)、getheaders()、fileno()等方法, 以及msg、version、status、readson、debuglevel、closed等属性。得到这个对象之后,把它赋值为res变量,然后就可以调用这些方法和属性,得到返回结果的一系列信息了。

  • 利用最基本的urlopen()方法,可以完成最基本的简单网页的GET请求抓取。

  • 想给链接传递一些参数,可以查看urlopen()函数的API:urllib.request.urlopen(url,data=None,[timeout,]*,cafile=None,capath=None,cadefault=False,context=None)可以发现,除了第一个参数可以传递URL之外,还可以传递其他内容。

data参数

data参数是可选的。如果要添加该参数,并且如果它是字节流编码格式的内容,即bytes类型,则需要通过bytes()方法转化。注:如果传递了这个参数,则它的请求方式不再是GET方式,而是POST方式。

import urllib.parse
import urllib.request

data = bytes(urllib.parse.urlencode({'word':'hello'}),encoding='utf-8')
response = urllib.request.urlopen('http://httpbin.org/post',data=data)
print(response.read().decode('utf-8'))

这里,传递的一个参数word,值是hello。它需要被转码成bytes(字节流)类型。其中转字节流采用了bytes()方法, 该方法的第一个参数需要是str(字符串)类型,需要用urllib.parse模块里的urlencode()方法来将参数字典转化为字符串;第二个参数指定编码格式,这里指定为utf-8。请求的站点是 httpbin.org,它可以提供HTTP请求测试,请求的URL链接可以用来测试POST请求,输出一些请求的信息,其中包含我们传递的data参数。

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "word": "hello"
  }, 
  "headers": {
    "Accept-Encoding": "identity", 
    "Connection": "close", 
    "Content-Length": "10", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "Python-urllib/3.7"
  }, 
  "json": null, 
  "origin": "123.157.129.56", 
  "url": "http://httpbin.org/post"
}

传递的参数出现在了form字段中,这表明了模拟了表单提交的方式,以POST方式传输数据。

timeout参数

timeout参数用于设置超时时间,单位为秒,意思就是如果请求超出了设置的这个时间,还没有得到响应,就会抛出异常。如果不指定这个该参数,就会使用全局默认时间。它支持HTTP、HTTPS、FTP请求。

import urllib.request

response = urllib.request.urlopen('http://httpbin.org/get',timeout=0.1)
print(response.read().decode())

运行结果如下:

......
urllib.error.URLError: <urlopen error timed out>

设置的超时时间为0.1秒。程序0.1秒过后,服务器依然没有响应,于是抛出了URLError异常。该异常属于urllib.error模块,错误原因是超时。
可以通过设置这个超时时间来控制一个网页如果长时间未响应,就跳过它的抓取。利用try except语句来实现。

import socket
import urllib.request

import urllib.error
try:
    response = urllib.request.urlopen('http://httpbin.org/get',timeout=0.1)
except urllib.error.URLError as e:
    if isinstance(e.reason,socket.timeout):
        print("Time Out!")

运行结果为:

Time Out!

按照常理来说,0.1秒内基本不可能得到服务器响应,因此输出了Time Out! 的提示。

其他参数

context参数:它必须是ssl.SSLContext类型,用来指定SSL设置。
cafile和capath两个参数:分别指定CA证书和它的路径,这个在请求HTTPS链接时会有用。
cadefault参数:现在已经弃用了,默认值为False。

Request

  • 利用urlopen()方法可以实现最基本请求的发起,但这几个简单的参数并不足以构建一个完整的请求。如果请求中需要加入headers等信息,就可以利用更为强大的Request类来创建。
  • Request构造方法:class urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
    • 第一个参数url用于请求URL,这是必传参数,其他都是可选参数。
    • 第二个参数data如果要传,必须传bytes(字节流)类型的。如果它是字典,可以先用urllib.parse模块里面的urlencode()编码。
    • 第三个参数headers是一个字典,他就是请求头,可以在构造请求时通过headers参数直接构造,也可以通过调用请求实例的add_headers()方法来添加。添加请求头最常用的用法就是通过修改User-Agent来伪装浏览器,默认的User-Agent是Python-urllib,可以通过修改它来伪装浏览器。
    • 第四个参数origin_req_host指的是请求方的host名称或者IP地址。
    • 第五个参数unverifiable表示这个请求是否是无法验证的,默认是False,意思就是说用户没有足够权限来选择接收这个请求的结果。例如,请求一个HTML文档中的图片,但是没有自动抓取图像的权限,这时unverifiable的值就是True。
    • 第六个参数method是一个字符串,用来指示请求使用的方法,比如GET、POST和PUT等。
from urllib import request,parse

url = 'http://httpbin.org/post'
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1',
    'Host':'httpbin.org'
}

dict = {
    'name':'Germey'
}

data = bytes(parse.urlencode(dict),encoding='utf-8')
req = request.Request(url=url, data=data, headers=headers, method="POST")
respose = request.urlopen(req)
print(respose.read().decode('utf-8'))

输出的结果为:

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "name": "Germey"
  }, 
  "headers": {
    "Accept-Encoding": "identity", 
    "Connection": "close", 
    "Content-Length": "11", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1"
  }, 
  "json": null, 
  "origin": "58.216.11.144", 
  "url": "http://httpbin.org/post"
}

观察结果发现,成功设置了data、headers和method。
另外,headers可以通过add_headers()方法来添加:

req = request.Request(url=url, data=data, method="POST")
req.add_headers('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163')

高级用法

  • 现在,我们可以构造请求,但是对于一些更高级的操作(比如Cookies处理、代理设置等),该如何去做呢?
    Handler的使用,可以把它理解为各种处理器,有专门处理登录验证的,有处理Cookies的,有处理代理设置的。
  • BaseHandler类,它是所有其他Handler的父类,提供最基本的方法,如default_open()、protocol_request()等。
  • 各种Handler子类,继承于BaseHandler类
    • HTTPDefaultErrorHandler:用于处理HTTP响应错误,错误都会抛出HTTPError类型的异常。
    • HTTPRedirectHandler:用于处理重定向。
    • HTTPCookiesProcessor:用于处理Cookies。
    • ProxyHandler:用于设置代理,默认代理为空。
    • HTTPPasswordMgr:用于管理密码,它维护了用户名和密码的表。
    • HTTPBasicAuthHandler:用于管理认证,如果一个链接打开时需要认证,那么可以用它来解决认证问题。
  • 还有其他的Handler类,就不一一列举了。详情可以参考官方文档:https://docs.python.org/3/library/urllib.request.html#urllib.request.BaseHandler
  • 还有一个比较重要的类 OpenerDirector,可以称其为Opener。之前使用过urlopen()这个方法实际上就是urllib提供的一个opener。那么为什么要引入Opener呢?为了实现更高级的功能,所以要深入一层进行配置,使用更底层的实例来完成操作。
  • 简而言之,利用Handler来构建Opener。使用几个实例来介绍。
验证
  • 有些网站在打开时就会弹出提示框,直接提示你输入用户名和密码,验证成功后才能查看界面。如果要请求这样的页面,可以借助HTTPBasicAuthHandler完成。
from urllib.request import HTTPPasswordMgrWithDefaultRealm,HTTPBasicAuthHandler,build_opener
from urllib.error import URLError

username = 'username'
password = 'password'
url = 'http://localhost:5000/'

p = HTTPPasswordMgrWithDefaultRealm()
p.add_password(None,url,username,password)
auth_handler = HTTPBasicAuthHandler(p)
opener = build_opener(auth_handler)

try:
	result = opener.open(url)
	html = result.read().decode('utf-8')
	print(html)
except URLError as e:
	print(e.reason)

这里首先实例化HTTPBasicAuthHandler对象,其参数是HTTPPasswordMgrWithDefaultRealm对象,它利用add_password()添加进去用户名和密码,这样就建立了一个处理验证的Handler。接下来,利用这个Handler并使用build_opener()方法构建一个Opener,这个Opener在发送请求时就相当于验证成功了。之后,利用Opener的open()方法打开链接,就可以完成验证了。这里获取的结果就是验证后的页面源码内容。

代理

在做爬虫的时候,免不了要使用代理,如果添加代理,可以如下做:

from urllib.error import URLError
from urllib.request import ProxyHandler,build_opener

proxy_handler = ProxyHandler({
    'http':'http:/127.0.0.1:9743',
    'https':'https://127.0.0.1:9743'
})
opener = build_opener(proxy_handler)
try:
    response = opener.open('https://www.baidu.com')
    print(response.read().decode('utf-8'))
except URLError as e:
    print(e.reason)

此代码中,在本地搭建了一个代理,它运行在9743端口上。其中使用了ProxyHandler,其参数是一个字典,键名是协议类型(比如HTTP或者HTTPS等),键值是代理链接,可以添加多个代理。然后利用这个Handler及build_opener()方法构造一个Opener,之后发送请求即可。

Cookies

Cookies的处理就需要相关的Handler了。先从网站上获取Cookies:

import http.cookiejar,urllib.request

# 声明一个CookieJar对象
cookie = http.cookiejar.CookieJar()
# 利用HTTPCookieProcessor来构建Handler
handler = urllib.request.HTTPCookieProcessor(cookie)
# 使用build_opener()方法构建出opener,执行open()函数
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
for item in cookie:
    print(item.name+"="+item.value)

运行结果如下:

BAIDUID=799C89AB9F89F09F03D29194F96D8B16:FG=1
BIDUPSID=799C89AB9F89F09F03D29194F96D8B16
H_PS_PSSID=1465_21103_28206_28131_27751_27244_20719
PSTM=1545779950
delPer=0
BDSVRTM=0
BD_HOME=0

Cookies实际上是以文本形式保存的。所以,可以将其输出为文件格式。

filename = 'cookies.txt'
# CookieJar就需要换成MozillaCookieJar
cookie = http.cookiejar.MozillaCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True, ignore_expires=True)

这时CookieJar就需要换成MozillaCookieJar,它在生成文件时会用到,是CookieJar的子类,可以用来处理Cookies和文件相关的事件,比如读取和保存。运行之后,可以得到一个cookies.txt文件。
还可以用LWPCookieJar来保存和读取,但是格式不同,libwww-perl(LWP)格式的cookies文件。

filename = 'cookies.txt'
# CookieJar就需要换成LWPCookieJar
cookie = http.cookiejar.LWPCookieJar(filename)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
cookie.save(ignore_discard=True, ignore_expires=True)

上面提到Cookies的保存,那么就需要读取Cookies,以LWPCookieJar格式的Cookies为例

cookie = http.cookiejar.LWPCookieJar(filename)
cookie.load('cookies.txt', ignore_discard=True, ignore_expires=True)
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open('http://www.baidu.com')
print(response.read().decode('utf-8'))

调用load()方法来读取本地的Cookies文件,获取到了Cookies的内容。前提:首先生成了LWPCookieJar格式的Cookies并保存成文件,然后读取Cookies之后使用同样的方法构建Handler和Opener即可完成操作。

异常处理Error模块

urllib的error模块定义了有request模块产生的异常。如果出现了问题,request模块就会抛出error模块中定义的异常。

URLError

  • URLError类来自urllib库的error模块,它继承自OSError类,是error异常模块的基类,由request模块产生的异常都可以通过捕获这个类来处理。
  • 它有一个reason属性,即返回错误的原因
from urllib import request, error
try:
	response = request.urlopen('https://cuiqingcai.com/index.html')
except error.URLError as e:
	print(e.reason)

打开一个不存在的页面,应该会报错,但捕获了URLError这个异常,程序没有报错。运行结果如下:

Not Found

HTTPError

它是URLError的子类,专门用来处理HTTP请求错误,比如认证请求失败等。它有3个属性:

  1. code:返回HTTP状态码,比如404表示网页不存在,500表示服务器内部错误等。
  2. reason:同父类一样,用于返回错误的原因。
  3. 返回请求头
from urllib import request, error
try:
	response = request.urlopen('https://cuiqingcai.com/index.html')
except error.HTTPError as e:
	print(e.code, e.reason, e.headers, sep='\n')

依然是同样的网址,捕获了HTTPError异常,输出结果:

404
Not Found
Server: nginx/1.10.3 (Ubuntu)
Date: Tue, 25 Dec 2018 23:47:54 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: close
Vary: Cookie
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Link: <https://cuiqingcai.com/wp-json/>; rel="https://api.w.org/"

补充说明

  • 因为存在子类父类的问题,在捕获异常时,选择先捕获子类的异常,再去捕获父类的异常。如下:
    from urllib import request, error
    try:
    	response = request.urlopen('https://cuiqingcai.com/index.html')
    except error.HTTPError as e:
    	print(e.code, e.reason, e.headers, sep='\n')
    except error.URLError as e:
    	print(e.reason)	
    
  • reason属性返回的不一定是字符串,也可能是一个对象。
import socket
import urllib.request
import urllib.error

try:
	response = urllib.request.urlopen('https://www.baidu.com', timeout = 0.01)
except urllib.error.URLError as e:
	print(type(e.reason))
	if isinstance(e.reason, socket.timeout):
		print('TIME OUT')

设置超时时间来强制抛出timeout异常。运行结果如下:

<class 'socket.timeout'>
TIME OUT

reason属性的结果是socket.timeout类。可以用isinstance()方法来判断它的类型,做出更为详细的判断。

解析链接parse模块

urllib库提供了parse模块,它定义了处理URL的标准接口,例如实现URL各部分的抽取、合并以及链接转换。支持如下协议的URL处理:file、ftp、gopher、hdl、http、https、imap、mailto、mms、news、nntp、prospero、rsync、rtsp、rtspu、sftp、sip、sips、snews、svn、svn+ssh、telnet和wais。

urlparse()

  • 该方法可以实现URL的识别和分段,实例如下:
from urllib.parse import urlparse

result = urlparse('http://www.baidu.com/index.html;user?id=5#comment')
print(type(result), result, sep='\n')
  • 运行结果如下:
<class 'urllib.parse.ParseResult'> 
ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')
  • 可以看到,返回结果是一个ParseResult类型的对象,它包含6个部分,分别是scheme、netloc、path、params、query和fragment。

  • 分析实例的URL:http://www.baidu.com/index.html;user?id=5#comment,可以发现urlparse()方法将其拆分成了6个部分。解析时有特定的分隔符:://前面的就是scheme,代表协议;第一个/符号前面便是netloc,即域名,后面是path,即访问路径;分号;后面是params,代表参数;问号?后面是查询条件query,一般用作GET类型的URL;井号#后面是锚点,用于直接定位页面内部的下拉位置。一个标准的链接格式:scheme://netloc/path;params?query#fragment

  • urlparse()方法的API用法:urllib.parse.urlparse(urlstring, scheme=' ', allow_fragments=True),可以看到它有三个参数。

    • urlstring:这是必填项,即待解析的URL。

    • scheme:它是默认的协议(比如http或https等)。假如这个链接没有带协议信息,会将这个作为默认的协议。scheme参数只有在URL中不包含scheme信息时才生效。如果URL中有scheme信息就会返回解析出的scheme。

    • allow_fragments:即是否忽略fragment。如果它被设置为False,fragment部分就会被忽略,它会被解析为path、parameters或者query的一部分,而fragment部分为空。

      from urllib.parse import urlparse
      
      result = urlparse('http://www.baidu.com/index.html;user?id=5#comment',allow_fragments=False)
      print(result)
      

      结果如下

      ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5#comment', fragment='')
      

      假设URL中不包含params和query

      from urllib.parse import urlparse
      
      result = urlparse('http://www.baidu.com/index.html#comment',allow_fragments=False)
      print(result)
      

      结果如下

      ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html#comment', params='', query='', fragment='')
      

      可以发现,当URL中不包含params和query时,fragment便会被解析为path的一部分。返回结果ParseResult实际上是一个元组,可以用索引顺序来获取,也可以用属性名获取。示例如下:

      from urllib.parse import urlparse
      		
      result = urlparse('http://www.baidu.com/index.html#comment',allow_fragments=False)
      print(result.scheme, result[0], result.netloc, result[1], sep='\n')
      

      运行结果如下:

      http
      http
      www.baidu.com
      www.baidu.com
      

urlunparse()

  • 有了urlparse(),相应的就有了它的对立方法urlunparse()。它接受的参数是一个可迭代对象,但是它的长度必须是6,否则会抛出参数数量不足或者过多的问题。实例如下:

    from urllib.parse import urlparse
    
    data = ['http', 'www.baidu.com', 'index.html', 'user', 'a=6', 'comment']
    print(urlunparse(data))
    
  • 这里的参数data用了列表类型,也可以用其他类型,如元组或特定的数据结构。

  • 运行结果如下,成功实现了URL的构造:

    http://www.baidu.com/index.html;user?a=6#comment
    

urlsplit()

这个方法和urlparse()方法非常类似,只不过它不再单独解析params这一部分,只返回5个结果。上面例子中的params会合并到path中。实例如下:

from urllib.parse import urlsplit

result = urlsplit('http://www.baidu.com/index.html#comment')
print(result)

运行结果如下:

SplitResult(scheme='http', netloc='www.baidu.com', path='/index.html', query='', fragment='comment')

可以发现,返回结果是SplitResult,它其实是一个元组类型,既可以用属性获取值,也可以用索引来获取。

from urllib.parse import urlsplit

result = urlsplit('http://www.baidu.com/index.html#comment')
print(result.scheme, result[0])

运行结果如下:

http http

urlunsplit()

与urlunparse()类似,它也是将链接各个部分组合成完整链接的方法,传入的参数也是一个可迭代对象,例如列表、元组等,唯一的区别是长度必须是5。实例如下:

from urllib.parse import urlparse
	
data = ['http', 'www.baidu.com', 'index.html', 'a=6', 'comment']
print(urlunsplit(data))

运行结果如下:

http://www.baidu.com/index.html?a=6#comment

urljoin()

提供一个base_url(基础链接)作为第一个参数,将新的链接作为第二个参数,该方法会分析base_url的scheme、netloc和path这3个内容并对新链接缺失的部分进行补充,最后返回结果。

from urllib.parse import urljoin

print(urljoin('http://www.baidu.com', 'FAQ.html'))
print(urljoin('http://www.baidu.com', 'https://cuiqingcai.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html'))
print(urljoin('http://www.baidu.com/about.html', 'https://cuiqingcai.com/FAQ.html?question=2'))
print(urljoin('http://www.baidu.com?wd=abc', 'https://cuiqingcai.com/index.php'))
print(urljoin('http://www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com', '?category=2#comment'))
print(urljoin('www.baidu.com#comment', '?category=2'))

运行结果如下:

http://www.baidu.com/FAQ.html
https://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html?question=2
https://cuiqingcai.com/index.php
http://www.baidu.com?category=2#comment
www.baidu.com?category=2#comment
www.baidu.com?category=2

可以发现,base_url提供三项内容scheme、netloc和path。如果这三项在新的链接里不存在,就予以补充;如果新的链接存在,就是用新的链接的部分。而base_url中的params、query和fragment是不起作用的。

urlencode()

构造GET请求参数,实例如下:

from urllib.parse import urlencode

# 声明一个字典来将参数表示出来
params = {
    'name':'germy',
    'age':'22'
}
base_url = 'http://www.baidu.com?'
# 调用urlencode()方法将字典序列化为GET请求参数
url = base_url + urlencode(params)
print(url)

运行结果:

http://www.baidu.com?name=germy&age=22

parse_qs()

反序列化,将一串GET请求参数利用此方法,就可以转回字典,示例如下:

from urllib.parse import parse_qs

query = 'name=germy&age=22'
print(parse_qs(query))

运行结果如下:

{'name':['germy'], 'age':['22']}

parse_qsl()

用于将参数转化为元组组成的列表,示例如下:

from urllib.parse import parse_qsl

query = 'name=germy&age=22'
print(parse_qsl(query))

运行结果如下:

[('name', 'germy'), ('age', '22')]

可以看到,运行结果是一个列表,而列表中的每一个元素都是元组,元组的第一个内容是参数名,第二个内容是参数值。

quote()

该方法可以将内容转化为URL编码的格式。URL中带有中文参数时,有时可能会导致乱码问题,此时用这个方法可以将中文字符转化为URL编码,示例如下:

from urllib.parse import quote

keyword = '壁纸’
url = 'https://www.baidu.com/s?wd=' + quote(keyword)
print(url)

结果:

https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8

unquote()

quote()的对应方法,对URL进行解码。

from urllib.parse import unquote

url = https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8
print(unquote(url))

结果:

https://www.baidu.com/s?wd=壁纸

分析Robots协议(robotparser模块)

Robots协议

  • Robots协议也称作爬虫协议、机器人协议,全名是网络爬虫排除标准(Robots Exclusion Protocol),用来告诉爬虫和搜索引擎哪些页面可以抓取,哪些不可以抓取。通常是一个名为robots.txt的文本文件,一般放在网站的根目录下。

  • 当搜索爬虫访问一个站点时,首先会检查这个站点根目录下是否存在robots.txt文件,如果存在,搜索爬虫会根据其中定义的爬取范围来爬取。如果没有找到这个文件,搜索爬虫就会访问所有可直接访问的页面。

  • robots.txt样例

    User-agent: *  #描述搜索爬虫的名称,*则代表该协议对任何爬虫有效
    Disallow: /    #指定不允许爬取的目录,/代表不允许抓取所有页面
    Allow: /public/  #一般和Disallow一起使用,不会单独使用,用来排除某些限制。表示可以抓取public目录 
    
    #禁止所有爬虫访问任何目录
    User-agent: * 
    Disallow: /
    
    #允许所有爬虫访问任何目录,也可以把robots.txt文件留空
    User-agent: * 
    Disallow:
    
    #禁止所有爬虫访问网站某些目录
    User-agent: * 
    Disallow: /private/
    Disallow: /tmp/
    
    #只允许一个爬虫访问
    User-agent: WebCrawler
    Disallow:
    User-agent: *
    Disallow: /
    

爬虫名称

常见的搜索爬虫名称和对应的网站

爬虫名称名称网站
BaiduSpider百度www.baidu.com
Googlebot谷歌www.google.com
360Spider360搜索www.so.com
YodaoSpider有道www.youdao.com
ia_archiverAlexawww.alexa.com
Scooteraltavistawww.altavista.com

robotparser模块

  • 该模块提供了一个类RobotFileParser,它可以根据某网站的robots.txt文件来判断一个爬取爬虫是否有权限来爬取这个网页。

  • 使用方法:在构造方法里传入robots.txt的链接即可。声明:urllib.robotparser.RobotFileParser(url=' ')

  • 常用方法:

    • set_url():用来设置robots.txt文件的链接。如果在创建RobotFileParser对象时已经传入了链接,就不需要再使用这个方法设置了。
    • read():读取robots.txt文件并进行分析。注意,这个方法执行一个读取和分析操作,如果不调用这个方法,接下来的判断都会为False,所以一定要调用此方法。这个方法不会返回任何内容,但是执行了读取操作。
    • parse():用来解析robots.txt文件,传入的参数是robots.txt某些行的内容,它会按照robots.txt的语法规则来分析这些内容。
    • can_fetch():该方法传入两个参数,第一个是User-agent,第二个是要抓取的URL。返回的内容是该搜索引擎是否可以抓取这个URL,返回的结果是True或False。
    • mtime():返回的是上次抓取和分析robots.txt的时间,这对于长时间分析和抓取的搜索爬虫是很有必要的,你可能需要定期检查来抓取最新的robots.txt。
    • modified():它同样对长时间分析和抓取的搜索爬虫很有帮助,将当前时间设置为上次抓取和分析robots.txt的时间。
  • 示例如下:

    from urllib.robotparser import RobotFileParser
    
    #创建RobotFileParser对象
    rp = RobotFileParser()
    #通过set_url()方法设置robots.txt的链接
    #也可以这么设置:rp = RobotFileParser('http://www.jianshu.com/robots.txt')
    rp.set_url('https://www.jianshu.com/robots.txt')
    rp.read()
    #判断网站是否可以被抓取
    print(rp.can_fetch('*','https://www.jianshu.com/p/2f09699006dd'))
    print(rp.can_fetch('*','https://jianshu.com/search?q=python&page=1&type=collections'))
    

    运行结果如下:

    False
    False
    

    同样可以使用parse()方法执行读取和分析,示例如下:

    from urllib.robotparser import RobotFileParser
    from urllib.request import urlopen
    
    #创建RobotFileParser对象
    rp = RobotFileParser()
    rp.parse(urlopen('http://www.jianshu.com/robots.txt').read().decode('utf-8').split('\n'))
    #判断网站是否可以被抓取
    print(rp.can_fetch('*','https://www.jianshu.com/p/2f09699006dd'))
    print(rp.can_fetch('*','https://jianshu.com/search?q=python&page=1&type=collections'))
    

ruquests库

上面,我们了解到urllib的基本用法,但是其中有很多不方便的地方,比如处理网页验证和Cookies时,需要些Opener和Handler来处理。为了更加方便实现这些操作,就有了更为强大的库requests。详细信息可以参考官方文档:http://docs.python-requests.org/

基本用法

  • urllib库中的urlopen()方法实际上是以GET方式请求网页,而requests中相应的方法就是get()方法。示例如下:

    import requests
    
    r = requests.get("https://www.baidu.com/")
    print(type(r))
    print(r.status_code)
    print(type(r.text))
    #print(r.text)
    print(r.cookies)
    

    运行结果如下:

    <class 'requests.models.Response'>
    200
    <class 'str'>
    <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
    
  • 说明:调用get()方法实现与urlopen()相同的操作,得到一个Response对象,然后分别输出了Response的类型、状态码、响应体的类型、内容(因为太长,所以特地不显示了)以及Cookies。通过运行结果可以发现,它的返回类型是requests.models.Response,响应体的类型是字符串str,Cookies的类型是RequestCookieJar。

  • requests还可以设置其他类型的请求

    r = requests.post('http://httpbin.org/post')
    r = requests.put('http://httpbin.org/put')
    r = requests.delete('http://httpbin.org/delete')
    r = requests.head('http://httpbin.org/get')
    r = requests.options('http://httpbin.org/get')
    

GET请求

HTTP中最常见的请求之一就是GET请求,下面首先来详细了解一下利用requests构建GET请求的方法。

基本实例

首先,构建一个最简单的GET请求,请求的链接是http://httpbin.org/get,该网站会判断如果客户端发起的是GET请求的话,会返回相应的请求信息。

import requests

r = requests.get("http://httpbin.org/get")
print(r.text)

运行结果如下:

{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.21.0"
  }, 
  "origin": "58.216.11.153", 
  "url": "http://httpbin.org/get"
}

如果对于GET请求,要附加额外的信息,要怎么添加呢?比如要添加两个参数,其中name是germy,age是22。要构造这个请求链接,可以直接写成:r = requests.get("http://httpbin.org/get?name=germy&age=22")。这样不人性化,有时添加的参数较多,不太方便,可以使用字典来存储。如:

import requests

data = {
    'name':'germy',
    'age':'22'
}
r = requests.get("http://httpbin.org/get",params=data)
print(r.text)

结果如下:

{
  "args": {
    "age": "22", 
    "name": "germy"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.21.0"
  }, 
  "origin": "58.216.11.153", 
  "url": "http://httpbin.org/get?name=germy&age=22"
}

另外,注意:网页的返回类型实际上是str类型,但是它很特殊,是JSON格式的。所以,要想直接解析返回结果,得到一个字典格式的话,可以直接调用json()方法。如:

import requests

data = {
    'name':'germy',
    'age':'22'
}
r = requests.get("http://httpbin.org/get",params=data)
print(type(r.text))
print(r.json())
print(type(r.json()))

结果如下:

<class 'str'>
{'args': {'age': '22', 'name': 'germy'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'close', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.21.0'}, 'origin': '123.157.129.57', 'url': 'http://httpbin.org/get?name=germy&age=22'}
<class 'dict'>

注意:如果返回的结果不是JSON格式,便会出现解析错误,抛出json.decoder.JSONDecodeError异常。

抓取网页

上面请求的链接返回的是JSON形式的字符串,那么如果请求普通的网页,则肯定能获得相应的结果了。

import requests
import re

#加入headers信息,模仿浏览器
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1'
}
r = requests.get("https://www.zhihu.com/explore",headers=headers)
#使用正则表达式匹配出所有问题内容
pattern = re.compile('explore-feed.*?question_link.*?>(.*?)</a>', re.S)
titles = re.findall(pattern, r.text)
print(titles)

运行结果如下:

['\n如何评价最近发现翼龙身上有疑似羽毛的结构?\n', '\n一个演员的演技能烂到什么程度?\n', '\n古代没有化学,古人是怎么确认杀人凶手的?\n', '\n有哪些对学生党来说很有用的小文具/小技巧/小知识/小东西?\n', '\n勇士 129:127 险胜快船,库里 42 分 0.5 秒上篮绝杀,如何评价这场比赛?\n', '\n如果精灵宝可梦里的精灵可以食用,你们想要把哪些宝可梦做成哪些菜?\n', '\n作为杨超越粉丝,你最大的感受是什么?\n', '\n怎样看待Bighit于2019年推出新男团?\n', '\n如何评价宋旻浩首张solo专辑《✖️✖️》?\n', '\n你最讨厌抖音里哪个网红?\n']
抓取二进制数据

图片、音频、视频这些文件本质上都是由二进制码组成的,由于有特定的保存格式和对应的解析方式,才能看到这些形形色色的多媒体。要抓取它们,需要拿到它们的二进制码。以GitHub站点的图标为例:

import requests

r = requests.get('https://github.com/favicon.ico')
print(r.text)
print(r.content)

注意:这里打印r.text时会出现乱码,因为源文件是二进制,没有进行转化就打印出str类型。
将提取的图片保存下来:

import requests

r = requests.get('https://github.com/favicon.ico')
with open('favicon.ico', 'wb') as f:
	f.write(r.content)

使用open()方法,第一个参数是文件名称, 第二个参数表示以二进制写的形式打开,可以向文件中写入二进制数据。运行之后,会出现一个名为favicon.ico的图标。

添加headers

如上例,访问知乎的时候不添加headers是被禁止访问的。

POST请求

除了GET请求之外,另外一种比较常见的请求是POST。

import requests

data = {'name':'germey', 'age':'22'}
r = requests.post('http://httpbin.org/post', data=data)
print(r.text)

运行结果如下:

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "age": "22", 
    "name": "germey"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Connection": "close", 
    "Content-Length": "18", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.21.0"
  }, 
  "json": null, 
  "origin": "115.238.194.89", 
  "url": "http://httpbin.org/post"
}

可以发现,获得返回结果,其中form部分就是提交的数据,证明POST请求成功。

响应

发送请求后,得到的自然是响应。在上面的示例中,使用text和content获取响应的内容。除此之外,还有很多属性和方法用来获取其他信息,比如状态码、响应头、Cookies等。

import requests

r = requests.get('http://www.jianshu.com')
#分别打印出状态码、响应头、Cookies、URL、请求历史
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)

状态码常用来判断请求是否成功,而requests提供了一个内置的状态码查询对象requests.codes,示例如下:

import requests

r = requests.get('http://www.jianshu.com')
exit() if not r.status.code == requests.codes.ok else print('Request Successfully')

通过比较返回码和内置的成功的返回码来保证请求得到了正常响应,输出成功请求的消息,否则程序终止,requests.codes.ok得到的成功的状态码200。下面是返回码和相应的查询条件:

# 信息性状态码
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')

高级用法

文件上传

requests可以模拟提交一些数据。上传前面我们下载下来的favicon.ico文件的示例如下:

import requests

files = {'file': open('favicon.ico', 'rb')}
r = requests.post("http://httpbin.org/post", files=files)
print(r.text)

运行结果如下:

{
  "args": {}, 
  "data": "", 
  "files": {
    "file": "data:application/octet-stream;...="
  }, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Connection": "close", 
    "Content-Length": "6665", 
    "Content-Type": "multipart/form-data; boundary=3fca310d8286eeb7bf62765bd3a97368", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.21.0"
  }, 
  "json": null, 
  "origin": "183.240.33.169", 
  "url": "http://httpbin.org/post"
}

file的内容省略,这个网站会返回响应,里面包含了files字段,而form字段是空的,这证明文件上传部分会单独有一个files字段来标识。

Cookies

  • 相较于urllib,requests库获取和设置cookies更简单。如下,获取Cookies:

    import requests
    
    r = requests.get("https://www.baidu.com")
    print(r.cookies)
    for key,value in r.cookies.items():
            print(key + '=' + value)
    

    运行结果如下:

    <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
    BDORZ=27315
    
  • 程序分析:首先调用cookies属性即可成功获得Cookies,可以发现它是一个RequestsCookieJar类型。然后用item()方法将其转化为元组组成的列表,遍历输出每一个Cookies的名称和值,实现Cookie的遍历分析。

  • 可以直接用Cookie来维持登录状态

    • 方法1:首先登录一个网站,将headers中的Cookie内容复制下来,在爬虫程序中设置到headers里面,然后发送请求即可。(这个较为简单,就不举例了。)
    • 方法2:通过cookies参数来设置,此方法需要构建RequestsCookieJar对象,而且需要分割一下cookies,相对来说繁琐。示例如下:
      import requests
      
      cookie = '这里是你登录的网站headers中的cookie内容'
      #新建一个RequestsCookieJar对象
      jar = requests.cookies.RequestsCookieJar()
      headers = {
      	'Host' : '这里是你访问的网站网址',
      	'User-Agent' : '浏览器头部'
      	}
      #将复制下来的cookies利用split()方法进行分割
      for cookie in cookies.split(';'):
      	key, value = cookie.split('=', 1)
      	#利用set()方法设置好每个Cookie的key和value
      	jar.set(key, value)
      #通过调用get方法将设置好的Cookie的key和value传递给cookies参数
      r = requests.get("http://****网站网址", cookies=jar, headers=headers)
      print(r.text)
      

会话维持

  • 在requests中,如果直接使用get()或post()等方法的确可以做到模拟网页的请求,但是这实际上是相当不同的会话,也就是说相当于你用了两个浏览器打开了不同的页面。假如如下场景:第一个请求利用post()方法登录了某个网站,第二次想获取成功登录后的自己的个人信息,用了一次get()方法去请求个人信息页面。实际上,这相当于打开两个浏览器,是两个完全不相关的会话,不能获取到个人信息。可以设置两次请求时的cookies一样,但是这样过于繁琐。

  • 解决这个问题的主要方法就是维持同一个会话,也就是相当于打开一个新的浏览器选项卡而不是一个新开的浏览器。不想每次都设置cookies,可以利用Session对象。示例如下:

    import requests
    
    #请求一个测试网址http://httpbin.org/cookies/set/number/123456789
    #请求这个网址时,可以设置一个cookie,名称叫做number,内容时123456789
    #后请求了http://httpbin.org/cookies,此网址可以获取当前的Cookies
    requests.get("http://httpbin.org/cookies/set/number/123456789")
    r = requests.get("http://httpbin.org/cookies")
    print(r.text)
    

    运行结果如下:

    {
      "cookies": {}
    }
    

    这样并不能如想象中那样获取到cookies,使用Session:

    import requests
    
    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"
      }
    }
    

    可见,能成功获取到Cookies。利用Session可以做到模拟同一个会话而不用担心Cookies的问题,它通常用于模拟登录成功之后再进行下一步的操作。

SSL证书验证

此外,requests还提供了证书验证的功能。当发送HTTP请求的时候,它会检查SSL证书,可以使用verify参数控制是否检查此证书。如果不加verify参数的话,默认是True,会自动验证。12306的证书没有被官方CA机构信任(貌似现在的证书是信任的),会出现证书验证错误的结果。

代理处理

  • 对于某些网站,在测试的时候请求几次,能正常获取内容。但是一旦开始大规模爬取,网站可能会弹出验证码,或者跳转到登录认证页面,更甚者可能会直接封禁客户端的IP,导致一定时间段内无法访问。为了防止这种情况发生,需要设置代理来解决这个问题,设置proxies参数。

    import requests
    
    proxies = {
    	'http': 'http://10.10.1.10:3128',
    	'https': 'http://10.10.1.10:1080',
    }
    
    requests.get("https://www.baiduc.com', proxies=proxies)
    

    直接运行这个程序是不行的,因为这个代理可能是无效的。要换成自己的有效代理。

  • 若代理需要使用HTTP Basic Auth,可以使用类似http://user:password@host:port这样的语法来设置代理,示例如下:

    import requests
    
    proxies = {
    	'http': 'http://user:password@10.10.1.10:3128/',
    }
    requests.get("https://www.baiduc.com', proxies=proxies)
    
  • 除了基本的HTTP代理外,requests还支持SOCKS协议的代理。安装socks这个库:pip3 install 'requests[socks]',示例如下:

    import requests
    	
    proxies = {
    	'http': 'sock5://user:password@host:port',
    	'https': 'sock5://user:password@host:port',
    	}
    requests.get("https://www.baiduc.com', proxies=proxies)
    

超时设置

在本机网络状况不好或者服务器网络响应太慢甚至无响应时,我们可能会等待特别久的时间才能收到响应,设置到最后收不到响应就报错。为了防止服务器不能及时响应,应该设置一个超时时间,即超过了这个时间还没有得到响应,那就报错。使用timeout参数,示例如下:

import requests

r = requests.get("https://www.baiduc.com', timeout=1)
print(r.status_code)

通过这样的方式,可以将超时时间设置为1秒,如果1秒没有响应,那就抛出异常。
实际上,请求分为两个阶段,即连接(connect)和读取(read)。上面设置的timeout将用作连接和读取二者的总和。如果要分别指定,就传入一个元组:r = requests.get("https://www.baiduc.com', timeout=(5,11,30))。如果想永久等待,可以将timeout设置为None,或者不设置直接留空,因为默认时None。这样的话,如果服务器还在运行,但是响应特别慢,那就慢慢等吧,它永远不会返回超时错误的。

身份认证

  • 在访问网站时,我们可能会遇到身份认证页面。此时可以使用requests自带的身份认证功能,示例如下:

    import requests
    from request.auth import HTTPBasicAuth
    
    r = requests.get("http://localhost:5000", auth=HTTPBasicAuth('username', 'password'))
    print(r.status_code)
    

    如果用户名和密码正确的话,请求就会自动认证成功,会返回200状态码;否则返回401状态码。

  • 如果参数都传入一个HTTPBasicAuth类,就显得有点繁琐了,更简单的写法:可以直接传入一个元组,它会默认使用HTTPBasicAuth这个类来认证。

    import requests
    	
    r = requests.get("http://localhost:5000", auth=('username', 'password'))
    print(r.status_code)
    
  • requests还提供其他认证方式,如OAuth认证,不过此时要安装oauth包,命令:pip3 install requests_oauthlib,示例如下:

    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)
    

Prepared Request

前面介绍urllib时,我们将请求表示为数据结构,其中各个参数都可以通过一个Request对象来表示。在requests中同样可以做到,这个数据结构叫做Prepared Request。示例如下:

在这里插入代码片from requests import Request,Session

url = 'http://httpbin.org/post'
data = {
    'name':'germey'
}
headers = {
    'User-Agent':'Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.9.168 Version/11.50'
}

s = Session()
#利用url、data、headers参数构造了一个Request对象
req = Request('POST',url,data=data,headers=headers)
#调用Session的prepare_request()方法将其转换为一个Prepared Request对象
prepped = s.prepare_request(req)
#调用send()方法发送
r = s.send(prepped)
print(r.text)

运行结果如下:

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "name": "germey"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Connection": "close", 
    "Content-Length": "11", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "Opera/9.80 (Windows NT 6.1; U; zh-cn) Presto/2.9.168 Version/11.50"
  }, 
  "json": null, 
  "origin": "183.240.33.169", 
  "url": "http://httpbin.org/post"
}

可以看到,达到了同样的POST请求效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值