Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/306.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'获取数据;按顺序同步异步IO_Python_Asynchronous_Python Asyncio - Fatal编程技术网

使用Python'获取数据;按顺序同步异步IO

使用Python'获取数据;按顺序同步异步IO,python,asynchronous,python-asyncio,Python,Asynchronous,Python Asyncio,我有一个Python2.7程序,它从网站中提取数据,并将结果转储到数据库中。它遵循消费者-生产者模型,并使用线程模块编写 只是为了好玩,我想用新的asyncio模块(3.4版)重写这个程序,但我不知道如何正确地完成 最关键的要求是程序必须按顺序从同一网站获取数据。例如,对于url“”,它应该首先获取“”,然后获取“”,然后获取“”。。。 如果未按顺序获取页面,则网站将完全停止交付页面,您必须从0开始 但是,另一个网站(“”)的另一个获取可以(也应该)同时运行(其他网站也有相应的限制) 线程模块非

我有一个Python2.7程序,它从网站中提取数据,并将结果转储到数据库中。它遵循消费者-生产者模型,并使用线程模块编写

只是为了好玩,我想用新的asyncio模块(3.4版)重写这个程序,但我不知道如何正确地完成

最关键的要求是程序必须按顺序从同一网站获取数据。例如,对于url“”,它应该首先获取“”,然后获取“”,然后获取“”。。。 如果未按顺序获取页面,则网站将完全停止交付页面,您必须从0开始

但是,另一个网站(“”)的另一个获取可以(也应该)同时运行(其他网站也有相应的限制)

线程模块非常适合这种情况,因为我可以为每个网站创建单独的线程,在每个线程中,它可以等到一个页面加载完毕后再获取另一个页面

下面是线程版本(Python2.7)的一段非常简化的代码片段:

下面是我如何尝试使用asyncio(在3.4.1中)实现的:

它以非连续的顺序获取并打印所有内容。嗯,我想这就是这些合作的全部想法。我是否应该不使用aiohttp而只使用urllib进行获取?但是,第一家餐馆的回迁会阻止其他餐馆的回迁吗?我是不是觉得这完全错了?
(这只是一个尝试按顺序取东西的测试。还没有到队列部分。)

对于不关心请求顺序的餐厅,您当前的代码可以正常工作。对菜单的所有十个请求将同时运行,并在完成后立即打印到标准输出

显然,这对需要连续请求的餐厅不起作用。您需要进行一些重构才能使其正常工作:

@asyncio.coroutine
def fetch(url):
    response = yield from aiohttp.request('GET', url)
    response = yield from response.read_and_close()
    return response.decode('utf-8')

@asyncio.coroutine
def print_page(url):
    page = yield from fetch(url)
    print(page)

@syncio.coroutine
def print_pages_sequential(url, num_pages):
    for food in range(num_pages):
        menu_url = url + '/' + str(food)
        yield from print_page(menu_url)

l = [print_pages_sequential('http://a-restaurant.com/menu', 10)]

conc_url = 'http://another-restaurant.com/menu'
for food in range(10):
    menu_url = conc_url + '/' + str(food)
    l.append(print_page(menu_url))

loop.run_until_complete(asyncio.wait(l))
我们没有将顺序餐厅的所有十个请求都添加到列表中,而是向列表中添加一个协同路由,它将按顺序迭代所有十个页面。其工作方式是,
yield from print\u page
将停止执行
print\u pages\u sequential
,直到
print\u page
请求完成,但它不会阻止同时运行的任何其他协同程序(就像附加到
l
的所有
print\u page
调用)

通过这种方式,您的所有“另一家餐馆”请求都可以完全并发运行,就像您希望的那样,您的“a-restaurant”请求将按顺序运行,但不会阻止任何“另一家餐馆”请求

编辑:

如果所有站点都有相同的顺序抓取要求,则可以进一步简化逻辑:

l = []
urls = ["http://a-restaurant.com/menu", "http://another-restaurant.com/menu"]
for url in urls:
    menu_url = url + '/' + str(food)
    l.append(print_page_sequential(menu_url, 10))

loop.run_until_complete(asyncio.wait(l))

asyncio.Task
是asyncio世界中
threading.Thread
的替代品。
asyncio.async
也会创建新任务

asyncio.gather
是等待多个协同进程的非常方便的方法,我更喜欢它而不是
asyncio.wait

@asyncio.coroutine
def fetch(url):
    response = yield from aiohttp.request('GET', url)
    response = yield from response.read_and_close()
    return response.decode('utf-8')

@asyncio.coroutine
def print_page(url):
    page = yield from fetch(url)
    print(page)

@asyncio.coroutine
def process_restaurant(url):
    for food in range(10):
        menu_url = url + '/' + str(food)
        yield from print_page(menu_url)

urls = ('http://a-restaurant.com/menu', 'http://another-restaurant.com/menu')
coros = []
for url in urls:
    coros.append(asyncio.Task(process_restaurant(url)))

loop.run_until_complete(asyncio.gather(*coros))

谢谢你,达诺。需要明确的是:所有餐厅都需要在其菜单中按顺序获取数据,但我希望同时从第一家餐厅和第二家餐厅获取数据(仅他们各自的菜单获取需要按顺序)。所以我想解决办法是
l=[打印页面]http://a-restaurant.com/menu,10),打印页面http://another-restaurant.com/menu“,10)]
然后运行
循环。运行直到完成(asyncio.wait(l))
(现在无法测试)。@user3313978啊,对不起,我误解了这个要求。考虑到该约束,您对解决方案的假设是正确的。我更新了我的答案以反映新的约束。这仍然无法按顺序启动请求,@dano。不幸的是,
collect
wait
以不确定的顺序安排任何传入的协程,因为它将它们包装在
Task
s中。看见解决方法是在将每个协同路由对象传递到
gather
wait
之前,手动为它们分配一个循环任务。e、 g.
l.append(循环。创建任务(打印页面\u顺序(菜单\u url,10))
很高兴知道<代码>异步IO似乎比我预期的要复杂一些。顺便说一句,
def process_restaurant(url)
缺少一个缩进级别。
process_restaurant
的标记是固定的。谢谢你的报告。
l = []
urls = ["http://a-restaurant.com/menu", "http://another-restaurant.com/menu"]
for url in urls:
    menu_url = url + '/' + str(food)
    l.append(print_page_sequential(menu_url, 10))

loop.run_until_complete(asyncio.wait(l))
@asyncio.coroutine
def fetch(url):
    response = yield from aiohttp.request('GET', url)
    response = yield from response.read_and_close()
    return response.decode('utf-8')

@asyncio.coroutine
def print_page(url):
    page = yield from fetch(url)
    print(page)

@asyncio.coroutine
def process_restaurant(url):
    for food in range(10):
        menu_url = url + '/' + str(food)
        yield from print_page(menu_url)

urls = ('http://a-restaurant.com/menu', 'http://another-restaurant.com/menu')
coros = []
for url in urls:
    coros.append(asyncio.Task(process_restaurant(url)))

loop.run_until_complete(asyncio.gather(*coros))