文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

如何用最快的方式发送 10 万个 HTTP 请求

2024-12-14 01:00

关注

假如有一个文件,里面有 10 万个 url,需要对每个 url 发送 http 请求,并打印请求结果的状态码,如何编写代码尽可能快的完成这些任务呢?

Python 并发编程有很多方法,多线程的标准库 threading,concurrency,协程 asyncio,当然还有 grequests 这种异步库,每一个都可以实现上述需求,下面一一用代码实现一下,本文的代码可以直接运行,给你以后的并发编程作为参考:

队列+多线程

定义一个大小为 400 的队列,然后开启 200 个线程,每个线程都是不断的从队列中获取 url 并访问。

主线程读取文件中的 url 放入队列中,然后等待队列中所有的元素都被接收和处理完毕。代码如下:

  1. from threading import Thread 
  2. import sys 
  3. from queue import Queue 
  4. import requests 
  5.  
  6. concurrent = 200 
  7.  
  8.  
  9. def doWork(): 
  10.     while True
  11.         url = q.get() 
  12.         status, url = getStatus(url) 
  13.         doSomethingWithResult(status, url) 
  14.         q.task_done() 
  15.  
  16.  
  17. def getStatus(ourl): 
  18.     try: 
  19.         res = requests.get(ourl) 
  20.         return res.status_code, ourl 
  21.     except
  22.         return "error", ourl 
  23.  
  24.  
  25. def doSomethingWithResult(status, url): 
  26.     print(status, url) 
  27.  
  28.  
  29. q = Queue(concurrent * 2) 
  30. for i in range(concurrent): 
  31.     t = Thread(target=doWork) 
  32.     t.daemon = True 
  33.     t.start() 
  34.  
  35. try: 
  36.     for url in open("urllist.txt"): 
  37.         q.put(url.strip()) 
  38.     q.join() 
  39. except KeyboardInterrupt: 
  40.     sys.exit(1) 

运行结果如下:

有没有 get 到新技能?

线程池

如果你使用线程池,推荐使用更高级的 concurrent.futures 库:

  1. import concurrent.futures 
  2. import requests 
  3.  
  4. out = [] 
  5. CONNECTIONS = 100 
  6. TIMEOUT = 5 
  7.  
  8. urls = [] 
  9. with open("urllist.txt"as reader: 
  10.     for url in reader: 
  11.         urls.append(url.strip()) 
  12.  
  13. def load_url(url, timeout): 
  14.     ans = requests.get(url, timeout=timeout) 
  15.     return ans.status_code 
  16.  
  17. with concurrent.futures.ThreadPoolExecutor(max_workers=CONNECTIONS) as executor: 
  18.     future_to_url = (executor.submit(load_url, url, TIMEOUT) for url in urls) 
  19.     for future in concurrent.futures.as_completed(future_to_url): 
  20.         try: 
  21.             data = future.result() 
  22.         except Exception as exc: 
  23.             data = str(type(exc)) 
  24.         finally: 
  25.             out.append(data) 
  26.             print(data) 

协程 + aiohttp

协程也是并发非常常用的工具了:

  1. import asyncio 
  2. from aiohttp import ClientSession, ClientConnectorError 
  3.  
  4. async def fetch_html(url: str, session: ClientSession, **kwargs) -> tuple: 
  5.     try: 
  6.         resp = await session.request(method="GET", url=url, **kwargs) 
  7.     except ClientConnectorError: 
  8.         return (url, 404) 
  9.     return (url, resp.status) 
  10.  
  11. async def make_requests(urls: set, **kwargs) -> None: 
  12.     async with ClientSession() as session: 
  13.         tasks = [] 
  14.         for url in urls: 
  15.             tasks.append( 
  16.                 fetch_html(url=url, session=session, **kwargs) 
  17.             ) 
  18.         results = await asyncio.gather(*tasks) 
  19.  
  20.     for result in results: 
  21.         print(f'{result[1]} - {str(result[0])}'
  22.  
  23. if __name__ == "__main__"
  24.     import sys 
  25.     assert sys.version_info >= (3, 7), "Script requires Python 3.7+." 
  26.     with open("urllist.txt"as infile: 
  27.         urls = set(map(str.strip, infile)) 
  28.     asyncio.run(make_requests(urls=urls)) 

grequests[1]

这是个第三方库,目前有 3.8K 个星,就是 Requests + Gevent[2],让异步 http 请求变得更加简单。Gevent 的本质还是协程。

使用前:

  1. pip install grequests 

使用起来那是相当的简单:

  1. import grequests 
  2.  
  3. urls = [] 
  4. with open("urllist.txt"as reader: 
  5.     for url in reader: 
  6.         urls.append(url.strip()) 
  7.  
  8. rs = (grequests.get(u) for u in urls) 
  9.  
  10. for result in grequests.map(rs): 
  11.     print(result.status_code, result.url) 

注意 grequests.map(rs) 是并发执行的。运行结果如下:

也可以加入异常处理:

  1. >>> def exception_handler(request, exception): 
  2. ...    print("Request failed"
  3.  
  4. >>> reqs = [ 
  5. ...    grequests.get('http://httpbin.org/delay/1', timeout=0.001), 
  6. ...    grequests.get('http://fakedomain/'), 
  7. ...    grequests.get('http://httpbin.org/status/500')] 
  8. >>> grequests.map(reqs, exception_handler=exception_handler) 
  9. Request failed 
  10. Request failed 
  11. [None, None, 

最后的话 

今天分享了并发 http 请求的几种实现方式,有人说异步(协程)性能比多线程好,其实要分场景看的,没有一种方法适用所有的场景,笔者就曾做过一个实验,也是请求 url,当并发数量超过 500 时,协程明显变慢。

 

来源:Python七号内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