☰
Python读取163邮箱邮件:requests、selenium与IMAP三种方案对比与实战
2026/10/5 12:49:30 网站建设 项目流程

做邮件自动化的朋友应该都遇到过这个需求:想把163邮箱收件箱里的邮件列表抓下来,或者进一步提取正文、附件,用于自动收验证码、整理订阅邮件、做邮件监控。我早期接到这个需求时第一反应就是写爬虫,但163邮箱网页版的登录和渲染远比想象中麻烦,试过 urllib、requests、selenium 三种方案后,发现它们各有各的适用场景,也存在各自的坑。这篇文章把三种方案的路数和取舍讲清楚,另外附上一个我后来才意识到的更优解:直接用 IMAP 协议读邮件,从根本上绕开网页爬取的各种麻烦。

如果你正准备用 Python 处理 163 邮箱,或者对 Web 自动化的技术选型感兴趣,这篇总结应该能帮你省下不少时间。我会直接贴出可用代码和踩坑记录,也会说明哪些地方需要你根据当前页面结构做适配。

1. 先把趋势看明白:爬邮箱网页版的本质是什么

1.1 邮件系统的动态渲染趋势,纯请求越来越难

很多人的第一反应是:收件箱不就是个列表页面吗,直接拿 requests 请求 HTML 再解析不就行了?理论上确实是这样,但实际操作中你会发现 163 邮箱网页版经过了多次改版,页面大量使用异步加载。你请求回来的 HTML 里,邮件列表的数据可能并不完整,需要在浏览器中执行 JavaScript 后才会渲染出来。

这就引出了 Web 爬虫里最核心的分水岭:目标页面是服务端渲染还是客户端渲染。早年的邮箱 Web 端会在 HTML 里直接输出邮件表格,requests 一把梭就能搞定;现在的邮箱页面普遍是前后端分离,列表数据往往通过内部的 JSON 接口异步返回,或者用类似 iframe 内嵌子页面的方式组织。用 requests 抓回来的 HTML,要么只是个骨架,要么数据结构复杂到你怀疑人生。

所以在动手之前,先花几分钟打开浏览器开发者工具,看 Network 面板里页面加载了哪些真实请求,HTML 里到底包不包含邮件列表数据。这一步决定了你后面选择哪条技术路线,也避免你写完一版 regex 解析后页面一改就全线崩溃。

1.2 三种 Web 方案的横向对比

urllib、requests、selenium 是处理 Web 自动化的三个典型层级:

  • urllib 是 Python 标准库自带的 HTTP 客户端,功能最底层,几乎所有细节都要手动处理,比如 Cookie 存储、请求头拼接、重定向跟随等。适合用来理解 HTTP 协议工作原理,但写业务代码效率低。
  • requests 是对 HTTP 的极佳封装,Session 自动管理 Cookie,代码简洁直观,是目前爬虫脚本的主力。适合接口调试、服务端渲染页面的抓取,以及配合内部 JSON 接口使用。
  • selenium 直接驱动真实浏览器,能完整执行页面 JavaScript,处理复杂交互,几乎可以模拟真人操作。缺点是重、慢,且依赖 ChromeDriver 等浏览器驱动版本匹配。

三者不是替代关系,而是递进关系。能不用浏览器就不用在浏览器里跑,这是爬虫工程师的基本素养,因为浏览器的开销和稳定性问题会让你在批量任务里吃尽苦头。

1.3 有一条更优路径:IMAP 协议

在继续讲三种 Web 方案之前,我必须先剧透一个结论:如果你只是想读自己的邮件,那 IMAP 协议才是更合理的方案。

IMAP 是邮件客户端与邮件服务器之间的标准协议,163 邮箱本来就支持。你在网页上看到的“收件箱”,其实就是 IMAP 服务器上的 INBOX 文件夹。用 Python 标准库 imaplib 就能直接连上去,像操作本地队列一样拉取主题、发件人、时间、正文,代码量只有 Web 爬虫的三分之一,还不用担心登录加密、验证码、页面改版之类的问题。

我把 Web 方案放在前面讲,是因为很多人明确要求“用爬虫”,或者需要适配的不是邮件协议而是网页端功能(比如标记已读、管理文件夹)。但从选型角度,你至少应该知道 IMAP 这条路的存在,具体对比我会在第 5 部分展开。

2. 方案一:urllib——最原始的 HTTP 模拟,适合理解原理

2.1 核心思路:CookieJar 保持会话 + POST 表单登录

