前言
在使用爬虫爬取数据的时候,当需要爬取的数据量比较大,且急需很快获取到数据的时候,可以考虑将单线程的爬虫写成多线程的爬虫。下面来学习一些它的基础知识和代码编写方法。
一、进程和线程
进程可以理解为是正在运行的程序的实例。进程是拥有资源的独立单位,而线程不是独立的单位。由于每一次调度进程的开销比较大,为此才引入的线程。一个进程可以拥有多个线程,一个进程中可以同时存在多个线程,这些线程共享该进程的资源,线程的切换消耗是很小的。因此在操作系统中引入进程的目的是更好地使多道程序并发执行,提高资源利用率和系统吞吐量;而引入线程的目的则是减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。
下面用简单的例子进行描述,打开本地计算机的”任务管理器”如图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网络爬虫入门到实战
本书介绍了Python3网络爬虫的常见技术。首先介绍了网页的基础知识,然后介绍了urllib、Requests请求库以及XPath、Beautiful Soup等解析库,接着介绍了selenium对动态网站的爬取和Scrapy爬虫框架,最后介绍了Linux基础,便于读者自主部署编写好的爬虫脚本。本书所有代码和相关素材可以到GitHub下载获取,地址为https://github.com/sfvsfv/Crawer。
本书主要面向对网络爬虫感兴趣的初学者。。
本书内容
本书通过简单易懂的案例,讲解Python语言的爬虫技术。全书共分为8章,第1章为网页的内容,第2~7章为爬虫的内容,第8章为Linux基础。
第1章:介绍了HTML和CSS的基础知识,虽然本章并不是直接与爬虫相关,但它是学习爬虫技术的基础。对于已经掌握基本网页基础的读者,可以选择跳过该章。
第2章:正式进入爬虫技术的学习阶段,这一章介绍了最基本的两个请求库(urllib和Requests),有知识点的讲解,也有实战案例的讲解。
第3章:本章对正则表达式做了详细的描述,同时有案例的实践。学完本章就可以掌握最基本的爬虫技术了。
第4章:主要介绍XPath解析库,配有实际的案例进行讲解,以帮助读者加深理解和巩固。
第5章:主要介绍另一个解析库Beautiful Soup,它在提取数据中也很方便,对相关知识点以及实际的案例都有所讲解。XPath和Beautiful Soup可以使信息的提取更加方便、快捷,是爬虫必备利器。
第6章:主要介绍selenium自动化测试。现在越来越多的网站内容是经过 JavaScript 渲染得到的,而原始 HTML 文本可能不包含任何有效内容,使用模块selenium实现模拟浏览器进行数据爬取是非常好的选择。
第7章:在大规模数据的爬取中,不太用得上基础模块,Scrapy 是目前使用最广泛的爬虫框架之一,本章介绍了Scrapy爬虫框架的详细搭建和实践。针对数据存储过程部分使用的MySql数据库,整章有多个实际的案例,以帮助读者加深理解和巩固。
第8章:主要介绍了Linux的基础知识点,以帮助读者能够在服务器部署脚本。
相关资源
本书所有代码和相关素材可以到GitHub下载获取,地址为https://github.com/sfvsfv/Crawer。关于代码的实用性需要声明:所有代码都是笔者在写书阶段编写的,如果有部分爬虫脚本失效,有可能是网站的结构发生了变化。希望读者在阅读本书的过程中,以学习笔者所介绍的方法为主。
Python网络爬虫入门到实战致谢
本书的撰写与出版得益于同行众多同类教程的启发,以及陈姗姗老师和同伴周培源的帮助,在此深表感谢。同时也感谢一路走来支持笔者的读者。由于本人水平有限,书中难免有不妥之处,诚挚期盼专家和广大读者批评指正。