本文目录导读:

我来为您设计一个完整的自动化邮件归档脚本方案,包含多种实现方式和最佳实践。
Python + IMAP(通用性强)
#!/usr/bin/env python3
"""
自动化邮件归档脚本 - Python版本
支持IMAP协议,适用于大多数邮箱服务
"""
import imaplib
import email
from email.header import decode_header
import datetime
import os
import logging
import time
from pathlib import Path
import json
from typing import List, Dict, Optional
import hashlib
class EmailArchiver:
"""邮件自动化归档类"""
def __init__(self, config_file: str = "config.json"):
"""
初始化归档器
Args:
config_file: 配置文件路径
"""
self.config = self._load_config(config_file)
self.imap = None
self.setup_logging()
def _load_config(self, config_file: str) -> Dict:
"""加载配置文件"""
with open(config_file, 'r', encoding='utf-8') as f:
return json.load(f)
def setup_logging(self):
"""设置日志记录"""
log_config = self.config.get('logging', {})
log_file = log_config.get('file', 'archive.log')
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file, encoding='utf-8'),
logging.StreamHandler()
]
)
self.logger = logging.getLogger(__name__)
def connect(self) -> bool:
"""连接到邮件服务器"""
try:
imap_config = self.config['imap']
self.imap = imaplib.IMAP4_SSL(
imap_config['server'],
imap_config['port']
)
self.imap.login(
imap_config['username'],
imap_config['password']
)
self.logger.info(f"成功连接到 {imap_config['server']}")
return True
except Exception as e:
self.logger.error(f"连接失败: {str(e)}")
return False
def disconnect(self):
"""断开连接"""
if self.imap:
try:
self.imap.logout()
self.logger.info("已断开连接")
except:
pass
def _decode_header(self, header_value: str) -> str:
"""解码邮件头"""
if header_value is None:
return ""
decoded_parts = []
for part, encoding in decode_header(header_value):
if isinstance(part, bytes):
try:
decoded = part.decode(encoding or 'utf-8')
except:
decoded = part.decode('utf-8', errors='ignore')
else:
decoded = part
decoded_parts.append(decoded)
return ''.join(decoded_parts)
def _get_email_date(self, msg) -> datetime.datetime:
"""获取邮件日期"""
date_str = msg.get('Date', '')
try:
from email.utils import parsedate_to_datetime
return parsedate_to_datetime(date_str)
except:
return datetime.datetime.now()
def _extract_email_content(self, msg) -> str:
"""提取邮件内容"""
content = ""
if msg.is_multipart():
for part in msg.walk():
if part.get_content_type() == "text/plain":
content = part.get_payload(decode=True)
if content:
try:
content = content.decode('utf-8')
except:
content = content.decode('utf-8', errors='ignore')
break
else:
content = msg.get_payload(decode=True)
if content:
try:
content = content.decode('utf-8')
except:
content = content.decode('utf-8', errors='ignore')
return str(content)
def get_inbox_emails(self, folder: str = "INBOX") -> List[Dict]:
"""获取收件箱邮件"""
emails = []
try:
# 选择文件夹(只读模式)
status, messages = self.imap.select(folder, readonly=True)
if status != 'OK':
self.logger.error(f"无法打开文件夹 {folder}")
return emails
# 获取邮件数量
email_count = int(messages[0])
self.logger.info(f"文件夹 {folder} 中有 {email_count} 封邮件")
# 获取所有邮件ID
status, msg_ids = self.imap.search(None, "ALL")
if status != 'OK':
return emails
# 配置中的归档条件
archive_config = self.config.get('archive_rules', {})
days_to_archive = archive_config.get('days_old', 30)
archive_folders = archive_config.get('folders', ['INBOX'])
cutoff_date = datetime.datetime.now() - datetime.timedelta(
days=days_to_archive
)
# 处理每封邮件
for msg_id in msg_ids[0].split():
try:
# 获取邮件详情
status, msg_data = self.imap.fetch(msg_id, '(RFC822)')
if status != 'OK':
continue
raw_email = msg_data[0][1]
msg = email.message_from_bytes(raw_email)
# 解析邮件信息
email_date = self._get_email_date(msg)
subject = self._decode_header(msg.get('Subject', ''))
sender = self._decode_header(msg.get('From', ''))
recipients = self._decode_header(msg.get('To', ''))
content = self._extract_email_content(msg)
# 检查是否满足归档条件
if email_date < cutoff_date:
email_info = {
'id': msg_id.decode(),
'date': email_date.strftime('%Y-%m-%d'),
'time': email_date.strftime('%H:%M:%S'),
'subject': subject,
'sender': sender,
'recipients': recipients,
'content_preview': content[:500],
'folder': folder,
'archived': False
}
# 应用过滤规则
if self._should_archive(email_info):
emails.append(email_info)
except Exception as e:
self.logger.error(f"处理邮件 {msg_id} 时出错: {str(e)}")
continue
except Exception as e:
self.logger.error(f"获取邮件失败: {str(e)}")
return emails
def _should_archive(self, email_info: Dict) -> bool:
"""判断是否应该归档某封邮件"""
rules = self.config.get('filter_rules', {})
# 排除重要发件人
exclude_senders = rules.get('exclude_senders', [])
for sender in exclude_senders:
if sender.lower() in email_info['sender'].lower():
return False
# 排除特定主题
exclude_subjects = rules.get('exclude_subjects', [])
for subject in exclude_subjects:
if subject.lower() in email_info['subject'].lower():
return False
# 仅包含特定发件人
include_senders = rules.get('include_senders', [])
if include_senders:
for sender in include_senders:
if sender.lower() in email_info['sender'].lower():
return True
return False
return True
def archive_emails(self, emails: List[Dict]) -> int:
"""归档邮件"""
archived_count = 0
for email_info in emails:
try:
if email_info.get('archived', False):
continue
# 根据月份创建归档文件夹
year_month = email_info['date'][:7] # YYYY-MM
archive_folder = f"Archive/{year_month}"
# 创建归档文件夹(如果不存在)
status, _ = self.imap.select(email_info['folder'])
if status != 'OK':
continue
# 复制邮件到归档文件夹
msg_id = email_info['id'].encode()
status, _ = self.imap.copy(msg_id, archive_folder)
if status == 'OK':
# 如果配置为移动(而不是复制),则删除原邮件
if self.config.get('archive_rules', {}).get('move', True):
self.imap.store(msg_id, '+FLAGS', '\\Deleted')
email_info['archived'] = True
archived_count += 1
self.logger.info(
f"已归档邮件: {email_info['subject'][:50]}"
)
except Exception as e:
self.logger.error(
f"归档邮件 {email_info.get('id')} 失败: {str(e)}"
)
continue
# 如果使用了删除标记,执行expunge
if archived_count > 0 and self.config.get('archive_rules', {}).get('move', True):
try:
self.imap.expunge()
except:
pass
return archived_count
def save_metadata(self, emails: List[Dict], output_file: str = "archive_metadata.json"):
"""保存归档元数据"""
metadata = {
'timestamp': datetime.datetime.now().isoformat(),
'archived_emails': emails
}
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(metadata, f, ensure_ascii=False, indent=2)
self.logger.info(f"元数据已保存到 {output_file}")
def run_archive_chunk(self, chunk_size: int = 100):
"""分块运行归档任务"""
all_emails = []
for folder in self.config.get('archive_rules', {}).get('folders', ['INBOX']):
self.logger.info(f"处理文件夹: {folder}")
emails = self.get_inbox_emails(folder)
all_emails.extend(emails)
# 分块处理
for i in range(0, len(emails), chunk_size):
chunk = emails[i:i+chunk_size]
if chunk:
archived = self.archive_emails(chunk)
self.logger.info(f"分块 {i//chunk_size + 1}: 归档了 {archived} 封邮件")
# 延迟以避免服务器限制
time.sleep(1)
# 保存元数据
self.save_metadata(all_emails)
return len(all_emails)
def run(self):
"""主运行函数"""
self.logger.info("=== 开始邮件归档任务 ===")
if not self.connect():
return
try:
archived_count = self.run_archive_chunk()
self.logger.info(f"总共归档了 {archived_count} 封邮件")
except Exception as e:
self.logger.error(f"归档任务执行失败: {str(e)}")
finally:
self.disconnect()
self.logger.info("=== 邮件归档任务结束 ===")
def main():
"""主函数"""
# 创建配置示例
config = {
"imap": {
"server": "imap.example.com",
"port": 993,
"username": "your_email@example.com",
"password": "your_password"
},
"archive_rules": {
"days_old": 30,
"folders": ["INBOX"],
"move": True
},
"filter_rules": {
"exclude_senders": ["urgent@company.com"],
"exclude_subjects": ["Important", "Urgent"],
"include_senders": [] # 空列表表示归档所有邮件
},
"logging": {
"file": "email_archive.log"
}
}
# 保存配置
with open("config.json", 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
# 运行归档器
archiver = EmailArchiver("config.json")
archiver.run()
if __name__ == "__main__":
main()
Windows PowerShell脚本
# EmailArchiver.ps1 - Windows版邮件归档脚本
param(
[string]$Mailbox,
[int]$DaysOld = 30,
[string]$OutputPath = "C:\EmailArchive",
[switch]$CreateScheduledTask
)
# 添加Microsoft Outlook库
Add-Type -AssemblyName "Microsoft.Office.Interop.Outlook"
class EmailArchiver {
[string]$Mailbox
[int]$DaysOld
[string]$OutputPath
[object]$Outlook
EmailArchiver([string]$mailbox, [int]$daysOld, [string]$outputPath) {
$this.Mailbox = $mailbox
$this.DaysOld = $daysOld
$this.OutputPath = $outputPath
# 初始化Outlook
$this.Outlook = New-Object -ComObject Outlook.Application
# 创建输出目录
if (-not (Test-Path $this.OutputPath)) {
New-Item -Path $this.OutputPath -ItemType Directory -Force
}
}
[void] StartArchive() {
Write-Host "开始归档邮件..." -ForegroundColor Green
try {
# 获取命名空间
$namespace = $this.Outlook.GetNamespace("MAPI")
# 获取收件箱
$inbox = $namespace.GetDefaultFolder([Microsoft.Office.Interop.Outlook]::OlDefaultFolders.olFolderInbox)
# 计算截止日期
$cutoffDate = (Get-Date).AddDays(-$this.DaysOld)
# 遍历邮件
$items = $inbox.Items
$items.Sort("[ReceivedTime]", $true) # 按接收时间排序
$archivedCount = 0
foreach ($item in $items) {
if ($item.Class -eq [Microsoft.Office.Interop.Outlook]::OlObjectClass.olMail) {
$email = [Microsoft.Office.Interop.Outlook.MailItem]$item
# 检查邮件日期
if ($email.ReceivedTime -lt $cutoffDate) {
# 创建归档文件夹结构
$yearMonth = $email.ReceivedTime.ToString("yyyy-MM")
$archiveFolder = Join-Path $this.OutputPath $yearMonth
if (-not (Test-Path $archiveFolder)) {
New-Item -Path $archiveFolder -ItemType Directory -Force
}
# 生成文件名
$safeSubject = $email.Subject -replace '[^\w\-]', '_'
$fileName = "$($email.ReceivedTime.ToString('yyyyMMdd_HHmmss'))_$safeSubject.eml"
$filePath = Join-Path $archiveFolder $fileName
# 转换并保存为EML格式
$this.ConvertToEML($email, $filePath)
# 可选的:移动到Outlook归档文件夹
if ($this.Mailbox) {
$this.MoveToArchiveFolder($email)
}
$archivedCount++
Write-Host "已归档: $($email.Subject)" -ForegroundColor Gray
}
}
}
Write-Host "归档完成,共归档 $archivedCount 封邮件" -ForegroundColor Green
} catch {
Write-Error "归档失败: $_"
} finally {
# 清理COM对象
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($this.Outlook) | Out-Null
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
}
}
[void] ConvertToEML($email, $outputPath) {
try {
# 创建和写出EML内容
$emlContent = @"
From: $($email.SenderEmailAddress)
To: $($email.To)
CC: $($email.CC)
Subject: $($email.Subject)
Date: $($email.ReceivedTime.ToString("r"))
$($email.Body)
"@
# 保存EML文件
[System.IO.File]::WriteAllText($outputPath, $emlContent, [System.Text.Encoding]::UTF8)
} catch {
Write-Warning "转换邮件失败: $_"
}
}
[void] MoveToArchiveFolder($email) {
try {
# 获取命名空间
$namespace = $this.Outlook.GetNamespace("MAPI")
# 创建归档文件夹
$emailFolder = $namespace.Folders.Item($email.Parent.FolderPath)
$archiveFolderName = "Archive_$($email.ReceivedTime.ToString('yyyy-MM'))"
$archiveFolder = $null
try {
$archiveFolder = $emailFolder.Folders.Item($archiveFolderName)
} catch {
$archiveFolder = $emailFolder.Folders.Add($archiveFolderName)
}
# 移动邮件
$email.Move($archiveFolder)
} catch {
Write-Warning "移动邮件失败: $_"
}
}
}
# 主程序
function Main {
$archiver = [EmailArchiver]::new(
$Mailbox,
$DaysOld,
$OutputPath
)
$archiver.StartArchive()
# 创建计划任务
if ($CreateScheduledTask) {
CreateScheduledTask
}
}
function CreateScheduledTask {
Write-Host "创建计划任务..." -ForegroundColor Cyan
$scriptPath = $MyInvocation.MyCommand.Path
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File `"$scriptPath`""
# 每周一上午9点运行
$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday -At "09:00"
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -DontStopOnIdleEnd
Register-ScheduledTask -TaskName "Email Archive" -Action $action -Trigger $trigger -Settings $settings -Force
Write-Host "计划任务已创建" -ForegroundColor Green
}
# 检查是否作为计划任务运行
if ($env:SCHEDULER_EXECUTING -eq $true -or $CreateScheduledTask) {
Main
} else {
# 交互式运行
Main
}
Shell + Mutt(Linux/Mac)
#!/bin/bash
# email_archive.sh - Linux/Mac邮件归档脚本
# 配置变量
IMAP_SERVER="imap.example.com"
EMAIL_USER="your_email@example.com"
EMAIL_PASS="your_password"
ARCHIVE_DIR="$HOME/email_archive"
DAYS_OLD=30
LOG_FILE="$HOME/email_archive.log"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# 错误日志
error_log() {
echo -e "${RED}$(date '+%Y-%m-%d %H:%M:%S') - ERROR: $1${NC}" | tee -a "$LOG_FILE"
}
# 检查依赖
check_dependencies() {
if ! command -v curl &>/dev/null; then
error_log "curl未安装"
exit 1
fi
}
# 配置Mutt环境
setup_mutt() {
cat > /tmp/archive_mutt.conf << EOF
set imap_user="$EMAIL_USER"
set imap_pass="$EMAIL_PASS"
set imap_check_subscribed
set use_imap
set folder="imap://$IMAP_SERVER"
set spoolfile="imap://$IMAP_SERVER/INBOX"
set record="+INBOX/Sent"
set editor="echo"
set askcc=no
set askbcc=no
set move=ask-yes
set save_name=yes
set delete=yes
set delete_untagged=yes
set sort=threads
set read_ratio=0.4
set timeout=120
set mail_check=150
EOF
}
# 创建归档目录结构
create_archive_structures() {
mkdir -p "$ARCHIVE_DIR"/{metadata,logs,emails}
log "创建归档目录结构"
}
# 归档邮件函数
archive_emails() {
local folder=$1
local cutoff_date=$(date -d "$DAYS_OLD days ago" +%Y-%m-%d)
log "开始归档文件夹 $folder 中的邮件($DAYS_OLD天前)"
# 使用curl直接操作IMAP
local temp_dir="$ARCHIVE_DIR/emails/$(date +%Y-%m)"
mkdir -p "$temp_dir"
# 获取邮件列表
curl -s --imap "$IMAP_SERVER" \
--user "$EMAIL_USER:$EMAIL_PASS" \
--create "$folder" \
--search "SINCE $cutoff_date" \
2>/dev/null | while read email_id; do
if [ ! -z "$email_id" ]; then
log "处理邮件 ID: $email_id"
# 下载邮件
local filename="${temp_dir}/email_${email_id}_$(date +%Y%m%d%H%M%S).eml"
curl -s --imap "$IMAP_SERVER" \
--user "$EMAIL_USER:$EMAIL_PASS" \
--fetch "$email_id" "$folder" \
2>/dev/null > "$filename"
# 保存元数据
echo "{\"id\":\"$email_id\",\"date\":\"$(date +%Y-%m-%d)\",\"folder\":\"$folder\"}" >> \
"$ARCHIVE_DIR/metadata/archive_$(date +%Y%m).json"
log "已保存邮件 $email_id 到 $filename"
# 可选:删除原邮件
# curl -s --imap "$IMAP_SERVER" \
# --user "$EMAIL_USER:$EMAIL_PASS" \
# --delete "$email_id" "$folder" \
# 2>/dev/null
fi
done
log "文件夹 $folder 归档完成"
}
# 压缩并归档
compress_archive() {
local archive_file="$ARCHIVE_DIR/email_archive_$(date +%Y%m).tar.gz"
log "压缩归档文件..."
tar -czf "$archive_file" -C "$ARCHIVE_DIR" emails metadata logs
# 删除原始文件
rm -rf "$ARCHIVE_DIR/emails/" "$ARCHIVE_DIR/metadata/"
log "归档压缩完成: $archive_file"
echo "$archive_file"
}
# 生成报告
generate_report() {
local archive_file=$1
local report_file="$ARCHIVE_DIR/archive_report_$(date +%Y%m).html"
log "生成HTML报告..."
# 从压缩文件读取元数据
local email_count=$(tar -tzf "$archive_file" | grep -c "\.eml$")
local archive_size=$(du -h "$archive_file" | cut -f1)
cat > "$report_file" << EOF
<!DOCTYPE html>
<html>
<head>邮件归档报告 - $(date +%Y年%m月)</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background-color: #4CAF50; color: white; padding: 20px; }
.stats { margin: 20px 0; }
.stat-item { margin: 10px 0; padding: 10px; background-color: #f9f9f9; }
</style>
</head>
<body>
<div class="header">
<h1>邮件归档报告</h1>
<p>归档时间:$(date)</p>
</div>
<div class="stats">
<h2>归档统计</h2>
<div class="stat-item">
<strong>归档邮件数量:</strong> $email_count 封
</div>
<div class="stat-item">
<strong>归档文件大小:</strong> $archive_size
</div>
<div class="stat-item">
<strong>归档来源:</strong> IMAP邮件服务器
</div>
</div>
</body>
</html>
EOF
log "报告已生成: $report_file"
}
# 主程序
main() {
log "开始执行邮件归档任务"
check_dependencies
setup_mutt
create_archive_structures
# 归档收件箱
archive_emails "INBOX"
# 可选:归档其他文件夹
# archive_emails "Work"
# archive_emails "Projects/ClientA"
# 压缩归档
archive_file=$(compress_archive)
# 生成报告
generate_report "$archive_file"
# 可选:上传到云存储
# upload_to_s3 "$archive_file"
# 清理临时文件
rm -rf "$ARCHIVE_DIR/logs"/*
log "邮件归档任务完成!"
echo -e "${GREEN}归档完成,报告位置:$ARCHIVE_DIR/archive_report_$(date +%Y%m).html${NC}"
}
# 运行主程序
main
配置文件示例
创建 config.json 文件:
{
"imap": {
"server": "imap.example.com",
"port": 993,
"username": "your_email@example.com",
"password": "your_email_password",
"security": "ssl"
},
"archive_rules": {
"days_old": 30,
"folders": ["INBOX", "Work"],
"move": true,
"delete_after": false,
"compress": true
},
"filter_rules": {
"exclude_senders": ["no-reply@newsletter.com", "notification@updates.com"],
"exclude_subjects": ["Support Ticket", "Invoice"]
},
"output": {
"path": "/path/to/archive",
"format": "eml",
"metadata": true,
"report": true
},
"logging": {
"file": "/var/log/email_archive.log",
"level": "INFO"
},
"schedule": {
"enabled": true,
"frequency": "weekly",
"day": "Monday",
"time": "09:00"
},
"notifications": {
"email": "admin@example.com",
"slack_webhook": "https://hooks.slack.com/services/xxx",
"enabled": false
}
}
最佳实践和建议
安全建议
- 保护好配置文件中的密码(使用环境变量或密钥管理服务)
- 使用OAuth2.0替代基本认证
- 设置文件夹权限只有管理员可访问
# 环境变量保护密码 export EMAIL_ARCHIVE_PASSWORD="your_secret_password"
自动化计划
Windows cron (任务计划程序):
schtasks /create /tn "Email Archive" /tr "powershell.exe -File C:\scripts\EmailArchiver.ps1" /sc weekly /d MON /st 09:00 /ru SYSTEM
Linux cron:
# 每周一凌晨2点运行 0 2 * * 1 /path/to/email_archive.sh >> /var/log/email_archive_cron.log 2>&1 # 每天检查邮件 0 */6 * * * /path/to/email_archive.sh --incremental
性能优化
# 分批处理
for batch in chunks(email_list, 100):
process_batch(batch)
time.sleep(5) # 避免服务器限制
错误处理
# 添加重试机制
def retry_on_failure(func, max_attempts=3):
for attempt in range(max_attempts):
try:
return func()
except Exception as e:
if attempt == max_attempts - 1:
raise
time.sleep(2 ** attempt) # 指数退避
监控和报告
- 记录归档运行状态
- 发送通知邮件或Slack消息
- 定期检查日志
使用方法
- 配置脚本参数(根据实际情况修改)
- 测试运行:先在小规模邮件上测试
- 设置权限:确保脚本有适当的文件权限
- 创建计划任务:使用cron或任务计划程序
- 监控运行:定期检查日志和输出
这个脚本提供了灵活的配置和多种归档方式,您可以根据实际需求调整,建议先在测试环境中运行,确保一切正常后再部署到生产环境。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播