urllib 做爬虫的思路其实和 requests 类似,只是所有事情都要自己动手。核心是两件事:第一,用 http.cookiejar.CookieJar 构造一个带有 Cookie 管理能力的 opener,因为登录后服务器会通过 Set-Cookie 头下发会话凭证,后续请求必须携带这个 Cookie 才能被识别为登录状态;第二,向登录接口提交表单数据,模拟用户在网页上的登录动作。

163 邮箱的登录页面向来不是单纯 POST 用户名密码就能过的,中间还有加密和验证码环节。我在最初用 urllib 实战时卡得最久的就是这一步。网易统一登录页会通过 JavaScript 对密码做 RSA 加密后再提交,密钥由服务端动态下发。这意味着你光靠 urllib 发请求是不够的,还得先抓取页面上的加密公钥,然后在 Python 里复刻一遍加密算法。

这里我给一个折中思路:可以先手动在浏览器登录 163 邮箱,然后把登录后的 Cookie 字符串提取出来,直接塞进 urllib 的请求头里。这种方式对个人脚本来说完全够用,虽然登录这一步还是手动的,但之后抓取收件箱列表的过程是自动化的。

2.2 urllib 实现收件箱列表解析的完整代码

我直接贴一段基于手动 Cookie 的 urllib 获取收件箱列表的示例。这段代码的核心是请求收件箱所在的实际 URL,然后将返回的 HTML 通过正则或简单的字符串处理提取邮件主题和发件人。

import urllib.request import re # 手动从浏览器开发者工具中复制的 Cookie cookie_str = "your_cookie_here" url = "https://mail.163.com/js6/s?func=mbox:listMessages" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Cookie": cookie_str, "Referer": "https://mail.163.com/" } req = urllib.request.Request(url, headers=headers) resp = urllib.request.urlopen(req, timeout=10) html = resp.read().decode("utf-8", errors="ignore") # 假设邮件列表中的主题和发件人通过特定 class 或者属性输出 # 不同时期页面结构差异很大,需要按实际 DOM 调整 subjects = re.findall(r'"subject":"(.*?)"', html) senders = re.findall(r'"sender":"(.*?)"', html) for idx, (subject, sender) in enumerate(zip(subjects, senders), 1): print(f"{idx}. 发件人: {sender} | 主题: {subject}")

实际运行时会发现一个问题:163 邮箱网页版的邮件列表数据非常喜欢用 JSON 格式内嵌在<script>标签里,或者通过单独的接口返回。用正则虽然能快速提取,但只要你没逃过字符转义问题(比如主题里出现引号),解析就会出错。所以我建议 urllib 方案只作为研究 HTTP 流向的教学工具,真正用来做业务还是得靠 requests + 更稳定的解析手段。

2.3 urllib 方案的真实局限性

可能有人会觉得,urllib 什么都能做,为什么大家还是更愿意用 requests?差别在于开发效率和代码可读性。urllib 处理 Cookie 要引入专门的 CookieJar,重定向行为需要额外配置,请求头一个没写全就可能被服务器拒绝,参数编码也得手动调用 urllib.parse.urlencode。这些在 requests 里都是非常自然的操作。

另外,urllib 并不天然支持连接池和 Session 这种抽象,每次请求都像第一次见面一样重建连接,性能和稳定性都不理想。如果你的任务是循环翻页抓取多个文件夹的邮件列表,urllib 的代码会写得越来越长,而 requests 只需要在同一 Session 上换 URL 就行。

所以我对 urllib 的定位是:用它写一两次脚本,搞清楚 HTTP 请求构成和 Cookie 机制就够了,真正干活不用它。

3. 方案二:requests——日常自动化任务的主力方案

3.1 requests.Session 与 urllib 的差异

requests 的设计思路和 urllib 最大的不同就是 Session 机制。Session 对象相当于一个持久化客户端,自动保存 Cookie,自动处理请求头如 Referer,连接复用也有底层 urllib3 连接池顶着。对于需要登录态的接口调用,你只需要登录一次,后续请求全部走同一个 Session,代码干净,思维负担小。

在 163 邮箱这个场景里,requests 的优势尤其明显:邮件列表数据如果是异步接口返回的,你可以直接对接口发请求拿 JSON,不用再浪费时间写正则;如果是服务端渲染的 HTML,也可以配合 BeautifulSoup 做结构化解析。相比 urllib,解析准确度和维护性上升一个台阶。

3.2 登录难题:网易的统一登录和密码加密

