系列索引:菜菜的并发编程笔记 | Python并发编程详解(持续更新~)
一、多组件的Pipeline技术架构
二、生产者消费者爬虫的架构
对于没有学过操作系统的同学可以理解成在生产者和消费者之间设置一个缓冲区,生产者生产的数据放入缓冲区,消费者从中取出数据供自己使用。
生产者消费者问题
是一个非常典型的问题,我觉得没有学过这一块知识的最好去补习一下,对后面学习理解线程通信、信号量机制都有帮助。
三、多线程数据通信的queue.Queue
多线程之间通信可以使用queue
类,在线程间创建一个队列,这是一种线程安全
的方式,当队列为空时消费者会阻塞直到有数据被放入对列,右侧是一些常用的查询方法。
四、代码编写实现生产者-消费者爬虫
基础代码部分参考上一篇文章,在代码中我们创建两个对列,分别是url_queue
,html_queue
,函数do_craw
可以看做生产者,获取网页数据用来给消费者do_parse
提供数据。
两个函数都先从对列中取数据,若对列为空则阻塞执行,以do_craw为例,当从queue中获取到数据后将获取到的数据(url)传入爬虫程序执行,然后将中间数据(即获取到的未解析的网页)放入另一queue对列,然后输出执行结果并随机休眠,防止服务器负载过大崩掉!但大家注意随机休息会使执行时间变长,而不是因为使用queue造成的效率变低!
import requests
from bs4 import BeautifulSoup
urls = [
f"https://www.cnblogs.com/sitehome/p/{page}"
for page in range(1, 50 + 1)
]
def craw(url):
#print("craw url: ", url)
r = requests.get(url)
return r.text
def parse(html):
# class="post-item-title"
soup = BeautifulSoup(html, "html.parser")
links = soup.find_all("a", class_="post-item-title")
return [(link["href"], link.get_text()) for link in links]
if __name__ == "__main__":
for result in parse(craw(urls[2])):
print(result)
上面是blog_spider的代码部分。
import queue
import blog_spider
import time
import random
import threading
def do_craw(url_queue: queue.Queue, html_queue: queue.Queue):
while True:
url = url_queue.get()
html = blog_spider.craw(url)
html_queue.put(html)
print(threading.current_thread().name, f"craw {url}",
"url_queue.size=", url_queue.qsize())
time.sleep(random.randint(1, 2))
def do_parse(html_queue: queue.Queue, fout):
while True:
html = html_queue.get()
results = blog_spider.parse(html)
for result in results:
fout.write(str(result) + "\n")
print(threading.current_thread().name, f"results.size", len(results),
"html_queue.size=", html_queue.qsize())
time.sleep(random.randint(1, 2))
if __name__ == "__main__":
url_queue = queue.Queue()
html_queue = queue.Queue()
for url in blog_spider.urls:
url_queue.put(url)
for idx in range(3):
t = threading.Thread(target=do_craw, args=(url_queue, html_queue),
name=f"craw{idx}")
t.start()
fout = open("02.data.txt", "w")
for idx in range(2):
t = threading.Thread(target=do_parse, args=(html_queue, fout),
name=f"parse{idx}")
t.start()
在main函数中主要是创建对列,把urls数据放入对列,然后先执行爬虫线程,再执行解析线程。下一篇我们将具体讲讲并发编写中线程安全
问题以及Lock
解决方案。
Python进阶之并发编程篇持续更新,欢迎
点赞收藏
+关注
上一篇:菜菜的并发编程笔记 |(三)利用多线程实现爬虫10倍加速
下一篇:菜菜的并发编程笔记 |(五)线程安全问题以及Lock解决方案
本人水平有限,文章中不足之处欢迎下方👇评论区批评指正~如果感觉对你有帮助,点个赞👍 支持一下吧 ~
不定期分享 有趣、有料、有营养内容,欢迎 订阅关注 🤝 我的博客 ,期待在这与你相遇 ~