直接上代码:(代码有注释,代码下方有解释)

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数据有问题,可以随时联系我,私聊或者评论,我会第一时间进行修改,进行更正