
1. ModuleNotFoundError: No module named yagmail 到底在告訴你什么1.1 這是Python最誠實的一類報錯如果你在腳本第一行寫了import yagmail然后運行終端卻甩回一行紅色日志ModuleNotFoundError: No module named yagmail別急著懷疑人生這一行信息量其實非常大。它的直譯是Python解釋器在當前環(huán)境中按照它自己的模塊搜索路徑也就是sys.path找了半天沒有找到名字叫yagmail的包。注意這不屬于語法錯誤也不屬于邏輯錯誤而是導入期錯誤。Python先把你的代碼做語法分析沒問題之后開始逐行執(zhí)行等真正跑到import yagmail這句話時才會去sys.path里列出的所有目錄里找包。全部找完還是沒有于是拋出一個ModuleNotFoundError。用生活化的話說就像你去廚房做菜菜譜第一步寫著“拿出生抽”你打開調(diào)料柜、臺面、冰箱都翻了一遍結(jié)果沒有于是你跟家里人喊了一嗓子“家里沒有生抽”。Python就是這個“喊話”機制它只會按固定目錄列表找不會自己跑到硬盤每個角落去全局搜索。所以當你看到這種報錯第一反應(yīng)應(yīng)該是不是腳本內(nèi)容有問題而是運行環(huán)境里少了東西或者運行環(huán)境和我以為的環(huán)境不是同一個。1.2 No module named 和 ImportError 不是一回事很多在早期Python版本里折騰過的朋友可能記得以前 import 失敗統(tǒng)一報ImportError沒有ModuleNotFoundError這一說。后來 Python 3.6 把“模塊不存在”單獨拆成了ModuleNotFoundError并且讓它成為ImportError的子類。這意味著兩件事第一如果你以前見過ImportError: No module named yagmail那和現(xiàn)在看到的ModuleNotFoundError: No module named yagmail本質(zhì)上是同一個問題只是版本不一樣Python的報錯分類更細分了。第二有些老代碼為了兼容會用except ImportError去捕獲導入異常這依然能捕獲到ModuleNotFoundError因為子類會被父類異常捕獲。但在排查Bug時看到ModuleNotFoundError要更聚焦它明確告訴你“這個包根本不存在”而不是“這個包存在但內(nèi)部某一步導錯了”那是另一個報錯方向。從實操角度講遇到No module named yagmail默認先判定“沒裝”但緊接著要意識到一個更扎心的問題裝了也可能找不到。因為這里有“環(huán)境”兩個字在中間作梗。這也就是為什么會出現(xiàn)“我明明pip install了為什么還是報錯”這種讓人抓狂的情況。2. 為什么你已經(jīng) pip install 了程序還是找不到2.1 虛擬環(huán)境和全局環(huán)境的錯位新手最容易掉進去的坑就是環(huán)境錯位。我給你描述一個極其常見的操作路徑你在 PyCharm 里新建了一個項目PyCharm 默認會自動創(chuàng)建venv虛擬環(huán)境。好項目右邊的終端里pip install yagmail執(zhí)行成功沒有報錯。接著你開心地去系統(tǒng)桌面打開 cmd 或者 Windows Terminal在另一個目錄運行python test.py結(jié)果彈出ModuleNotFoundError: No module named yagmail。問題出在哪PyCharm 的項目終端里默認已經(jīng)把虛擬環(huán)境激活了你安裝yagmail是裝進了venv的site-packages。但你在系統(tǒng)終端里運行的python是全局的 Python 解釋器。全局環(huán)境的第三方包目錄里沒有yagmail自然而然就報錯了。所以在排查之前你要先分清“代碼是在哪個環(huán)境運行的”。想驗證當前 Python 到底是哪一套可以運行python -c import sys; print(sys.executable)如果在虛擬環(huán)境里打印出來的路徑會指向項目目錄下的venv/bin/pythonWindows 下是venv\Scripts\python.exe。如果執(zhí)行結(jié)果指向/usr/bin/python或者某個不知名路徑那就說明你根本沒在項目環(huán)境里跑腳本。我見過太多人刪了好幾次包、重裝好幾遍yagmail還是報錯最后發(fā)現(xiàn)只是終端窗口用錯了。建議是以后跑 Python 腳本盡量從同一套集成開發(fā)環(huán)境的終端窗口去跑比如 PyCharm 的項目終端、VS Code 的集成終端因為這些終端通常會幫你自動激活已選中的環(huán)境。不建議開一個獨立的系統(tǒng) cmd 去跑工程里的腳本那不是不行而是你很容易忘記激活環(huán)境。2.2 pip 和 python 可能根本不是同一套工具第二個高頻原因是pip對應(yīng)的 Python 環(huán)境和你的python命令對應(yīng)的 Python 環(huán)境不是同一個?,F(xiàn)在很多電腦里同時裝了好幾個 Python官方 python.org 的安裝包、Anaconda、MiniConda、Windows Store 里裝的 Python、還有各種 IDE 自帶的 Python。它們各自有獨立的site-packages目錄。如果某個程序在 PATH 里搶占了先機你在命令行敲pip時它指向的可能是一套環(huán)境敲python時它又指向了另一套環(huán)境。解決方案極其簡單但極其重要不要使用裸的pip命令改用python -m pip方式。python -m pip install yagmail這條命令的意思是把pip當作python這個解釋器的一個模塊來運行這樣就保證了 pip 一定跟隨當前這個 python 走裝進當前這個 python 對應(yīng)的site-packages。只要運行腳本時用的也是同一個python就不會發(fā)生“裝到 A 環(huán)境、跑在 B 環(huán)境”的詭異事件。這條經(jīng)驗不只是針對yagmail。你以后遇到No module named numpy、No module named opencv、No module named pkg_resources甚至No module named pip排查第一步永遠是問自己當前正在運行的 Python 到底是哪一套包又裝到哪一套去了把這兩個問題弄清楚至少能解決一半以上的導入類報錯。2.3 系統(tǒng)Python保護機制externally-managed-environment近幾年在 Debian、Ubuntu 等 Linux 發(fā)行版上直接給系統(tǒng) Pythonpip install經(jīng)常會看到這樣的報錯error: externally-managed-environment標題相關(guān)熱詞里出現(xiàn)的pip install modelscope error: externally-managed-environment就是這個問題。很多人第一次見到這個錯誤以為電腦壞了其實不是。這是系統(tǒng)在保護自己背后是 PEP 668 規(guī)范。系統(tǒng) Python 的第三方包主要由apt包管理器來維護如果你拿pip亂裝一通很容易覆蓋系統(tǒng)關(guān)鍵依賴甚至把整個python3環(huán)境搞崩。遇到這種報錯我最推薦的做法是在項目目錄下創(chuàng)建一個虛擬環(huán)境在venv里安裝不要強行和系統(tǒng)環(huán)境對著干。如果只是臨時驗證一個小模塊也可以使用--break-system-packages參數(shù)強制安裝但我不建議把這個養(yǎng)成長期習慣。你要是后面發(fā)現(xiàn)了依賴沖突會非常痛苦。3. 一步步把 yagmail 裝進正確的環(huán)境3.1 安裝前先做一次環(huán)境體檢不要一上來就復制粘貼pip install yagmail我先建議你花 20 秒做環(huán)境體檢。按順序執(zhí)行下面幾條命令python --version python -m pip --version python -m pip show yagmail第一條看 Python 版本第三條看當前環(huán)境里是否已經(jīng)存在yagmail。如果最后一條命令輸出了Name: yagmail、Version: 0.6.3、Location: ...這樣的信息說明當前這個 Python 環(huán)境里早就裝好了那你應(yīng)該回頭檢查之前腳本運行用的到底是哪個環(huán)境。如果pip show沒有輸出任何信息只是安靜地回到命令提示符就說明確實沒有安裝。如果你創(chuàng)建了虛擬環(huán)境但還沒激活Windows 下用venv\Scripts\activateLinux 和 macOS 下用source venv/bin/activate激活后命令行的最前面一般會出現(xiàn)括號里寫著venv的提示符。這一步做好之后再繼續(xù)安裝。3.2 安裝命令與鏡像源選擇最基礎(chǔ)、最穩(wěn)的安裝命令是python -m pip install yagmail如果你在國內(nèi)的網(wǎng)絡(luò)環(huán)境里安裝可能會遇到下載慢、連接超時、卡在進度條上不動的情況。這時候可以把包源切換到清華鏡像python -m pip install yagmail -i https://pypi.tuna.tsinghua.edu.cn/simple這個-i參數(shù)的意思是--index-url也就是臨時指定本次安裝使用的 PyPI 鏡像源。鏡像源的原理很簡單它相當于把 PyPI 官方倉庫在海外服務(wù)器上的包同步了一份放在國內(nèi)訪問速度會明顯更快。需要注意的是鏡像源站的同步會有短暫延遲極少情況下會碰到某個包的最新版還沒同步過來這時把版本調(diào)低一級或者用官方源再試一次就好。如果你不是在一個全新的虛擬環(huán)境里安裝而是直接對系統(tǒng) Python 操作還可能會看到這么一行提示Defaulting to user installation because normal site-packages is not writeable翻譯一下就是當前site-packages目錄不可寫pip 決定把包裝到當前用戶目錄下。這種情況通常出現(xiàn)在 macOS 和 Linux 上。你確實可以接著用但我還是會建議優(yōu)先進入一個虛擬環(huán)境再裝省得后續(xù)權(quán)限問題反復出現(xiàn)。3.3 安裝后真的要驗證一次很多人在終端看到Successfully installed yagmail-0.6.3就以為萬事大吉了轉(zhuǎn)頭運行腳本又報錯。為什么因為pip install成功只能說明包裝進了當前pip對應(yīng)的環(huán)境但不代表你運行腳本時使用的解釋器環(huán)境一定就是剛才那個。所以安裝完成之后我建議在同一個終端里運行一次動態(tài)導入python -c import yagmail; print(yagmail.__version__)如果成功打印出版本號比如0.6.3那就說明當前這個終端環(huán)境里真正可以用yagmail了。只要后續(xù)你始終用同一個環(huán)境去跑腳本就不會再看到No module named yagmail。記住一個原則pip show驗證的是安裝元數(shù)據(jù)import驗證的是真實的運行時環(huán)境。把兩條命令都在同一個終端里跑一遍兩者一致才算徹底閉合。4. 安裝過程中那些反復把人勸退的問題4.1 老被提示 pip 版本太舊很多朋友在安裝任何包之前都會看到一行警告WARNING: You are using pip version 21.1.1; however, version 25.0.1 is available.這種提示看著很煩但說實話老版本的 pip 去安裝yagmail這種小包大概率也能成功??扇绻恢蓖涎硬簧売龅侥承┮蕾嚱馕鰪碗s的包、或者對新版 wheel 二進制包支持不佳的包時就容易卡住而且報錯信息還很難讀懂。我的習慣是在新建虛擬環(huán)境并激活之后第一件事先升級 pippython -m pip install --upgrade pip等pip版本升上去以后再去install yagmail。你會發(fā)現(xiàn)后續(xù)的依賴解析更快關(guān)于externally-managed-environment之類的提示也會更準確。升級完 pip 后再順手看看鏡像源如果之前裝的時候用的是默認源升級也會更快。4.2 網(wǎng)絡(luò)超時、ReadTimeoutError、連接被重置在終端里遇到ReadTimeoutError或者Connection reset by peer之類的報錯是網(wǎng)絡(luò)原因不是命令寫錯了。默認情況下pip 會從官方 PyPI 下載包如果你的網(wǎng)絡(luò)和官方源之間的連接不穩(wěn)定下載容易中斷。做法有三步一是加超時時間和重試次數(shù)python -m pip install yagmail --timeout 60 --retries 5二是直接用國內(nèi)鏡像源前面已經(jīng)說過用-i參數(shù)臨時指定即可。三是如果每次都很慢可以設(shè)置全局默認鏡像源一勞永逸python -m pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple設(shè)置完之后默認的 pip 下載行為就都走鏡像源了。值得注意的是如果你在公司內(nèi)網(wǎng)某些鏡像站可能訪問不了這時優(yōu)先用內(nèi)網(wǎng)自建的 PyPI 鏡像而不是硬套公共鏡像地址。4.3 依賴沖突和全局環(huán)境“大雜燴”yagmail 本身依賴很少通常只有python-dateutil之類的輕量庫所以單獨安裝它時幾乎遇不到依賴沖突。但如果你把所有 Python 包都塞進同一個全局環(huán)境時間久了就會出問題。比如foo包依賴舊版urllib3bar包依賴新版urllib3兩個包裝在一起pip 在解析依賴時可能直接報出讓你看不懂的“Dependency conflict”錯誤。這就要回到前面反復強調(diào)的虛擬環(huán)境。每一個項目都應(yīng)該有自己獨立的虛擬環(huán)境而不是一個環(huán)境里住滿了所有項目的地盤。這就像你不會讓十個家庭共用一間廚房一樣各種調(diào)料和生活習慣混在一起遲早會打起來。實際工作中我見過為了解一個依賴沖突查了三天的人最后發(fā)現(xiàn)把項目單獨venv一下問題在十分鐘之內(nèi)就解決掉了。4.4 總覺得要 sudo 才能裝用sudo pip install是很多人踩過的最大的坑。你可能覺得系統(tǒng)提示Permission denied那就加sudo好了。但在 Linux 系統(tǒng)里sudo pip install會把包直接寫進系統(tǒng)級 Python 的site-packages一旦裝錯版本或卸載不當很容易把系統(tǒng)依賴搞亂。更麻煩的是sudo安裝的包權(quán)限屬于 root你以后的普通用戶進程想刪都刪不掉。正確的解法是如果你只是自己開發(fā)用就創(chuàng)建虛擬環(huán)境不需要 root 權(quán)限也不會弄臟系統(tǒng)。如果非要為系統(tǒng) Python 裝包用python -m pip install --user yagmail安裝到用戶目錄避免動系統(tǒng)全局目錄。把“少用 sudo 裝 Python 包”這條刻進腦子里可以幫你避開無數(shù)麻煩。5. yagmail 到底能干什么以及真實使用體驗5.1 一個最小可用的發(fā)郵件腳本在解決完環(huán)境問題之后也許你會好奇yagmail這個包到底解決什么問題。簡單說它是一個用來“讓 Python 發(fā)送郵件變得非常簡單”的庫。如果用 Python 標準庫smtplib去發(fā)郵件你需要手動處理 SMTP 連接、登錄、構(gòu)造郵件頭、構(gòu)造 MIME 文本、加附件等一堆操作少說也要幾十行代碼。而yagmail把大部分細節(jié)都包掉了代碼可以少到一個令人驚訝的程度import yagmail yag yagmail.SMTP(useryougmail.com, passwordyour_app_password) yag.send( toreceiverexample.com, subject測試郵件, contents這是用 yagmail 發(fā)送的正文內(nèi)容。 )就這么幾行一封郵件就發(fā)出去了。這就是yagmail存在的意義明明一個自動化任務(wù)只需要很小的代碼量你沒必要為了它手寫太復雜的底層邏輯。對于跑數(shù)據(jù)定時報表、發(fā)監(jiān)控告警、給用戶批量發(fā)通知這類腳本來說它的簡潔性非常有吸引力。5.2 使用中需要注意的細節(jié)第一個要注意的是密碼問題。上面示例代碼里的明文密碼我個人不推薦直接寫死在腳本里尤其當腳本要提交到 git 倉庫時。至少也要從環(huán)境變量里讀取或者使用郵箱服務(wù)商提供的“應(yīng)用專用密碼”。像 Gmail 這類郵箱開啟兩步驗證之后可以為某個應(yīng)用單獨生成一個隨機密碼比主密碼安全得多。第二個細節(jié)是自定義 SMTP 服務(wù)器。默認情況下yagmail會根據(jù)你傳入的郵箱后綴自動推斷 SMTP 服務(wù)器比如 Gmail 就自動去smtp.gmail.com。如果你用的是公司企業(yè)郵箱后綴可能比較小眾自動推斷會失敗這時需要顯式傳入host參數(shù)yag yagmail.SMTP(useryoucompany.com, passwordyour_password, hostsmtp.company.com)第三個細節(jié)是附件路徑。寫contents[附件文件.pdf]時如果使用相對路徑腳本執(zhí)行時的工作目錄必須和文件實際路徑一致。為了減少不確定性盡量用絕對路徑或者先通過os.path.abspath把相對路徑轉(zhuǎn)成絕對路徑。第四個細節(jié)是關(guān)于送達率的現(xiàn)實認識。腳本顯示發(fā)送成功只代表 SMTP 服務(wù)端接收了這個郵件請求不代表收件方一定會把它放進收件箱。你的發(fā)信域名如果缺少 SPF、DKIM 等設(shè)置或者服務(wù)器 IP 信譽不好郵件很容易進垃圾箱。這是任何郵件自動化腳本都無法完全繞開的現(xiàn)實問題。6. 以后遇到 ModuleNotFoundError可以直接查這張表6.1 報錯現(xiàn)象與排查操作對照報錯現(xiàn)象可能原因推薦操作ModuleNotFoundError: No module named yagmail當前環(huán)境沒有安裝這個包用python -m pip install yagmail安裝安裝后仍報相同錯誤運行腳本的 Python 和安裝包的 Python 不是同一個環(huán)境檢查python -c import sys; print(sys.executable)externally-managed-environment系統(tǒng) Python 受 PEP 668 保護創(chuàng)建并激活虛擬環(huán)境后再安裝Defaulting to user installation because normal site-packages is not writeable當前用戶對 site-packages 無寫權(quán)限優(yōu)先使用虛擬環(huán)境或使用--user裝用戶目錄WARNING: You are using pip version 21.1.1pip 版本太舊python -m pip install --upgrade pipReadTimeoutError或連接被重置網(wǎng)絡(luò)到默認源不穩(wěn)定使用國內(nèi)鏡像源或增加--timeout和--retries某個import報錯但包明明存在同名下其他包干擾或者工作目錄錯位在正確環(huán)境里重裝并用python -c import xxx驗證這張表看起來簡單但覆蓋了我這些年處理 Python 導入類報錯時遇到的大多數(shù)情況??梢哉f光是把“python -m pip”和“確認當前解釋器路徑”這兩個習慣養(yǎng)成市面上 70% 的ModuleNotFoundError都影響不到你。6.2 我處理這類問題時的幾條個人習慣最后把我自己的一套操作流程分享出來。新建一個 Python 項目時我永遠先執(zhí)行python -m venv venv然后激活虛擬環(huán)境。激活完先看一眼提示符前有沒有環(huán)境名再順手把 pip 升到最新版。接下來才是安裝項目需要的依賴并第一時間把依賴列表固化到requirements.txt里。等到項目收斂需要部署到新機器時一條pip install -r requirements.txt就能復現(xiàn)完整環(huán)境而不是靠記憶“當時好像裝過那幾個包”。遇到報錯時我從來不追著錯誤信息死磕而是先把當前的sys.path和sys.executable打印出來。確認環(huán)境再看報錯往往問題已經(jīng)解決一半。如果我想在群里或者社區(qū)問別人我會一次性把python --version、python -m pip --version、完整的報錯日志這三樣信息貼出來。很多人只甩一句“我裝不上”沒有版本信息、沒有日志神仙也難一眼定因。說實話像ModuleNotFoundError: No module named yagmail這種報錯幾乎每個用過 Python 的人都會遇到。它看著簡單把你卡住的時間可能比任何高級特性都長。學會把自己從“只會寫代碼的人”變成“會管理環(huán)境的人”你往后的 Python 路上會順很多。