虽然 requests 写请求很爽,但登录 163 邮箱依然是绕不开的坎。网易统一登录地址在 reg.163.com 域名下,页面会加载一段加密脚本。正常情况下,提交登录表单时密码字段已经变成 RSA 加密后的密文,同时页面还会校验验证码(常见的是滑块验证)。直接拿明文密码 POST 是过不了服务器的。

有两条路可以走:

第一种,完整复刻登录流程。先去页面源码里找到 RSA 公钥,然后使用 rsa 库对密码进行加密,再模拟提交。这一步还要处理验证码问题。滑块验证码的自动化识别复杂且不稳定,我建议不要硬碰硬,可以在检测到验证码时手动完成一次,之后 Cookie 就驻留在 Session 里了。

第二种,更务实:仍然采用“浏览器手动登录一次,导出 Cookie 给 requests 用”的方式。这种方式适合抓取频率不高、运行环境不固定的个人脚本,省去所有加密和验证码的麻烦。

把 Cookie 导入 requests 的姿势很简单,我直接贴代码。这种做法的核心就是构造一个 requests.cookies.RequestsCookieJar 对象,把键值对塞进去,然后挂在 Session 上。

import requests session = requests.Session() session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" }) # 手动从浏览器复制 Cookie 字符串,格式类似 "name1=value1; name2=value2" cookie_str = "name1=value1; name2=value2" for item in cookie_str.split(";"): key, value = item.strip().split("=", 1) session.cookies.set(key, value) resp = session.get("https://mail.163.com/", timeout=10) print(resp.status_code)

这里有个细节:163 邮箱顶部可能还有一个主域名跳转,第一次请求 163.com 后服务器会下发新的 Cookie,所以如果你发现某些接口提示未登录,可能是跳转过程中漏掉了中间域的 Cookie。最简单的办法是把所有域名下的 Cookie 都复制进 session,包括 .163.com、.mail.163.com 等。

3.3 用 requests 获取收件箱列表的代码和解析容错

登录问题解决后,抓取收件箱列表就简单了。下面是我用 requests 加 BeautifulSoup 解析邮件列表的示例。注意,页面结构会随版本变化,所以我在代码里做了多级容错:先尝试从 JSON 接口拿数据,失败再去解析 HTML。

import requests from bs4 import BeautifulSoup import json def fetch_inbox(session, folder_url): resp = session.get(folder_url, timeout=10) resp.encoding = "utf-8" # 方式一:如果能直接从接口拿到 JSON try: data = resp.json() messages = data.get("var", {}).get("list", []) for msg in messages: print(msg.get("subject"), msg.get("sender")) return except ValueError: pass # 方式二:解析 HTML soup = BeautifulSoup(resp.text, "html.parser") mail_items = soup.select(".mailListItem") # 选择器根据实际页面调整 for item in mail_items: subject_node = item.select_one(".subject") sender_node = item.select_one(".sender") if subject_node: print(subject_node.get_text(strip=True), "|", sender_node.get_text(strip=True))

如果你是直接对内部的异步接口发起请求,那么拿到的基本都是 JSON 格式。163 邮箱的接口返回值通常经过一层 JSONP 封装,可能在变量赋值里,也可能在 callback 函数里。遇到这种情况,可以用正则先抽取 JSON 对象,再交给 json.loads 解析。

import re match = re.search(r"var listData = (.*?);", resp.text, re.S) if match: data = json.loads(match.group(1)) for msg in data.get("list", []): print(msg.get("subject"), msg.get("from"))

这个方案跑起来很稳,缺点是接口地址和返回字段名可能改版。我建议在代码里加上“字段不存在时的兜底逻辑”,比如用 msg.get("subject") 而不是 msg["subject"],避免单个字段异常导致整个脚本中断。

4. 方案三:selenium——浏览器兜底,动态渲染也不怕

4.1 为什么还需要 selenium

有些场景下,requests 方案会走到死胡同:页面里某个关键数据怎么都找不到接口,或者必须点击某个按钮后才出现文件夹列表;又或者登录验证码太复杂,连人工介入都不方便。这时候 selenium 就是最后一层兜底。

selenium 的价值在于它是真实浏览器环境,用户能看到操作过程,调试直观,对反爬虫的扰动更小。163 邮箱网页版虽然绝大多数数据接口都能直接拿到 JSON,但如果你对接口不熟悉,最快验证页面行为的方式就是用 selenium 跑一遍。另外,如果需要模拟用户操作,比如自动打开某封邮件、点击加载更多、切换文件夹,selenium 都是最直接的工具。

