Outlook不支持账号密码改OAuth2.0认证方式获取outlook邮箱收件箱以及附件(python)

发布时间:2026/8/25 8:04:56

Outlook不支持账号密码改OAuth2.0认证方式获取outlook邮箱收件箱以及附件(python)
1.在Azure 门户注册应用程序微软文档地址重定向的地址配置(微软地址) https://login.microsoftonline.com/common/oauth2/nativeclient注册应用地址2.程序代码#安装包以及需要的驱动 pip3 install playwright playwright installget_token1() 》 grant_type: client_credentials最简单 ,不需要模拟登录去获取code,只需要把获取邮箱地址 /me/部分换成/users/xxxddd.com(公司对应邮箱)即可import base64 import json import logging from io import BytesIO from playwright.sync_api import sync_playwright import time import urllib.parse import requests from pia.utils.cache import get_redis_value, set_redis_expire from pia.utils.constants import OUTLOOK_CLIENT_ID, OUTLOOK_TENANT_ID, OUTLOOK_CLIENT_SECRET, OUTLOOK_REDIRECT_URI, \ COS_OUTLOOK_DIR, OUTLOOK_TOP from pia.utils.cos_upload import upload_stream_to_cos, check_exists from pia.utils.reids_key import OUTLOOK_TOKEN log logging.getLogger(__name__) #这一步很重要是给用户读取收件箱的权限赋予给你注册的应用这样才能基于应用去读取邮箱这里需要账号和密码 #账号登录 官方文档 https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow def get_authorization_code(_user_name, _pass_word): with sync_playwright() as play_wright: browser play_wright.chromium.launch(headlessTrue) context browser.new_context(localezh-CN, accept_downloadsTrue) try: # Open new page page context.new_page() url fhttps://login.microsoftonline.com/{OUTLOOK_TENANT_ID}/oauth2/v2.0/authorize?client_id{OUTLOOK_CLIENT_ID}response_typecoderedirect_uri{OUTLOOK_REDIRECT_URI}response_modequeryscopehttps%3A%2F%2Fgraph.microsoft.com%2Fmail.readstate12345 page.goto(url) page.click([placeholder\电子邮件、电话或\\ Skype\]) page.fill([placeholder\电子邮件、电话或\\ Skype\], _user_name) with page.expect_navigation(): page.click(text下一步) page.click([placeholder\密码\]) page.fill([placeholder\密码\], _pass_word) # 点击登录并等待导航 with page.expect_navigation(timeout6000): page.click(text登录, timeout2000) # 如果有确认信息点击是 try: page.click(text是, timeout2000) except: pass # 上一步没生效在执行一次 try: page.click(text是, timeout2000) except: pass # 如果有确认信息点击是 try: # 点击接受按钮 page.click(input[typesubmit][nameidSIButton9][value接受], timeout2000) except: pass time.sleep(3) # 解析 URL 获取授权码 query dict(urllib.parse.parse_qsl(urllib.parse.urlsplit(page.url).query)) authorization_code query.get(code) if not authorization_code: # todo 待开发 print(f*****************{_user_name}: 密码错误\n\n) return return authorization_code except Exception as e: print(foutlook获取code失败:::{traceback.format_exc()}) # 输出异常信息 # 记录错误继续执行后续操作 return finally: context.close() browser.close() def get_token(authorization_code): param {client_id: OUTLOOK_CLIENT_ID, code: authorization_code, redirect_uri: OUTLOOK_REDIRECT_URI, grant_type: authorization_code, client_secret: OUTLOOK_CLIENT_SECRET } token_headers {Content-Type: application/x-www-form-urlencoded} token_url fhttps://login.microsoftonline.com/{OUTLOOK_TENANT_ID}/oauth2/v2.0/token res requests.post(urltoken_url, headerstoken_headers, dataparam) access_token json.loads(res.text).get(access_token) return fBearer {access_token} # api官方文档 https://learn.microsoft.com/en-us/graph/api/mailfolder-list-messages?viewgraph-rest-1.0 def get_inbox(authorization, str_start, str_end): condition ?$filter if str_start: condition fReceivedDateTime ge {str_start} and condition freceivedDateTime lt {str_end} # 获取收件箱里面的 邮件 endpoint fhttps://graph.microsoft.com/v1.0/me/mailFolders/inbox/messages{condition}$top{OUTLOOK_TOP} http_headers {Authorization: authorization, Accept: application/json, Content-Type: application/json} data requests.get(endpoint, headershttp_headers, streamFalse).json() return data def get_email_attachments(authorization, email_id): # 获取收件箱里面的邮件附件 endpoint fhttps://graph.microsoft.com/v1.0/me/messages/{email_id}/attachments http_headers {Authorization: authorization, Accept: application/json, Content-Type: application/json} data requests.get(endpoint, headershttp_headers, streamFalse).json() return data #程序入口 def deal_user_email(_user_name, _pass_word, str_start, str_end) - list: result [] redis_key f{OUTLOOK_TOKEN}:{_user_name} # 缓存 authorization get_redis_value(redis_key) if authorization: pass else: authorization_code get_authorization_code(_user_name, _pass_word) authorization get_token(authorization_code) if authorization: set_redis_expire(redis_key, authorization, 60 * 60) if authorization: email get_inbox(authorization, str_start, str_end) if email: email_values email.get(value) if email_values: for value in email_values: # 邮件 id email_id value.get(id) # 是否存在附件 True/False has_attachments value.get(hasAttachments) value.update({attachments: {}}) if has_attachments and email_id: attachment_dict upload_attachment_to_cos(authorization, email_id, _user_name) value.update({attachments: attachment_dict}) result.append(value) else: log.error(foutlook user_name: {_user_name} Authorization Failed) return result 附件上传到cos def upload_attachment_to_cos(authorization, email_id, _user_name): attachment_dict {} attachments get_email_attachments(authorization, email_id) if attachments: attachment_values attachments.get(value) if attachment_values: for _value in attachment_values: # 附件 name attachment_name _value.get(name) # 附件 内容 attachment_content _value.get(contentBytes) # Step 1: 解码 Base64 字符串 decoded_data base64.b64decode(attachment_content) # Step 2: 创建一个 BytesIO 对象作为文件流 file_stream BytesIO(decoded_data) object_name f{COS_OUTLOOK_DIR}/{_user_name}/{email_id}/{attachment_name} is_exists, url check_exists(object_name) if not is_exists: url upload_stream_to_cos(file_stream, object_name) attachment_dict.update({attachment_name: url}) return attachment_dict #client_credentials 该方式最简单,拿到token后 只要是该应用下的 邮箱 xxxddd.com 都能直接获取 def get_token1(): # 替换为您的应用信息 client_id tenant_id client_secret # 请求访问令牌 token_url fhttps://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token data { grant_type: client_credentials, client_id: client_id, client_secret: client_secret, scope: https://graph.microsoft.com/.default } response requests.post(token_url, datadata) if response.status_code 200: access_token response.json().get(access_token) # 获取收件箱里面的 邮件 endpoint fhttps://graph.microsoft.com/v1.0/users/xxxddd.com/mailFolders/inbox/messages http_headers {Authorization: access_token, Accept: application/json, Content-Type: application/json} data requests.get(endpoint, headershttp_headers, streamFalse).json() print(fAccess Token: {access_token}) else: print(fError: {response.status_code}, {response.text})import traceback from datetime import datetime, timedelta from django.core.management import BaseCommand import logging import uuid from django.db import transaction from pia.models import PiaOutLookTask, PiaOutLookData from pia.utils.aes import decrypted from pia.utils.outlook import deal_user_email logging logging.getLogger(task) #任务的方式拉取 class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument(trace_id, typestr) def handle(self, *args, **options): trace_id options[trace_id] if not trace_id: trace_id str(uuid.uuid4()) self.trace_id trace_id self.writeLog(outlook email start) outlook_task PiaOutLookTask.objects.values(id, user_name, pwd, execution_time) for x in outlook_task: _id x.get(id) user_name x.get(user_name) self.writeLog(f############## outlook email user_name:{user_name} start ##############) pwd x.get(pwd) execution_time x.get(execution_time) # 获取当前时间 current_time datetime.now() # 格式化为 YYYY-MM-DD end_time current_time.strftime(%Y-%m-%dT%H:%M:%S) try: if user_name and pwd: _pwd decrypted(pwd) result deal_user_email(user_name, _pwd, f{execution_time}Z, F{end_time}Z) with transaction.atomic(): PiaOutLookTask.objects.filter(id_id).update(status0, execution_timeend_time) outlook_data PiaOutLookData.objects.filter(outlook_task_id_id, start_timeexecution_time, end_timeend_time) if outlook_data: outlook_data.update(contentresult) else: PiaOutLookData.objects.create(outlook_task_id_id, start_timeexecution_time, end_timeend_time, contentresult) self.writeLog(f############## outlook email user_name:{user_name} end ##############) except Exception: PiaOutLookTask.objects.filter(id_id).update(status1, execution_timeend_time) self.writeLog( f############## outlook email user_name:{user_name} execution failed::::{traceback.format_exc()}) self.writeLog(outlook email end) def writeLog(self, msg: str): logging.info(f[{self.trace_id}] {msg})

