Python爬虫中,网络获取数据快而本地写入慢,应该使用什么数据结构来缓冲?
面试遇到的问题
Python爬虫中,网络获取数据快而本地写入慢,应该使用什么数据结构来缓冲?
丢缓存慢慢写
用队列(queue.Queue)就行,这是典型的生产者-消费者模型。
爬虫线程(生产者)快速抓取数据后,直接扔进队列,然后立刻去抓下一个。另一个专门的写入线程(消费者)从队列里慢慢取数据,按自己的节奏写入本地文件或数据库。这样网络请求就不会被慢速的I/O操作阻塞。
下面是个简单示例,用了标准库的queue和threading:
import queue
import threading
import time
import requests
from bs4 import BeautifulSoup
class BufferedCrawler:
def __init__(self, buffer_size=100):
self.data_queue = queue.Queue(maxsize=buffer_size)
self.stop_flag = False
def fetch_data(self, url):
"""生产者:模拟快速抓取数据"""
try:
# 模拟网络请求
# response = requests.get(url)
# soup = BeautifulSoup(response.content, 'html.parser')
# data = soup.title.string if soup.title else 'No Title'
time.sleep(0.1) # 模拟网络延迟
data = f"Data from {url}"
return data
except Exception as e:
return f"Error fetching {url}: {e}"
def producer(self, url_list):
"""生产者线程函数"""
for url in url_list:
if self.stop_flag:
break
data = self.fetch_data(url)
# 如果队列满,这里会阻塞,直到消费者取走数据
self.data_queue.put(data)
print(f"Produced: {data}")
# 发送结束信号
self.data_queue.put(None)
def consumer(self, output_file):
"""消费者线程函数:慢速写入"""
with open(output_file, 'w', encoding='utf-8') as f:
while True:
# 如果队列空,这里会阻塞,直到生产者放入数据
item = self.data_queue.get()
if item is None: # 结束信号
self.data_queue.task_done()
break
# 模拟慢速写入
time.sleep(0.5)
f.write(item + '\n')
f.flush()
print(f"Consumed: {item}")
self.data_queue.task_done()
def run(self, url_list, output_file):
"""启动爬虫"""
# 启动生产者线程
producer_thread = threading.Thread(target=self.producer, args=(url_list,))
# 启动消费者线程
consumer_thread = threading.Thread(target=self.consumer, args=(output_file,))
producer_thread.start()
consumer_thread.start()
producer_thread.join()
consumer_thread.join()
if __name__ == "__main__":
# 示例URL列表
urls = [f"http://example.com/page{i}" for i in range(10)]
crawler = BufferedCrawler(buffer_size=5)
crawler.run(urls, "output.txt")
关键点:
queue.Queue是线程安全的,不用自己加锁。- 通过设置
maxsize可以控制内存使用,防止数据积压。 - 生产者放
None作为结束信号,消费者收到就退出。 task_done()和join()可以用来等待所有数据处理完成。
如果数据量很大或者要持久化缓冲,可以考虑用multiprocessing.Queue或者直接上消息队列(如RabbitMQ、Redis),但一般爬虫用线程队列就够了。
总结:用队列解耦抓取和写入。
中间加一个 redis
队列
如果不是面试的话……把获取数据的频率放慢点
数据结构差别不大吧……用最简单的数组?多加几个硬盘应该更好使
看获取到的数据量,如果多无解的,除非降低爬虫爬取速度。少可以缓存队列啥的。
如果进来的数据量远大于能写入的,队列只能缓解。还是要想办法优化数据写入的速度才行。
网络 IO 比本地 IO 快?还真没碰到过。一般都爬取的时候多开点,扔队列里慢慢写倒是有的。
保存到数据库,开多线程写。
关数据结构什么事?
意思应该是这样,比如从网络上获取每一千份数据,写入一次本地数据,所以前者频率更高。可以使用双 buffer,一个满后,切换 buffer,将满的写入硬盘后清空,这样互不干扰。具体 buffer 是数组、队列还是更复杂的结构,要看具体数据格式。
多买点固态硬盘
说错了,
没看清楚,如果写比较慢,如果不管也可以实时写;
不过估计题主的意思是写的放在一起写, 那可以做一个 batch 写,每个一段时间写;其他时间写线程休眠即可.
生产者消费者模型,blockingqueue,redis 缓存,kafka 队列,要不加内存,要不加硬盘
是否可以这样理解这个题目:
有一个爬虫,本地采用数据结构 A 存数据,
由于从网络上获取数据的频率快,本地写入数据的频率慢,这里做一个假设:
十秒钟获取 1000 条数据,但是十秒钟才能插入一次数据这样的频率,
因此,在插入数据之前,这 1000 条数据采用数据结构 B 来保存
意味着必须批量插入到本地的数据结构 A 中,
试着思考一下这个需求:用什么数据结构好,也就是 A 和 B 应该是什么数据结构呢,什么数据结构实现了这种场景下最优时间复杂度和空间复杂度?
这个题目还真不好理解…

