Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/joomla/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python异步/等待下载URL列表_Python_Asynchronous_Python Asyncio_Aiohttp - Fatal编程技术网

Python异步/等待下载URL列表

Python异步/等待下载URL列表,python,asynchronous,python-asyncio,aiohttp,Python,Asynchronous,Python Asyncio,Aiohttp,我正试图从FTP服务器上下载30000多个文件,在谷歌搜索之后,使用异步IO似乎是个好主意。但是,下面的代码未能下载任何文件并返回超时错误。我真的很感激任何帮助!谢谢 class pdb: def __init__(self): self.ids = [] self.dl_id = [] self.err_id = [] async def download_file(self, session, url): t

我正试图从FTP服务器上下载30000多个文件,在谷歌搜索之后,使用异步IO似乎是个好主意。但是,下面的代码未能下载任何文件并返回超时错误。我真的很感激任何帮助!谢谢

class pdb:
    def __init__(self):
        self.ids = []
        self.dl_id = []
        self.err_id = []


    async def download_file(self, session, url):
        try:
            with async_timeout.timeout(10):
                async with session.get(url) as remotefile:
                    if remotefile.status == 200:
                        data = await remotefile.read()
                        return {"error": "", "data": data}
                    else:
                        return {"error": remotefile.status, "data": ""}
        except Exception as e:
            return {"error": e, "data": ""}

    async def unzip(self, session, work_queue):
        while not work_queue.empty():
            queue_url = await work_queue.get()
            print(queue_url)
            data = await self.download_file(session, queue_url)
            id = queue_url[-11:-7]
            ID = id.upper()
            if not data["error"]:
                saved_pdb = os.path.join("./pdb", ID, f'{ID}.pdb')
                if ID not in self.dl_id:
                    self.dl_id.append(ID)
                with open(f"{id}.ent.gz", 'wb') as f:
                    f.write(data["data"].read())
                with gzip.open(f"{id}.ent.gz", "rb") as inFile, open(saved_pdb, "wb") as outFile:
                    shutil.copyfileobj(inFile, outFile)
                os.remove(f"{id}.ent.gz")
            else:
                self.err_id.append(ID)

    def download_queue(self, urls):
        loop = asyncio.get_event_loop()
        q = asyncio.Queue(loop=loop)
        [q.put_nowait(url) for url in urls]
        con = aiohttp.TCPConnector(limit=10)
        with aiohttp.ClientSession(loop=loop, connector=con) as session:
            tasks = [asyncio.ensure_future(self.unzip(session, q)) for _ in range(len(urls))]
            loop.run_until_complete(asyncio.gather(*tasks))
        loop.close()
如果我删除
try
部分,则显示错误消息:

回溯(最近一次呼叫最后一次):
文件“test.py”,第111行,在
x、 下载队列(URL)
下载队列中第99行的文件“test.py”
循环。运行\u直到\u完成(asyncio.gather(*任务))
文件“/home/yz/miniconda3/lib/python3.6/asyncio/base\u events.py”,第467行,运行直到完成
返回future.result()
解压缩文件“test.py”,第73行
数据=等待自我。下载文件(会话、队列\u url)
下载文件中第65行的文件“test.py”
返回{“error”:remotefile.status,“data”:“”}
文件“/home/yz/miniconda3/lib/python3.6/site-packages/async_timeout/init.py”,第46行,在退出
从无引发asyncio.TimeoutError
并发.futures.\u base.TimeoutError

在这里,您开始同时下载所有URL的过程。这意味着您也开始计算所有这些的超时时间。一旦它是一个大数字,比如30000,由于网络/ram/cpu容量的原因,它就不能在10秒内完成

为了避免这种情况,您应该保证同时启动的协同程序的限制。通常可以使用同步原语(如)来实现这一点

看起来是这样的:

sem = asyncio.Semaphore(10)

# ...

async def download_file(self, session, url):
    try:
        async with sem:  # Don't start next download until 10 other currently running
            with async_timeout.timeout(10):

作为@ MikhailGerasimov的信号量方法的替代,您可以考虑使用运算符:

from aiostream import stream, pipe

async def main(urls):
    async with aiohttp.ClientSession() as session:
        ws = stream.repeat(session)
        xs = stream.zip(ws, stream.iterate(urls))
        ys = stream.starmap(xs, fetch, ordered=False, task_limit=10)
        zs = stream.map(ys, process)
        await zs
下面是一个使用管道的等效实现:

async def main3(urls):
    async with aiohttp.ClientSession() as session:
        await (stream.repeat(session)
               | pipe.zip(stream.iterate(urls))
               | pipe.starmap(fetch, ordered=False, task_limit=10)
               | pipe.map(process))
您可以使用以下协程对其进行测试:

async def fetch(session, url):
    await asyncio.sleep(random.random())
    return url

async def process(data):
    print(data)
请参阅本文和中的更多aiostream示例


免责声明:我是项目维护者。

没有错误消息吗?您已经做了什么诊断?可能是因为您已将超时设置为10秒:async_timeout.timeout(10)@KlausD。很抱歉没有包括那个部分。刚刚编辑过。@Matej是的,但是如果我删除那行代码,代码就会冻结,所以我想其他地方仍然有问题。你可以增加值-10秒可能不足以下载文件。我知道问题出在哪里了。。。我猜aiohttp无法处理ftp URL。谢谢你的建议!流是如何限制同时下载的文件量的?@wolfdawn它只是限制可以同时运行的
fetch
任务量。一旦达到限制,
map
流将等待任务完成,然后向
zip
流请求新参数。谢谢!我错过了那部分。非常感谢。
async def fetch(session, url):
    await asyncio.sleep(random.random())
    return url

async def process(data):
    print(data)