在使用爬虫爬取数据的时候,当需要爬取的数据量比较大,且急需很快获取到数据的时候,可以考虑将单线程的爬虫写成多线程的爬虫。下面来学习一些它的基础知识和代码编写方法。
一、进程和线程
进程可以理解为是正在运行的程序的实例。进程是拥有资源的独立单位,而线程不是独立的单位。由于每一次调度进程的开销比较大,为此才引入的线程。一个进程可以拥有多个线程,一个进程中可以同时存在多个线程,这些线程共享该进程的资源,线程的切换消耗是很小的。因此在操作系统中引入进程的目的是更好地使多道程序并发执行,提高资源利用率和系统吞吐量;而引入线程的目的则是减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。
下面用简单的例子进行描述,打开本地计算机的”任务管理器”如图1所示,这些正在运行的程序叫作进程。如果将一个进程比喻成一个工作,指定10个人来做这份工作,这10个人就是10个线程。因此,在一定的范围内,多线程效率比单线程效率更高。

图1.任务管理器
二、Python中的多线程与单线程
在我们平时学习的过程中,使用的主要是单线程爬虫。一般来说,如果爬取的资源不是特别大,使用单线程即可。在Python中,默认情况下是单线程的,简单理解为:代码是按顺序依次运行的,比如先运行第一行代码,再运行第二行,依次类推。在前面章节所学习知识中,都是以单线程的形式实践的。
举个例子,批量下载某网站的图片,由于下载图片是一个耗时的操作,如果依然采用单线程的方式下载,那么效率就会特别低,意味着需要消耗更多的时间等待下载。为了节约时间,这时候我们就可以考虑使用多线程的方式来下载图片。
threading模块是Python中专门用来做多线程编程的模块,它对thread进行了封装,使用更加方便。例如需要对写代码和玩游戏两个事件使用多线程进行,案例代码如下。
import threading``import time``# 定义第一个``def coding(): for x in range(3): print('%s正在写代码\n' % x) time.sleep(1)``# 定义第二个``def playing(): for x in range(3): print('%s正在玩游戏\n' % x) time.sleep(1)``# 如果使用多线程执行``def multi_thread(): start = time.time() # Thread创建第一个线程,target参数为函数命 t1 = threading.Thread(target=coding) t1.start() # 启动线程 # 创建第二个线程 t2 = threading.Thread(target=playing) t2.start() # join是确保thread子线程执行完毕后才能执行下一个线程 t1.join() t2.join() end = time.time() running_time = end - start print('总共运行时间 : %.5f 秒' % running_time)``# 执行``if __name__ == '__main__': multi_thread() # 执行单线程
运行结果如图2所示。

图2.多线程运行结果
那么执行单线程会消耗多少时间,案例代码如下所示。
import time``# 定义第一个``def coding(): for x in range(3): print('%s正在写代码\n' % x) time.sleep(1)``# 定义第二个``def playing(): start = time.time() for x in range(3): print('%s正在玩游戏\n' % x) time.sleep(1) end = time.time() running_time = end - start print('总共运行时间 : %.5f 秒' % running_time)``def single_thread(): coding() playing()``# 执行``if __name__ == '__main__': single_thread() # 执行单线程
运行结果如图3所示。

图3.单线程运行结果
经过以上多线程和单线程的运行结果,可以看出多线程中写代码和玩游戏是一起执行的,单线程中则是先写代码再玩游戏。从时间上来说,可能只有细微的差距,当执行工作量很大的时候,便会发现多线程消耗的时间会更少,从这个案例中我们也可以知道,当所需要执行的任务并不多的时候,只需要编写单线程即可。
三、单线程改为多线程
以某直播的图片爬取为例,案例代码如下。
import requests``from lxml import etree``import time``import os`` ``dirpath = '图片/'``if not os.path.exists(dirpath): os.mkdir(dirpath) # 创建文件夹`` ``header = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36'``}``def get_photo(): url = 'https://www.huya.com/g/4079/' # 目标网站 response = requests.get(url=url, headers=header) # 发送请求 data = etree.HTML(response.text) # 转化为html格式 return data`` ``def jiexi(): data = get_photo() image_url = data.xpath('//a//img//@data-original') image_name = data.xpath('//a//img[@class="pic"]//@alt') for ur, name in zip(image_url, image_name): url = ur.replace('?imageview/4/0/w/338/h/190/blur/1', '') title = name + '.jpg' response = requests.get(url=url, headers=header) # 在此发送新的请求 with open(dirpath + title, 'wb') as f: f.write(response.content) print("下载成功" + name) time.sleep(2)`` ``if __name__ == '__main__': jiexi()
如果需要修改为多线程爬虫,只需要修改主函数即可,例如创建4个线程进行爬取,案例代码如下所示。
if __name__ == "__main__": threads = [] start = time.time() # 创建四个进程 for i in range(1, 5): thread = threading.Thread(target=jiexi(), args=(i,)) threads.append(thread) thread.start() for thread in threads: thread.join() end = time.time() running_time = end - start print('总共消耗时间 : %.5f 秒' % running_time) print("全部完成!") # 主程序``
学习资源推荐
如果你也喜欢编程,想通过学习Python获取更高薪资,这里给大家分享一份Python学习资料。
😝朋友们如果有需要的话,可以V扫描下方二维码免费领取🆓
学好 Python 不论是就业还是做副业赚钱都不错,但要学会 Python 还是要有一个学习规划。最后大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!
#### **一、Python学习路线**


二、Python基础学习
1. 开发工具

2. 学习笔记

3. 学习视频

三、Python小白必备手册

四、数据分析全套资源

五、Python面试集锦
1. 面试资料


2. 简历模板

** 因篇幅有限,仅展示部分资料,添加上方即可获取**
本文介绍了在大数据量爬取场景下,如何通过将单线程爬虫转换为多线程,利用Python的threading模块提高效率。通过进程和线程概念,对比了单线程与多线程的执行方式,以及在直播图片爬取案例中实现多线程的方法。
682

被折叠的 条评论
为什么被折叠?



