Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/356.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脚本中向Google进行身份验证的正确机制是什么?_Python_Google Oauth_Gdata_Google Contacts Api - Fatal编程技术网

在独立Python脚本中向Google进行身份验证的正确机制是什么?

在独立Python脚本中向Google进行身份验证的正确机制是什么?,python,google-oauth,gdata,google-contacts-api,Python,Google Oauth,Gdata,Google Contacts Api,我有一些代码,我用它将Gmail联系人的电子邮件地址提取到文本文件中。它是一个在cron作业中运行的简单Python脚本,基于(当前为v2.0.18) 截至本月早些时候,由于谷歌的原因,这一点不再有效。由此产生的错误如下所示: {'status': 401, 'body': '<?xml version="1.0" encoding="UTF-8"?>\n<errors xmlns="http://schemas.google.com/g/2005">\n <err

我有一些代码,我用它将Gmail联系人的电子邮件地址提取到文本文件中。它是一个在cron作业中运行的简单Python脚本,基于(当前为v2.0.18)

截至本月早些时候,由于谷歌的原因,这一点不再有效。由此产生的错误如下所示:

{'status': 401, 'body': '<?xml version="1.0" encoding="UTF-8"?>\n<errors xmlns="http://schemas.google.com/g/2005">\n <error>\n  <domain>GData</domain>\n  <code>required</code>\n  <location type="header">Authorization</location>\n  <internalReason>Login Required</internalReason>\n </error>\n</errors>\n', 'reason': 'Unauthorized'}
我知道这会发生,并在其他地方处理它(比如AppEngine应用程序),但忘记了我必须转换这个脚本。现在我在这里,我发现我不知道我应该如何使这项工作

我在Google Apps Developer博客或StackOverflow上找到的所有参考资料都表明,解决方案是使用OAuth2Token。但是,这需要来自GoogleAPI控制台的客户机id和客户机机密,该控制台与应用程序绑定。我没有申请表。我只想通过我的脚本进行身份验证

有人能建议在独立脚本中执行此操作的正确方法吗?还是我运气不好,再也没有办法做到这一点了

这是现有联系人代码的核心:

from gdata.contacts.service import ContactsService, ContactsQuery

user = "myuser@gmail.com"
password = "mypassword"

addresses = set()
client = ContactsService(additional_headers={"GData-Version":"2"})
client.ssl = True
client.ClientLogin(user, password)
groups = client.GetGroupsFeed()
for group in groups.entry:
   if group.content.text == "System Group: My Contacts":
      query = ContactsQuery()
      query.max_results = 9999   # large enough that we'll get "everything"
      query.group = group.id.text
      contacts = client.GetContactsFeed(query.ToUri())
      for contact in contacts.entry:
         for email in contact.email:
            addresses.add(email.address.lower())
      break
return addresses
理想情况下,我希望用一些其他机制替换
client.ClientLogin()
,这些机制使用gdata保留其余代码。或者,如果gdata不能真正做到这一点,我愿意转换到其他提供类似功能的库

有人能建议在独立脚本中执行此操作的正确方法吗?还是我运气不好,再也没有办法做到这一点了

没有比你现在使用的机制更好的机制了。您必须设置并使用OAuth2,然后重写脚本

为了使其尽可能不受未来影响,您可以切换到最新版本。使用此API,您可以使用,这对于您的用例来说可能更简单


我知道,这不是您希望听到的答案,但我认为这是唯一的答案。

使用curl编写shell脚本比使用curl更容易 搞乱格达图书馆。正如预期的那样,我能够完成大部分任务 在脚本之外,根据

完成验证过程后,我获得了4项必需的凭证: 客户端id、客户端密码、访问令牌和刷新令牌。 根据谷歌的文档,访问令牌最终会过期。你可以 通过请求令牌管理器刷新令牌来获取新的访问令牌。 当您这样做时,您显然会得到一个新的访问令牌,但不会得到新的刷新 代币

我将客户端id和密码以及刷新令牌存储在
凭据中
JSON格式的文件。由于访问令牌随时间而变化,因此它存储在
access
文件中,也是JSON格式

脚本的重要部分如下所示:

#!/bin/ksh

CLIENT_ID=$(cat ${CREDENTIALS} | jq -r ".client_id")
CLIENT_SECRET=$(cat ${CREDENTIALS} | jq -r ".client_secret")
REFRESH_TOKEN=$(cat ${CREDENTIALS} | jq -r ".refresh_token")
ACCESS_TOKEN=$(cat ${ACCESS} | jq -r ".access_token")

CONTACTS_URL="https://www.google.com/m8/feeds/contacts/default/full?access_token=${ACCESS_TOKEN}&max-results=5000&v=3.0"
ERROR=$(curl --show-error --silent --fail "${CONTACTS_URL}" -o ${CONTACTS_XML} 2>&1)
RESULT=$?
if [[ ${RESULT} -eq 0 ]]
then
   cat ${CONTACTS_XML} | grep 'gd:email' | sed 's/^.*address="//g' | sed 's/".*$//g' | tr '[:upper:]' '[:lower:]' | sort | uniq
elif [[ ${RESULT} -eq 22 ]]
then
   echo "${ERROR}" | grep -q "401"
   if [[ $? -eq 0 ]]
   then
      TOKEN_URL="https://www.googleapis.com/oauth2/v3/token"
      REFRESH_PARAMS="client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&refresh_token=${REFRESH_TOKEN}&grant_type=refresh_token"
      ERROR=$(curl --show-error --silent --fail --data "${REFRESH_PARAMS}" ${TOKEN_URL} -o ${REFRESH_JSON})
      RESULT=$?
      if [[ ${RESULT} -eq 0 ]]
      then
         ACCESS_TOKEN=$(cat ${REFRESH_JSON} | jq -r ".access_token")
         jq -n --arg access_token "${ACCESS_TOKEN}" '{"access_token": $access_token, }' > ${ACCESS}

         CONTACTS_URL="https://www.google.com/m8/feeds/contacts/default/full?access_token=${ACCESS_TOKEN}&max-results=5000&v=3.0"
         ERROR=$(curl --show-error --silent --fail "${CONTACTS_URL}" -o ${CONTACTS_XML} 2>&1)
         RESULT=$?
         if [[ ${RESULT} -eq 0 ]]
         then
            cat ${CONTACTS_XML} | grep 'gd:email' | sed 's/^.*address="//g' | sed 's/".*$//g' | tr '[:upper:]' '[:lower:]' | sort | uniq
         else
            print "Unexpected error: ${ERROR}" >&2
            exit 1
         fi
      else
         print "Unexpected error: ${ERROR}" >&2
         exit 1
      fi
   else
      print "Unexpected error: ${ERROR}" >&2
      exit 1
   fi
else
   print "Unexpected error: ${ERROR}" >&2
   exit 1
fi

这并不是世界上最漂亮的东西,但我一直在寻找一些又快又脏的东西,这很管用。

哇。令人惊讶的是,一开始如此简单的事情,却在一瞬间变得如此复杂。因为我只需要对联系人进行只读访问,所以看起来OAuth2设备流应该满足我的需要。如果我正确理解了文档,看起来我可以手动执行初始请求和同意,然后在必要时让脚本续订令牌以保持长期访问…?这很好。我将接受这个答案,但我也会在下面的脚本中剪切粘贴,以防对其他人有用。