相关新闻

实用小工具

实用小工具

2026/8/25 7:54:56

目录1、win置顶窗口小工具2、查看串口实时数据Device Monitoring Studio3、通过使用Dependency工具检测是否缺少动态库。4、CopyTranslator 实时翻译5、VOFA 可扩展多功能串口助手6、fairygui 游戏GUI解决方案7、winmerge 和 cc-compare 文件比对工具(1)…

机械臂速成小指南(二十二):机械臂逆运动学的数值解方法

机械臂速成小指南(二十二):机械臂逆运动学的数值解方法

2026/8/25 7:54:56

👨‍🏫🥰🥳需要机械臂相关资源的同学可以在我的CSDN主页中寻找哦🤖😽🦄 指南目录📖: 🎉🎉机械臂速成小指南(零点五)&…

文件上传漏洞攻防全解析:从原理到实战的纵深防御体系构建

文件上传漏洞攻防全解析:从原理到实战的纵深防御体系构建

2026/8/25 7:54:56

1. 项目概述:从“上传”到“沦陷”的惊险一跃在网络安全的世界里,文件上传功能就像一扇连接用户与服务器内部世界的“门”。这扇门本应只允许特定格式、特定大小的“访客”进入,但一旦门锁(安全机制)存在缺陷&#xff…

