发送请求 —— urllib.request

发送请求 - urllib.request

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

urlopen()

urlopen() 的基本使用

1
2
3
4
5
6
7
import urllib.request

response = urllib.request.urlopen('https://www.baidu.com') # 报错
print(response)
print(type(response))
print(response.read())
print(response.read().decode('utf-8'))

报错:urllib.error.URLError:

解决:Python 坑之CERTIFICATE_VERIFY_FAILED

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import urllib.request

response = urllib.request.urlopen('http://www.baidu.com/')
# 完成最基本的简单网页的GET请求
# 当然你也可以直接访问http://www.baidu.com,不带有ssl证书验证的http
# 同时你访问带有ssl证书验证的网址时,有可能会拿不到html
print(response)
print(type(response)) # <class 'http.client.HTTPResponse'>

response = response.read()
print(response)
print(type(response)) # <class 'bytes'>

response = response.decode('utf-8')
print(response)
print(type(response)) # <class 'str'>

with open('baidu.html','w',encoding='utf-8') as f:
f.write(response)
  • class ‘http.client.HTTPResponse’
    • 是一个HTTPResponse类型的对象
    • 包含read()、readinto()、getheader(name)、getheaders()、fileno()等方法
    • 包含msg、version、status、reason、debuglevel、closed等属性

urlopen()函数的API

