大家好!前几天写了一篇个人程序小文,里面提到调用未记录的“秘密”API很有意思,需要从浏览器复制cookies才能访问。有读者问怎么做,我就详细说一下,但是过程很简单。我们还将讨论您在调用未记录的API时可能遇到的错误和道德问题。让我们以GoogleHangouts为例。我选择它并不是因为这个例子最有用(我觉得官方的API更实用),而是因为这个场景下比较有用的站点很多都是小站点,API被滥用的小站点会受到损害更大。所以我们使用GoogleHangouts,因为我100%确定Google论坛不受这种启发式算法的影响。让我们现在开始!第一步:打开开发者工具,找到一个JSON响应我浏览了https://hangouts.google.com,在Firefox的开发者工具中打开“网络”选项卡,找到了一个JSON响应。您还可以使用Chrome的开发者工具。打开后界面如下图所示:找到其中一个“Type”栏显示为json的请求。我搜索了一会儿寻找一个有趣的请求,突然我发现了一个“people”端点,它似乎返回了我们的联系信息。听起来很有趣,让我们来看看。第2步:复制为cURL在下一步中,我右键单击我感兴趣的请求,然后单击“复制”->“复制为cURLCopyascURL”。然后我将curl命令粘贴到终端并运行它。结果如下:$curl'https://people-pa.clients6.google.com/v2/people/?key=REDACTED'-XPOST......(省略了很多请求标头)警告:二进制输出可能会弄乱你的终端。使用“--output-”告诉Warning:curl将它输出到您的终端,或考虑“--outputWarning:”以保存到文件。您可能会想——奇怪,“二进制输出无法在您的终端上正常显示”有什么问题?原因是浏览器默认向服务器发送请求头时带有Accept-Encoding:gzip,deflate参数,会对输出结果进行压缩。我们可以将输出通过管道传输到gunzip以解压缩,但我们发现没有此参数更容易请求。所以我们删除了一些不相关的请求头。第3步:删除不相关的请求标头下面是我从浏览器获得的完整curl命令。有很多行!我用反斜杠(\)分隔请求,这样每个请求头占一行,看起来更清晰:curl'https://people-pa.clients6.google.com/v2/people/?key=REDACTED'\-XPOST\-H'User-Agent:Mozilla/5.0(X11;Linuxx86_64;rv:96.0)Gecko/20100101Firefox/96.0'\-H'Accept:*/*'\-H'Accept-Language:en'\-H'Accept-Encoding:gzip,deflate'\-H'X-HTTP-Method-Override:GET'\-H'Authorization:SAPISIDHASHREDACTED'\-H'Cookie:REDACTED'-H'Content-Type:application/x-www-form-urlencoded'\-H'X-Goog-AuthUser:0'\-H'来源:https://hangouts.google.com'\-H'连接:keep-alive'\-H'Referer:https://hangouts.google.com/'\-H'Sec-Fetch-Dest:empty'\-H'Sec-Fetch-Mode:cors'\-H'Sec-Fetch-Site:same-site'\-H'Sec-GPC:1'\-H'DNT:1'\-H'Pragma:no-cache'\-H'Cache-Control:no-cache'\-H'TE:trailers'\--data-raw'personId=101777723309&personId=1175339043204&personId=1115266537043&personId=116731406166&extensionSet.extensionNames=HANGOUTS_ADDITIONAL_DATA&extensionSet.extensionNames=HANGOUTS_OFF_NETWORK_GAIA_GET&extensionSet.extensionNames=HANGOUTS_PHONE_DATA&includedProfileStates=ADMIN_BLOCKED&includedProfileStates=DELETED&includedProfileStates=PRIVATE_PROFILE&mergedPersonSourceOptions.includeAffinity=CHAT_AUTOCOMPLETE&coreIdParams.useRealtimeNotificationExpandedAcls=true&requestMask.includeField.paths=person.email&requestMask.includeField.paths=person.gender&requestMask.includeField.paths=person.in_app_reachability&requestMask.includeField.paths=person.metadata&requestMask.includeField.paths=person.name&requestMask.includeField.paths=person.phone&requestMask.includeField.paths=person.photo&requestMask.includeField.paths=person.read_only_profile_info&requestMask.includeField.paths=person.organization&requestMask.includeField.paths=人。location&requestMask.includeField.paths=person.cover_photo&requestMask.includeContainer=PROFILE&requestMask.includeContainer=DOMAIN_PROFILE&requestMask.includeContainer=CONTACT&key=REDACTED'乍一看好像内容很多,现在不用再去想每一行是什么意思了,只需要删除不相关的行即可。我通常通过删除行来验证是否可以删除行以查看是否有任何错误——只要请求没有错误,就继续删除请求标头。通常,您可以删除Accept*、Referer、Sec-*、DNT、User-Agent和缓存相关的标头。在这个例子中,我把请求剪切成下面的样式:curl'https://people-pa.clients6.google.com/v2/people/?key=REDACTED'\-XPOST\-H'Authorization:SAPISIDHASHREDACTED'\-H'Content-Type:application/x-www-form-urlencoded'\-H'Origin:https://hangouts.google.com'\-H'Cookie:REDACTED'\--data-raw'personId=101777723309&personId=1175339043204&personId=1115266537043&personId=116731406166&extensionSet.extensionNames=HANGOUTS_ADDITIONAL_DATA&extensionSet.extensionNames=HANGOUTS_OFF_NETWORK_GAIA_GET&extensionSet.extensionNames=HANGOUTS_PHONE_DATA&includedProfileStates=ADMIN_BLOCKED&includedProfileStates=DELETED&includedProfileStates=PRIVATE_PROFILE&mergedPersonSourceOptions.includeAffinity=CHAT_AUTOCOMPLETE&coreIdParams.useRealtimeNotificationExpandedAcls=true&requestMask.includeField.paths=person.email&requestMask.includeField.paths=person.gender&requestMask.includeField.paths=person.in_app_reachability&requestMask.includeField.paths=person.metadata&requestMask.includeField.paths=person.name&requestMask.includeField.paths=person.phone&requestMask.includeField.paths=person.photo&requestMask.includeField.paths=person.read_only_profile_info&requestMask.includeField.paths=person.organization&requestMask.includeField.paths=person.location&requestMask.includeField.paths=person.cover_photo&requestMask.includeContainer=PROFILE&requestMask.includeContainer=DOMAIN_PROFILE&requestMask.includeContainer=CONTACT&key=REDACTED'所以我只需要4个请求标头:授权-类型,来源和Cookie更易于管理。第4步:在Python中发出请求现在我们知道了我们需要哪些请求标头,我们可以将curl命令转换为Python程序!这部分是一个相当机械的过程,目标只是发送与Python中的cUrl相同的数据。下面是代码示例。我们使用Python的requests包来实现与前面的curl命令相同的功能。我将整个长请求分解为一组元组,以使其看起来更清晰。importrequestsimporturllibdata=[('personId','101777723'),#我也稍微修改了这些ID('personId','117533904'),('personId','111526653'),('personId','116731406'),('extensionSet.extensionNames','HANGOUTS_ADDITIONAL_DATA'),('extensionSet.extensionNames','HANGOUTS_OFF_NETWORK_GAIA_GET'),('extensionSet.extensionNames','HANGOUTS_PHONE_DATA'),('includedProfileStates','ADMIN_BLOCKED'),('includedProfileStates','DELETED'),('includedProfileStates','PRIVATE_PROFILE'),('mergedPersonSourceOptions.includeAffinity','CHAT_AUTOCOMPLETE'),('coreIdParams.useRealtimeNotificationExpandedAcls','true'),('requestMask.includeField.paths','person.email'),('requestMask.includeField.paths','person.gender'),('requestMask.includeField.paths','person.in_app_reachability'),('requestMask.includeField.paths','person.metadata'),('requestMask.includeField.paths','person.name'),('requestMask.includeField.paths','person.phone'),('requestMask.includeField.paths','person.photo'),('requestMask.includeField.paths','person.read_only_profile_info'),('requestMask.includeField.paths','person.organization'),('requestMask.includeField.paths','person.location'),('requestMask.includeField.paths','person.cover_photo'),('requestMask.includeContainer','PROFILE'),('requestMask.includeContainer','DOMAIN_PROFILE'),('requestMask.includeContainer','CONTACT'),('key','REDACTED')]response=requests.post('https://people-pa.clients6.google.com/v2/people/?key=REDACTED',headers={'X-HTTP-Method-Override':'GET','Authorization':'SAPISIDHASHREDACTED','Content-Type':'application/x-www-form-urlencoded','Origin':'https://hangouts.google.com','Cookie':'已编辑',},data=urllib.parse.urlencode(data),)print(response.text)我运行了这个程序并且它工作了——它输出了一堆JSON数据!奇妙!你会注意到我在某些地方替换了REDACTED,因为如果我把原始数据把它列出来,这样你就可以用我的帐户访问谷歌论坛了,这是非常糟糕的,运行结束!现在我可以随意修改Python程序,比如传入不同的参数,或者解析结果等等。我不打算用它做其他有趣的事情,因为我对这个API一点兴趣都没有,我只是用它来说明请求API的过程。但是您确实可以对返回的一堆JSON进行一些处理。curlconverter看起来很强大有人评论说你可以使用https://curlconverter.com/自动将curl转换为Python(和其他一些语言!),这看起来很神奇——我是手动完成的。我将它用于此示例,一切似乎都运行良好。跟踪API的进度并不容易我不会夸大跟踪API进度的难度——API中发生的事情并不明显!我不知道传递给这个GoogleForumsAPI的一堆参数有什么用!但是有些参数看起来很直观,比如requestMask.includeField.paths=person.email可能意味着“包括每个人的电子邮件地址”。所以我只关心我能理解的参数,而不关心我不能理解的参数。(理论上)适用于所有场景有人可能会质疑——这种方法是不是适用于所有场景?答案是肯定的——浏览器并不神奇!浏览器发送到您的服务器的所有内容都是HTTP请求。因此,如果我复制浏览器发送的所有HTTP请求标头,后端会认为该请求是从我的浏览器发出的,而不是来自Python程序。当然,我们去掉了一些浏览器发送的请求头,所以理论上后端可以识别请求是来自浏览器还是Python程序,但他们通常不会检查。此处向读者提出一些警告-某些Google服务的后端以(对我而言)难以理解的方式与前端进行通信,因此即使您理论上可以模拟来自前端的请求,但在实践中可能行不通。更容易受到攻击的更大的API将有更多的保护。我们已经知道如何调用未记录的API。现在让我们谈谈可能出现的问题。问题1:会话cookie过期一个大问题是我正在使用我的google会话cookie进行身份验证,因此当我的浏览器会话过期时,脚本将不起作用。这意味着它不会工作很长时间(我宁愿调用一个真正的API),但如果我只是想一次快速抓取一小部分数据,那很好。问题2:滥用如果我向小型网站发出请求,我的Python脚本可能会导致服务中断,因为请求数量超出了它们的处理能力。所以我在发出请求时尽量谨慎,尽量不要太快发送很多请求。这一点尤其重要,因为没有官方API的网站往往规模较小,而且没有足够的资源。显然在这个例子中这不是问题——我想我在写这篇文章的过程中已经向Google论坛后端发出了20个请求,他们肯定能够处理它。如果您使用您的账户身份过度访问此API并导致故障,您的账户可能会被暂时封禁(合理)。我只下载我自己的数据或公共数据——我的目的不是寻找网站的弱点。请记住,每个人都可以访问您未记录的API。我认为本文中最重要的信息不是如何使用其他人未记录的API。虽然它很有趣,但它有一些局限性,而且我不经常这样做。更重要的是,任何人都可以通过这种方式访问??您的后端API!每个人都有开发者工具和网络选项卡,很容易看到你传递给后端的参数,修改它们。因此,如果一个人通过修改某些参数来获取其他用户的信息,是不值得提倡的。我想大多数提供公共API的开发人员都知道这一点,但我再次提到它是因为每个初学者都应该知道。:)