python爬虫笔记(第四章)

前言

01 概述

到⽬前为⽌, 我们可以解决爬⾍的基本抓取流程了. 但是抓取效率还是不够⾼. 如何提⾼抓取效率呢? 我们可以选择多线程, 多进程, 协程等操作完成异步爬⾍. 我们⽬前写的爬⾍可以理解为单线程, ⽐喻为单⻋道公路.如何提⾼效率呢? 很简单, 搞成多⻋道就OK了啊. 异步爬⾍你就可以理解为多⻋道同时进⾏爬取. 本章涉及内容:

  1. 快速学会多线程
  2. 快速学会多进程
  3. 线程池和进程池
  4. 扒光新发地
  5. 协程
  6. 多任务异步协程实现
  7. aiohttp模块详解
  8. 扒光⼀本⼩说
  9. 综合训练-抓取⼀部电影

02 多线程

一个进程里至少有一个线程,可以有多个线程。进程是资源单位, 每一个进程至少要有一个线程。线程是执行单位。启动每一个程序默认都会有一个主线程。

  1. 单线程的效果:
# 单线程
def func():
    for i in range(1000):
        print("func", i)


if __name__ == __main__:
    func()
    for i in range(1000):
        print("main", i)

执⾏过程: 程序启动 --> 加载func() --> 执⾏main --> 调⽤func() -->func执⾏完毕, 继续执⾏main中的内容 整个过程是⼀条线跑下来的, 这就是单线程.

  1. 多线程 程序效果: main和func交替执⾏(如果速度够快, 给我们的感觉就是⼀起执⾏) 执⾏过程: 加载func() -> 执⾏main -> 创建⼦线程t -> ⼦线程t启动 ->执⾏func中的内容 |-> 继续执⾏main
# 多线程写法1
from threading import Thread
#
# def func():
#     for i in range(1000):
#         print("func", i)
#
# if __name__ ==__main__:
#     t = Thread(target=func)# 创建线程并给线程安排任务
#     t.start() #多线程状态为可以开始工作状态, 具体的执行时间由CPU决定
#
#     for i in range(1000):
#         print("main", i)

#多线程写法2
class MyThread(Thread):
    def run(self): # 固定的    -> 当线程被执行的时候, 被执行的就是run()
        for i in range(1000):
            print("子", i)

if __name__ == __main__:
    t = MyThread()
    # t.run #方法的调用了,变成单线程了
    t.start()
    for i in range(1000):
        print("主线程", i)

03 多进程

通过主程序去创建多个进程来完成并⾏的效果

from multiprocessing import Process
from threading import Thread

# def func():
#     for i in range(1000):
#         print("子进程", i)
#
# if __name__ == __main__:
#     p = Process(target=func)
#     p.start()
#     for i in range(1000):
#         print("Z主线程", i)

def func(name):
    for i in range(1000):
        print(name, i)

if __name__ == __main__:
    t1 = Thread(target=func, args=("周杰伦",))# 传递参数必须是元组
    t1.start()
    t2 = Thread(target=func, args=("王力宏",))
    t2.start()

04 线程池和进程池

线程池: 一次性开辟一些线程. 我们用户直接给线程池子提交任务. 线程任务的调度交给线程池来完成

from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
# ThreadPoolExecutor线程池,ProcessPoolExecutor进程池
def fn(name):
    for i in range(1000):
        print(name, i)

if __name__ == __main__:
    # 创建线程池
    with ThreadPoolExecutor(50) as t:
        for i in range(100):
            t.submit(fn, name=f"线程{i}")
    # with外的东西等待线程池中的任务全部执行完毕,才继续执行(守护)
    print("123")

05线程池案例,抓取新发地菜价

经验分享 程序员 微信小程序 职场和发展