目录
- 一、提升爬虫的速度
- 二、并发和并行
- 三、同步和异步
- 四、多线程爬虫
- 五、简单单线程爬虫
- 多线程
- 简单的多线程爬虫实例
- 使用Queue的多线程爬虫
- 多进程爬虫
- 使用multiprocessing的多进程爬虫
- 最后
一、提升爬虫的速度
爬虫可以从获取网页、解析网页、存储数据来实现一些基本的。现在记录一些进阶部分:提升爬虫速度,主要有3中方法:多线程爬虫、多进程爬虫、多协程爬虫。对比普通单线程爬虫,使用这3种方法爬虫的速度能成倍的提升。
二、并发和并行
并发是指在一段时间内发生的若干时间的情况
并行是值在同一时刻发生若干事件的情况
三、同步和异步
同步就是并发并行的各个任务不是独自运行的,任务之间有一定交替顺序,像接力赛一样。
异步就是并发和并行的各个任务独立运行互不干扰。每个任务都不在同一个赛道上面跑步的速度不受其他选手影响
四、多线程爬虫
多线程爬虫是以并发的方式执行的。也就是说,多线程并不能真正的同时执行,而是通过进程的快速切换加快网络爬虫的速度的。
在操作IO的时候使用多线程可以提升程序执行效率
五、简单单线程爬虫
link_list = []
with open(r'C:\Users\K1567\Desktop\alexa.txt', 'r') as file:file_list = file.readlines()for e in file_list:link = e.split('\t')[1]link = link.replace('\n', '')link_list.append(link)
stat = time.time()
for e in link_list:try:r = requests.get(e)print(r.status_code, e)except Exception as erro:print('Error:', erro)
end = time.time()
print('串行的总时长为:', end - stat)
多线程
python两种使用多线程的方法。
函数式:调用_thread模块中的start_new_thread()
类包装式:调用Threading库创建线程,从threading.thread继承。
# 为线程定义一个函数
def print_time(threadName, delay):
count = 0
while count < 3:time.sleep(delay)count += 1print(threadName, time.ctime())
# _thread.start_new_thread(print_time, ("Thread-1", 1))
# _thread.start_new_thread(print_time, ("Thread-2", 2))
# print("Main Finished")class myThread(threading.Thread):
def __init__(self, name, delay):threading.Thread.__init__(self)self.name = nameself.delay = delaydef run(self):print("Starting" + self.name)print_time(self.name, self.delay)print("Exiting" + self.name)def print_time(threadName, delay):counter = 0while counter < 3:time.sleep(delay)print(threadName, time.ctime())counter += 1
threads = []# 创建新线程
thread1 = myThread("Thread-1", 1)
thread2 = myThread("Thread-2", 2)# 开启新线程
thread1.start()
thread2.start()# 添加线程到线程列表
threads.append(thread1)
threads.append(thread2)# 等待所有线程完成
for t in threads:t.join()
print("Exiting Main Thread")
run():以表示线程活动的方法
start():启动线程活动
join([time]):组设调用线程直至线程的join()方法被调用为止
isAlive():返回线程是否是活动的
getNmae():返回线程名称
setName():设置线程名
上面代码中,thread1 = myThread(“Thread-1”, 1),然后在myThread这个类中对线程进行设置,使用run()表示线程运行方法当counter小于3时打印线程名称和时间。然后使用thread1.start()开启线程,使用threads.append(thread1)添加线程到线程列表中,用t.join()等待所有线程完成才会继续执行主线程。
简单的多线程爬虫实例
import threading
link_list = []
with open(r'C:\Users\K1567\Desktop\alexa.txt', 'r') as file:file_list = file.readlines()for e in file_list:link = e.split('\t')[1]link = link.replace('\n', '')link_list.append(link)
stat = time.time()class myThread(threading.Thread):def __init__(self, name, link_range):threading.Thread.__init__(self)self.name = nameself.link_range = link_rangedef run(self):print("Starting" + self.name)crawler(self.name, self.link_range)print("Exiting" + self.name)def crawler(threaName, link_range):for i in range(link_range[0], link_range[1] + 1):try:r = requests.get(link_list[i], timeout=20)print(threaName, r.status_code, link_list[i])except Exception as e:print(threaName, 'Error:', e)thread_list = []link_range_list = [(0, 200), (201, 400), (401, 600), (601, 800), (801, 1000)]# 创建
for i in range(1, 6):thread = myThread("Thread-" + str(i), link_range_list[i - 1])thread.start()thread_list.append(thread)
# 等待所有线程完成
for i in thread_list:i.join()
end = time.time()print('简单多线程爬虫的总时长为:', end - stat)
上面代码中,将1000个网页分成5份,然后利用for循环创建了5个线程,将这些网页分别指派到5个线程中运行
使用Queue的多线程爬虫
python的Queue模块提供了同步的、线程安全的队列类,包括FIFO(先进先出)队列、LIFO(后入先出)队列和优先级队列PriorityQueue。
例子:
开启五个线程然后通过队列的方式,把一千个网页平均分配给这五个线程
link_list = [] # 网页连接
with open(r'C:\Users\K1567\Desktop\alexa.txt', 'r') as file:file_list = file.readlines()for e in file_list:link = e.split('\t')[1]link = link.replace('\n', '')link_list.append(link)
# 开始时间
stat = time.time()
# 继承Thread类
class myThread(threading.Thread):def __init__(self, name, q):threading.Thread.__init__(self)self.name = nameself.q = qdef run(self):print("Starting" + self.name)while True:try:crawler(self.name, self.q)except Exception as e:breakprint("Exiting" + self.name)def crawler(threaName, q):# 获取队列中的链接url = q.get(timeout=2)try:r = requests.get(url, timeout=20)print(q.qsize(), threaName, r.status_code, url)except Exception as e:print(q.qsize(), threaName, url, 'Error', e)threadlist = ['Thread-1', 'Thread-2', 'Thread-3', 'Thread-4', 'Thread-5']
# 建立一个队列对象
workQueue = Queue.Queue(1000)
threads = []# 创建新线程
for tName in threadlist:thread = myThread(tName, workQueue)thread.start()threads.append(thread)# 填充队列
for url in link_list:workQueue.put(url) # 填充队列# 等待所有线程完成
for t in threads:t.join()end = time.time()
print('简单多线程爬虫的总时长为:', end - stat)
多进程爬虫
python的多线程爬虫只能运行在单核上,各个线程以并发的方式异步运行。由于GIL的存在,多线程并不能发挥多核CPU的资源。
作为提升python网络爬虫的速度的另外一种方法,多进程爬虫则可以利用CPU的多核,多进程就需要用到multiprocessing这个库。
使用multiprocess这个库有两种方法,一种是使用Process+queue的方法,另外一种是pool+queue的方法。
使用multiprocessing的多进程爬虫
当进程数大于cpu的内核数量时,等待运行的进程会等其他进程运行完让出内核。所以我们需要了解计算机的cpu核心数量。
查看当前电脑spu核
from multiprocessing import cpu_count
print(cpu_count())
多线程爬虫实例:
1.Process+queue的方法,在多进程中,每个进程都可以单独设置它的属性,如果将daemon设置为true,当父进程结束后,子进程就会自动终止。
from multiprocessing import Queue, Process
link_list = []
with open(r'C:\Users\K1567\Desktop\alexa.txt', 'r') as file:file_list = file.readlines()for e in file_list:link = e.split('\t')[1]link = link.replace('\n', '')link_list.append(link)
stat = time.time()# Process子进程
class MyProcess(Process):def __init__(self, q):Process.__init__(self)self.q = qdef run(self):print("Starting", self.pid)while not self.q.empty():crawler(self.q)print("Exiting" + str(self.pid))def crawler(q):url = q.get(timeout=2)try:r = requests.get(url, timeout=20)print(q.qsize(), r.status_code, url)except Exception as e:print(q.qsize(), url, 'Error', e)if __name__ == '__main__':workQueue = Queue(1000)# 填充队列for url in link_list:workQueue.put(url)for i in range(0, 5):p = MyProcess(workQueue)p.daemon = Truep.start()p.join()end = time.time()print('简单多进程爬虫的总时长为:', end - stat)
2.使用pool+queue的多进程爬虫:当被操作数目不大时,可以直接利用multiprocessing中的process动态生成多个进程,十几个还好,如果成百上千个目标,手动的限制进程数量就太繁琐,此时可以使用pool发挥进程池的功效。
最后
本文所有模块\环境\源码\教程皆可点击此处跳转免费领