文章目录
PYTHON并发知识学习
课程内容:
- 多线程:t=threading.Thread(target=func_name,args=(xx,))、t.start()、t.join()、queue.Queue、Lock、ThreadPoolEexcutor()
- 多进程:CPU密集型,使用多线程的方式,反而会拖慢速度。因为线程运行时,资源获取与释放消耗时间。原理:多核CPU并行。
- 多协程:异步IO-asyncio,超级循环+IO复用。
第一节 python并发编程简介
并发编程,缩短运行时间。
哪些程序提速的方法?
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F6mS1qPq-1658309193112)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713173202924.png)]](https://i-blog.csdnimg.cn/blog_migrate/0be5f32a2e799ebd0db07f5d412eb083.png)
单线程串行:CPU-IO-CPI-IO如此一步一步执行
多线程并发:CPU与IO可以并行,IO的读取不需要CPU参与,实现并发。原理上仍是单CPU处理。
多CPU并行:多核CPU的电脑,可实现多任务CPU-IO执行,是真正的并行执行进行加速。
多机器并行:在多CPU并行的基础上,多机器进行任务运行。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kyQFfsAG-1658309193115)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713175115605.png)]](https://i-blog.csdnimg.cn/blog_migrate/60f82022f07ec9a2e34c8956ab18ad2e.png)
第二节 怎样选择多线程多进程多协程
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JXIConZL-1658309193117)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713175309432.png)]](https://i-blog.csdnimg.cn/blog_migrate/05dc45a24b35ade271780e36f4e4705f.png)

![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zicrfnO0-1658309193125)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713175253961.png)]](https://i-blog.csdnimg.cn/blog_migrate/eb4b281adce96cb2ec5dae2fdca40a67.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aJvMtNp7-1658309193127)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713175509991.png)]](https://i-blog.csdnimg.cn/blog_migrate/2c9da3574a278f4c5ee787b7c8429ebe.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C7kmjJRU-1658309193129)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713181639954.png)]](https://i-blog.csdnimg.cn/blog_migrate/f8a910278032dababf8cc3ffdef3b967.png)
第三节 python速度慢的罪魁祸首,全局解释器GIL
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QgAPwldM-1658309193132)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713181847925.png)]](https://i-blog.csdnimg.cn/blog_migrate/74bf60c47f772b0938fd3d99bf03cf4a.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vg16L10n-1658309193133)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713182040585.png)]](https://i-blog.csdnimg.cn/blog_migrate/5a253c3561c4d96bb0daaf13ceb502ec.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RdkTlenC-1658309193135)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713182251193.png)]](https://i-blog.csdnimg.cn/blog_migrate/a8513c13944a2b1c964cbded179c5787.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wswKOkh4-1658309193136)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713182922415.png)]](https://i-blog.csdnimg.cn/blog_migrate/6e3c9b7ff5256c9a3a1b24166c48bd0e.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FJyALcRt-1658309193138)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713183349271.png)]](https://i-blog.csdnimg.cn/blog_migrate/9f1cf71b25ee5ee112411cb4418625da.png)
第四节 使用多线程,python爬虫被加速10倍
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VouUledT-1658309193140)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713183440423.png)]](https://i-blog.csdnimg.cn/blog_migrate/a46de3bc32ef0df2deee1ec3e4835a9c.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qsP9POlB-1658309193142)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713183506514.png)]](https://i-blog.csdnimg.cn/blog_migrate/6a4d4e37ba62d9c99e4bc5fe4974cd74.png)
import requests
urls = [f"http://www.cnblogs.com/#p{page}"
for page in range(1, 50 + 1)]
def craw(url):
r = requests.get(url)
print(url, len(r.text))
craw(urls[0])
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import threading
import time
import note
def single_thread():
print("single_thread begin")
for url in note.urls:
note.craw(url)
print("single_thread end")
def muti_thread():
print("multi_thread begin")
threads = []
for url in note.urls:
threads.append(
threading.Thread(target=note.craw, args=(url,)) # url后添加逗号,说明是元组;不加逗号,说明是字符串。
)
for thread in threads:
thread.start()
for thread in threads:
thread.join()
print("multi_thread end")
if __name__ == '__main__':
start = time.time()
single_thread()
end = time.time()
print("single_thread cost:", end - start, "seconds")
start = time.time()
muti_thread()
end = time.time()
print("multi_thread cost:", end - start, "seconds")
第五节 python实现生产者消费者爬虫
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PV81h7kc-1658309193143)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713191047745.png)]](https://i-blog.csdnimg.cn/blog_migrate/6fc1a2c0f0cd72b88fb472c2a40416ca.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-98Ud15ss-1658309193144)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713191227612.png)]](https://i-blog.csdnimg.cn/blog_migrate/f6eec4087840ef50c19e0f395795c299.png)
生产者生产的结果,会通过中间数据,传给消费者。
生产者将“输入数据”作为原料,消费者将“输出数据”进行输出。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ryxsK6g-1658309193146)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713191240861.png)]](https://i-blog.csdnimg.cn/blog_migrate/fa5c57bfa3400fb92a9f7d6d17af3b89.png)
这架构共有两个processor
processor①:获取待爬取的URL,进行网页的下载
下载好的内容放在网页队列中
processor②:消费者消费网页的数据,进行解析与数据存储。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q9jKlMXJ-1658309193148)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713191325564.png)]](https://i-blog.csdnimg.cn/blog_migrate/e017026bb1b017cddc2057f62de704b0.png)
queue.queue是阻塞函数,当队列已满,需要put填入数据。当队列没有元素,会等存在数据了再进行获取。
note.py文件
import requests
# 可以从html或xml文件中提取数据的python库
from bs4 import BeautifulSoup
urls = [f"http://www.cnblogs.com/#p{page}"
for page in range(1, 50 + 1)]
def craw(url):
r = requests.get(url)
return r.text
def parse(html):
# class="post-item-title"
soup = BeautifulSoup(html, "html.parser") # 获取html
links = soup.find_all("a", class_="post-item-title") # 文本解析
# 获取url和标题文本
return [(link["href"], link.get_text()) for link in links]
if __name__ == '__main__':
for result in parse(craw(urls[2])):
print(result)
02.producer_consumer_spider.py文件
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import queue
import note
# 为了查看中间过程,①解析完后,进行sleep;②添加日志--主要是打印线程的名字
import time
import random
import threading
# 开始爬行,此为生产者方法,会有输入数据过程。
def do_craw(url_queue: queue.Queue, html_queue: queue.Queue):
while True:
url = url_queue.get()
html = note.craw(url)
html_queue.put(html)
# 打印当前线程的名称、当前的url、队列的大小
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 = note.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__':
"""
复杂的爬虫,可以分很多模块。
每个模块,都可以使用不同的线程组进行处理。
线程组之间,通过queue.Queue()进行交互。
通过queue.Queue(),主线程将数据扔进去。
url_queue数据传入生产者中后,产出html_queue。
消费者获得html_queue后,产出数据至fout文件。
"""
url_queue = queue.Queue()
html_queue = queue.Queue()
for url in note.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()
第六节 python线程安全问题以及解决方案
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FBGB1YRH-1658309193149)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713224515623.png)]](https://i-blog.csdnimg.cn/blog_migrate/c6de4493eb7ce962339bcffd6e18defe.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KUKnxvA3-1658309193150)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713225357426.png)]](https://i-blog.csdnimg.cn/blog_migrate/98691abb277e8159aed6ebde9cdc7254.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GZ2jRg01-1658309193152)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220713225530214.png)]](https://i-blog.csdnimg.cn/blog_migrate/a4bc2c78dedc2e18d5249984b5615c7d.png)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import threading
import time
lock=threading.Lock()
class Account:
def __init__(self,balance):
self.balance=balance
def draw(account,amount):
with lock:
if account.balance>=amount:
# 添加此语句,一定会使tb运行失败----在if执行完后,就出现线程的切换,使得tb余额为-600。
# 原因:sleep导致当前线程的阻塞,进行线程的切换。
time.sleep(0.1)
print(threading.current_thread().name,"取钱成功")
account.balance-=amount
print(threading.current_thread().name,"余额",account.balance)
else:
print(threading.current_thread().name,"取钱失败,余额不足")
if __name__ == '__main__':
account=Account(1000)
# 此处的name与threading.current_thread().name对应
ta=threading.Thread(name="ta",target=draw,args=(account,800))
tb=threading.Thread(name="tb",target=draw,args=(account,800))
ta.start()
tb.start()
第七节 python好用的线程池ThreadPoolExecutor
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TFJk5via-1658309193153)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715111526186.png)]](https://i-blog.csdnimg.cn/blog_migrate/b3208328840f738f28bfbaffd7c6ce2e.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d812BVOx-1658309193156)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715111837549.png)]](https://i-blog.csdnimg.cn/blog_migrate/17f3b0db6de40e81fde315a2f2b0b67c.png)
痛点:
新建线程系统需要分配资源,终止线程系统需要回收资源。如果可以重用线程,则可以减去新建/终止的开销。
解决办法:
线程池由2部分组成:线程池+队列。
- 线程池本身,里面是提前建好的线程。线程池中的资源会被重复地使用。
- 当新任务来了,不是直接加入线程池,而是加入任务队列。
流程:
- 线程池中已经创建完的线程,会挨个取出队列中的线程任务,进行依次的执行。
- 线程池中的任务执行完后,会接着取下一个任务进行执行。
- 当任务队列中没有新任务了,线程池不会销毁,等待下一个任务的到来。
总结:
通过任务队列与可重用的线程池,实现了线程池功能。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ycoeyytO-1658309193157)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715113941779.png)]](https://i-blog.csdnimg.cn/blog_migrate/7412d13c73993a3b189363eef2750ae8.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-40ipHmFg-1658309193158)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715114000741.png)]](https://i-blog.csdnimg.cn/blog_migrate/fb8b6ad3685c060268f08d7c38e0637c.png)
使用生产者线程组,实现map下的线程池写法。
使用消费者线程组,实现future下的两种线程池写法。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import concurrent.futures
import blog_spider
# craw
with concurrent.futures.ThreadPoolExecutor() as pool:
htmls = pool.map(blog_spider.craw, blog_spider.urls) # 注意,此处用的是urls,是一个列表
htmls = list(zip(blog_spider.urls, htmls)) # 列表中的每个元素都是元组,元组中有url和html
for url, html in htmls:
print(url, len(html))
print("craw over")
# parse
with concurrent.futures.ThreadPoolExecutor() as pool:
futures = {}
# 方法一:顺序输出
# 将future和url建立关系
for url, html in htmls:
future = pool.submit(blog_spider.parse, html)
futures[future] = url
# for future.url in futures.items():
# print(url,future.result())
# 方法二:不按顺序输出
for future in concurrent.futures.as_completed(futures):
url = futures[future]
print(url, future.result())
第八节 python使用线程池在web服务中实现加速
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WDEmZAMJ-1658309193159)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715131630708.png)]](https://i-blog.csdnimg.cn/blog_migrate/141e3e5b630252ce33266babbe5bcb49.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BiDKouqM-1658309193160)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715133231167.png)]](https://i-blog.csdnimg.cn/blog_migrate/6cb9e49096cebec2aa7b351460b92ef7.png)
启动flask的方法:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PqelsVfm-1658309193163)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715133726689.png)]](https://i-blog.csdnimg.cn/blog_migrate/0be090de321f87905be6c307dc3d97b0.png)
flask默认写法:
app = flask.Flask(__name__)
def read_file():
time.sleep(0.1)
return "file result"
def read_db():
time.sleep(0.2)
return "db result"
def read_api():
time.sleep(0.3)
return "api result"
@app.route('/')
def index():
result_file = read_file()
result_db = read_db()
result_api = read_api()
return json.dumps({
"result_file": result_file,
"result_db": result_db,
"result_api": result_api,
})
if __name__ == '__main__':
app.run()
使用线程池加速后的结果:
对象获取:pool.submit(read_file)
结果获取:result_file.result()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
import time
from concurrent.futures import ThreadPoolExecutor
import flask
pool = ThreadPoolExecutor()
app = flask.Flask(__name__)
def read_file():
time.sleep(0.1)
return "file result"
def read_db():
time.sleep(0.2)
return "db result"
def read_api():
time.sleep(0.3)
return "api result"
@app.route('/')
def index():
result_file = pool.submit(read_file)
result_db = pool.submit(read_db)
result_api = pool.submit(read_api)
return json.dumps({
"result_file": result_file.result(),
"result_db": result_db.result(),
"result_api": result_api.result(),
})
if __name__ == '__main__':
app.run()
几乎同时运行的话,就差不多300ms。总时间几乎为最长线程的时间。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a91naA27-1658309193164)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715142947420.png)]](https://i-blog.csdnimg.cn/blog_migrate/7867297f58f78c7bc3b387b237d8303e.png)
第九节 使用多进程multiprocessing模块加速程序的运行
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kaf4F5N1-1658309193165)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715143226219.png)]](https://i-blog.csdnimg.cn/blog_migrate/13e0d08e49d2eacef54369d3c248dc58.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ptQQT7vg-1658309193166)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715144038828.png)]](https://i-blog.csdnimg.cn/blog_migrate/551dae70940c079eae50328e6b621133.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wLZ0yUE2-1658309193167)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715144140140.png)]](https://i-blog.csdnimg.cn/blog_migrate/6c7bf83e404006ffe6b202a1334b956b.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ZzSpWmq-1658309193168)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715150633150.png)]](https://i-blog.csdnimg.cn/blog_migrate/d82856ccfbfff76a21d27b4e0768e3df.png)
代码实现:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import math
import time
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
PRIMES = [112272535095293] * 100
def is_prime(n):
if n < 2:
return False
if n == 2:
return True
if n % 2 == 0:
return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True
def single_thread():
for number in PRIMES:
is_prime(number)
def multi_thread():
with ThreadPoolExecutor() as pool:
pool.map(is_prime, PRIMES)
def multi_process():
with ProcessPoolExecutor() as pool:
pool.map(is_prime, PRIMES)
if __name__ == '__main__':
start = time.time()
single_thread()
end = time.time()
print("single_thread,cost:", end - start, "seconds")
start = time.time()
multi_thread()
end = time.time()
print("multi_thread,cost:", end - start, "seconds")
start = time.time()
multi_process()
end = time.time()
print("multi_process,cost:", end - start, "seconds")
输出结果:
single_thread,cost: 44.58890986442566 seconds
multi_thread,cost: 46.927555561065674 seconds
multi_process,cost: 8.359080076217651 seconds
第十节 python在Flask服务中使用多进程池加速程序运行
和第9节类似的运行情况,但进程池的执行中出现报错。原因:他们的环境之间都是相互完全隔离的。有一点限制,当定义这个pool时,它所依赖的这些函数必须都已经声明完了。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import flask
from concurrent.futures import ProcessPoolExecutor
import math
import json
process_pool = ProcessPoolExecutor()
app = flask.Flask(__name__)
def is_prime(n):
if n < 2:
return False
if n == 2:
return True
if n % 2 == 0:
return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True
@app.route("/is_prime/<numbers>")
# 传入逗号分隔的数字列表,进行批量调用
def api_is_prime(numbers):
number_list = [int(x) for x in numbers.split(",")]
results = process_pool.map(is_prime, number_list)
return json.dumps(dict(zip(number_list, results)))
if __name__ == '__main__':
app.run()
博主建议将进程池的声明放在app.run()前面。但是我执行后,没有太大的区别。
if __name__ == '__main__':
process_pool = ProcessPoolExecutor()
app.run()
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jx0f3bxL-1658309193169)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715161845796.png)]](https://i-blog.csdnimg.cn/blog_migrate/cb3098f263b13d3a48127cfe7a5435bc.png)
第十一节 python异步IO实现并发爬虫
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bFx9paEG-1658309193171)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715163002806.png)]](https://i-blog.csdnimg.cn/blog_migrate/9a155a2034114a7c9f35471f2eee7e86.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8HpnaxEu-1658309193172)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220715163308206.png)]](https://i-blog.csdnimg.cn/blog_migrate/837ef26a89b1c878ac076bc21d00980f.png)

步骤:
1、asyncio:至尊循环;get_event_loop():while…True超级循环。
2、async def myfunc(url)中,async:说明这个函数是一个协程。
await:代表IO,IO执行完毕后,不进行阻塞,直接进入下一个程序的执行asycio.get_event_loop()
3、批量创建task任务:loop.create_task,参数是具体的协程函数。
4、对tasks任务,放到loop.run_until_complete进行事件的执行,并等待完成。
总结:
整体来说,这个异步爬行的框架也是单线程执行的。但是他会多个URL的并发爬虫同时进行。
注意一点,异步IO编程中,依赖的库要支持await,也就是说支持异步IO特性。否则单线程就不能并发执行了。
协程是异步IO执行的函数。协程与普通函数的区别:协程需要超级循环来调度。
汇总:
异步IO实际是超级循环+IO多路复用的结合。
IO多路复用:IO时,CPU可以干其他事情。即当前任务处于IO时,CPU处理下一任务计算。
超级循环:当所有的任务都按照IO多路复用后,再返回来处理第一个任务剩余CPU需计算的任务。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import asyncio
import aiohttp
import blog_spider
async def async_craw(url): # 定义一个异步爬虫的协程
print("craw url:",url)
async with aiohttp.ClientSession() as session: # 异步协程的对象
async with session.get(url) as resp:
result = await resp.text() # await不会一直等待,会切换到下一个url的爬取
print(f"craw url:{url},{len(result)}")
# 协程是异步IO中执行的函数。他与普通函数的区别:协程需要超级循环进行调度。
# 获取超级循环
loop=asyncio.get_event_loop()
# 定义一个tasks列表
tasks=[
loop.create_task(async_craw(url))
for url in blog_spider.urls]
import time
start=time.time()
loop.run_until_complete(asyncio.wait(tasks))
end=time.time()
print("use time seconds:",end-start)
# 单线程异步爬虫,大部分情况下,是快于多线程的。
# 所以多线程的爬取时的并发,需要进行多线程不同调度的切换。本身是耗费时间的。
# 但单线程异步爬虫,没有切换的开销。所以执行起来相对来说是更快的。
第十二节 在异步IO中使用信号量控制爬虫并发度
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yUXlfhUP-1658309193174)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220718145828298.png)]](https://i-blog.csdnimg.cn/blog_migrate/eb05cdce7078bc029b3c912675206bf8.png)
当线程完成一次对该semaphore对象的等待时,代表执行一次并发,所以计数值减一。
通过semephore控制并发度。当信号量满了后,会进入等待状态。防止爬虫将目标网站爬坏,超出他的处理能力。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k1QtLeVR-1658309193175)(C:\Users\Cheryl_Xu\AppData\Roaming\Typora\typora-user-images\image-20220718160255128.png)]](https://i-blog.csdnimg.cn/blog_migrate/52a1fa7eef8142da7c7beed6d4f7cae6.png)

422

被折叠的 条评论
为什么被折叠?



