python清华大学_清华大学软件工程师教你如何Python高效爬虫

本文介绍了在Python中实现高效爬虫的策略,重点讨论了进程、线程和协程的使用。作者推荐使用多进程结合协程的方案,以充分利用多核并行和协程的低开销。文中提供了包含单线程、多线程、多进程及不同协程组合的爬虫代码示例,并进行了性能对比测试。
摘要由CSDN通过智能技术生成

前言

由于项目需要建立一个尽可能全面的药品图片库,所以今天就在各种爬取药品图片。由于目前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

看到这里的话,说明你还是真看进去这篇文章了。希望支持一下小弟,欢迎关注.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值