python之数据文件批量清洗,入库

python之数据文件批量清洗,入库

因为最近有一个数据接入清洗的项目,别的工具用起来有些觉得不太顺手,就学着写了一个数据清洗入库的通用模板,节省一些工作量。主要流程为:数据库连接---文件批量导入----文件批量清洗---根据清洗后的文件在数据库自动创表----清洗后的数据直接入库。

主要用到的模块为pandas、cx_Oracle、os、datetime、sqlalchemy。

```python
#连接数据库
import cx_Oracle
import pandas as pd

print("\033[10;31;50m准备连接数据库\033[0m")

try:
    conn=cx_Oracle.connect('数据库账号','数据库登录密码','host/数据库名称')
    cursor=conn.cursor() #创建游标
    print("\033[10;31;50m数据库连接成功\033[0m")
    print(' ')
except:
    print("\033[10;31;50m数据库连接失败\033[0m")
    print(' ')

    数据库连接好之后开始从本地批量接入需要清洗的数据文件。这里有设定清洗规则码表,一个字段对应一种或多种清洗的规则,码表的大致格式如下,可以根据自己的需求来定义清洗规则:

|表格名称|字段中文名称|字段英文名称|字段顺序|清洗规则|清洗规则中文
|表1|字段1|字段1名称| 1  |1 | 处理空格
|表1|字段2|字段2名称|  2 |1, 3| 处理空格,统一大小写
|表1|字段3|字段3名称|  3 |4 | 日期格式转换
……
|表n|字段n|字段n名称| n |1,2,3,4,5,6,7…… | ……

```python
#批量读入数据文件
import os
import shutil
import re
import glob
import time
from datetime import datetime
print("\033[10;31;50m批量接入文件\033[0m")
filepath=r'E:/测试/样例数据/'  #该地址为文件夹地址
newpath=r'E:/测试/清洗后的数据文件/' 
fieldcode=pd.read_excel('E:/测试/fieldcode.xlsx',index=False)  #导入清洗规则码表
fieldcode['表名']=fieldcode['表名'] + '.xlsx'  #加上后缀
files=os.listdir(filepath)
files_xlsx=list(filter(lambda x:x[-5:] == '.xlsx',files))  #将文件夹中的所有表的表名读过来,存为列表
 在批量读取文件的这一块代码中有些包是不需要的,是我在写的时候没改之前用到的,可以自行选择。

文件导入之后开始数据清洗程序。
print("\033[10;31;50m开始数据文件清洗程序\033[0m")

