架構設計
- 帳號池:收集 QQ、163、126、Gmail、Yahoo 等支援 SMTP 的郵箱,建立陣列
- 收件清單:從文字檔讀取 Email,每行一個
- 發送邏輯:遍歷收件清單,依序嘗試帳號池發送,成功即換下一位收件人
- 失敗重試:單封郵件若帳號 A 失敗,自動切換帳號 B、C... 直到成功或耗盡
- 頻率控制:每封間隔
sleep(3),每 200 封 shuffle 打亂帳號順序,降低被封風險
- 日誌記錄:成功/失敗分檔記錄(含帳號、錯誤碼),便於事後核對
- 定時執行:Linux
crontab 或 systemd timer 後台常駐
核心程式碼
1. 入口腳本 mail_001.php
<?php
define('ROOT_PATH', __DIR__);
require_once 'library/senderApi.php';
require_once 'config/senderListConf.php';
shuffle($senderListConf); // 初始打亂
$emails = array_filter(array_map('trim', explode("\r\n", file_get_contents('data/email.txt'))));
$html = file_get_contents('https://taobao.1080www.com'); // 郵件內容(可改為模板引擎)
foreach ($emails as $idx => $to) {
$user = explode('@', $to)[0];
$senderIdx = 0;
$senderIdx = smtp_mail($to, '秋季補水護膚指南', $html, 'https://taobao.1080www.com/', $user, $senderListConf, $senderIdx);
sleep(3); // 節流
if ($idx > 0 && $idx % 200 === 0) shuffle($senderListConf); // 定期打亂
}
die("SendingOver.................\n");
?>
2. 封裝層 senderApi.php(含重試遞迴)
<?php
error_reporting(E_ERROR);
require_once "phpmailer/phpmailer.class.php";
function log_write(string $file, string $msg): void {
file_put_contents($file, $msg . "\n", FILE_APPEND | LOCK_EX);
}
function smtp_mail(string $to, string $subject, string $body, string $extra, string $user, array $accounts, int $idx = 0): int {
$batch = date('Y_m_d_H');
$mail = new PHPMailer();
$mail->IsSMTP();
$acc = $accounts[$idx] ?? die("帳號池耗盡\n");
$mail->Host = $acc['Host'];
$mail->SMTPAuth = true;
$mail->Username = $acc['Username'];
$mail->Password = $acc['Password'];
$mail->From = $acc['Username'];
$mail->FromName = '淘寶推薦';
$mail->CharSet = 'UTF-8';
$mail->Encoding = 'base64';
$mail->AddAddress($to, $user);
$mail->AddReplyTo('ken@cscsws.com', '淘寶推薦');
$mail->IsHTML(true);
$mail->Subject = $subject;
$mail->Body = $body;
$mail->AltBody = strip_tags($body);
if ($mail->Send()) {
log_write(ROOT_PATH . "/log/{$batch}ok.txt", "$user 發送成功 via {$acc['Username']}");
return $idx;
}
$err = $mail->ErrorInfo;
log_write(ROOT_PATH . "/log/{$batch}failed.txt", "$user 失敗 via {$acc['Username']} : $err");
// 遞迴嘗試下一個帳號
if (isset($accounts[$idx + 1])) {
return smtp_mail($to, $subject, $body, $extra, $user, $accounts, $idx + 1);
}
return $idx; // 全部失敗
}
?>
3. 帳號配置 config/senderListConf.php
<?php
$senderListConf = [
['Host' => 'smtp.qq.com', 'Username' => 'xxx@qq.com', 'Password' => 'auth_code'],
['Host' => 'smtp.163.com', 'Username' => 'xxx@163.com', 'Password' => 'auth_code'],
['Host' => 'smtp.126.com', 'Username' => 'xxx@126.com', 'Password' => 'auth_code'],
['Host' => 'smtp.gmail.com', 'Username' => 'xxx@gmail.com', 'Password' => 'app_password'],
['Host' => 'smtp.mail.yahoo.com','Username' => 'xxx@yahoo.com', 'Password' => 'app_password'],
['Host' => 'smtp.sohu.com', 'Username' => 'xxx@sohu.com', 'Password' => 'password'],
['Host' => 'smtp.139.com', 'Username' => 'xxx@139.com', 'Password' => 'password'],
];
?>
關鍵細節與避坑指南
| 問題 | 原因 | 解法 |
| Gmail/Outlook 拒收或進垃圾箱 | 無 SPF/DKIM/DMARC、IP 信譽低、內容觸發規則 | 配置域名解析、使用專用郵件服務(SendGrid、Mailgun、Amazon SES) |
| QQ/163 密碼錯誤 | 需開啟「SMTP 服務」獲取授權碼,非登入密碼 | 郵箱設定 → 客戶端授權碼 → 複製到配置 |
| 大量發送被封 IP/帳號 | 頻率過高、內容相似度高、收件箱投訴 | 帳號池輪換、內容模板變量化、每日限量、退訂連結 |
| 附件亂碼/過大 | 未正確設定 Encoding、超過郵箱限制(通常 20-50MB) | 大檔上傳雲存儲發連結、base64 編碼、分卷壓縮 |
| 中文主題/發件人名亂碼 | 未編碼 | PHPMailer 內建支援 UTF-8,確保 CharSet=UTF-8 |
生產級升級建議
- 改用隊列:Redis + Worker 進程,支援併發、重試策略、優先級、延遲發送
- 模板引擎:Twig/Blade 渲染個性化內容,避免全量相似觸發反垃圾
- 退訂/黑名單:資料庫記錄退訂用戶,發送前過濾
- 追蹤像素:嵌入 1px 圖片統計打開率、點擊率
- 專業郵件服務:SendGrid、Mailgun、Postmark、Amazon SES、阿里云郵件推送,到達率 98%+,免維護 IP 信譽
- 監控告警:失敗率 > 5% 發送 Slack/钉钉/郵件告警
結語
本文展示一個「能跑通」的 PHP 郵件群發原型:多帳號輪換、失敗遞迴重試、頻率控制、日誌分檔。核心邏輯約 100 行,適合中小規模(萬級/日)營銷場景。若業務增長至十萬級以上,強烈建議遷移至專業郵件 API 服務,將精力集中在內容運營而非基礎設施維護上。
- 上一篇文章: PHP 腳本超時機制深度解析:max_execution_time、max_input_time、Zend 定時器與 Windows/Linux 差異
- 下一篇文章: 46 個 PHP 實用程式碼片段大全:簡訊、郵件、防注入、地理定位、圖片、壓縮、加密、API 整合
Copyright ©
程式碼之家 All Rights Reserved