openpyxl - "copy/paste" range of cells

Mahle picture Mahle · Mar 27, 2018 · Viewed 14.1k times · Source

I'm new to Python and I'm trying to adapt some of my VBA code to it using the openpyxl library. On this particular case, I'm trying to copy 468 rows in a single column from a workbook according to the string in the header and to paste them in another workbook in a particular column that has another specific string as a header. I can't simply select the range of cells I want to copy because this is part of a report automation and the headers change positions from file to file.

What's the function I need to use to copy each of the 468 cells from one workbook into the 468 cells of the second workbook? Or alternatively how can I copy a range of cells and then paste them in another workbook? Here is my code and I know exactly what's wrong: I'm copying one cell (the last from the first workbook) repeatedly into the 468 cells of the second workbook.

#!/usr/bin/python3

import pdb
import openpyxl
from openpyxl.utils import column_index_from_string

wb1 = openpyxl.load_workbook('.../Extraction.xlsx')
wb2 = openpyxl.load_workbook('.../Template.xlsx')

ws1 = wb1.active
first_row1 = list(ws1.rows)[0]             #to select the first row (header)
for cell in first_row1:
    if cell.value == "email":
        x = cell.column                    #to get the column
        y = column_index_from_string(x)    #to get the column's index

for i in range(2, 469):
    cell_range1 = ws1.cell(i, y)           #the wrong part

ws2 = wb2.active
first_row2 = list(ws2.rows)[0]
for cell in first_row2:
    if cell.value == "emailAddress":
        w = cell.column
        z = column_index_from_string(w)

for o in range(2, 469):
    cell_range2 = ws2.cell(o, z)
    cell_range2.value = cell_range1.value

path = '.../Test.xlsx'
wb2.save(path)

Answer

rt87 picture rt87 · Dec 11, 2019

It is actually quite easy to create such a function:

from openpyxl.utils import rows_from_range

def copy_range(range_str, src, dst):

    for row in rows_from_range(range_str):
        for cell in row:
            dst[cell].value = src[cell].value

    return

Note that range_str is a regular string such as "A1:B2" and src and dest both have to be valid sheet objects. However, if you are copying large ranges, this might take a while, as the read/writes seem to be rather time-consuming.