convert_xlsx.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. import inspect
  2. import os
  3. import sys
  4. from bs4 import BeautifulSoup
  5. sys.path.append(os.path.dirname(__file__) + "/../")
  6. from format_convert.convert_tree import _Document, _Page, _Table, _Sentence
  7. import logging
  8. import traceback
  9. import pandas as pd
  10. import numpy as np
  11. import xlrd
  12. from format_convert.utils import get_logger, log, memory_decorator
  13. from format_convert.wrapt_timeout_decorator import timeout
  14. @memory_decorator
  15. def xlsx2text(path, unique_type_dir):
  16. log("into xlsx2text")
  17. try:
  18. try:
  19. # sheet_name=None, 即拿取所有sheet,存为dict
  20. df_dict = pd.read_excel(path, header=None, keep_default_na=False, sheet_name=None)
  21. except Exception as e:
  22. log("xlsx format error!")
  23. return [-3]
  24. df_list = [sheet for sheet in df_dict.values()]
  25. sheet_text = ""
  26. for df in df_list:
  27. text = '<table border="1">' + "\n"
  28. for index, row in df.iterrows():
  29. text = text + "<tr>"
  30. for r in row:
  31. text = text + "<td>" + str(r) + "</td>" + "\n"
  32. # print(text)
  33. text = text + "</tr>" + "\n"
  34. text = text + "</table>" + "\n"
  35. sheet_text += text
  36. return [sheet_text]
  37. except Exception as e:
  38. log("xlsx2text error!")
  39. traceback.print_exc()
  40. return [-1]
  41. class XlsxConvert:
  42. def __init__(self, path, unique_type_dir, is_xls=False):
  43. self._doc = _Document(path)
  44. self.path = path
  45. self.unique_type_dir = unique_type_dir
  46. # xls直接用xlrd读取
  47. self.is_xls = is_xls
  48. self.workbook = None
  49. self.sheet_list = []
  50. # 防止读太多列行
  51. self.col_limit = 100
  52. self.row_limit = 2000
  53. # 防止sheet太多
  54. self.sheet_limit = 10
  55. @timeout(30, timeout_exception=TimeoutError, use_signals=False)
  56. def read(self):
  57. # xlrd 为了读取合并单元格 或 直接读取xls
  58. workbook = xlrd.open_workbook(self.path)
  59. if not self.is_xls:
  60. # pandas
  61. # df = pd.read_excel(self.path, header=None, keep_default_na=False, sheet_name=None)
  62. use_xlrd = 0
  63. try:
  64. df = pd.read_excel(self.path, header=None, keep_default_na=False,
  65. sheet_name=None, usecols=[x for x in range(self.col_limit)],
  66. nrows=self.row_limit)
  67. sheet_list = [sheet for sheet in df.values()]
  68. except:
  69. traceback.print_exc()
  70. print('pandas读取xlsx失败')
  71. use_xlrd = 1
  72. else:
  73. use_xlrd = 1
  74. if use_xlrd:
  75. # xlrd -> pandas
  76. data_list = []
  77. for sheet in workbook.sheets():
  78. data = []
  79. # 读取工作表中的内容
  80. for row_idx in range(sheet.nrows):
  81. if row_idx >= self.row_limit:
  82. break
  83. row = sheet.row_values(row_idx)[:self.col_limit]
  84. data.append(row)
  85. # 将读取的数据转换为 pandas DataFrame
  86. df = pd.DataFrame(data)
  87. data_list.append(df)
  88. sheet_list = data_list
  89. # 使用了定时装饰器,需直接返回结果,直接赋值对象变量无效
  90. # self.workbook = workbook
  91. # self.sheet_list = self.sheet_list[:self.sheet_limit]
  92. return workbook, sheet_list
  93. def init_package(self):
  94. # 各个包初始化
  95. try:
  96. self.workbook, self.sheet_list = self.read()
  97. # self.df, self.workbook = self.read()
  98. # self.sheet_list = [sheet for sheet in self.df.values()]
  99. # self.re_read = 0
  100. # for s in self.sheet_list:
  101. # if s.shape[1] > self.col_limit and s.shape[0] > self.row_limit:
  102. # self.re_read = 3
  103. # break
  104. # elif s.shape[0] > self.row_limit:
  105. # self.re_read = 2
  106. # break
  107. # elif s.shape[1] > self.col_limit:
  108. # self.re_read = 1
  109. # break
  110. # if self.re_read == 3:
  111. # self.df = pd.read_excel(self.path, header=None, keep_default_na=False,
  112. # sheet_name=None, usecols=[x for x in range(self.col_limit)],
  113. # nrows=self.row_limit)
  114. # if self.re_read == 2:
  115. # self.df = pd.read_excel(self.path, header=None, keep_default_na=False,
  116. # sheet_name=None, nrows=self.row_limit)
  117. # elif self.re_read == 1:
  118. # self.df = pd.read_excel(self.path, header=None, keep_default_na=False,
  119. # sheet_name=None, usecols=[x for x in range(self.col_limit)])
  120. # if self.re_read > 0:
  121. # self.sheet_list = [sheet for sheet in self.df.values()]
  122. # print(self.sheet_list[0].shape)
  123. except:
  124. if self.is_xls:
  125. log("cannot open xls!")
  126. else:
  127. log("cannot open xlsx!")
  128. traceback.print_exc()
  129. self._doc.error_code = [-3]
  130. def convert(self):
  131. log('into xlsx_convert')
  132. # 先判断特殊xlsx文件,可能是html文本
  133. is_html_xls = False
  134. try:
  135. with open(self.path, 'r', encoding='utf-8') as f:
  136. html_str = f.read()
  137. soup = BeautifulSoup(html_str, 'lxml')
  138. text = soup.text
  139. is_html_xls = True
  140. except:
  141. pass
  142. if is_html_xls:
  143. self._page = _Page(None, 0)
  144. _sen = _Sentence(text, (0, 0, 0, 0))
  145. self._page.add_child(_sen)
  146. self._doc.add_child(self._page)
  147. return
  148. self.init_package()
  149. if self._doc.error_code is not None:
  150. return
  151. sheet_no = 0
  152. for sheet in self.sheet_list:
  153. self._page = _Page(None, sheet_no)
  154. self.convert_page(sheet, sheet_no)
  155. if self._doc.error_code is None and self._page.error_code is not None:
  156. self._doc.error_code = self._page.error_code
  157. self._doc.add_child(self._page)
  158. sheet_no += 1
  159. def convert_page_230101(self, sheet):
  160. text = '<table border="1">' + "\n"
  161. # 剔除多余空列
  162. max_row_len = 0
  163. max_col_len = 0
  164. if self.re_read:
  165. for index, row in sheet.iterrows():
  166. col_len = 0
  167. row_empty_flag = 1
  168. for i in range(len(row)):
  169. if row[i] not in [None, "", np.nan]:
  170. row_empty_flag = 0
  171. col_len = i
  172. if self.re_read == 3 or self.re_read == 1:
  173. if col_len > max_col_len:
  174. max_col_len = col_len
  175. if self.re_read == 3 or self.re_read == 2:
  176. if row_empty_flag == 0:
  177. max_row_len = index
  178. for index, row in sheet.iterrows():
  179. if self.re_read == 3 or self.re_read == 2:
  180. if index > max_row_len:
  181. break
  182. text = text + "<tr>"
  183. if self.re_read == 3 or self.re_read == 1:
  184. row = row[:max_col_len+1]
  185. for r in row:
  186. text = text + "<td>" + str(r) + "</td>" + "\n"
  187. # print(text)
  188. text = text + "</tr>" + "\n"
  189. text = text + "</table>" + "\n"
  190. _table = _Table(text, (0, 0, 0, 0), is_html=True)
  191. self._page.add_child(_table)
  192. def convert_page_2405024(self, sheet, sheet_no):
  193. # 剔除多余空列
  194. max_row_len = 0
  195. max_col_len = 0
  196. if self.re_read:
  197. for index, row in sheet.iterrows():
  198. col_len = 0
  199. row_empty_flag = 1
  200. for i in range(len(row)):
  201. if row[i] not in [None, "", np.nan]:
  202. row_empty_flag = 0
  203. col_len = i
  204. if self.re_read == 3 or self.re_read == 1:
  205. if col_len > max_col_len:
  206. max_col_len = col_len
  207. if self.re_read == 3 or self.re_read == 2:
  208. if row_empty_flag == 0:
  209. max_row_len = index
  210. row_list = []
  211. for index, row in sheet.iterrows():
  212. if self.re_read == 3 or self.re_read == 2:
  213. if index > max_row_len:
  214. break
  215. if self.re_read == 3 or self.re_read == 1:
  216. row = row[:max_col_len+1]
  217. col_list = []
  218. for r in row:
  219. col_list.append(str(r))
  220. row_list.append(col_list)
  221. # xlrd 获取合并单元格位置
  222. sheet_xlrd = self.workbook.sheet_by_index(sheet_no)
  223. merged_cell_list = sheet_xlrd.merged_cells
  224. merged_cell_list.sort(key=lambda x: (x[0], x[1], x[2], x[3]))
  225. # print("merged_cell_list", merged_cell_list)
  226. # 复制填充合并单元格
  227. for row_start, row_end, col_start, col_end in merged_cell_list:
  228. if row_start >= len(row_list) or row_end > len(row_list):
  229. continue
  230. if col_start >= len(row_list[row_start]) or col_end > len(row_list[row_start]):
  231. continue
  232. copy_cell = row_list[row_start][col_start]
  233. for i in range(row_start, row_end):
  234. row = row_list[i]
  235. # 第一行补少一个,其他行需补多一个
  236. if i == row_start:
  237. col_start_real = col_start+1
  238. else:
  239. col_start_real = col_start
  240. for j in range(col_start_real, col_end):
  241. if row[j] == "":
  242. row[j] = copy_cell
  243. # 拼接html表格
  244. text = '<table border="1">' + "\n"
  245. for row in row_list:
  246. text = text + "<tr>"
  247. for col in row:
  248. text = text + "<td>" + str(col) + "</td>" + "\n"
  249. text = text + "</tr>" + "\n"
  250. text = text + "</table>" + "\n"
  251. _table = _Table(text, (0, 0, 0, 0), is_html=True)
  252. self._page.add_child(_table)
  253. def convert_page(self, sheet, sheet_no):
  254. row_list = self.delete_empty_row_col(sheet)
  255. # xlrd 获取合并单元格位置
  256. sheet_xlrd = self.workbook.sheet_by_index(sheet_no)
  257. merged_cell_list = sheet_xlrd.merged_cells
  258. merged_cell_list.sort(key=lambda x: (x[0], x[1], x[2], x[3]))
  259. # print("merged_cell_list", merged_cell_list)
  260. # # 复制填充合并单元格
  261. # for row_start, row_end, col_start, col_end in merged_cell_list:
  262. # if row_start >= len(row_list) or row_end > len(row_list):
  263. # continue
  264. # if col_start >= len(row_list[row_start]) or col_end > len(row_list[row_start]):
  265. # continue
  266. # copy_cell = row_list[row_start][col_start]
  267. # for i in range(row_start, row_end):
  268. # row = row_list[i]
  269. # # 第一行补少一个,其他行需补多一个
  270. # if i == row_start:
  271. # col_start_real = col_start+1
  272. # else:
  273. # col_start_real = col_start
  274. # for j in range(col_start_real, col_end):
  275. # if row[j] == "":
  276. # row[j] = copy_cell
  277. #
  278. # # 拼接html表格
  279. # text = '<table border="1">' + "\n"
  280. # for row in row_list:
  281. # text = text + "<tr>"
  282. # for col in row:
  283. # text = text + "<td>" + str(col) + "</td>" + "\n"
  284. # text = text + "</tr>" + "\n"
  285. # text = text + "</table>" + "\n"
  286. # 保留合并单元格
  287. table = []
  288. for row in row_list:
  289. new_row = []
  290. # print('row', row)
  291. for col in row:
  292. cell = {'text': col, 'rowspan': 1, 'colspan': 1}
  293. new_row.append(cell)
  294. table.append(new_row)
  295. for row_start, row_end, col_start, col_end in merged_cell_list:
  296. # print('111row_start, row_end, col_start, col_end', row_start, row_end, col_start, col_end)
  297. row_end = min(row_end, len(table))
  298. if row_start >= row_end:
  299. continue
  300. col_end = min(col_end, len(table[row_start]))
  301. if col_start >= col_end:
  302. continue
  303. # print('len(table)', len(table), 'len(table[row_start])', len(table[row_start]))
  304. merge_cell = table[row_start][col_start]
  305. # print('row_start, row_end, col_start, col_end', row_start, row_end, col_start, col_end, merge_cell.get('text'))
  306. merge_cell['rowspan'] = row_end - row_start
  307. merge_cell['colspan'] = col_end - col_start
  308. # 多余的删掉
  309. for i in range(row_start, row_end):
  310. row = table[i]
  311. if i == row_start:
  312. col_start_real = col_start+1
  313. else:
  314. col_start_real = col_start
  315. for j in range(col_start_real, col_end):
  316. if row[j].get('text') == "":
  317. row[j]['delete'] = 1
  318. # 拼接html表格
  319. text = '<table border="1">' + "\n"
  320. for row in table:
  321. text = text + "<tr>"
  322. for col in row:
  323. if col.get('delete'):
  324. continue
  325. text = text + "<td rowspan={} colspan={}>{}</td>\n".format(col.get('rowspan'), col.get('colspan'), col.get('text'))
  326. text = text + "</tr>" + "\n"
  327. text = text + "</table>" + "\n"
  328. _table = _Table(text, (0, 0, 0, 0), is_html=True)
  329. self._page.add_child(_table)
  330. def delete_empty_row_col(self, sheet):
  331. # 删除xlsx全为空的行列
  332. sheet.dropna(how='all', axis=1, inplace=True)
  333. sheet.dropna(how='all', axis=0, inplace=True)
  334. # 剔除多余空列
  335. max_row_len = 0
  336. max_col_len = 0
  337. for index, row in sheet.iterrows():
  338. col_len = 0
  339. row_empty_flag = 1
  340. for i in range(len(row)):
  341. if row[i] not in [None, "", np.nan]:
  342. row_empty_flag = 0
  343. col_len = i
  344. if col_len > max_col_len:
  345. max_col_len = col_len
  346. if row_empty_flag == 0:
  347. max_row_len = index
  348. row_list = []
  349. for index, row in sheet.iterrows():
  350. if index > max_row_len:
  351. break
  352. row = row[:max_col_len+1]
  353. col_list = []
  354. for r in row:
  355. col_list.append(str(r))
  356. row_list.append(col_list)
  357. return row_list
  358. def get_html(self):
  359. try:
  360. self.convert()
  361. except:
  362. traceback.print_exc()
  363. self._doc.error_code = [-1]
  364. if self._doc.error_code is not None:
  365. return self._doc.error_code
  366. return self._doc.get_html()