requests模块的介绍

  • requests的作用

    通过python来模拟请求网址

  • 一个模拟请求由以下四个部分组成

    • url
    • method
    • body
    • headers
  • 模拟请求百度

     没有安装requests库的同学, 在当前python环境下执行以下语句安装第三方库
     pip install requests

    install_requests.png

    import requests
    
    
    def request_baidu():
        url = "https://www.baidu.com/"
        # body = ""
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"
        }
        response = requests.get(url=url, headers=headers)
        print(response.text)

理解多线程和多进程

  • 什么是进程?什么是线程?

    • 进程: 可以简单地认为是一个程序. 进程是操作系统分配资源的最小单位.
    • 线程: 一个进程可以有多个线程, 每个线程可以独立完成一些任务. 线程是操作系统进行运算调度的最小单位.
  • 多线程demo

    from threading import Thread    
    for i in range(10):
        # 只是创建了线程对象
        t = Thread(target=request_baidu)
        # 启动线程
        t.start()
  • 多进程demo

    from multiprocessing import Process   
    for i in range(10):
        # 只是创建了进程对象
        p = Process(target=request_baidu)
        # 启动进程
        p.start()
  • 多线程

    • 等待任务完成后回到主进程

      通过调用Thread对象的join方法

      # 保存当前thread对象
      thread_array = []
      for i in range(10):
          t = Thread(target=request_baidu, args=(i, ))
          thread_array.append(t)
          t.start()
      # 调用thread对象join接口, 等待任务完成后回到主进程
      for t in thread_array:
          t.join()
      print("done!")
    • 如何拿到返回结果

      • 赋值到全局变量当中, 添加到可变对象之中

        result = []
        def request_baidu(index):
            ...
            result.append(response)
            
        if __name__ == "__main__":
            thread_array = []
            for i in range(10):
                t = Thread(target=request_baidu, args=(i, ))
                thread_array.append(t)
                t.start()
            for t in thread_array:
                t.join()
            print("done!")
            print(result)
  • 多进程

    • 等待任务完成后回到主进程

      通过调用Process对象的join方法

    • 如何拿到返回结果

      无法通过全局变量存储返回结果.

      多进程相当于启动了多个程序, 共同执行了同一份代码, 他们之间的内存地址完全不一样

      import requests
      import time
      from threading import Thread
      from multiprocessing import Process
      
      result = []
      print(f"主进程result内存地址: {id(result)}")
      
      def request_baidu(index):
          time.sleep(2)
          url = "https://www.baidu.com/"
          # body = ""
          headers = {
              "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"
          }
          response = requests.get(url=url, headers=headers)
          print(f"当前请求序号: {index}, 返回结果状态码: {response.status_code}")
          print(f"子进程result内存地址: {id(result)}")
          result.append(response)
      
      # 如果没有判断入口代码段if __name__ == "__main__", 多进程程序会报错
      # 原因是windows和pycharm的进程阻塞带来的问题
      if __name__ == "__main__":
          process_array = []
          for i in range(10):
              p = Process(target=request_baidu, args=(i, ))
              process_array.append(p)
              p.start()
          for p in process_array:
              p.join()
          print("done!")
          print(result)
  • 多进程和多线程的异同点

    • 相同点

      • 都是对cpu工作时间段的描述, 只是颗粒度不同.

        简单地说就是多进程和多线程都会调用cpu资源的, 但是进程可以启动多个线程去执行.

      • linux内核态不区分进程和线程
    • 不同点

      • 进程有自己的独立地址空间, 建立数据表来维护代码段, 堆栈段和数据段, 而线程共享进程中的资源, 使用相同的地址空间, 所以线程间的切换快得多.
      • 因为线程共享进程的全局变量, 静态变量等对象, 线程间的通信更为方便, 而进程间的通信更加复杂, 需要以ipc的方式进行.
      • 多进程要比多线程要健壮. 进程之间一般不会相互影响, 而多线程有一条线程崩溃, 会导致整个进程跟着发生崩溃或者无法正常退出等.

全局解释器锁(GIL)

  • 计算密集型

    主要占用cpu资源

  • IO密集型

    IO就是input output, 需要等待的一些任务

    • 网络请求会有网络延迟
    • 和数据库交互需要等待数据库查询事件
    • 读写硬盘
  • 多进程在处理计算密集型程序的时候比多线程块

    由于全局解释器锁的存在, 一个进程下, 只允许一个线程执行Python程序的字节码(当前代码文件的二进制表示).

    简单地说, 创建的10个线程其实在争夺一个cpu资源. 但是遇到io操作会让渡cpu资源.

  • 如何绕过GIL?

    • 将多线程方法改为多进程
    • 将计算密集型任务转移给C扩展.
    • 分布式计算引擎spark, Apache
    • 使用PyPy解释器, 工业上几乎没人这么用, 因为PyPy并不成熟.
Last modification:September 5, 2022
如果觉得我的文章对你有用,请随意赞赏