看了上一篇文章的朋友们会发现虽然我们可以爬取整个相册,但是速度非常慢,需要一个一个来爬,排队等待的效率是极其低下的。针对这种大部分时间浪费在网络阻塞的情况,python的多线程方法在这里大显神威。那什么是多线程呢,很简单: 假如需要做的事情是搬砖,而电脑是搬砖工人,通常程序都是单线程运行。也就是说只有一个工人在搬砖。如果觉得这样速度很慢,你可以
多请几个工人
首先让我们引入python自带的多线程的库threading
import threading
改良我们的worker()
1 2 3 4 5 6 7 8 | def worker(): global girl_entry while girl_entry: lock.acquire() entry_base = girl_entry.pop(0) lock.release() pic_link_pool=pic_seeker(entry_base[0]) pic_downloader(pic_link_pool,entry_base[1]) |
和之前相比我们的worker
- 在第3行添加了循环
- 在第5,7行添加了锁的操作
循环
一个不能自发工作的worker不是好worker,所以我们将之前主函数的循环移动到worker里面使之能够获得下一个链接继续工作
锁的操作
由于我们将要使用多线程,为了保证girl_entry列表不会被弄乱,或产生不必要的错误(几个线程同时获得了一个entry_link)。我们必须对该全局变量的读写操作上锁来保证相同时间内只能有一个线程从该列表取得入口链接,与取得链接操作对应的是第5行:
entry_base = girl_entry.pop(0)
我们只需要在该操作的前后来进行锁的获取和释放即可。
创建一个多线程爬虫的类
1 2 3 4 5 6 7 8 9 10 | class Worker_thread(threading.Thread): def __init__(self,func,thread_id): super(Worker_thread,self).__init__() self.thread_id = thread_id self.func = func def run(self): print("线程%d启动"%self.thread_id) self.func() print("线程%d结束"%self.thread_id) |
- 我们创建了一个Worker_thread继承了threading.Thread里面定义的所有属性和方法,只需要添加你想要使该线程运行的内容即可。
- 在这里我们使用self.func来接受外部传入的函数作为该Worker_thread中运行的函数。在后面我们会将worker传入。
- self.thread_id是后外部传入的数字,用来表示线程名字,可以去掉
启动多线程
1 2 3 4 5 6 | def main(): threads = [Worker_thread(worker,i) for i in range(1,3)] for thread in threads: thread.start() for t in threads: t.join() |
- 第2行我们使用列表解析式生成了多个线程类对象(Worker_thread),将我们的worker作为对象传入
- 第3,4行我们使用 .start()方法启动每一条线程,注意我们使用的是start方法而不是run方法,但是我们实际上执行的是run
- 第5,6行表示,只有当子线程结束后,主线程才可以结束。使用.join()方法。因为默认我们的主线程结束后子线程会被放入后台继续运行的。