redhat6 + python3.6 + crontab + Oracle客户端
模块 | 用到的功能 |
---|---|
cx_Oracle | 连接Oracle数据库 |
openpyxl | 导出excel |
zipfile | 压缩文件 |
smtplib | 发送邮件 |
添加邮件附件 |
##数据库相关参数:主机 、数据库用户、数据库密码、数据库sid、数据库字符集nls_lan
ora_host = '192.168.1.100'
ora_user = 'test'
ora_pwd = 'test'
ora_sid = 'test'
##nls_lan = 'AMERICAN_AMERICA.ZHS16GBK'
##数据库共享库目录
##lib_path = '/opt/oracle/instantclient_11_2'
##执行脚本(py,sql)目录
exe_path = '/root/python/prod/test/bin'
##数据库导出sql语句文件,多个文件用逗号隔开;如果sql文件在执行脚本目录下,可以使用相对路径,否则使用绝对路径;
##注意:标点符号都是英文的,sql文件中的时间变量必须是 &date 或 &yyyymmdd
sql_files = '测试脚本1.sql,测试脚本2.sql'
##sql语句中的导数日期 yesterday :表示昨天 ,目前只支持这一种情况,以后可以酌情增加其它日期
exp_date = 'yesterday'
##导出excel目录
excel_path = "/root/python/prod/test/data"
##导出excel文件sheet名,sheet名称与上面参数sql_files中的每个sql文件中sql条数一一对应,如果没有设置对应名称,则sheet名称默认
excel_sheet_names = [['测试脚本1'], ['测试脚本2'], ['测试脚本3']]
##导出exel文件是否压缩 1:压缩 0:不压缩 删除此参数
##is_zip = 1
##压缩后文件名前缀 ,压缩文件名=前缀+导数日期
zipfile_name = '测试脚本'
##是否发送邮件 1: 发送 0:不发送
is_mail = 1
##邮箱发送服务器
mail_server = 'smtp.163.com'
##邮箱发送服务器端口
mail_port = '25'
##发送邮件
mail_from = '[email protected]'
##发送邮件密码
mail_pass = 'example'
##接收邮件
mail_to = '[email protected],[email protected]'
##抄送邮件
mail_cc = '[email protected],[email protected]'
##邮件标题
mail_subject = '测试脚本'
##邮件内容
mail_body = """
您好:
提取数据见附件,请查收;
-------------------------------------------------------------------------------------
test
test
系统在使用过程中如遇到问题,请及时与运维团队联系
热线电话:11111111111
运维邮箱:[email protected]
"""
第二个执行脚本,如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cx_Oracle as cx
from openpyxl import Workbook
import datetime
import os
import zipfile
from config_ora2excel import * ##导入配置文件
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email import encoders
from email.header import Header
from email.mime.base import MIMEBase
##设置环境变量,不知道为什么不生效,程序还是报错:Error: DPI-1047: Cannot locate a 64-bit Oracle Client library: "libclntsh.so: cannot open shared object file: No such file or irectory". See https://oracle.github.io/odpi/doc/installation.html#linux for help
#os.environ['LD_LIBRARY_PATH'] = lib_path
#os.environ['NLS_LANG'] = nls_lan
#os.environ.setdefault("LD_LIBRARY_PATH", lib_path)
#os.environ.setdefault("NLS_LANG", nls_lan)
def get_yesterday():
today = datetime.date.today()
oneday = datetime.timedelta(days=1)
yesterday = today - oneday
return yesterday
def get_data_date():
# 获取数据日期
##data_date = input('请输入数据日期,格式yyyymmdd,默认为昨天:')
data_date = exp_date
if data_date.strip().lower() == 'yesterday':
yesterday = get_yesterday()
data_date = yesterday.strftime("%Y%m%d")
##print(data_date)
return data_date
def get_weeks():
#获取周次
data_date = get_data_date()
y = int(data_date[:4])
m = int(data_date[4:6])
d = int(data_date[6:])
weeks = datetime.date(y,m,d).isocalendar()[1]
return weeks
def ora_to_excel(data_date):
try:
ret = 0
#print(os.environ['LD_LIBRARY_PATH'])
#print(os.environ['NLS_LANG'])
# 连接数据库
# connect = cx.connect(user+'/'+pwd+'@'+host+'/'+sid)
connect = cx.connect('{}/{}@{}/{}'.format(ora_user, ora_pwd, ora_host, ora_sid))
print('连接数据库成功!!')
# 获取数据日期
data_date = data_date
print(data_date)
##切换到脚本执行目录
os.chdir(exe_path)
# 获取sql执行并导出到excel,可能是多条sql,需要分别执行
fi = 0
for f_sql in sql_files.split(','):
start = datetime.datetime.now()
##print(type(f_sql))
cursor = connect.cursor()
print(f_sql)
with open(f_sql, 'r', encoding='utf-8') as f:
sqls = f.read()
j = 0
wb = Workbook()
for sql in sqls.split(';'):
sql = sql.strip()
if not sql:
break
##替换变量 sql中的变量必须是 &date 或 &yyyymmdd
# sql = sql.lower()
sql = sql.replace('&date', data_date)
sql = sql.replace('&yyyymmdd', data_date)
##sql = sql.decode('utf-8')
print('-----------------------------------------开始执行sql:\n', sql)
cursor.execute(sql)
print('-----------------------------------------执行sql成功\n')
print('-----------------------------------------开始导出excel\n')
sht = wb.create_sheet(title='Mysheet'+str(j),index=j)
## 如果变量设置了sheet名称,则修改
if len(excel_sheet_names) >= fi + 1:
if len(excel_sheet_names[fi]) >= j + 1 and excel_sheet_names[fi][j].strip():
sht.title = excel_sheet_names[fi][j]
print(sht.title)
# 处理表头
i = 1
for col in cursor.description:
sht.cell(1, i).value = col[0]
i += 1
##print('11')
# 写数据
##offset = 2
while True:
results = cursor.fetchmany(10000)
if not results:
break
resLen = len(results)
for res in results:
sht.append(res)
##在开头插入一列
###print('1111')
sht.insert_cols(1)
###print('22')
###print(sht.max_row)
# 给第一列写入行标
for k in range(2, sht.max_row+1):
sht.cell(k,1).value = k - 1
# 获取导出文件目录及文件名
exl_file = f_sql.split(os.sep)[-1].split('.')[0] + '_' + data_date + '.xlsx'
if excel_path.strip():
exl_file = excel_path + os.sep + exl_file
print(exl_file)
j += 1
wb.save(exl_file)
##wb.close()
cursor.close()
print('-----------------------------------------导出excel成功!\n')
print('导出excel 花费时间:', datetime.datetime.now() - start, '\n\n')
fi += 1
ret = 1
except Exception as e:
print('Error:', e)
##raise e
finally:
connect.close()
return ret
def get_zip_file(input_path, wd, results):
"""
在目录下查找包含关键字的文件
input_path: 目录
wd: 关键字
results:查找结果列表
"""
try:
files = os.listdir(input_path)
for filename in files:
fp = os.path.join(input_path, filename)
if os.path.isfile(fp) and wd in filename:
# 使用相对路径
results.append('.' + os.sep + filename)
except Exception as e:
print(e)
raise e
def zip_files(input_path, output_zip_name, data_date):
"""
压缩文件
input_path:目录
output_zip_name:zip文件名
data_date :数据日期
"""
try:
ret = None
start = datetime.datetime.now()
filelists = []
wd = data_date + '.xlsx'
get_zip_file(input_path, wd, filelists)
if len(filelists) > 0:
print('-----------------------------------------开始压缩文件')
f = zipfile.ZipFile(input_path + os.sep + output_zip_name, 'w', zipfile.ZIP_DEFLATED)
# 切换到打包目录
os.chdir(input_path)
for file in filelists:
f.write(file)
print(input_path + os.sep + output_zip_name)
print('-----------------------------------------压缩文件完成')
print('压缩文件 花费时间:', datetime.datetime.now() - start, '\n\n')
f.close()
print('压缩文件名:',input_path + os.sep + output_zip_name)
ret = output_zip_name
return ret
except Exception as e:
print('Error:', e)
raise e
finally:
return ret
def send_mail(zipfile):
# 构造一个MIMEMultipart对象代表邮件本身
msg = MIMEMultipart()
msg['From'] = mail_from
msg['To'] = mail_to
msg['Cc'] = mail_cc
weeks = str(get_weeks())
mail_sub = mail_subject.replace('{weeks}',weeks)
msg['Subject'] = Header(mail_sub,'utf-8').encode()
msg.attach(MIMEText(mail_body,'html','utf-8'))
# 切换到zip文件目录
os.chdir(excel_path)
#二进制模式读取zip文件
with open(zipfile,'rb') as f:
zipf = zipfile.split('.')[0]
# 设置附件的MIME和文件名,这里是zip类型:
mime = MIMEBase('zip', 'zip', filename=zipfile)
# 加上必要的头信息:
#mime.add_header('Content-Disposition', 'attachment', file_name=Header(zipfile,'utf-8').encode())
#mime.add_header('Content-Disposition', 'attachment', filename=('gbk', '', zipfile))
#mime.add_header('Content-Disposition', 'attachment', filename=('utf-8', '', zipfile)) ##这种方式发送,邮件客户端收到邮件,中文附件名还是可能有乱码
mime.add_header('Content-Disposition', 'attachment', filename= Header(zipfile,'gbk').encode() ) ## ##中文附件名称outlook,网页,foxmail均显示正常
#mime.add_header('Content-ID', '<0>')
#mime.add_header('X-Attachment-Id', '0')
# 把附件的内容读进来:
mime.set_payload(f.read())
# 用Base64编码
encoders.encode_base64(mime)
#添加到MIMEMultipart
msg.attach(mime)
try:
print('-----------------------------------------发送邮件开始')
s = smtplib.SMTP()
s.connect(mail_server,mail_port)
s.login(mail_from,mail_pass)
##sendmail的第2个参数邮件地址是个list,所以将字符串用split转为list
s.sendmail(mail_from,mail_to.split(',')+mail_cc.split(','),msg.as_string())
s.quit()
print('-----------------------------------------发送邮件结束')
except smtplib.SMTPException as e:
print('发送失败:',e)
return
def main():
try:
print('--Program begin',datetime.datetime.now(),'--------------------------------------')
print(os.environ['LD_LIBRARY_PATH'])
print(os.environ['NLS_LANG'])
data_date = get_data_date()
oret = ora_to_excel(data_date)
##oret = 1
weeks = str(get_weeks())
zfile_name = zipfile_name.replace('{weeks}',weeks)
output_zip_name = zfile_name + '_' + data_date + '.zip'
if oret == 1:
zfile = zip_files(excel_path, output_zip_name, data_date)
###zfile = output_zip_name
if zfile:
if is_mail == 1:
send_mail(zfile)
print('--Program End', datetime.datetime.now(), '--------------------------------------')
except Exception as e:
print(e)
## finally:
## end = input('请输入回车键结束!')
if __name__ == '__main__':
main()
32 8 * * * . ~/.bash_profile; /root/python/prod/test/bin/test_ora_2_excel.py >> /root/python/prod/test/logs/test_ora_2_excel.log 2>&1
上面配置中 . ~/.bash_profile; 是为了让配置的环境变量LD_LIBRARY_PATH,NLS_LANG 生效
: No such file or directory
解决办法:vi xxx.py 打开文件,输入 : ,然后在底线命令模式下,输入 set ff 显示 fileformat=dos ,再在命令模式下数据 set ff=unix 回车 ,然后保存文件,再执行文件正常
解决办法:
将mime.add_header('Content-Disposition', 'attachment', filename=zipfile)
替换为下面两种写法后显示正常
mime.add_header('Content-Disposition', 'attachment', file_name=Header(zipfile,'utf-8').encode())
mime.add_header('Content-Disposition', 'attachment', filename=('gbk', '', zipfile))
os.environ[‘LD_LIBRARY_PATH’] = lib_path
os.environ[‘NLS_LANG’] = nls_lan
os.environ.setdefault(“LD_LIBRARY_PATH”, lib_path)
os.environ.setdefault(“NLS_LANG”, nls_lan)
临时解决办法:
crontab 调用脚本时,前面添加 . ~/.bash_profile 让环境变量生效,
或者可以写一个shell脚本,内容包含 . ~/.bash_profile 和 执行python脚本 ,然后crontab调用这个shell
解决办法:
mime.add_header('Content-Disposition', 'attachment', filename= Header(zipfile,'gbk').encode() ) ## ##中文附件名称outlook,网页,foxmail均显示正常
解决办法:使用国内镜像安装
[root@Vsens bin]# pip install openpyxl -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
Looking in indexes: http://pypi.douban.com/simple
Collecting openpyxl
Downloading http://pypi.doubanio.com/packages/f5/39/942a406621c1ff0de38d7e4782991b1bac046415bf54a66655c959ee66e8/openpyxl-2.6.3.tar.gz (173kB)
|████████████████████████████████| 174kB 2.0MB/s
Collecting jdcal (from openpyxl)
Downloading http://pypi.doubanio.com/packages/f0/da/572cbc0bc582390480bbd7c4e93d14dc46079778ed915b505dc494b37c57/jdcal-1.4.1-py2.py3-none-any.whl
Collecting et_xmlfile (from openpyxl)
Downloading http://pypi.doubanio.com/packages/22/28/a99c42aea746e18382ad9fb36f64c1c1f04216f41797f2f0fa567da11388/et_xmlfile-1.0.1.tar.gz
Installing collected packages: jdcal, et-xmlfile, openpyxl
Running setup.py install for et-xmlfile ... done
Running setup.py install for openpyxl ... done
Successfully installed et-xmlfile-1.0.1 jdcal-1.4.1 openpyxl-2.6.3