前言
由于项目需要建立一个尽可能全面的药品图片库,所以今天就在各种爬取药品图片。由于目前CPU占用几乎100%, 也没法干别的事情,就趁着这段时间写篇小文章把Python爬虫这块一次性总结下。这篇文章建议收藏,相信我,以后你写爬虫一定会有帮助。
python里面共有进程、线程、协程三个层次概念,那么我们爬虫的时候无非就是选择:单线程爬取, 单线程+协程爬取, 多线程爬取, 多线程 + 协程爬取, 多进程爬取, 多进程 + 协程爬取这么6种组合方案。爬虫的时候我们最需要利用起来的就是等待网络响应的这段时间。大多数人一看到高效爬取肯定首先想到多线程爬取。这肯定是没问题的。但是显然在Python里面,多线程还远远不够高效。我在之前的系列里面讲了很多python线程的缺陷以及协程的好处。在Python多线程困境这篇文章中介绍过由于GIL锁的存在,使得线程没法并行执行,只能并发执行。而在并发的情况下,协程的创建、切换开销远远小于线程。所以使用协程而不是使用线程在Python里面是更优的解决方案。
正文
说了这么多,到底怎么样的方案是最高效的爬取方案?答案是: 多进程 + 协程。
协程的方案肯定要比多线程快,因为协程的切换开销非常之小。而且协程的并发度可以非常高。我们一般开线程也就几十个线程,协程的并发度可以达到999个。但协程的缺点就是没法并行运行。
所以多进程 + 协程的方案既利用了并行,又利用了并发。完美了利用了多核,同时又让每一个线程的时间片被充分的利用。
当然了,咱都知道,talk is cheap, show me the code。 在下面这个代码中,我把上面所说的6种爬虫方案都写了:单线程爬取, 单线程+协程爬取, 多线程爬取, 多线程 + 协程爬取, 多进程爬取, 多进程 + 协程爬取。 并依次测试。通过运行测试时间我们也看出,开4个线程的时候,时间大约为单线程的25.9%, 而协程的方案时间大约为单线程的4.3%, 再开4个进程同时配合协程之后的时间大约为单线程的1%,即为协程方案的1/4。(因为开了4个进程)
代码我粘在这里。
rom multiprocessing.dummy import Pool as ThreadPool
from multiprocessing import Process
import multiprocessing
import requests
import time
import asyncio
import aiohttp
import threading
OPTION = {"COROUTINE": 0, "SINGLE_THREAD": 0,
"MULTI_THREADS": 0, "MULTI_THREADS_COROUTINE": 0, "MULTI_PROCESSES": 0,"MUTL_PROCESSES_COROUTINE": 0,}
urls = []
def getsource(url):
_ = requests.get(url)
return
async def agetsource(url):
async with aiohttp.request("GET", url) as response:
await response.text()
return
def singleThread():
for url in urls:
getsource(url)
def multithreads():
pool = ThreadPool(4)
_ = pool.map(getsource, urls)
pool.close()
pool.join()
def multiprocesses():
pool = multiprocessing.Pool(processes=4)
for url in urls:
pool.apply_async(getsource, (url,))
pool.close()
pool.join()
async def amain(index, pool_size):
loop = asyncio.get_event_loop()
start_index = index * int(len(urls) / pool_size)
end_index = min(len(urls), start_index + int(len(urls) / pool_size))
for url in urls[start_index:end_index]:
_ = loop.create_task(agetsource(url))
while (len(asyncio.all_tasks(loop)) > 1):
await asyncio.sleep(2)
def main(index, pool_size):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# loop = asyncio.get_event_loop()
loop.run_until_complete(amain(index, pool_size))
def mutithreads_coroutine():
threads = []
for index in range(4):
threads.append(threading.Thread(target=main, args=(index, 4,)))
for index in range(4):
threads[index].start()
for index in range(4):
threads[index].join()
def multiprocesses_coroutine():
processes = []
for index in range(4):
processes.append(Process(target=main, args=(index, 4,)))
for index in range(4):
processes[index].start()
for index in range(4):
processes[index].join()
if __name__ == "__main__":
for option in OPTION:
OPTION[option] = 1
factor = 1
start_time = time.time()
# 准备测试数据
urls.clear()
for i in range(1, 80):
newpage = 'http://tieba.baidu.com/p/3522395718?pn=' + str(i)
urls.append(newpage)
if OPTION["MUTL_PROCESSES_COROUTINE"]:
multiprocesses_coroutine()
if OPTION["SINGLE_THREAD"]:
singleThread()
if OPTION["MULTI_THREADS"]:
multithreads()
if OPTION["MULTI_THREADS_COROUTINE"]:
mutithreads_coroutine()
if OPTION["COROUTINE"]:
main(0, 1)
if OPTION["这了MULTI_PROCESSES"]:
multiprocesses()
end_time = time.time()
print(f"Time consuming for option = {factor * (end_time - start_time)}")
OPTION[option] = 0
结果贴在这里
总结
就是一篇应用了,帮助大家重温一下协程的一些内容。希望这篇文章能帮助到大家~。最后,真心的询问下,怎么样爬虫,才能不踩雷。我爬取的时候战战兢兢,很担心被xxx。TAT
看到这里的话,说明你还是真看进去这篇文章了。希望支持一下小弟,欢迎关注.