爬虫流程
之前没了解过相关东西,觉得大体流程无非是发送http request, 然后把爬来的数据进行存储。
读了一个相关代码实现后,往深里钻,里面东西还特别多。核心流程还是一样,但是考虑到效率就会涉及到很多东西。流程方面可以参考这里
代码仓库
网上谁便找了个,代码量不大,适合学习使用这里。
代码解读
类图
- 其中WebSpider 是对外的门面类,其中聚合了抓取线程、解析线程、保存结果线程;还聚合了抓取任务队列、解析任务队列、保存任务队列。
- 其中线程有反过来关联WebSpider 一次来操作队列; 线程中有关联了具体干活的Worker类。
- Worker泛化具体的抓取、解析、保存操作。里面自定义具体操作。抓取里面进行http请求,解析里面进行正则匹配, 保存进行输出重定向。
流程图
维护了三个任务队列:Fetch, Parser, Saver 分别代表抓取队列、解析队列、保存队列。
初始
- 其中,1、2、3步骤都在各自的线程中运行,初始时会在Fetch Queue 中塞入一个初始url,以此保证后续动作的进行。
- 整个流程由队列中数据驱动进行,至于线程停止的时机,直到所有任务队列为空时,因为Parser会把解析到的次级url放入Fetch队列,那么队列如何保证可以有为空的条件呢,这里Paser干活类中有个控制抓取url层级的值,当抓取的url层级超过限制的层级后,就不会再往Fetch 队列中插入了。
关键知识
布隆过滤
- python 引入pybloom_live 包来使用。可以理解为hashmap的替代,但是在数据量巨大时hashmap 会有空间占用巨大的问题。 但是布隆过滤器也有缺点,他可以告诉“某样东西一定不存在或者可能存在”,即无法保证确定的存。
- 此程序中在Fetch 获取到新url后会更新过滤器中值。在数据量小时,看不出布隆过滤的优势。
Queue
此程序的关键代码理解我觉得主要是queue的使用,剖开具体的操作细节,代码运行流程可以通过Queue 来看明白。
可以看下面一个生产者–消费者问题,代码中的queue 的使用是一样的。
def test_multiply_thread_queue():
num_fetch_threads = 5
enclosure_queue = Queue()
def consumer(i, q):
while True:
print("consumer begin")
url = q.get(block=True)
print('%s: consumer: %s end. %s' % (i, url, time.ctime()))
# time.sleep(1)
q.task_done()
for i in range(num_fetch_threads):
worker = Thread(target=consumer, args=(i, enclosure_queue))
worker.setDaemon(True)
worker.start()
def producer(i, q):
itm = 'url {}'.format(i)
# print('producer: %s begin. %s' % (itm, time.ctime()))
#time.sleep(i)
q.put(itm)
print('producer: %s end. %s' % (itm, time.ctime()))
num_product_threads = 5
for i in range(num_product_threads):
worker = Thread(target=producer, args=(i, enclosure_queue))
worker.setDaemon(True)
worker.start()
# Now wait for the queue to be empty, indicating that we have
# processed all of the downloads.
print('*** Main thread waiting')
enclosure_queue.join()
print('*** Done')
- Queue 自身保证线程安全。这里join 为’Blocks until all items in the queue have been gotten and processed.’,等待所有任务被取出, 而后往下执行。这里join的效果可以通过调整consumer和producer中的sleep看出效果。
- 当生产者执行较快,消费者线程肯定能消费完才结束程序;
当生产者较慢,消费者较快时,生产者可能没生产几个就会退出程序,因为在enclosure_queue.join()时发现队列为空,主线程直接往下执行了。