直接上代码:(代码有注释,代码下方有解释)
1.可读取excel中的xlsx格式文件
2.可读取excel中的xls格式文件
3.可直接读取网页上的excel的超链接
4.解决有列合并的情况以及行合并的问题,由合并的情况,分别对应的填充
5.返回数据为 {"Sheet1":[{},{},{}....], "Sheet2":[{},{},{}....]} 注意,有的工作表中的名字是自己工作表的名字,这里只举例
import os
from datetime import datetime
from pprint import pprint
import requests
import win32com.client as win32
import xlrd
from collections import Counter
from fake_useragent import UserAgent
from xlrd import xldate_as_tuple
class ReadExcel:
"""
1.可读取excel中的xlsx格式文件
2.可读取excel中的xls格式文件
3.可直接读取网页上的excel的超链接
读出的格式为[{},{},{}]。。。
每一个字典为,一行对应列名的一个字典
"""
def __init__(self, file_path=None, index=None, header=None, auto_header=False, url=None, headers=None):
"""
传入的参数的解释
:param file_path: (str)如果要读取的是文件,传入路径参数,绝对路径和相对路径都可以,兼容性强
:param index: (int)选择第几行为列名,索引从0开始
:param header: (list)传入列名,要与数据列对应,不建议用,除非表中没有头
:param auto_header: (bool)自动识别列名
:param url: (str)如果要直接读取url, 直接传入网页中excel中的超链接
:param headers: (dict)如果读取的超链接,有的网站的文件有反爬,所以为了防止
需要注意的是,file_path和url只能二选一,意为读取文件或者超链接
index、header、auto_header 只需要三选一即可
headers 可传可不传,不传时,请求头只有User-Agent,一般的文件都可以
如果你的文件也有反爬的话,建议传入headers
"""
self.file_path = file_path
self.url = url
self.headers = headers
self.index = index
self.header = header
self.auto_header = auto_header
# 判断文件类型
if file_path is not None: # 传入的是文件
if 'xlsx' in file_path: # 如果传入的文件是 xlsx 文件类型,将xlsx文件类型转化为xlsx
# 将原来的文件名,替换成同名不同类型的文件,即xls结尾的文件(注意这块直接得到的是文件名,虽然传入的完整路径)
fileName = os.path.basename(file_path).replace("xlsx", 'xls')
# 文件导出的目录路径和文件名称,文件名称如上
export_name = os.path.join(os.path.dirname(os.path.abspath(file_path)), fileName)
self.xlsx_to_xls(file_path, export_name) # 开始进行转换
self.file_path = export_name # 设置文件路径为转换后的文件路径
else:
# 如果是xls直接将目录进行拼接,绝对路径,并且设置文件路径
self.file_path = os.path.join(os.path.dirname(os.path.abspath(file_path)), os.path.basename(file_path))
else: # 超链接
if self.headers is None: # 如果没有传入请求头,默认的只有随机的User-Agent
self.headers = {
"User-Agent": UserAgent().random
}
else:
# 如果传入了请求头,就用该请求头,用随机的User-Agent,有就更新,无则加入进去
self.headers.update({
"User-Agent": UserAgent().random
})
# 设置文件路径为读取excel超链接的文件之后的文件路径,当前路径,读取后会删除文件
self.file_path = self.read_url_excel()
# formatting_info=True, 防止merged_cells获取合并的行或者列时为空列表
self.workbook = xlrd.open_workbook(self.file_path, formatting_info=True) # 读取文件
self.sheet_names = self.workbook.sheet_names() # 获取该文件所有的工作表名
# 如果index和header都没有传入,则设置index为0,即就是从第一行开始读取
if self.index is None or self.header is not None:
self.index = 0
def merge_cell(self, sheet_info):
"""
:param sheet_info: 传入表格的对象
:return:返回,有合并的行和列的键值对
"""
merge = {}
merge_cells = sheet_info.merged_cells
for (rlow, rhigh, clow, chigh) in merge_cells:
value_mg_cell = sheet_info.cell_value(rlow, clow)
if sheet_info.cell(rlow, clow).ctype == 3: # 对读出来的时间格式进行转换
date = datetime(*xldate_as_tuple(value_mg_cell, 0))
value_mg_cell = date.strftime('%Y-%m-%d')
if rhigh - rlow == 1:
# Merge transverse cells
for n in range(chigh - clow - 1):
merge[(rlow, clow + n + 1)] = value_mg_cell
elif chigh - clow == 1:
for n in range(rhigh - rlow - 1):
merge[(rlow + n + 1, clow)] = value_mg_cell
return merge
def xlsx_to_xls(self, fname, export_name, delete_flag=False):
"""
将xlsx文件转化为xls文件
:param fname: 传入待转换的文件路径(可传绝对路径,也可传入相对路径,都可以)
:param export_name: 传入转换后到哪个目录下的路径(可传绝对路径,也可传入相对路径,都可以)
:param delete_flag: 转换成功后,是否删除原来的xlsx的文件,默认删除 布尔类型
:return: 无返回值
"""
excel = win32.gencache.EnsureDispatch('Excel.Application')
excel.Visible = False
excel.DisplayAlerts = False
absolute_path = os.path.join(os.path.dirname(os.path.abspath(fname)), os.path.basename(fname))
save_path = os.path.join(os.path.dirname(os.path.abspath(fname)), os.path.basename(export_name))
wb = excel.Workbooks.Open(absolute_path)
wb.SaveAs(save_path, FileFormat=56) # FileFormat = 51 is for .xlsx extension
wb.Close() # FileFormat = 56 is for .xls extension
excel.Application.Quit()
# 是否删除原来的数据文件
if delete_flag:
os.remove(absolute_path)
def list_dic(self, key_list, value_list):
"""
两个列表组成字典
:param key_list: 键名列表
:param value_list: 值列表
:return: 返回组成好的字典
"""
dic = dict(map(lambda x, y: [x, y], key_list, value_list))
return dic
def read_url_excel(self):
# 请求
r = requests.get(url=self.url, headers=self.headers)
file_b = r.content
# 判断文件类型, 确定文件类型和名称
if 'xlsx' in self.url:
name = 'temp.xlsx'
else:
name = 'temp.xls'
# 保存文件
with open(name, 'wb') as f:
f.write(file_b)
# 判断是否需要转换,并且确定文件路径
if name == 'temp.xlsx':
export_name = 'temp.xls'
self.xlsx_to_xls(name, export_name, delete_flag=True)
else:
export_name = name
return export_name
def read_excel(self):
# 取出所有的工作表的名称
sheet_name_list = self.sheet_names
dic_data = {} # 所有的工作表字典,对应该工作表中的数据,键值对形式
for sheet_name in sheet_name_list:
sheet_simple = [] # 每张工作表
# 某个工作表的名称
sheet = self.workbook.sheet_by_name(sheet_name)
if sheet.nrows != 0:
auto_index = 0 # 自动识别头在第几行
# 根据这些数据确定,行索引, 可以自己去设置,更改,按自己的需求
flag_list = ['项目名称', '工程名称', '许可证号', '证号', '许可证编号', '建设规模',
'施工单位', '勘察单位', '投资额', '勘察单位', '设计单位', '工程地址', '工程地址', '层数']
# 不要包含这些数据的行, 可以自己去设置,更改,按自己的需求
del_list = ['表', '明细', '合计', '总计']
if self.auto_header: # 自动识别头
for row_i in range(0, sheet.nrows): # 循环每一行
# 每行所有数据连起来的字符串
row_data = ''.join([str(r_v) for r_v in sheet.row_values(row_i)]).strip()
for del_str in del_list: # 跳过表名
if del_str not in row_data:
for flag in flag_list:
if flag in row_data: # 判断表头
auto_index = row_i
header = [str(row_value).replace("\n", "").replace(" ", "") for row_value in
sheet.row_values(auto_index) if
row_value != '']
row_index = auto_index + 1
else: # 手动头或者自定义头
if self.header is None: # 如果没有传入表头,则用index取表头
header = [row_value.replace("\n", "").replace(" ", "") for row_value in
sheet.row_values(self.index) if row_value != '']
row_index = self.index
else: # 传入了头,就用
header = self.header
row_index = 0
values_merge_cell = self.merge_cell(sheet) # 这里是调用处理合并单元格的函数
# row_index 从第几行开始读取
for i in range(row_index, sheet.nrows): # 开始为组成字典准备数据
other_line = []
# 处理类型
# 可参考
for j in range(sheet.ncols):
ctype = sheet.cell(i, j).ctype # 表格的数据类型
cell = sheet.cell_value(i, j)
if ctype == 3:
# 转成datetime对象
date = datetime(*xldate_as_tuple(cell, 0))
cell = date.strftime('%Y-%m-%d')
other_line.append(cell)
# other_line = sheet.row_values(i)
# print(other_line)
# print(other_line)
for key in values_merge_cell.keys():
if key[0] == i:
if key[1] != '':
other_line[key[1]] = str(values_merge_cell[key]).replace("\xa0", "")
dic = self.list_dic(header, other_line) # 调用组合字典的函数,传入key和value,字典生成
# print(self.url)
# # pprint(dic)
# print(other_line)
value_list = [str(v).strip() for v in dic.values()]
value_str = ''.join(value_list).strip()
pass_list = del_list + flag_list
pass_flag = True
for pass_str in pass_list:
if pass_str in value_str:
pass_flag = False
break
if pass_flag and len(Counter(value_list)) > 1 and value_str != '':
try:
del dic['序号']
except:
pass
# pprint(dic)
sheet_simple.append(dic)
dic_data[str(sheet_name)] = sheet_simple
os.remove(self.file_path)
return dic_data
if __name__ == '__main__':
# 读取文件,自动识别头
readexcel = ReadExcel(file_path='./abcd/W020200220406735094729 (1).xls', auto_header=True)
pprint(readexcel.read_excel())
# 读取超链接,自动识别头,里面有表格合并
readexcel_1 = ReadExcel(url='http://zjj.laibin.gov.cn/xxcx/bjgb/P020211122537271486773.xls', auto_header=True)
pprint(readexcel_1.read_excel())
注意的几点问题:
1. 创建类,传入参数是,如果是文件格式,就用file_path=‘文件路径’ 的方式,如果是超链接的方式 就用 url='超链接'
2. 里面的flag_list 和del_list 根据需要可以去修改,如果没有该需求,可以把这个两个列表设置我空列表
3.如果你读出来的excel数据有问题,可以随时联系我,私聊或者评论,我会第一时间进行修改,进行更正