Java面试必考:数据结构核心原理与应用解析

Java面试必考:数据结构核心原理与应用解析

2026/8/25 8:54:58

1. 为什么数据结构是Java面试的必考项十年前我刚参加工作时,第一次Java面试就被问到了HashMap的实现原理。当时支支吾吾说不清楚红黑树和链表的转换阈值,结果自然是被淘汰。后来做了面试官才发现,数据结构问题能直接暴露候选人的基本功扎实程…

银行信贷系统测试全流程与高频面试解析

银行信贷系统测试全流程与高频面试解析

2026/8/25 8:54:58

1. 银行信贷测试的核心价值与行业现状在金融科技高速发展的今天,银行信贷系统作为资金流转的核心枢纽,其稳定性和安全性直接关系到金融机构的运营风险。作为一名经历过数十个银行测试项目的从业者,我深刻体会到信贷系统测试与其他领域测试的本…

Redis面试核心考点与实战技巧解析

Redis面试核心考点与实战技巧解析

2026/8/25 8:54:58

1. Redis面试核心考点解析Redis作为当今最流行的内存数据库之一,已经成为技术面试中的必考内容。根据我多年参与技术面试和辅导的经验,80%的Redis相关问题都集中在以下五个核心领域。掌握这些知识点不仅能应对面试,更能提升日常开发中对Redis…

FreeRTOS任务创建详解:从xTaskCreate参数到调度原理

FreeRTOS任务创建详解:从xTaskCreate参数到调度原理

2026/8/25 8:54:58

1. 为什么“创建任务”是FreeRTOS入门的第一道门槛FreeRTOS不是一块板砖,而是一套精密运转的微型操作系统内核。很多人刚接触时以为“写个while(1)循环不就完事了”,结果一上手就卡在第一个函数——xTaskCreate()。这背后根本不是语法问题,而…

大模型面试必问:KV-Cache机制解析与优化

大模型面试必问:KV-Cache机制解析与优化

2026/8/25 8:54:58

1. 为什么大模型面试必问KV-Cache机制?这个问题几乎出现在所有大模型相关岗位的技术面试中,根本原因在于KV-Cache直接关系到推理效率这个核心生产指标。当面试官抛出这个问题时,实际上是在考察候选人对以下三个维度的理解深度:自回…

DashPlayer 切分长视频实操指南:粘贴时间戳,一步到位切好章节

DashPlayer 切分长视频实操指南:粘贴时间戳,一步到位切好章节

2026/8/25 8:44:58

DashPlayer 切分长视频实操指南:粘贴时间戳,一步到位切好章节 【免费下载链接】DashPlayer 为英语学习者量身打造的视频播放器,助你通过观看视频、沉浸真实语境,轻松提升英语水平。#美剧 #播放器 #听力 项目地址: https://gitco…

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

2026/8/24 19:53:32

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

2026/8/24 19:56:07

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

2026/8/24 21:16:09

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南

2026/8/25 0:04:34

三步把QQ空间历史说说导出到本地:GetQzonehistory 极简指南 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory Meta Description:GetQzonehistory 是一个QQ空间历史说…

洛谷 P7912:[CSP-J 2021 T4] 小熊的果篮 ← 双向链表

洛谷 P7912:[CSP-J 2021 T4] 小熊的果篮 ← 双向链表

2026/8/25 0:04:35

【题目来源】 https://www.luogu.com.cn/problem/P7912 【题目描述】 小熊的水果店里摆放着一排 n 个水果。每个水果只可能是苹果或桔子,从左到右依次用正整数 1,2,…,n 编号。连续排在一起的同一种水果称为一个“块”。小熊要把这一排水果挑到若干个果篮里&#x…

Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG

Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG

2026/8/25 0:04:35

Transformers.js 网页端图像抠图实战:零后端 3 行代码返回透明 PNG 【免费下载链接】transformers.js State-of-the-art Machine Learning for the web. Run 🤗 Transformers directly in your browser, with no need for a server! 项目地址: https:/…

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

2026/8/22 2:02:26

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…

导师推荐!2026最新AI论文工具测评与实用推荐

导师推荐!2026最新AI论文工具测评与实用推荐

2026/8/22 4:13:47

2026年真正好用的AI论文工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

告别游戏崩溃:XCOM 2模组管理器的智能革命

告别游戏崩溃:XCOM 2模组管理器的智能革命

2026/8/22 1:32:34

告别游戏崩溃:XCOM 2模组管理器的智能革命 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_mirrors/xc/xcom2-lau…