Рассмотрите возможность использования xrange () вместо range (), я считаю, что xrange является генератором, тогда как range () расширяет весь список.
Я бы сказал, что не читайте весь файл в память или не храните всю распакованную структуру в памяти.
В настоящее время вы храните оба в памяти, в то же время, это будет довольно большим. Таким образом, у вас есть как минимум две копии ваших данных в памяти плюс несколько метаданных.
Также финальная строка
f.write(os.linesep.join(data))
Может означать, что у вас временно есть третья копия в памяти (большая строка со всем выходным файлом).
Так что я бы сказал, что вы делаете это довольно неэффективно, сохраняя весь входной файл, весь выходной файл и достаточное количество промежуточных данных в памяти одновременно.
Использование генератора для разбора - неплохая идея. Подумайте о том, чтобы записать каждую запись после того, как вы сгенерировали ее (затем ее можно удалить и использовать повторно), или, если это вызывает слишком много запросов на запись, группируйте их, скажем, в 100 строк одновременно.
Аналогично, чтение ответа может быть выполнено кусками. Поскольку они фиксированные записи, это должно быть достаточно просто.