1
2
3
urllib.request.urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
*, cafile=None, capath=None, cadefault=False,
context=None)
  • data

    • data=bytes(data),data接收的内容必须是bytes类型
    • 一旦使用这个参数,请求方式变为POST

      1
      2
      3
      4
      5
      6
      import urllib.request
      import urllib.parse

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

    • timeout参数用于设置超时时间,单位为秒,请求超过这个设置时间,还没有得到响应,就会抛出异常。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      import urllib.request

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

      '''
      运行结果:
      urllib.error.URLError: <urlopen error timed out>
      错误原因超时
      设置为超时时间为0.1秒,0.1秒过后,程序无响应,于是抛出URLError异常
      URLError属于urllib.error模块,
      '''
    • 设置超时时间来控制一个网页如果长时间未响应,就跳过它的抓取。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      import urllib.request
      import urllib.error
      import socket

      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')

      '''
      判断异常是不是socket.timeout类型,就是超时异常,从而确定它确实是因为超时而报错
      isinstance(object,classinfo):
      判断一个对象是否是一个已知的类型
      object == classinfo

      isinstance 和 type() 的区别:
      type()不能判断子类的实例化对象是不是属于父类

      推荐使用isinstance()
      '''
    • context:必须是ssl.SSLContext类型,用来指定SSL设置

    • cafile:指定CA证书
    • capth:指定CA证书的路径

      Request()

      使用urlopen()方法可以实现最基本的请求的发起,在之前的学习中, 就已经发现,它有一定的限制条件,并且可以从它的API接口中发现,参数也十分的简单,不足以构建一个完整的请求,那么Request类就能构建一个完整的需求。

      Request()的基本使用

      1
      2
      3
      4
      5
      6
      7
      import urllib.request

      # 将请求独立成一个对象,这样可以丰富、灵活的配置参数
      request = urllib.request.Request('http://httpbin.org/get')
      # 依然使用urlopen来发送请求
      response = urllib.request.urlopen(request)
      print(response.read().decode('utf-8'))

      Request()函数的API

      1
      2
      3
      urllib.request.Request(url, data=None, headers={},
      origin_req_host=None, unverifiable=False,
      method=None)
    • url:

      • 请求url,必传参数,其他都是可选
    • data:
      • 和urllib.request.urlopen()中一样,必须传bytes类型
      • 如果传的类型是字典,可以先用urllib.parse.urlencode()编码
    • headers:
      • 是字典类型,请求头
      • 可以在构造请求时通过headers参数直接构造
      • 另一种方式通过调用add_headers()方法添加,动态添加
    • origin_req_host:
      • 请求方的host名称或者ip地址
    • unverifiable:
      • 表示这个请求是否是无法验证的,默认为False,意思就是用户没有足够的权限来选择接受这个请求的结果
    • method:

      • 用来指示请求使用的方法,比如GET、POST、PUT等
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      from urllib import request,parse

      # url
      url = 'http://httpbin.org/post'
      # 请求头
      headers = {
      'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'
      }
      # post请求数据
      data = {
      'name':'David'
      }
      # 传的类型是字典,所以要先用urllib.parse.urlencode()编码,指定编码格式为utf-8
      data = bytes(parse.urlencode(data),encoding='utf-8')
      # 构建POST请求
      request = request.Request(url=url,data=data,headers=headers,method='POST')
      response = request.urlopen(request)
      # bytes用utf-8解码
      print(response.read().decode('utf-8'))

      '''
      返回结果:
      {
      "args": {},
      "data": "",
      "files": {},
      "form": {
      "name": "David" # data传入的数据出现在form中,只有post请求才有
      },
      "headers": {
      "Accept-Encoding": "identity",
      "Content-Length": "10",
      "Content-Type": "application/x-www-form-urlencoded",
      "Host": "httpbin.org",
      "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
      },
      "json": null,
      "origin": "61.171.88.179, 61.171.88.179",
      "url": "https://httpbin.org/post"
      }
      '''

      Handler

      Handler中有各种处理器,例如专门处理登陆验证,处理cookies的,处理代理设置等,利用这些我们可以做到HTTP请求中的所有事情。

      BaseHandler类

      BaseHandler类是,所有其他Handler的父类,它提供了最基本的方法,例如:default_open()、protocol_request()等

    • HTTPDefaultErrorHandler: 用于处理http响应错误,错误抛出HTTPError类型的异常

    • HTTPRedirectHandler:用于处理重定向
    • HTTPCookieProcessor:用于处理Cookies
    • ProxyHandler:用于设置代理,默认代理为空
    • HTTPPasswordMgr:用于管理密码,它维护用户名和密码表
    • HTTPBasicAuthHandler:用于管理认证,当链接需要认证时,它可以解决认证问题

      OpenerDirector类

      通常称OpenerDirector为Opener,之前的urlopen()和Request就是urllib为我们封装了极其常用的请求方法,更多功能需要用到底层的实例来完成操作,所以要用到Opener。

      验证

      请求网站时,弹出提示框,提示你需要输入用户名和密码,验证后才能查看页面

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      from urllib.request import HTTPPasswordMgrWithDefaultRealm,HTTPBasicAuthHandler,build_opener
      from urllib.error import URLError

      username = 'username'
      password = 'password'
      url = 'http://0.0.0.0/#/Auth/get_basic_auth__user___passwd_'

      p = HTTPPasswordMgrWithDefaultRealm()
      # 利用add_password添加进去用户名和密码
      p.add_password(None,url,username,password)
      # 建立一个处理验证的Handler
      # 实例化HTTPBasicAuthHandler对象,其参数是HTTPPasswordMgrWithDefaultRealm对象
      auth_handler = HTTPBasicAuthHandler(p)
      # 利用Handler使用build_opener()方法构建一个Opener
      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)

      代理

      爬虫免费代理

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      from urllib.request import ProxyHandler,build_opener
      from urllib.error import URLError

      proxy_handler = ProxyHandler({
      'http':'http://0.0.0.0:9999',
      # 可以去西刺copy两个注意区分http和https,当然还有socks服务器
      'https':'https://0.0.0.0:9999'
      })
      opener = build_opener(proxy_handler)
      try:
      response = opener.open('http://www.baidu.com')
      print(response.read().decode('utf-8'))
      except URLError as e:
      print(e.reason)

      爬虫付费代理

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      # 方式一
      import urllib.request

      #1.用户名密码和代理
      username = 'username'
      password = 'password'
      proxy = '127.0.0.1:8080'
      #2.创建密码管理器,添加用户名和密码
      password_manager = urllib.request.HTTPPasswordMgrWithDefaultRealm()
      password_manager.add_password(None,proxy_money,use_name,pwd)
      #3.创建可以验证代理ip的处理器
      handle_auth_proxy = urllib.request.ProxyBasicAuthHandler(password_manager)
      #4.根据处理器创建opener
      opener_auth = urllib.request.build_opener(handle_auth_proxy)
      #5.发送请求
      response = opener_auth.open("http://www.baidu.com")
      print(response.read())
      1
      2
      3
      4
      5
      6
      7
      8
      9
      # 方式二
      #1.代理ip
      money_proxy ={"http":"username:password@192.168.12.11:8080"}
      #2.代理的处理器
      proxy_handler=urllib.request.ProxyHandler(money_proxy)
      #3.通过处理器创建opener
      opener = urllib.request.build_opener(proxy_handler)
      #4.open发送请求
      opener.open("http://www.baidu.com")

      Cookies

      获取网站cookies

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      import http.cookiejar
      import urllib.request

      # 声明一个CookieJar对象
      cookies = http.cookiejar.CookieJar()
      # 利用HTTPCookieProcessor构建一个handler
      handler = urllib.request.HTTPCookieProcessor(cookies)
      # 构建opener
      opener = urllib.request.build_opener(handler)
      response = opener.open('http://www.baidu.com')
      print(type(cookies))
      for cookie in cookies:
      print(cookie.name + '=' + cookie.value)

      将cookies保存为文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      import urllib.request
      import http.cookiejar

      # 保存cookies的文件名
      filename = 'cookies.txt'
      # 生成文件时,需要用到MozillaCookieJar 是 CookieJar的子类,处理和cookies和文件有关的事件
      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)

      从文件中读取cookies

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      import urllib.request
      import http.cookiejar

      # 什么格式存的 就选什么格式
      cookie = http.cookiejar.MozillaCookieJar()
      # 用load()方法读取本地的cookies文件,获取cookies内容
      cookie.load('cookies.txt', ignore_expires=True, ignore_discard=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'))