想问问各位
为什么我用线程池来下载数据保存的excel中,只能保存最后一条数据
用的是openpyxl储存的
各位有头绪吗
当你使用线程池下载数据时,线程池中的线程可能会同时向同一个Excel文件写入数据,这可能会导致写入数据的冲突,从而只保存最后一条数据。
```python
import openpyxl
from concurrent.futures import ThreadPoolExecutor
def write_data_to_excel(excel_path, row, col, data):
try:
wb = openpyxl.load_workbook(excel_path)
except FileNotFoundError:
wb = openpyxl.Workbook()
ws = wb.active
ws.cell(row=row, column=col, value=data)
with open(excel_path, 'a') as f:
wb.save(f)
def download_and_save_data(url, excel_path, row, col):
# 下载数据
data = download_data(url)
# 保存数据到Excel
write_data_to_excel(excel_path, row, col, data)
def main():
excel_path = 'data.xlsx'
urls = [...]
row = 1
col = 1
with ThreadPoolExecutor(max_workers=8) as executor:
futures = []
for url in urls:
futures.append(executor.submit(download_and_save_data, url, excel_path, row, col))
row += 1
col += 1
# 等待所有任务完成
for future in futures:
future.result()
if __name__ == '__main__':
main()
```
在这个示例中,我们定义了一个write_data_to_excel函数,它使用openpyxl库将数据写入Excel文件。为了避免冲突,我们使用了一个with open(excel_path, 'a') as f:的语句来打开Excel文件,这样每个线程都可以在文件末尾追加数据。注意,这里我们需要传递文件对象而不是文件名给wb.save(),否则可能会导致冲突。
在download_and_save_data函数中,我们首先下载数据,然后调用write_data_to_excel函数将数据写入Excel文件。
在main函数中,我们使用ThreadPoolExecutor创建了一个线程池,并将download_and_save_data函数提交给线程池来执行。注意,我们将每个任务的行和列位置分别保存在row和col变量中,以确保写入的数据不会发生冲突。
最后,在等待所有任务完成后,我们的Excel文件中应该包含了所有下载的数据。
完善后的代码:
import win32com.client as win32 # 导入模块
import os
def split_excel(filename, save_name, group_num, title_row=1, excel_app=win32.gencache.EnsureDispatch('Excel.Application')):
"""作者小小明的csdn:https://blog.csdn.net/as604049322"""
filename = os.path.abspath(filename)
wb = excel_app.Workbooks.Open(filename)
try:
sheet = wb.ActiveSheet
max_rows = sheet.UsedRange.Rows.Count
max_cols = sheet.UsedRange.Columns.Count
if title_row > 1:
start = sheet.Range(sheet.Cells(
1, 1), sheet.Cells(title_row-1, max_cols))
rng = sheet.Range(sheet.Cells(title_row, 1),
sheet.Cells(max_rows, max_cols))
# 设置自动列宽
rng.EntireColumn.AutoFit()
header = sheet.Range(sheet.Cells(title_row, 1),
sheet.Cells(title_row, max_cols)).Value[0]
if isinstance(group_num, str):
for i, value in enumerate(header, 1):
if group_num == value:
group_num = i
break
names = set(sheet.Range(sheet.Cells(title_row+1, group_num),
sheet.Cells(max_rows, group_num)).Value)
if not save_name.endswith(".xlsx") and not os.path.exists(save_name):
os.makedirs(save_name)
new_wb = None
for name, in names:
if not name:
continue
if save_name.endswith(".xlsx"):
new_sheet = excel_app.Sheets.Add(
After=wb.Worksheets(wb.Worksheets.Count))
else:
new_wb = excel_app.Workbooks.Add()
new_sheet = new_wb.ActiveSheet
new_sheet.Name = name
if title_row > 1:
wb.Activate()
sheet.Activate()
start.Copy()
if new_wb:
new_wb.Activate()
new_sheet.Activate()
new_sheet.Range("A1").Activate()
new_sheet.Paste()
wb.Activate()
sheet.Activate()
rng.AutoFilter(Field=group_num, Criteria1=name)
rng.Copy()
if new_wb is not None:
new_wb.Activate()
new_sheet.Activate()
new_sheet.Range(f"A{title_row}").Activate()
new_sheet.Paste()
new_sheet.Range(new_sheet.Cells(1, 1), new_sheet.Cells(
1, max_cols)).EntireColumn.AutoFit()
if not save_name.endswith(".xlsx"):
new_wb.SaveAs(os.path.abspath(f"{save_name}/{name}.xlsx"))
new_wb.Close()
sheet.AutoFilterMode = False
rng.EntireColumn.AutoFit()
if save_name.endswith(".xlsx"):
wb.SaveAs(os.path.abspath(save_name))
else:
wb.Save()
finally:
wb.Close()
分别测试一下: