一、提升爬虫的速度

爬虫可以从获取网页、解析网页、存储数据来实现一些基本的。现在记录一些进阶部分:提升爬虫速度,主要有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 += 1
    print(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 = name
    self.delay = delay
 
def run(self):
    print("Starting" + self.name)
    print_time(self.name, self.delay)
    print("Exiting" + self.name)
 
def print_time(threadName, delay):
    counter = 0
    while 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 = name
		self.link_range = link_range
 
	def 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 = name
		self.q = q
 
	def run(self):
		print("Starting" + self.name)
		while True:
			try:
				crawler(self.name, self.q)
			except Exception as e:
				break
		print("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 = q
 
	def 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 = True
		p.start()
		p.join()
	end = time.time()
	print('简单多进程爬虫的总时长为:', end - stat)

2.使用pool+queue的多进程爬虫:当被操作数目不大时,可以直接利用multiprocessing中的process动态生成多个进程,十几个还好,如果成百上千个目标,手动的限制进程数量就太繁琐,此时可以使用pool发挥进程池的功效。

最后

👇👇👇课件源码、资料、素材、解答、皆点击下方获取呀👇👇👇

本文所有模块\环境\源码\教程皆可点击此处跳转免费领

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