代价也很明显:启动浏览器大约消耗上百 M 内存,每次操作多一步等待,跑批量任务时的速度远比不上 requests。

4.2 selenium 操作 163 收件箱的完整流程

selenium 的关键是先解决浏览器驱动问题。我用 Chrome 举例,确保本机 Chrome 版本和 chromedriver 匹配,否则启动就会报 SessionNotCreatedException。驱动装好以后,核心步骤就是:打开登录页、输入账号密码、等待登录成功、跳转到收件箱、提取邮件列表元素。

登录 163 邮箱时有个比较烦人的点:登录框可能在 iframe 里。你在主页面找不到输入框,需要先切换进 iframe 才能操作。这一步卡住过很多人,我贴一段兼容 iframe 的登录代码。

from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Chrome() wait = WebDriverWait(driver, 10) driver.get("https://mail.163.com/") # 邮箱登录页一般有 iframe,需要切换 driver.switch_to.frame("login-frame") # frame id 或 name 按实际调整 # 等账号输入框出现 user_input = wait.until(EC.presence_of_element_located((By.NAME, "email"))) user_input.send_keys("your_username@163.com") password_input = driver.find_element(By.NAME, "password") password_input.send_keys("your_password") # 登录按钮可能有多个,选择可见的那个 login_button = driver.find_element(By.CSS_SELECTOR, "a#dologin") login_button.click() # 等待登录完成,回到默认内容 driver.switch_to.default_content() wait.until(EC.url_contains("mail.163.com")) print("登录成功") # 进入收件箱 driver.get("https://mail.163.com/js6/main.jsp")

登录成功后,提取邮件列表的核心操作是等待列表元素的出现。目标元素通常是包含主题、发件人、时间的行容器,用 XPath 或 CSS 选择器遍历。注意,邮箱页面为了性能,列表行可能部分复用,元素数量和邮件数量不是严格的对应关系,需要配合滚动来加载更多内容。

4.3 WebDriverWait 显式等待与 iframe/弹窗处理

selenium 最大的坑就是“元素还没渲染出来,你去点它”。163 邮箱的网络请求多,尤其是第一次登录后,列表数据加载需要时间。如果代码里全部用 time.sleep(5) 这种固定等待,要么等太久,要么网络慢时依然超时。正确姿势是显式等待,也就是 WebDriverWait 配合 expected_conditions。

例如等待某个主题文本出现:

wait.until(EC.presence_of_element_located((By.XPATH, "//div[@class='d']//span[@class='subject']")))

还有 iframe 的嵌套问题。163 邮箱的邮件详情页也可能使用 iframe,处理思路和登录 iframe 一样:先 switch_to.frame 进入,解析完再 switch_to.default_content 退出。如果你在页面里怎么都找不到元素,第一反应应该是检查当前上下文是否还在正确 frame。

弹窗方面,163 邮箱偶尔会有引导浮层。如果你的脚本被浮层挡住点击,可以用 EC.element_to_be_clickable,先等待元素可点击,再执行 click。或者手动关闭浮层:

try: close_btn = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".close-btn"))) close_btn.click() except Exception: pass

这种“能关就关,关不掉忽略”的逻辑在自动化脚本里非常实用。

4.4 div+ul+li 组合定位与上传文件等细节

热搜词里有一个很典型的场景:页面元素不是原生下拉框,而是由 div、ul、li 组合出来的自定义下拉框。这在 163 邮箱的文件夹切换里很常见。遇到这种控件,selenium 的 Select 类是失效的,你需要先点击触发下拉展开的 div,再定位 ul 下的 li 项。

# 假设点击一个自定义下拉框,然后选择“收件箱” driver.find_element(By.CSS_SELECTOR, "div.select-wrapper").click() time.sleep(1) driver.find_element(By.XPATH, "//ul[@class='select-options']/li[text()='收件箱']").click()

另外,selenium 上传本地文件也是个常见需求。很多人以为要先打开文件选择对话框,再用 pywin32 或 AutoIT 操作窗口,但实际上 selenium 提供了最简方式:只要 input 标签存在,直接用 send_keys 传入本地文件绝对路径即可。

file_input = driver.find_element(By.CSS_SELECTOR, "input[type='file']") file_input.send_keys("C:/path/to/attachment.txt")

这些细节看似皮毛,但在自动化测试或采集脚本里经常是卡住进度的关键点。

5. 绕开网页爬取:直接通过 IMAP 协议读邮件

5.1 IMAP 是什么,为什么推荐

