2016-03-10 19 views
8

Trong câu hỏi trước đó, một trong những tác giả của aiohttp cách vui lòng đề nghị fetch multiple urls with aiohttp sử dụng async with cú pháp mới từ Python 3.5:web asyncio cào 101: lấy nhiều url với aiohttp

import aiohttp 
import asyncio 

async def fetch(session, url): 
    with aiohttp.Timeout(10): 
     async with session.get(url) as response: 
      return await response.text() 

async def fetch_all(session, urls, loop): 
    results = await asyncio.wait([loop.create_task(fetch(session, url)) 
            for url in urls]) 
    return results 

if __name__ == '__main__': 
    loop = asyncio.get_event_loop() 
    # breaks because of the first url 
    urls = ['http://SDFKHSKHGKLHSKLJHGSDFKSJH.com', 
      'http://google.com', 
      'http://twitter.com'] 
    with aiohttp.ClientSession(loop=loop) as session: 
     the_results = loop.run_until_complete(
      fetch_all(session, urls, loop)) 
     # do something with the the_results 

Tuy nhiên khi một trong những yêu cầu phá vỡ session.get(url) (như trên vì http://SDFKHSKHGKLHSKLJHGSDFKSJH.com) lỗi không được xử lý và toàn bộ sự cố.

tôi tìm cách để chèn kiểm tra về kết quả của session.get(url), ví dụ tìm kiếm địa điểm cho một try ... except ..., hoặc cho một if response.status != 200: nhưng tôi chỉ không hiểu làm thế nào để làm việc với async with, await và các đối tượng khác nhau.

async with vẫn còn rất mới, không có nhiều ví dụ. Nó sẽ rất hữu ích cho nhiều người nếu một thuật sĩ asyncio có thể hiển thị như thế nào để làm điều này. Sau tất cả những điều đầu tiên, hầu hết mọi người sẽ muốn thử nghiệm với asyncio sẽ nhận được nhiều tài nguyên đồng thời.

Goal

Mục đích là chúng ta có thể kiểm tra the_results và nhanh chóng xem một trong hai:

  • url này thất bại (và tại sao: mã trạng thái, có lẽ tên ngoại lệ), hoặc
  • này url đã hoạt động và đây là đối tượng phản hồi hữu ích

Trả lời

9

tôi sẽ sử dụng gather thay vì wait, mà có thể trở lại trường hợp ngoại lệ như các đối tượng, mà không tăng chúng. Sau đó, bạn có thể kiểm tra từng kết quả, nếu đó là trường hợp ngoại lệ.

import aiohttp 
import asyncio 

async def fetch(session, url): 
    with aiohttp.Timeout(10): 
     async with session.get(url) as response: 
      return await response.text() 

async def fetch_all(session, urls, loop): 
    results = await asyncio.gather(
     *[fetch(session, url) for url in urls], 
     return_exceptions=True # default is false, that would raise 
    ) 

    # for testing purposes only 
    # gather returns results in the order of coros 
    for idx, url in enumerate(urls): 
     print('{}: {}'.format(url, 'ERR' if isinstance(results[idx], Exception) else 'OK')) 
    return results 

if __name__ == '__main__': 
    loop = asyncio.get_event_loop() 
    # breaks because of the first url 
    urls = [ 
     'http://SDFKHSKHGKLHSKLJHGSDFKSJH.com', 
     'http://google.com', 
     'http://twitter.com'] 
    with aiohttp.ClientSession(loop=loop) as session: 
     the_results = loop.run_until_complete(
      fetch_all(session, urls, loop)) 

Các xét nghiệm:

$python test.py 
http://SDFKHSKHGKLHSKLJHGSDFKSJH.com: ERR 
http://google.com: OK 
http://twitter.com: OK 
+0

Tuyệt vời, cảm ơn bạn rất nhiều! Tôi sẽ cần phải tiêu hóa điều này, nhưng sau khi chơi với nó một chút nó có vẻ khá linh hoạt. +1, chấp nhận. :) –

+1

Câu trả lời hay. Một điều tôi tò mò, vì bạn ngay lập tức lặp lại kết quả sau khi thực hiện 'asyncio.gather' sẽ không tốt hơn nếu bạn làm 'asyncio.as_completed' trong danh sách' fetch'es? Bằng cách này, bạn có thể lặp lại những người đã hoàn thành ngay lập tức so với chờ đợi cho họ tất cả để kết thúc? – dalanmiller

+0

@dalanmiller: nó yêu cầu xử lý ngoại lệ, như trong câu trả lời của Padraic Cunningham. Nhưng nếu bạn cần kết quả cho mỗi tương lai ngay lập tức thì đây là con đường. – kwarunek

4

Tôi cách xa chuyên gia asyncio nhưng bạn muốn bắt lỗi mà bạn cần phải nắm bắt một lỗi ổ cắm:

async def fetch(session, url): 
    with aiohttp.Timeout(10): 
     try: 
      async with session.get(url) as response: 
       print(response.status == 200) 
       return await response.text() 
     except socket.error as e: 
      print(e.strerror) 

Chạy mã và in the_results:

Cannot connect to host sdfkhskhgklhskljhgsdfksjh.com:80 ssl:False [Can not connect to sdfkhskhgklhskljhgsdfksjh.com:80 [Name or service not known]] 
True 
True 
({<Task finished coro=<fetch() done, defined at <ipython-input-7-535a26aaaefe>:5> result='<!DOCTYPE ht...y>\n</html>\n'>, <Task finished coro=<fetch() done, defined at <ipython-input-7-535a26aaaefe>:5> result=None>, <Task finished coro=<fetch() done, defined at <ipython-input-7-535a26aaaefe>:5> result='<!doctype ht.../body></html>'>}, set()) 

Bạn có thể thấy chúng tôi có được bắt lỗi và các cuộc gọi tiếp tục vẫn thành công trở lại html.

Chúng ta nên có thể thực sự được bắt một OSError như socket.error là A deprecated alias of OSError từ python 3.3:

async def fetch(session, url): 
    with aiohttp.Timeout(10): 
     try: 
      async with session.get(url) as response: 
       return await response.text() 
     except OSError as e: 
      print(e) 

Nếu bạn cũng muốn để kiểm tra phản ứng là 200, đưa bạn nếu trong thử quá và bạn có thể sử dụng thuộc tính lý do để có thêm thông tin:

async def fetch(session, url): 
    with aiohttp.Timeout(10): 
     try: 
      async with session.get(url) as response: 
       if response.status != 200: 
        print(response.reason) 
       return await response.text() 
     except OSError as e: 
      print(e.strerror) 
+0

Cảm ơn bạn rất nhiều! Hai câu trả lời tuyệt vời, tôi ước tôi có thể chọn cả hai. Chọn @kwarunek 's vì nó hoạt động ra khỏi hộp, nhưng 1 và tôi sẽ đi tìm hai câu trả lời tốt nhất của bạn để upvote.:) –

Các vấn đề liên quan