发布时间: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:59:55

Mac挖矿病毒排查与清理实战:从异常发热到彻底清除

1. 项目概述:当Mac风扇狂转,我意识到事情不简单那天下午,我正在用MacBook Pro处理一份文档,风扇突然开始发出持续的、不寻常的啸叫声,机身C面靠近Touch Bar的位置烫得可以煎鸡蛋。我下意识地看了一眼活动监视器&#x…

2026/8/25 7:59:55

Calibre电子书格式转换实用工具

Calibre电子书格式转换实用工具 【免费下载链接】calibre The official source code repository for the calibre ebook manager 项目地址: https://gitcode.com/GitHub_Trending/ca/calibre 想让一本EPUB格式的书在只支持MOBI的阅读器上打开,格式转换是绕不…

2026/8/25 10:25:44

AI大模型赋能安全测试实战:内网渗透、代码审计与漏洞利用

1. 从“人肉扫描”到“智能协同”:安全测试的范式转移 如果你和我一样,在安全测试这个行当里摸爬滚打了几年,一定经历过这样的场景:面对一个庞大的内网资产列表,手动一个个IP去扫端口、识别服务、测试弱口令&#xff0…

2026/8/25 10:25:44

Python集合与字典深度解析:从哈希表原理到实战应用场景

1. 项目概述:从“容器”到“工具”的认知跃迁刚接触Python那会儿,我也曾把set和dict混为一谈,觉得它们都是用来装东西的“容器”,无非一个装单个元素,一个装键值对。直到在项目里踩了几个不大不小的坑,比如…

2026/8/25 10:25:44

Python集合与字典深度解析:从哈希表原理到高效应用场景

1. 项目概述:从“容器”到“映射”,理解Python两大核心数据结构在Python的日常开发中,set(集合)和dict(字典)是高频使用的两个内置数据结构。很多刚入门的开发者,甚至一些有经验的程…

2026/8/25 10:20:34

nginx - 开启 gzip 压缩

文章目录一、 服务器端开启 Gzip 压缩二、 客户端开启 Gzip 压缩(也需要配置 nginx)三、总结1️. vite-plugin-compression 的作用2️. Nginx Gzip 压缩与插件的区别3️. 实际项目选择建议四、常见问题1️. Nginx 配置作用域规则2. gzip_static on; 的作…

2026/8/25 1:04:19

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

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

2026/8/24 1:12:32

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

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

2026/8/24 8:17:29

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

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

2026/8/25 0:04:14

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

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

2026/8/25 0:04:14

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

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

2026/8/24 13:42:17

实测才敢推 AI论文网站 2026最新测评与推荐

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

2026/8/24 18:13:48

2026必备!AI论文网站测评:最新推荐与深度对比

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

2026/8/25 1:08:14

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

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