异步循环内的异步循环

Posted

技术标签:

【中文标题】异步循环内的异步循环【英文标题】:Asyncio Loop Within Asyncio Loop 【发布时间】:2018-10-17 21:46:30 【问题描述】:

我刚刚开始使用 Asyncio,我正在尝试使用它来解析网站。

我正在尝试解析站点的 6 个部分 (self.signals),每个部分都有 N 个带有表格的页面,所以基本上我正在尝试异步调用哪个部分的循环,并异步每个部分的页面。这是我目前所拥有的。

class FinViz():
    def __init__(self):
        self.url = 'https://finviz.com/screener.ashx?v=160&s='

        self.signals = 
            'Earnings_Before' : 'n_earningsbefore',
            'Earnings_After' : 'n_earningsafter',
            'Most_Active' : 'ta_mostactive',
            'Top_Gainers' : 'ta_topgainers',
            'Most_Volatile' : 'ta_mostvolatile',
            'News' : 'n_majornews',
            'Upgrade' : 'n_upgrades',
            'Unusual_Volume' : 'ta_unusualvolume' 
        

        self.ticks = []

    def _parseTable(self, data):
        i, signal = data
        url = self.signals[signal] if i == 0 else self.signals[signal] + '&r='.format(str(i * 20 + 1))
        soup = BeautifulSoup(urlopen(self.url + url, timeout = 3).read(), 'html5lib')
        table = soup.find('div', 'id' : 'screener-content').find('table', 
            'width' : '100%', 'cellspacing': '1', 'cellpadding' : '3', 'border' : '0', 'bgcolor' : '#d3d3d3')
        for row in table.findAll('tr'):
            col = row.findAll('td')[1]
            if col.find('a'):
                self.ticks.append(col.find('a').text)


    async def parseSignal(self, signal):
        try:
            soup = BeautifulSoup(urlopen(self.url + self.signals[signal], timeout = 3).read(), 'html5lib')

            tot = int(soup.find('td', 'class' : 'count-text').text.split()[1])

            with concurrent.futures.ThreadPoolExecutor(max_workers = 20) as executor:
                loop = asyncio.get_event_loop()
                futures = []
                for i in range(tot // 20 + (tot % 20 > 0)):
                    futures.append(loop.run_in_executor(executor, self._parseTable, (i, signal)))


                for response in await asyncio.gather(*futures):
                    pass    
        except URLError:
            pass


    async def getAll(self):
        with concurrent.futures.ThreadPoolExecutor(max_workers = 20) as executor:
            loop = asyncio.get_event_loop()
            futures = []
            for signal in self.signals:
                futures.append(await loop.run_in_executor(executor, self.parseSignal, signal))

            for response in await asyncio.gather(*futures):
                pass
        print(self.ticks)

if __name__ == '__main__':

    x = FinViz()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(x.getAll())

这确实成功地完成了这项工作,但它在某种程度上比我在没有asyncio 的情况下进行解析要慢。

对于异步菜鸟有什么建议吗?

编辑:添加完整代码

【问题讨论】:

【参考方案1】:

记住 python 有一个 GIL,所以线程代码不会提高性能。为了可能加快速度,请使用ProcessPoolExecutor,但请注意,您会产生以下开销:

    pickle/unpickling 数据到子流程工作人员 腌制/取消腌制结果发送回主进程

您可以避免 1. 如果您在 fork 安全环境中运行并将数据存储在全局变量中。

您还可以执行诸如共享内存映射文件之类的操作...共享原始字符串/字节也是最快的。

【讨论】:

以上是关于异步循环内的异步循环的主要内容,如果未能解决你的问题,请参考以下文章

循环内的异步函数完成后如何调用函数?

如何在节点 js 中正确使用等待/异步与 for 循环

如何在库函数中使用异步事件循环

js的事件循环机制:同步与异步任务(setTimeout,setInterval)宏任务,微任务(Promise,process.nextTick)

js事件循环

JavaScript异步处理问题,循环处理异步任务,并拿到数据,Nodejs循环异步任务接口处理