for i in files_xlsx:
    for i in files_xlsx:
        data=pd.read_excel(filepath+i,index=False).fillna(value='null')#将所有需要清洗的文件的空值全部填充为null
        for j in data.columns:
            etl_rule=fieldcode[fieldcode['表名']==i][fieldcode['字段英文名']==j]
            #将清洗规则转为list然后进行遍历
            dc_rule=etl_rule["清洗规则"].map(lambda x:list(str(x)))
            for k in dc_rule:
                for m in k[:]:
                    if m==",":
                        k.remove(m)  #移除list中的","元素
                    else:
                        m
                        for n in dc_rule:
                            for p in n:
                                if p == '1':  #去空格
                                    data[j]=data[j].map(lambda x:''.join(str(x).split()))
                                elif p == '3':  #统一大小写
                                    data[j]=data[j].map(lambda x:str(x).upper())
                                elif p == '4':  #日期格式转换,将yyyymmddhhmiss转为以yyyy-mm-dd HH:MI:SS
                                    data[j]=data[j].map(lambda x:datetime.strptime(str(x),"%Y%m%d%H%M%S").strftime("%Y-%m-%d %H:%M:%S"))
                                elif p == '5':   #匹配验证手机号或固话,并把固话格式统一,将固话中间的横杠全部去掉
                                    data[j]=data[j].map(lambda x:(''.join(list([x[0] for x in re.findall(r'((?,str(x))])).replace('-',''))) #join是去除list外面的中括号,list([x[0]是取正则后的第一个元素,replace是将固话格式统一
                                elif p == '6':  #时间戳转为日期格式
                                    data[j]=data[j].map(lambda x:time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(float(x/1000))))
                                elif p == '7':  #正则匹配清洗身份证号
                                    data[j]=data[j].map(lambda x:''.join(list([x[0] for x in re.findall(r'((?,str(x))])))
                                elif p == '8':   #正则匹配仅保留数字
                                    data[j]=data[j].map(lambda x:(''.join(re.findall(r'\d+\.?\d*',str(x)))))
                                else:
                                    data[j]=data[j]
                            
                                    
    write=pd.ExcelWriter(newpath+i)
    data.to_excel(writer,sheet_name='清洗后的表',index=False) #将清洗后的文件数据另存到本地
    writer.close()
    print(i+':写入成功')

数据清洗之后开始在数据库中创建表。
print("\033[10;31;50m根据清洗后的文件在数据库建表\033[0m")

#从文件夹中导入数据并在数据库中建表
for i in files_xlsx:
    new_data=pd.read_excel(newpath+i,index=False)
    columns=new_data.columns.tolist()
    types=new_data.ftypes
    field=[]  #用来接收字段名称的列表
    table=[]  #用来接收字段名称和字段类型的列表
    for item in columns:  #开始对字段类型进行转换
        if 'int64' in types[item]:
            char=item + 'VARCHAR(255)'
        elif 'float64' in types[item]:
            char=item + 'NUMBER'
        elif 'obiect' in types[item]:
            char=item + 'VARCHAR(255)'
        elif 'datetime' in types[item]:
            char=item + 'Date'
        else:
            char=item + 'VARCHAR(255)'
        table.append(char)
        fields.append(item)
    tables=','.join(table)
    fields=','.join(field)
    #组建建表语句
    table_sql='create table nb_dc_'+ i.split('.')[0]+' ('+tables+' )' #在这里面创建数据库表时最后不能带分号
    
    #开始创建数据库表
    print("表:nb_dc_" + i.split('.')[0]+',开始创建___')
    cursor.execute(table_sql)
    conn.commit()
    print("表:nb_dc_" + i.split('.')[0]+',创建成功')
    
    #用于测试删表时的操作
    #drop_table_sql='drop table nb_dc_'+i.split('.')[0]
    #cursor.execute(drop_table_sql)
    #conn.commit()
数据库建表之后,将清洗后的数据批量导入。
print("\033[10;31;50m将清洗好的数据导入数据库\033[0m")
from sqlalchemy import create_engine

engine=create_engine("oracle://数据库名称:数据库密码@host/数据库名称",encoding='utf-8',echo=True)
#将清洗好的数据导入到数据库中
for i in files_xlsx:
    new_data=pd.read_excel(newpath+i,index=False)
    new_data.to_sql("nb_dc_"+i.split('.')[0],engine,index=False,if_exists='append',chunksize=10000)#如果表在数据库中不存在就新建表,已存在就直接导入数据,chunksize=10000是指将数据分批次导入,一次导入10000条
    print("\033[10;31;50m数据导入成功\033[0m")
    print("表nb_dc_"+i.split('.')[0]+"导入成功")

 整个流程大致如上,创建表的那个代码模块其实可以单独放,以防运行报错的时候,不能再重跑代码。

**********************************************************************************************************
因为实际项目中,数据的清洗量都会很大,所以可以考虑采用多线程的方式提高数据的清洗、入库效率。
#连接数据库
import cx_Oracle
import pandas as pd
import os
import re
import time
from time import ctime
from datetime import datetime
from sqlalchemy import create_engine
import threading

engine=create_engine("oracle://数据库名称:数据库密码@host/数据库名称",encoding='utf-8',echo=True)
print("\033[10;31;50m准备连接数据库\033[0m")

try:
    conn=cx_Oracle.connect('数据库账号','数据库登录密码','host/数据库名称')
    cursor=conn.cursor() #创建游标
    print("\033[10;31;50m数据库连接成功\033[0m")
    print(' ')
except:
    print("\033[10;31;50m数据库连接失败\033[0m")
    print(' ')



#批量读入数据文件
print("\033[10;31;50m批量接入文件\033[0m")
filepath=r'E:/测试/样例数据/'            #该地址为输入文件地址
newpath=r'E:/测试/清洗后的数据文件/'     #该地址为处理后的数据文件地址
fieldcode=pd.read_excel('E:/测试/fieldcode.xlsx',index=False)  #导入清洗规则码表
fieldcode['表名']=fieldcode['表名'] + '.xlsx'  #加上后缀
files=os.listdir(filepath)
files_xlsx=list(filter(lambda x:x[-5:] == '.xlsx',files))  #将文件夹中的所有表的表名读过来,存为列表


#统一文本中的所有标点符号的方法是去除所有半角全角符号,只保留字母、数字、中文
def remove_punctuation(line):
    rule=re.compile(r"[^a-zA-Z0-9\u4e00-\u9fa5]")
    line=rule.sub('|',line)
    return line


#读取文件写入并开始清洗
print("\033[10;31;50m开始数据文件清洗程序\033[0m")
def readfile(filepath,newpath,filename,fieldcode):
    i=filepath+filename
    data=pd.read_excel(i,index=False).fillna(value='null')#将所有需要清洗的文件的空值全部填充为null
    for j in data.columns:
        etl_rule=fieldcode[fieldcode['表名']==filename][fieldcode['字段英文名']==j]
        #将清洗规则转为list然后进行遍历
        dc_rule=etl_rule["清洗规则"].map(lambda x:list(str(x)))
        for k in dc_rule:
            for m in k[:]:
                if m==",":
                    k.remove(m)  #移除list中的","元素
                else:
                    m
                    for n in dc_rule:
                        for p in n:
                            if p == '1':  #去空格
                                data[j]=data[j].map(lambda x:''.join(str(x).split()))
                            elif p == '3':  #统一大小写
                                data[j]=data[j].map(lambda x:str(x).upper())
                            elif p == '4':  #日期格式转换,将yyyymmddhhmiss转为以yyyy-mm-dd HH:MI:SS
                                data[j]=data[j].map(lambda x:datetime.strptime(str(x),"%Y%m%d%H%M%S").strftime("%Y-%m-%d %H:%M:%S"))
                            elif p == '5':   #匹配验证手机号或固话,并把固话格式统一,将固话中间的横杠全部去掉
                                data[j]=data[j].map(lambda x:(''.join(list([x[0] for x in re.findall(r'((?,str(x))])).replace('-',''))) #join是去除list外面的中括号,list([x[0]是取正则后的第一个元素,replace是将固话格式统一
                            elif p == '6':  #时间戳转为日期格式
                                data[j]=data[j].map(lambda x:time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(float(x/1000))))
                            elif p == '7':  #正则匹配清洗身份证号
                                data[j]=data[j].map(lambda x:''.join(list([x[0] for x in re.findall(r'((?,str(x))])))
                            elif p == '8':   #正则匹配仅保留数字
                                data[j]=data[j].map(lambda x:(''.join(re.findall(r'\d+\.?\d*',str(x)))))
                            #elif p=='9':  #把所有的符号都替换成|,然后把一行数据拆分多行
                             #   data[j]=data[j].map(lambda x:remove_punctuation(x))
                              #  end_data=data.drop(j,axis=1).join(data[j].str.split('|',expand=True).stack().reset_index(level=1,drop=True).rename(j))
                            else:
                                pass#data[j]=data[j]
                        
                                
    write=pd.ExcelWriter(newpath+filename)
    data.to_excel(writer,sheet_name='清洗后的表',index=False) #将清洗后的文件数据另存到本地
    
    #当表格中有需要用到第9个清洗规则的再把数据文件存为这个,因为第九个规则没有生效的时候,数据框名称不会变
    #end_data.to_excel(writer,sheet_name='清洗后的表',index=False)
    
    writer.close()
    print(filename+':写入成功')



#启用多线程清洗数据
files_xlsx
threads=[]
files=range(len(files_xlsx))  

#创建线程
for i in files:  #通过文件数来设定线程的数量
    start_time=time.time()
    t1=threading.Thread(target=readfile(filepath,newpath,files_xlsx[i],fieldcode),args=(files_xlsx[i],))
    threads.append(t1)
    
if __name__ == '__main__':
    #启动线程
    for i in files:
        threads[i].start()
    #阻塞线程
    threads[i].join()
    #主线程
    print("all over %s" %ctime())
    end_time=time.time()
    time_buff=end_time - start_time  #计算总消耗时间
    print("共耗时:%s" %time_buff)




#从文件夹中导入数据并在数据库中建表
#建表成功后不能再继续跑这一块的程序,因为数据库中表名已被占用
print("\033[10;31;50m根据清洗后的文件在数据库建表\033[0m")
for i in files_xlsx:
    new_data=pd.read_excel(newpath+i,index=False)
    columns=new_data.columns.tolist()
    types=new_data.ftypes
    field=[]  #用来接收字段名称的列表
    table=[]  #用来接收字段名称和字段类型的列表
    for item in columns:  #开始对字段类型进行转换
        if 'int64' in types[item]:
            char=item + 'VARCHAR(255)'
        elif 'float64' in types[item]:
            char=item + 'NUMBER'
        elif 'obiect' in types[item]:
            char=item + 'VARCHAR(255)'
        elif 'datetime' in types[item]:
            char=item + 'Date'
        else:
            char=item + 'VARCHAR(255)'
        table.append(char)
        fields.append(item)
    tables=','.join(table)
    fields=','.join(field)
    #组建建表语句
    table_sql='create table nb_dc_'+ i.split('.')[0]+' ('+tables+' )' #在这里面创建数据库表时最后不能带分号
    
    #开始创建数据库表
    print("表:nb_dc_" + i.split('.')[0]+',开始创建___')
    cursor.execute(table_sql)
    conn.commit()
    print("表:nb_dc_" + i.split('.')[0]+',创建成功')
    
    #用于测试删表时的操作
    #drop_table_sql='drop table nb_dc_'+i.split('.')[0]
    #cursor.execute(drop_table_sql)
    #conn.commit()



#将清洗好的数据导入到数据库中
print("\033[10;31;50m将清洗好的数据导入数据库\033[0m")
def insertintoDB(newpath,filename):
    new_data=pd.read_excel(newpath+filename,index=False)
    new_data.to_sql("nb_dc_"+filename.split('.')[0],engine,index=False,if_exists='append',chunksize=10000)#如果表在数据库中不存在就新建表,已存在就直接导入数据,chunksize=10000是指将数据分批次导入,一次导入10000条
    print("\033[10;31;50m数据导入成功\033[0m")
    print("表nb_dc_"+filename.split('.')[0]+"导入成功")



#启用多线程插入数据
files_xlsx
threads=[]
files=range(len(files_xlsx))  

#创建线程
for i in files:  #通过文件数来设定线程的数量
    start_time=time.time()
    t2=threading.Thread(target=insertintoDB(newpath,files_xlsx[i]),args=(files_xlsx[i],))
    threads.append(t2)
    
if __name__ == '__main__':
    #启动线程
    for i in files:
        threads[i].start()
    #阻塞线程
    threads[i].join()
    #主线程
    print("all over %s" %ctime())
    end_time=time.time()
    time_buff=end_time - start_time  #计算总消耗时间
    print("共耗时:%s" %time_buff)

你可能感兴趣的:(python,数据库,大数据)