2009年05月10日 Sunday , 1,526 次点击

一个月前写了个脚本,打算由Linux的cron触发,每天定时往指定的邮箱中发送一张照片。

PYTHON:
  1. #!/usr/bin/python
  2. # --*-- coding: utf-8 --*--
  3. # 2009年 04月 21日 星期二 00:28:42 CST
  4. from email.MIMEMultipart import MIMEMultipart
  5. from email.MIMEText import MIMEText
  6. from email.MIMEImage import MIMEImage
  7. import email.Utils
  8. import smtplib
  9. import datetime
  10. import locale
  11. import os
  12. import getpass
  13. import Image
  14. import tempfile
  15. import sys
  16.  
  17. # 邮件发送者的电邮地址
  18. fromAddr = 'my_address@gmail.com'
  19. # 所有要发送到的电邮地址
  20. toAddrs = ['target_address_1@yahoo.com.cn','target_address_2@gmail.com']
  21. # 保存已发送图片信息的日志文件
  22. logHistory = '/home/lenin/scripts/data/sweethome/history.log'
  23. # 程序运行时输出的日志
  24. logApp = '/home/lenin/scripts/data/sweethome/app.log'
  25. # 图片存放文件夹
  26. basePath = '/home/lenin/scripts/data/sweethome/images'
  27. # 保存目录中所有可发送的图片文件路径的列表
  28. files = []
  29.  
  30. # 发送邮件的函数
  31. def SendMail(fromAddr, toAddrs, filePath):
  32.     Shout('开始发送图片……')
  33.     # 创建邮件
  34.     mail = MIMEMultipart()
  35.     mail['To'] = ','.join(toAddrs)
  36.     mail['From'] = fromAddr
  37.     mail['Subject'] = '每日一图'+'('+datetime.datetime.strftime(datetime.datetime.now(), '%c')+')'
  38.     mail['Date'] = email.Utils.formatdate(localtime=1)
  39.     # 创建邮件内容
  40.     mailContent = '''
  41.    hi,
  42.        sweethome.py每天由Linux操作系统的cron后台守护进程在指定时间触发,每次自动发送一张指定目录中的图片。
  43.        sweethome.py会自动检测图片的大小,并将分辨率较大的图片同比缩放至800像素以内(以最长边为准),然后再行发送,目的是减小图片大小以适应天朝糟糕的网速。
  44.    明儿见
  45.    '''
  46.     body = MIMEText(mailContent, 'plain', 'utf-8')
  47.     mail.attach(body)
  48.     # 创建附件
  49.     fp = open(filePath, 'rb')
  50.     try:
  51.         mailImage = MIMEImage(fp.read())
  52.     except IOError:
  53.         Shout('读取图片文件“'+filePath+'”失败,邮件无法发送!')
  54.         sys.exit()
  55.     else:
  56.         mailImage.add_header('Content-disposition', 'attachment', filename=datetime.datetime.now().__str__().replace(' ', '_')+'.jpg')
  57.     finally:
  58.         fp.close()
  59.     mail.attach(mailImage)
  60.     # 发送
  61.     svr = smtplib.SMTP('smtp.gmail.com', '587')
  62.     svr.starttls()
  63.     svr.login('my_address', getpass.getpass('Input the password:'))
  64.     svr.sendmail(fromAddr, toAddrs, mail.as_string())
  65.     svr.quit()
  66.     Shout('图片发送成功!')
  67.  
  68. # 遍历basePath,将所有可发送的图片文件的路径保存到files列表中
  69. def WalkDir(basePath):
  70.     if os.path.isdir(basePath):
  71.         for item in os.listdir(basePath):
  72.             WalkDir(basePath+os.sep+item)
  73.     else:
  74.         if ['jpg', 'png', 'gif', 'bmp', 'tif'].count(basePath[-3:].lower()) == 1:
  75.             try:
  76.                 files.append(basePath)
  77.             except NameError:
  78.                 Shout('存放图片文件路径的list变量“files”未定义!')
  79.                 sys.exit()
  80.  
  81. # 检查指定图片文件的路径在logHistory日志中是否已存在
  82. def HasNotBeenSent(file):
  83.     if not os.path.exists(file) or not os.path.isfile(file) or not os.path.exists(logHistory):
  84.         return True
  85.     fp = open(logHistory, 'rb')
  86.     try:
  87.         oldFiles = fp.readlines()
  88.     except IOError:
  89.         print datetime.datetime.now().__str__()+':读日志失败,检查日志文件“'+logHistory+'”是否可读或存在!'
  90.         sys.exit()
  91.     else:
  92.         for oldFile in oldFiles:
  93.             if oldFile.endswith(file):
  94.                 return False
  95.         return True
  96.     finally:
  97.         fp.close()
  98.  
  99. # 记录日志
  100. def AppendLog(log, info):
  101.     fp = open(log, 'a')
  102.     try:
  103.         fp.write(info + '\r\n')
  104.     except IOError:
  105.         print datetime.datetime.now().__str__()+':写日志失败,检查日志文件“'+log+'”是否可写!'
  106.     finally:
  107.         fp.close()
  108.  
  109. # 记录日志并输出到终端
  110. def Shout(msg):
  111.     print datetime.datetime.now().__str__()+':'+msg
  112.     AppendLog(logApp, datetime.datetime.now().__str__()+'|'+msg)
  113.  
  114. if __name__=='__main__':
  115.     # 设置区域,否则下面的日期格式化字符串是英文
  116.     locale.setlocale(locale.LC_ALL, 'zh_CN.UTF-8')
  117.     # 递归遍历所有图片
  118.     WalkDir(basePath)
  119.     for file in files:
  120.         # 读取日志,判断当前图片是否被发送过
  121.         if HasNotBeenSent(file):
  122.             #获取图片大小
  123.             img = Image.open(file)
  124.             width = img.size[0]
  125.             height = img.size[1]
  126.             if width> height and width> 800:
  127.                 width = 800
  128.                 height = height*800/width
  129.             elif width <height and height> 800:
  130.                 width = width*800/height
  131.                 height = 800
  132.             #调整图片大小并保存为临时文件
  133.             img.thumbnail((width, height), Image.ANTIALIAS)
  134.             tmpPicPath = os.path.join(tempfile.gettempdir(), 'one_picture_per_day-' + datetime.datetime.now().__str__().replace(' ', '_') + '.jpg')
  135.             while os.path.exists(tmpPicPath):
  136.                 tmpPicPath = os.path.join(tempfile.gettempdir(), 'one_picture_per_day-' + datetime.datetime.now().__str__().replace(' ', '_') + '.jpg')
  137.             img.save(tmpPicPath)
  138.             # 发送图片
  139.             SendMail(fromAddr, toAddrs, tmpPicPath)
  140.             # 记录日志
  141.             AppendLog(logHistory, datetime.datetime.now().__str__()+'|'+file)
  142.             # 清除图片
  143.             try:
  144.                 os.unlink(file)
  145.             except:
  146.                 Shout('成功发送“'+file+'”后删除原文件失败!')
  147.             #一次只发送一张图片
  148.             break
  149.         else:
  150.             try:
  151.                 os.unlink(file)
  152.             except:
  153.                 Shout('图片“'+file+'”曾被发送过,删除文件失败!')
  154.                 continue
  155.             Shout('文件(' + file + ')曾被发送过,删除并发送下一幅新图片!')

