在独立 Python 脚本中验证 Google 的正确机制是什么?
What is the correct mechanism to authenticate to Google in a standalone Python script?
我有一些代码可以用来将 Gmail 联系人的电子邮件地址提取到文本文件中。这是一个在 cron 作业中运行的简单 Python 脚本,并且基于 Python gdata library(当前为 v2.0.18)。
从本月早些时候开始,由于 Google deprecating the ClientLogin protocol,这不再有效。产生的错误如下所示:
{'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 应用程序),但忘记了我必须转换这个脚本。现在我在这里,我发现我不知道我应该如何完成这项工作。
我找到的所有参考文献(例如 Whosebug 上的 here on the Google Apps Developer Blog or here and here)都建议解决方案是使用 OAuth2Token。但是,这需要来自 Google API 控制台的客户端 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
理想情况下,我希望用其他一些使用 gdata 保留其余代码的机制来替换 client.ClientLogin()
。或者,如果这不能用 gdata 真正完成,我愿意转换到其他提供类似功能的库。
Can someone please suggest the proper way to do this in a standalone script? Or am I out of luck and there's no mechanism to accomplish this any more?
没有比您正在使用的机制更好的了。您将必须设置 Cloud Developer project 并使用 OAuth2,然后重写您的脚本。
为了使其尽可能面向未来,您可以切换到最新的 Contacts API. With this API, you can use the OAuth2 Device flow,这对于您的用例来说可能更简单。
不是你希望听到的答案,我知道,但我认为这是唯一的答案。
最终,使用 curl 拼凑一个 shell 脚本比
弄乱 gdata 库。正如预期的那样,我能够完成大部分
在脚本之外手动验证过程
OAuth2 Device Flow instructions.
完成验证过程后,我获得了 4 个必需的凭据:
客户端 ID、客户端密码、访问令牌和刷新令牌。
根据 Google 的文档,访问令牌最终会过期。你可以
通过要求令牌管理器刷新令牌来获取新的访问令牌。
当你这样做时,你显然会得到一个新的访问令牌,但 不是 一个新的刷新
令牌。
我将客户端 ID 和密码以及刷新令牌存储在 CREDENTIALS
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
这不是世界上最漂亮的东西,但我一直在寻找一些快速而肮脏的东西,这很管用。
我有一些代码可以用来将 Gmail 联系人的电子邮件地址提取到文本文件中。这是一个在 cron 作业中运行的简单 Python 脚本,并且基于 Python gdata library(当前为 v2.0.18)。
从本月早些时候开始,由于 Google deprecating the ClientLogin protocol,这不再有效。产生的错误如下所示:
{'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 应用程序),但忘记了我必须转换这个脚本。现在我在这里,我发现我不知道我应该如何完成这项工作。
我找到的所有参考文献(例如 Whosebug 上的 here on the Google Apps Developer Blog or here and here)都建议解决方案是使用 OAuth2Token。但是,这需要来自 Google API 控制台的客户端 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
理想情况下,我希望用其他一些使用 gdata 保留其余代码的机制来替换 client.ClientLogin()
。或者,如果这不能用 gdata 真正完成,我愿意转换到其他提供类似功能的库。
Can someone please suggest the proper way to do this in a standalone script? Or am I out of luck and there's no mechanism to accomplish this any more?
没有比您正在使用的机制更好的了。您将必须设置 Cloud Developer project 并使用 OAuth2,然后重写您的脚本。
为了使其尽可能面向未来,您可以切换到最新的 Contacts API. With this API, you can use the OAuth2 Device flow,这对于您的用例来说可能更简单。
不是你希望听到的答案,我知道,但我认为这是唯一的答案。
最终,使用 curl 拼凑一个 shell 脚本比 弄乱 gdata 库。正如预期的那样,我能够完成大部分 在脚本之外手动验证过程 OAuth2 Device Flow instructions.
完成验证过程后,我获得了 4 个必需的凭据: 客户端 ID、客户端密码、访问令牌和刷新令牌。 根据 Google 的文档,访问令牌最终会过期。你可以 通过要求令牌管理器刷新令牌来获取新的访问令牌。 当你这样做时,你显然会得到一个新的访问令牌,但 不是 一个新的刷新 令牌。
我将客户端 ID 和密码以及刷新令牌存储在 CREDENTIALS
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
这不是世界上最漂亮的东西,但我一直在寻找一些快速而肮脏的东西,这很管用。