python treq_开始使用Treq在Python中进行异步调用

python treq

Twisted Requests( treq )包是基于流行的Twisted库构建的HTTP客户端,该库用于异步请求。 异步库提供了并行执行大量网络请求的功能,而对CPU的影响相对较小。 这在需要获得多个所需信息之前需要发出多个请求的HTTP客户端中很有用。 在本文中,我们将通过一个示例进行工作,该示例进行异步调用以使用treq进行探索。

定义要解决的问题

我喜欢玩实时策略游戏《皇家大逃杀》。 虽然它不是开源的,但它确实具有一个公共API,我们可以使用它来显示异步请求如何派上用场。

Clash Royale是一款移动战略玩家与玩家的游戏,玩家在竞技场中玩纸牌赢。 每张卡具有不同的优缺点,并且不同的玩家更喜欢不同的卡。 Clash Royale会记住玩家玩得最多的一张牌; 这是他们的“收藏夹”卡片。 玩家们聚集在一起,可以互相帮助。 Supercell是Clash Royale的开发人员,发布了基于HTTP的API,可在其中查询不同的统计信息。

您可以注册一个帐户来跟随本教程,但是如果您不这样做,仍然可以了解我们正在构建的内容。 如果您确实想注册帐户,请通过Clash Royale 开发人员门户创建API令牌。 然后,在个人资料下选择“创建新密钥”,然后输入名称,描述和有效的IP地址。 (需要一个确切的地址,因此我使用此站点来查找我的地址。)由于您永远不应在代码中保存API密钥,因此请将其作为单独的文件保存在〜/ .crtoken中


   
   
$ ls ~ / .crtoken
/ home / moshez / .crtoken

扭曲的程序

运行基于Twisted的程序需要许多其他程序包,以使体验尽可能流畅。 我不会在本教程中介绍所有这些内容,但是每个内容都值得探索以了解更多信息。

为了更容易了解发生了什么,让我们从打印Hello World的介绍性程序开始,然后我们将讨论其功能:


   
   
import collections , json , os , sys , urllib . parse
from twisted. internet import task , defer
import treq

with open ( os . path . expanduser ( "~/.crtoken" ) ) as fpin:
    token = fpin. read ( ) . strip ( )

def main ( reactor ) :
    print ( "Hello world" )
    return defer. succeed ( None )

task. react ( main , sys . argv [ 1 : ] )

这将导入比“ Hello world”示例所需的模块更多的模块。 我们将在程序的最终版本中使用这些模块,这些模块将完成异步查询API的更为复杂的任务。 导入后,程序将从文件中读取令牌并将其存储在变量token中 。 (我们现在不打算对令牌做任何事情,但是很高兴看到该语法。)接下来是一个接受Twisted React器main函数。 React堆有点像扭曲包装的复杂机械的接口。 在这种情况下, main函数将作为参数发送,并被提供一个附加参数。

主体返回defer.succeed(None) 。 这就是它返回正确类型的值的方式:一个延迟的值,但是已经被“触发”或“调用”了。 因此,根据需要,该程序将在打印Hello world之后立即退出。

接下来,我们将研究异步函数的概念并确保


   
   
async def get_clan_details ( clan ) :
      print ( "Hello world" , clan )

def main ( reactor , clan ) :
    return defer. ensureDeferred ( get_clan_details ( clan ) )

task. react ( main , sys . argv [ 1 : ] )

在该程序中,应从相同的导入开始,我们将所有逻辑移至异步函数get_clan_details 。 就像常规函数一样, 异步函数在最后具有隐式返回None 。 但是,异步函数(有时也称为协同例程)与Deferred不同的类型 。 为了让Twisted(自Python 1.5.2开始存在)使用此现代功能,我们必须使用sureDeferred来修改协

尽管我们可以不用协例程来编写所有逻辑,但是使用异步语法将使我们能够编写更易于理解的代码,并且需要将更少的代码移入嵌入式回调中。

下一个要引入的概念是等待 。 稍后,我们将等待网络呼叫,但为简单起见,现在,我们将等待计时器。 Twisted具有一个特殊的功能task.deferLater ,它将在经过一段时间后调用具有给定参数的功能。

以下程序将花费五秒钟来完成:


   
   
async def get_clan_details ( clan , reactor ) :
     out = await task. deferLater (
         reactor ,
          5 ,
          lambda clan: f "Hello world {clan}" ,
         clan
      )
      print ( out )

def main ( reactor , clan ) :
    return defer. ensureDeferred ( get_clan_details ( clan , reactor ) )

task. react ( main , sys . argv [ 1 : ] )

关于类型的注释: task.deferLater返回Deferred ,大多数不具有该值的Twisted函数也是如此。 在运行Twisted事件循环时,我们可以等待 Deferred值和协同例程。

函数task.deferLater将等待五秒钟,然后调用lambda ,计算要打印的字符串。

现在,我们有了编写高效的氏族分析程序所需的所有Twisted构建基块!

带有treq的异步调用

由于我们将使用全局React堆,因此我们不再需要将React堆作为计算这些统计数据的函数中的参数来接受:

 async def get_clan_details ( clan ) : 

使用令牌的方法是作为标头中的“承载”令牌:

     headers = { b 'Authorization' : b 'Bearer ' + token . encode ( 'ascii' ) } 

我们希望发送氏族标签,它将是字符串。 氏族标记以开头,因此在将其放入URL之前必须对其加引号。 这是因为具有特殊含义“ URL片段”:

      clan = urllib . parse . quote ( clan ) 