如果你可以接受不使用“网页爬虫”的形式,那么 IMAP 协议是最省心的路子。IMAP 是邮件客户端与邮件服务器之间的标准协议,你用的网易邮箱客户端、手机自带邮件 App 底层就是走 IMAP(或 POP3)。Python 标准库 imaplib 已经实现了客户端逻辑,你只需要处理字符串解析,不用关心网页结构。

IMAP 的这种“绕过浏览器操作”优势非常明显:第一,不依赖页面 DOM,网易怎么改版都不影响你的流程;第二,请求量小,通常一次 select 加一次 search 就能拿到整个收件箱的邮件 ID 列表,再按需 fetch 内容,对服务器的压力远小于 Web 自动化;第三,协议稳定,接口几乎不变,代码可以长期使用。

说白了,听到“爬邮件”这个词,第一反应不应该是 selenium,而是 IMAP。正如我在第 1 部分提到的那样,Web 爬虫通常是在没有邮件服务器权限或者需要操作特定网页功能时才必须使用。

5.2 163 邮箱开启 IMAP 服务并获取授权码

用 IMAP 连 163 邮箱有一个关键前提:需要在网页端设置里开启 IMAP/SMTP 服务,并且用“授权码”而不是邮箱密码登录。这是 163 的安全策略,目的是避免你的邮箱密码直接暴露给第三方客户端。

具体操作路径:登录 163 邮箱网页版,进入设置 -> POP3/SMTP/IMAP,开启 IMAP 服务。开启过程中会要求发送一条短信验证,验证通过后会生成一个 16 位授权码。这个授权码只显示一次,保存好。后面 imaplib 登录时,“密码”这个位置填的就是授权码。

这个点我当初踩过坑:直接用邮箱密码去连 imap.163.com,一直报认证失败,后来才意识到需要开启服务和授权码。如果你发现 login 一直失败,第一反应就检查这个。

5.3 imaplib 获取收件箱列表和邮件正文的完整示例

下面的代码用 imaplib 连接 163 邮箱的 IMAP 服务器,选择 INBOX,搜索所有邮件,然后拉取最近几封邮件的发件人和主题。

import imaplib import email from email.header import decode_header def decode_mime_header(raw): if raw is None: return "" decoded = decode_header(raw) result = [] for text, charset in decoded: if isinstance(text, bytes): charset = charset or "utf-8" result.append(text.decode(charset, errors="ignore")) else: result.append(text) return "".join(result) HOST = "imap.163.com" APP_PASSWORD = "your_authorization_code" # 不是邮箱密码! conn = imaplib.IMAP4_SSL(HOST, 993) conn.login("your_username@163.com", APP_PASSWORD) conn.select("INBOX") # 搜索所有邮件,返回的是邮件 ID 列表 status, data = conn.search(None, "ALL") mail_ids = data[0].split() # 拉取最近 5 封 for mail_id in mail_ids[-5:]: status, msg_data = conn.fetch(mail_id, "(RFC822)") msg = email.message_from_bytes(msg_data[0][1]) subject = decode_mime_header(msg.get("Subject")) sender = decode_mime_header(msg.get("From")) print(f"主题: {subject} | 发件人: {sender}") conn.logout()

这段代码注意几点:邮件主题和发件人可能是 MIME 编码过的,比如 =?UTF-8?B?...?= 这种,所以要用 email.header.decode_header 解码;fetch 返回的报文是 bytes,用 email.message_from_bytes 转换;搜索条件可以更精确,比如用 UNSEEN 拿未读邮件,或用 SINCE 限定日期,这样每次拉取的就只是增量邮件,效率更高。

5.4 三种 Web 方案 vs IMAP 的选型对照表

我根据自己的经验做了一张选型对照表,方便你按实际需求挑方案:

方案登录难度解析稳定性请求速度是否推荐长期使用适用场景
urllib难,需手动加密/带Cookie差,页面改版就崩中不推荐学习HTTP原理、救急
requests中,建议手动Cookie中,依赖接口和结构快视情况接口明确、结构稳定的抓取
selenium中,需处理iframe/滑块高,只要元素能找到就行慢不推荐长期批量页面交互复杂、临时兜底
IMAP低,开服务+授权码高,协议稳定非常快强烈推荐各类邮件读取、监控、归档

可以看到,IMAP 在绝大多数需要“读邮件”的场景下都是最合适的。requests 适合调试接口和做轻量验证,selenium 则适合作为最后手段,千万别一上来就把项目压在 selenium 上,那会浪费大量时间在等待和元素抖动上。