显见,用python发送邮件是比较简单的。在完善这个程序的过程中,有些体会:

  1. 要善于利用“try...except...else...finally...”的异常处理机制预防程序中的隐患
  2. 文件的读取有可能出现异常,对这种异常的捕获应该放在“fp.read()”时,然后把“fp.close()”放在finally中
  3. 使用getpass模块的getpass()函数获取用户输入的密码而不回显
  4. 使用”os.sep“可以自动获取当前操作系统下路径的分割符,而”os.path.join()“函数可以根据当前操作系统的情况和两个路径的情况将两者连接成正确的路径格式
  5. gmail需要使用TLS安全连接,因此,相当于一般的电邮,要在创建SMTP对象后调用其“starttls()”方法
  6. 使用“locale.setlocale(locale.LC_ALL, 'zh_CN.UTF-8')”设置本地环境,否则”datetime.datetime.strftime(datetime.datetime.now(), '%c')“得到的日期时间将是英文格式
  7. 使用PIL处理图片,应付一些常用操作绰绰有余并且相当简单
    • 使用Image模块的open函数获取指定图片的对象
    • 调用图片对象的thumbnail()方法获取指定大小的缩略图时,使用”Image.ANTIALIAS“常量以获取最佳图片质量
    • 图片对象的save()方法能够根据指定的存储路径的后缀名转换图片格式
Tags :

随机日志

來留言吧!


Please copy the string C57pQW to the field below:

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word

2 個留言

巧啊,昨天我写了个 python 脚本自动把log的分析结果做附件发给我,不过你这个要完善得多,哈哈。

楞,看来咱都一德性;我下了龙书第二版,准备看了。影印的不知道质量怎么样,1000多页啊,俺滴神啊。

 
 

留言板RSS 引用 URI

來留言吧!

«
»