第一步是获取氏族的详细信息,包括氏族成员:


   
   
     res = await treq. get ( "https://api.clashroyale.com/v1/clans/" + clan ,
                          headers = headers )

注意,我们必须等待 treq.get调用。 由于它是异步网络调用,因此我们必须明确说明何时等待和获取信息。 只是用的await语法来调用一个函数递延 不会让我们以异步的全功率(我们会看到以后怎么办吧)。

接下来,获取标题后,我们需要获取内容。 treq库为我们提供了一个直接解析JSON的辅助方法:

      content = await res. json ( ) 

内容包括有关氏族的一些元数据(对于我们当前的目的而言并不有趣),以及包含氏族成员的memberList字段。 请注意,尽管其中包含有关玩家的一些数据,但当前收藏夹卡并不包含在其中。 它确实包含了我们可以用来检索更多数据的唯一“玩家标签”。

我们收集所有玩家标签,并且由于它们也以开头,因此我们用URL引用它们:


   
   
     player_tags = [ urllib . parse . quote ( player [ 'tag' ] )
                    for player in content [ 'memberList' ] ]

最后,我们了解了treq和Twisted的真正功能:一次生成对播放器数据的所有请求! 确实可以加快此类任务的执行速度,该任务一遍又一遍地查询API。 在具有速率限制的API的情况下,这可能会出现问题。

有时候,我们需要考虑我们的API所有者,而不要遇到任何速率限制。 在Twisted中有明确支持速率限制的技术,但它们超出了本教程的范围。 (一个重要的工具是defer.DeferredSemaphore 。)


   
   
     requests = [ treq. get ( "https://api.clashroyale.com/v1/players/" + tag ,
                          headers = headers )
                  for tag in player_tags ]

旁白:等待,延迟和回调

对于那些对返回的对象的细节感到好奇的人,下面仔细看看发生了什么。

请记住,请求不会直接返回JSON正文。 之前,我们使用了wait,这样我们就不必担心请求返回的确切信息了。 他们实际上返回了DeferredDeferred可以具有附加的回调 ,该回调将修改Deferred。 如果回调返回 Deferred,则Deferred的最终值将是返回的Deferred的

因此,对于每个延迟的对象,我们都附加一个回调,该回调将检索正文的JSON:


   
   
      for request in requests:
         request. addCallback ( lambda result: result. json ( ) )

将回调附加到Deferreds是一种更手动的技术,它使代码难以遵循,但更有效地使用了异步功能。 具体来说,由于我们要同时附加所有回调,因此无需等待网络调用(可能需要很长时间)来指示如何对结果进行后处理。

从递延到价值观

在收集所有结果之前,我们无法计算最受欢迎的收藏卡。 我们有一个Deferred列表,但是我们想要的是一个Deferred ,它获得一个list值 。 这种反转正是Twisted函数defer.gatherResults所做的:

      all_players = await defer. gatherResults ( requests ) 

这个看似无辜的电话是我们充分利用Twisted力量的地方。 该defer.gatherResults函数立即返回一个deferred当所有的成分都Deferreds解雇只会 ,将与结果火。 它甚至为我们提供了免费的错误处理:如果出现任何Deferreds错误,它将立即返回失败的deferred,这将导致await引发异常。

现在我们已经掌握了所有玩家的详细信息,我们需要获取一些数据。 我们将使用Python最酷的内置插件之一collections.Counter 。 此类记录了事物的清单,并统计了每件事物出现的次数,这正是我们进行点票或人气竞赛所需要的:


   
   
     favorite_card = collections . Counter ( [ player [ "currentFavouriteCard" ] [ "name" ]
                                          for player in all_players ] )

最后,我们打印它:

      print ( json. dumps ( favorite_card. most_common ( ) , indent = 4 ) ) 

放在一起

因此,将它们放在一起,我们有:


   
   
import collections , json , os , sys , urllib . parse
from twisted. internet import task , defer
import treq

with open ( os . path . expanduser ( "~/.crtoken" ) ) as fpin:
    token = fpin. read ( ) . strip ( )


async def get_clan_details ( clan ) :
     headers = headers = { b 'Authorization' : b 'Bearer ' + token . encode ( 'ascii' ) }
     clan = urllib . parse . quote ( clan )
     res = await treq. get ( "https://api.clashroyale.com/v1/clans/" + clan ,
                          headers = headers )
     content = await res. json ( )
     player_tags = [ urllib . parse . quote ( player [ 'tag' ] )
                    for player in content [ 'memberList' ] ]
     requests = [ treq. get ( "https://api.clashroyale.com/v1/players/" + tag ,
                          headers = headers )
                  for tag in player_tags ]
      for request in requests:
         request. addCallback ( lambda result: result. json ( ) )
     all_players = await defer. gatherResults ( requests )
     favorite_card = collections . Counter ( [ player [ "currentFavouriteCard" ] [ "name" ]
                                          for player in all_players ] )
      print ( json. dumps ( favorite_card. most_common ( ) , indent = 4 ) )

def main ( reactor , clan ) :
    return defer. ensureDeferred ( get_clan_details ( clan ) )

task. react ( main , sys . argv [ 1 : ] )

得益于Twisted和treq的高效性和表达语法,这就是我们对API进行异步调用所需的全部代码。 而且,如果您想知道结果如何,我的战队中最喜欢的卡片列表是降序排列的Wizard,Mega Knight,Valkyrie和Royal Giant。

希望您喜欢使用Twisted编写更快的API调用!

翻译自: https://opensource.com/article/20/3/treq-python

python treq

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值