6. 常见问题与排查技巧实录

6.1 429 too many requests:请求太频繁被限流怎么处理

如果你用 requests 或 urllib 频繁请求 163 邮箱相关接口,大概率会遇到 429 状态码,错误信息通常类似exceeded retry limit, last status: 429 too many requests。这是服务器在做限流,说明你在短时间内请求量太大,被识别为风险行为。

解决办法也很朴素:

  • 给请求之间加随机延时,比如time.sleep(random.uniform(1, 3))。
  • 控制整体并发,别用多线程或者多进程同时开太多 Session。实际项目中如果并发量超过 5,我就建议改用 IMAP 协议,因为 IMAP 一次连接可以连续处理大批邮件,对服务端更友好。
  • 请求到达一定次数后强制休息 30 秒到 1 分钟,让限流窗口过去。
  • 一定要设置重试机制,遇到 429 时等待一段时间后重试,不要无限重试。
import requests import time def get_with_retry(session, url, max_retries=3): for i in range(max_retries): resp = session.get(url, timeout=10) if resp.status_code == 429: wait = 2 ** i print(f"收到 429,等待 {wait} 秒后重试") time.sleep(wait) continue return resp raise RuntimeError("重试多次依然被限流")

这种指数退避的策略非常管用,既简单又有效。实际跑的时候建议在日志里记录每次请求的状态码和耗时,方便事后分析。

6.2 登录失败、验证码弹窗:真实场景下的处理姿势

登录失败是 163 邮箱脚本最常见的痛点。如果你用 requests 模拟登录,提示密码错误,先检查是不是加密环节出了问题。最简单验证方式:抓取网页版登录成功后产生的 Cookie,直接复用,绕开加密逻辑,而不是死磕 JS 逆向。

如果登录时弹出了滑块验证码,千万不要想着完全自动化识别。滑块识别成本高、容易被风控,而且对普通脚本来说根本没有必要。我建议的方案是:程序负责把浏览器窗口弹出来,人手动拖一下滑块,然后脚本继续跑。这种方式既稳定又不会被封号。

如果是 selenium 场景,登录成功后注意等待页面跳转完成,不要立刻去点其他元素。163 邮箱登录成功后会有一个短暂的过渡页,可能还会弹出“设置密保/绑定手机”之类的引导。如果你的脚本执行太快,很容易点到不该点的东西。

6.3 元素定位不到、Cookie 失效、邮件变 base64 的排查清单

  • 元素定位不到:第一先看页面是否加载完,用显式等待代替 sleep;第二看元素是否在 iframe 里,需要先 switch_to.frame;第三看是否为隐藏元素,可能需要先点击某个父元素触发显示。
  • Cookie 失效:Cookie 通常有几个小时到几天的有效期,取决于你抓取的接口。如果脚本突然报未登录,重新手动登录一次并更新 Cookie 即可。我习惯把 Cookie 保存到本地 JSON 文件,每天开始运行前检查是否过期。
  • 邮件正文显示成 base64:邮件正文可能使用了 base64 编码传输。用 email 库解析时,需要判断 payload 是否为 base64,然后进行解码。判断方法是在头部查看 Content-Transfer-Encoding 字段。IMAP 场景下处理这个比较简单,因为 email 库自带 decode 逻辑,只要你用 email.message_from_bytes 解析就能自动处理大部分情况。

还有一个我在实战中发现的规律:爬 163 邮箱网页版时,尽量选择内部接口而不是解析整张 HTML 页面。内部接口返回的数据结构虽然初期需要摸索,但一旦找到,稳定性远超脆弱的 DOM 解析。

最后再说点实在的

我这个项目实际落地时,最终选型是 IMAP + 少量 requests。IMAP 负责邮件列表、正文、附件的拉取,requests 只用来处理网页端特有的操作(比如某些设置页的修改)。selenium 只是前期确认页面结构时用过一次,后来就再也没有出现在正式脚本里。如果你在多个方案之间纠结,我的建议很简单:先考虑邮件协议,再考虑网页接口,最后才考虑浏览器自动化。这样不仅能省力,还能让你的脚本活得更久。

另外一个小技巧:无论你用哪种方案,请务必加日志和异常收集。邮件项目最容易出问题的地方是单个邮件解析失败导致整个任务中断。我在写 imaplib 脚本时会对每一封邮件的解析做 try-except,失败时把邮件 ID 记录下来继续下一封,最后统一查看失败列表。这个习惯让我少熬了很多夜,推荐给你。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询