在写爬虫时,在数据量比较大时大家一定遇见过爬取速度不理想的问题吧。这次小编以上次的“360图片爬虫”,把它分别改写成了多进程,多线程,以及多线程 + 多进程的爬虫做了一个对比。上一篇链接如下:
Start
以上是没有加多进程,多线程的串行图片爬虫下载 1000 张图片所需要的时间。
多进程
大家先来看一看多进程是怎么创建的吧~
1.在父进程基础下创建一个进程:
"""
跨平台
提供了一个 Process 类来描述进程对象。创建子进程,只需要传入一个执行函数和函数的参数
,即可完成一个 Process 实例创建,用 start() 方法启动进程,用 join() 方法实现进程
间的同步
"""
import os
from multiprocessing import Process
#子进程要执行的代码
def run_proc(name):
print('Child process %s (%s) Running ...' % (name,os.getpid())) # os.getpid() 获取进程 id
if __name__ == '__main__':
print('Parent process %s.' % os.getpid())
for i in range(5):
p = Process(target=run_proc,args=(str(i),)) # 第一个参数为任务函数,第二个为传进去的任务参数
print('Process will start.')
p.start()
p.join()
print('Process end.')
2.创建进程池,默认是以计算机有几个 CPU 就创建几个:
"""
Pool 可以提供指定数量的进程供用户调用,默认大小是 CPU 的核数。但有新的请求提交到
Pool 中时,池还没有满,就会创建一个新的进程用来执行该请求;如已达到最大数,就会等待
知道有进程结束,才会创建新的进程来处理他
"""
from multiprocessing import Pool
import os,time,random
def run_task(name):
print('Task %s (pid = %s) is running ...' % (name,os.getpid()))
time.sleep(random.random() * 3)
print('Task %s end.' % name)
if __name__ == '__main__':
print('Current process %s.' % os.getpid())
p = Pool(processes=3)
for i in range(5):
p.apply_async(run_task,args=(i,)) #添加进程任务,i 为传进去的进程任务的参数
pass
print('Waiting for all subprocesses done...')
p.close() #不再添加新进程
p.join() #等待所有子进程执行完毕,调用之前必须先调用 close(),针对 Pool 对象
print('All subprocesses done.')
以上就是多进程的创建,小编得爬虫以这样的基础修改后的运行时间提升了一般如下:
多线程
大家先来看一看多线程是怎么创建的吧~
方法一:
"""
把一个函数传入并创建 Tread 实例
"""
import random
import time,threading
#新线程执行的代码
def thread_run(urls):
print('Current %s is running...' % threading.current_thread().name)
for url in urls:
print('%s --->>> %s' % (threading.current_thread().name,url))
time.sleep(random.random())
print('%s ended.' % threading.current_thread().name)
print('%s is runnung...' % threading.current_thread().name)
t1 = threading.Thread(target=thread_run,name='Thread_1',args=(['url_1',
'url_2','url_3'],))
t2 = threading.Thread(target=thread_run,name='Thread_2',args=(['url_4',
'url_5','url_6'],))
t1.start()
t2.start()
t1.join()
t2.join()
print('%s ended.' % threading.current_thread().name)
方法二:
"""
从 treading.Thread 继承创建线程类
"""
import random
import threading
import time
class myThreading(threading.Thread):
def __init__(self,name,urls):
threading.Thread.__init__(self,name=name) #初始化线程
self.urls = urls
def run(self):
print('Current %s is running...' % threading.current_thread().name)
for url in self.urls:
print('%s --->>> %s' % (threading.current_thread().name,url))
time.sleep(random.random())
print('%s ended.' % threading.current_thread().name,url)
print('%s is running...' % threading.current_thread().name)
t1 = myThreading(name='Tread_1',urls=['url_1','url_2','url_3'])
t2 = myThreading(name='Tread_2',urls=['url_4','url_5','url_6'])
t1.start()
t2.start()
t1.join()
t2.join()
print('%s ended.' % threading.current_thread().name)
以上是多线程的两种创建方法,小编的爬虫以此为基础修改后,有一个疑惑,速度并没有提升太多,还比串行慢了:
多进程 + 多线程
小编把串行爬虫改成了多进程 + 多线程,发现,比只用多进程快了 2 秒,速度又提升了一点。
这里多进程是用在请求网页,而多线程是用在了图片下载。
End
多进程:
用多进程速度会有显著提升,多进程一般用于计算密集型(for 循环这些),在爬虫中,一般用于请求的 url 列表。
多线程:
单独用多线程用于爬虫速度没有太大的改变,多线程一般用于 I/O 密集型(文件读写这些),在爬虫中一般有读写文件,下载图片,视频音乐。
多进程 + 多线程:
两者结合在适合的地方使用,速度会有所改变,不是任何程序都一定用,要对症下药嘛。小编这个爬虫两者结合,速度虽然只提升了 2 秒,但也是有所改变。