[Урок 9] . Чтение и запись txt-файлов в iMacros. Разбираем говнокод и смотрим «Best Practice».

Вот мы неспеша и подобрались к очень интересной теме. Запись и сохранение информации при помощи iMacros и Javascript — вещь крайне необходимая. Подобные функции нам понадобятся постоянно и везде. Допустим, у нас есть текстовый файл, а в нем список групп вконтакте, в которые нужно зайти и оставить в каждой рекламное сообщение с предложением увеличить пенис, например. Или наоборот, нам понадобится снять позиции сайтов в гугле или яндексе по разным запросам и сохранить все это в файл или таблицу Excel.

Как НЕ нужно считывать и сохранять текстовые файлы

У меня слезы на глазах наворачивались, когда я видел, как другие люди пытались решить подобную задачу, нестандартно эксплотируя функционал iMacros. Сложно было удержаться, чтобы не переименовать функции во что-то более нормальное, но решил оставить как есть, пример из интернета:

Так записывать в файлы НЕ надо:

function sohranyaem_v_txt(sohranyaem) {
	var macro;
	iimSet("SOHRANYAEM",sohranyaem);
	macro = "CODE:";
	macro += "ADD !EXTRACT {{SOHRANYAEM}}"+"\n";
	macro += "SAVEAS TYPE=EXTRACT FOLDER=C:\\iMacros FILE=otchet.txt"+ "\n";
	iimPlay(macro)
}

// Текст для сохранения
var sohranyaem = 'текст, который мы собираемся сохранить'

// Сохраняем в файл
sohranyaem_v_txt(sohranyaem);

Во-первых, привыкайте называть функции и переменные нормальными именами, желательно английскими и в camelCase, например: function saveToFile (text) {...}. Данное решение, несмотря на свою убогость, рабочее, но если нам понадобится чтение из файла, такой подход не подойдет, придется искать костыли и грабли с совершенно другой логикой. Код будет неконсистентный, к тому же есть решение намного лучше.

Вот еще плохой пример с просторов интернета, это макрос для автологина на почту.

Так читать файлы НЕ надо:

 var put = 'C:\\iMacros\\Macros'
 var url = 'https://www.yandex.ua'
 var count = Number(window.prompt("введите цифру в какую почту зайти"+ "\n"+"1-aa@yandex.com"+ "\n"+"2-bb@yandex.com"+ "\n"+"3-cc@yandex.com"+ "\n"))
 var tabPoshta = 'yandexPoshta.csv';
 var macroStart;
 macroStart ="CODE:";
 iimSet("COUNT",count); 
 macroStart +="CLEAR" + "\n";// очищаем куки
 macroStart +="SET !DATASOURCE_DELIMITER ;" + "\n";// обозначаем разделитель в таблице ;
 macroStart +="CMDLINE !DATASOURCE "+put+"\\"+tabPoshta+ "\n"; 
 macroStart +="SET !DATASOURCE_COLUMNS 2" + "\n"; //iMacros количество колонок в этом файле
 macroStart +="SET !DATASOURCE_LINE {{COUNT}} " + "\n"; //из какой строки по счету нужно извлечь информацию
 macroStart += "ADD !EXTRACT {{!COL1}}"+ "\n";//почта
 macroStart += "ADD !EXTRACT {{!COL2}}"+ "\n";//пароль
 iimPlay(macroStart)
 var poshta = iimGetLastExtract(1);//вытягиваем почту в джаваскрипт
 var pass = iimGetLastExtract(2);//вытягиваем пароль в джаваскрипт
 var macroVhod;
 macroVhod ="CODE:";
 macroVhod += "URL GOTO="+url+ "\n";//заход на адрес
 macroVhod += "TAG POS=1 TYPE=INPUT:TEXT FORM=ACTION:https://passport.yandex.ru/passport?mode=auth&retpath=https://mail.yandex.ua ATTR=ID:* CONTENT="+poshta+ "\n";//вводим в поле почту
 macroVhod += "SET !ENCRYPTION NO"+ "\n";//не шифруем пароль
 macroVhod += "TAG POS=1 TYPE=INPUT:PASSWORD FORM=ACTION:https://passport.yandex.ru/passport?mode=auth&retpath=https://mail.yandex.ua ATTR=ID:* CONTENT="+pass+ "\n";//вводим пароль
 macroVhod += "TAG POS=2 TYPE=BUTTON FORM=ACTION:https://passport.yandex.ru/passport?mode=auth&retpath=https://mail.yandex.ua ATTR=TXT:Войти"+ "\n";//нажимаем кнопку войти.
 iimPlay(macroVhod)

Такой жуткий код отпугнет  не только новичка, но и профи. Тут используются стандартные функции для работы с форматом CSV и они ужасны. Дело даже не в говнокоде, а в том, что при таком подходе мы не имеем удобного доступа к каждой ячейке таблицы, много лишних промежуточных переменных. Еще огромный минус, это скорость работы скрипта, написанного подобным образом. Это не пустословие, я как-то делал бота в таком стиле, он брал, добавлял и изменял информацию в таблице, объемом несколько сотен строк — для операции поиска, перебора и замены значений уходило около минуты(!). При помощи Javascript, это можно сделать за тысячные доли секунды. В следующем уроке я вам расскажу про формат CSV, и покажу во много раз более изящный и простой способ работать с Excel-таблицами. Вы сами увидите, как можно удобно и приятно манипулировать даже большими табличными данными.

Правильная работа с txt-файлами при помощи iMacros и Javascript

В прошлых уроках вы уже сталкивались с использованием Components и Classes в iMacros. Там, разумеется, есть классы для чтения и записи файлов. Но, сегодня мы не будем их использовать. Помните, мы смотрели содержимое this при помощи window.console.log(this);. Дело в том, что если там покопаться, можно найти объект imns, внутри которого есть объект FIO, которые отвечает за файловый ввод-вывод, этот объект используется самим iMacros, например, когда вы в боковой панели сохраняете макрос или открываете его для редактирования.

Рассмотрим несколько необходимых нам методов у этого объекта:
imns.FIO.readTextFile(file) —  считывает текстовый файл и возвращает его содержимое
imns.FIO.writeTextFile(file, text) — создает текстовый файл, если он не был создан и записывает в него текст
imns.FIO.appendTextFile(file, text) — создает текстовый файл, если он не был создан, либо открывает существующий файл и в конец дописывает текст(удобно для записи логов и статистики в файлы)

Для работы с вводом-выводом обычно используется дескриптор файла, это своего рода указатель на файл, в нашем случае для его получения используется метод imns.FIO.openNode(pathFileName);. Все функции выше в качестве параметра file принимают именно дескриптор файла а не строку с указанием пути к файлу.

Чтение txt-файла и быстрый доступ к любой строке

У текстового файла необязательно должно быть расширение «.txt», могут быть и любые другие. Давайте создадим маленький текстовый файл с несколькими ссылками и сохраним его в папку C:\BOTS\iMacros\.

links.txt

http://ya.ru
http://google.com
http://yahoo.com
http://bing.com

Теперь напишем функцию, которая будет получать в качестве параметра путь к файлу а возвращать содержимое файла.

var loadFile = function (fileName) {
	var fileDescriptor = imns.FIO.openNode(fileName);
	return imns.FIO.readTextFile(fileDescriptor);
}

// Пример использования
var text = loadFile("C:\\BOTS\\iMacros\\links.txt");
window.console.log(text); // Выводит содержимое файла

Обратите внимание, что при указании пути к файлу мы экранируем бэкслеши, поскольку это спецсимвол. Для Unix\Linux систем путь бы выглядел иначе, например "/var/imacros/links.txt", слеши поменяли свое направление и  их экранировать не надо. Учтите эту разницу при написании кроссплатформенных скриптов под разные операционные системы.

Уже неплохо, но хотелось бы иметь удобный доступ к каждой строке и  возможность проходиться по строкам в цикле. Для этого давайте преобразуем текст в массив, где каждый элемент массива — это отдельная строка. Еще, мы оставим возможность получать текст целиком. То есть, теперь наша функция будет возвращать не строку, а объект, содержащий в себе текст целиком и массив строк:

var loadFile = function (fileName) {
	var fileDescriptor = imns.FIO.openNode(fileName);
	var text = imns.FIO.readTextFile(fileDescriptor);
	return {
		text: text,
		strings: text.split('\r\n') // стандартные символы перевода каретки и новой строки в текстовых файлах
	};
}

// Пример использования
var content = loadFile("C:\\BOTS\\iMacros\\links.txt").text;
window.console.log(content); // Выведет весь текст


var strings = loadFile("C:\\BOTS\\iMacros\\links.txt").strings; // Массив строк
window.console.log(strings.length); // 4 - Количество строк в файле
window.console.log(strings[0]);    // "http://ya.ru" - первая строка
window.console.log(strings[3]);    // "http://bing.com" - четвертая строка

// По очереди заходим на каждый сайт из файла с промежутком в 5 секунд
for (var i = 0; i < strings.length; i++) {
	iimPlayCode(`
		URL GOTO=${strings[i]}
		WAIT SECONDS=5
	`);
}

Посмотрите, что получилось, мы одной строкой загружаем файл в переменную и дальше можем делать с ним что угодно. Доступ к строкам по индексу — это удобно, но есть еще более крутой вариант, где доступ будет осуществляться не только по индексам, но и по ключам, я говорю про JSON. О нем напишу позже.

Запись текста в txt(текстовый файл)

Для записи текста в файлы, мы будем использовать все тот же объект iMacros — imns.FIO. Тут кода получится поменьше.

var saveToFile = function (fileName, text) {
	var fileDescriptor = imns.FIO.openNode(fileName);
	imns.FIO.writeTextFile(fileDescriptor, text);
}

// Пример использования
saveToFile("C:\\BOTS\\iMacros\\text.txt", "текст для сохранения");

Данный код создаст файл text.txt и запишет туда наш текст. Если мы запустим макрос несколько раз, то все данные каждый раз будут перезаписываться новыми данными.

Добавление в конец текстового файла, пишем лог

Иногда бывает удобно просто дописывать в файл какие-либо данные,  например при ведении логов. Давайте напишем функцию для добавления текста в конец любого файла и сделаем небольшую обертку для одновременной записи логов и в файл и в консоль. Плюс, мы будем автоматически проставлять дату и время для каждого сообщения.

var appendToFile = function (fileName, text) {
	var fileDescriptor = imns.FIO.openNode(fileName);
	imns.FIO.appendTextFile(fileDescriptor, text);
}

var log = function (msg) {
	// Добавляем дату и время в начало строки и добавляем перевод строки в конец
	var text = "[ " + new window.Date().toLocaleDateString() + " " + new window.Date().toLocaleTimeString() + "] " + msg + "\r\n";

	// Записываем в файл и выводим в консоль
	appendToFile("C:\\BOTS\\iMacros\\log.txt", text);
	window.console.log(text);
}

// Пример использования
log('Начинаем сбор с крана: nagibaka.ru...');
log('Собрано 500 сатоши(22 копейки)');
log('Поиск следующего крана...');

Если вы все правильно сделали, то у вас получится вот так:

Sublime Text iMacros

Примечание: если у вас в консоли или в файле выводятся иероглифы, то нужно сохранить файл вашего скрипта в кодировке UTF-8 с BOM. Я использую Sublime Text 3 и для удобной работы с кодировками у меня установлен плагин Encoding Helper. В данном редакторе кодировка меняется из меню File.

iMacros Javascript

На сегодня, думаю хватит. В следующих уроках мы разберем такие популярные форматы файлов, как CSV и JSON, научимся генерировать и читать файлы, следуя правилам этих форматов и конечно, напишем удобные функции для работы с ними.

43 комментариев

    • Брать:
      request.open(«GET», «https://content-sheets.googleapis.com/v4/spreadsheets/ID Вашей таблицы/values/Лист1!A3:A40?key=Ваш ключ», false);
      Сыпать:
      request.open(«GET», «https://script.google.com/macros/s/ID Вашей таблицы/exec?col1=данные&col2=данные», false);
      В Google Sheet принимаем данные:
      function doGet(e) {
      row = Номер строки;
      var inputCol1 = ContentService.createTextOutput(JSON.stringify(e.parameter.col1)).getContent();
      var inputCol2 = ContentService.createTextOutput(JSON.stringify(e.parameter.col2)).getContent();
      sheet.getRange(row, 1).setValue(inputCol1);
      sheet.getRange(row, 2).setValue(inputCol1);
      }
      И можно забыть про csv — файлы. )
      И исходные данныи и результат в Google Sheet

      Ответить
  1. Минусы данного метода:
    — проблематично работать с данными в формате CSV (по сути с таблицами сохраненными в виде текста), при чтении файла подобной функцией;
    — опять же, проблематично записать данные в виде таблицы (CSV-формат файла), для дальнейшей обработки в Excel’e;
    Решением данных проблем может быть только дополнительный обработчик, приводящий текст к нужному виду, перед сохранением в файл или после чтения данных из файла

    Ответить
    • В десятом уроке уроке подробно разжевывается, как работать с CSV. Как преобразовывать в js-массив и обратно + сохранение. Проблем никаких нету, наоброт, мы можем работать с таблицей, как с нативным двумерным массивом и использовать полный функционал Javascript.

      Ответить
  2. Думаю, вы имели ввиду 10 укор, а не 9.
    Ок, суть моего коммента не противоречит ни уроку 9, ни 10.
    Как уже написал выше, решением является написание отдельной функции преобразующей данные.

    Ответить
  3. Не работает в Firefox:
    Если пользуюсь appendToFile, то выводит ReferenceError: imns is not defined, line 30 (Error code: -991)

    Ответить
    • Доделал код из примера «как не надо делать» :)) но теперь всё работает.
      В новой версии, с 9, (я так понял для поддержки многопроцессорности) исключили много возможностей из imacros, в том числе imns, обращение к window.console.
      function saveToFile(savetext) {
      var macro;
      iimSet(«SAVETEXT», savetext);
      macro = «CODE:»;
      macro += «ADD !EXTRACT {{SAVETEXT}}»+»\n»;
      macro += «SAVEAS TYPE=EXTRACT FOLDER=D:\\123 FILE=PaLBotLog.txt»;
      iimPlay(macro)
      }

      var log = function (msg) {
      var now = new Date();
      var text = «[» + now + «] » + msg + «\r\n»;
      saveToFile(text);
      }

      Ответить
      • вот и нужна она эта многопроцессорность?
        особенно если учесть, что кроме этого в 9 версии теперь не работают даже русскоязычные пути к файлам

        Ответить
  4. А как быть c imns.FIO в iMacros 9? Может альтернативу кто подскажет
    P.S. Мозилу даунгрейдить нельзя.

    Ответить
    • Виталий, альтернатива есть.
      Например, попробуйте http://stackoverflow.com/questions/9667445/how-to-read-write-file-with-xul или погуглить «xul read file», «xul write file».
      Пока нету времени, чтобы добавить решение в статью, но могу подсказать куда копать. Все функции в imns — это обертки, подобные тем, которые делаю я в данной статье. Если ваш уровень знаний позволит вам ковыряться в исходниках, то можете попробывать вытащить реализацию функций чтения и записи файлов.
      В версии iMacros 8.9.7 открываете файл utils.js — у меня он находится тут C:\FirefoxPortable\Data\profile\extensions\{81BF1D23-5F17-408D-AC6B-BD6DF7CAF670}\modules\utils.js.

      Функция для чтения из файла там выглядит вот так:

      readTextFile: function( file) {
                  var charset = this.detectBOM(file), data = "";
                  if (charset != "unknown")
                      return this._read(file, charset);
      
                  charset_list = [];
                  if (imns.is_windows()) {
                      
                      
                      imns.Cu.import("resource://gre/modules/ctypes.jsm", this);
                      var kernel32_dll = this.ctypes.open("kernel32.dll");
                      var GetACP = kernel32_dll.declare(
                          "GetACP", this.ctypes.default_abi, this.ctypes.uint32_t
                      );
                      var codepage = GetACP();
                      kernel32_dll.close();
                      charset_list = [this._win_CP_to_charset_name(codepage)];
                  }
      
                  charset_list = charset_list.concat(["UTF-8", "UTF-16", "UTF-32"]);
                  for (var i = 0; i < charset_list.length; i++) {
                      data = this._read(file, charset_list[i]);
                      
                      
                      if (data.match(/\uFFFD|\0/))
                          continue;
                      else
                          return data;
                  }
      
                  throw new Error("Unable to detect the file ("+
                                  file.path+
                                  ") charset");
              },
      

      Но этого недостаточно, чтобы все заработало, нужно вытаскивать сопутствующие методы и функции + импортировать необходимые модули.

      Ответить
      • Спасибо, буду разбираться. С js только начинаю работать, пока многое не понятно. Если получится то, что задумал обязательно отпишусь.

        Ответить
  5. Как зделать так чтоб данные в определённой колонке которые находятся в текстовом файле вставлялись в конец новой ссылки которая прописанна внутри сценария аймакроса

    Ответить
  6. Отличные посты! Как рекомендация, у кого не работает: пространство imns не работает на последних версиях firefox и imacros. У меня заработало на firefox 46.01 и imacros 8.9.7 http download.imacros.net archive iMacros-Fx imacros_for_firefox-8.9.7-fx.xpi

    Ответить
  7. Большое спасибо!
    Подскажите, возможно ли решить: текст загружаю с дропбокса, сохраняю, в листере строки видны, а в notepad уже нет, склеены.
    Поэтому ошибка can not parse macro line: m.facebook.com/natasha.kli…ko.3 (Error code: -910)

    Ответить
    • notepad или notepad++ ?
      notepad не воспринимает перенос строк по \n только по \r\n, что, впрочем, ни как не мешает iMacros корректно обрабатывать такие строки!
      Ошибка же, can not parse macro line говорит о некорректной строке в макросе, так что смотрите что вы там намудрили в коде, например, при вставке строки из файла в макрос

      Ответить
      • Если открыть CSV файл, то там кодировка 1251. Соответственно если сохраняю его в UTF8, то появляются проблемы с кирилицей.

        Ответить
    • А что проверка на ошибки уже не действует? Или лишь бы спросить, не думая?
      При чтении файла — вернет результат с содержимым файла, при отсутствии файла вернет ошибку.
      В чем сложность-то?

      Ответить
  8. Здравствуйте. Вот у меня не работает этот код в консоле. Скачивал разные версии Mazilla вплоть до 28, везде выдает ошибку ReferenceError: imns is not defined [Подробнее]. Файл и папка существуют. В чем может быть проблема?
    var loadFile = function (fileName) {
    var fileDescriptor = imns.FIO.openNode(fileName);
    return imns.FIO.readTextFile(fileDescriptor);
    }

    // Пример использования
    var text = loadFile(«C:\\BOTS\\iMacros\\links.txt»);
    window.console.log(text); // Выводит содержимое файла

    Ответить
    • Поиск по тексту ошибки в гугле?! Не?!
      Обьявите что такое inns,iMacros не знает что это
      В гугле вторая ссылка на аналогичную проблему на офф. форуме по imacros
      Ссылок, не даю, тут они требуют премодерации

      Ответить
  9. Так я в консоле запускаю, а не в imacros. Должно window.console.log(text); // Выводить содержимое файла/ А она не выводит не чего.
    Добавил
    var wm = imns.Cc[«@mozilla.org/appshell/window-mediator;1»].getService(imns.Ci.nsIWindowMediator);
    var win = wm.getMostRecentWindow(«navigator:browser»);
    Все равно консоль выдает ReferenceError: imns is not defined [Подробнее]

    Ответить
  10. Обычный вопрос создаю скрипт по старой технологии все работает но скорость чтения и записи низкая , вы привели пример как реализовать для бастродействия. imns.FIO не могу на место где нужно вставить логин и пароль на почте чтобы вставлялись данные с таблицы старый метод работает {{!COL1}} и {{!COL2}} просто вставляю а как реализовать это с вашим методом

    Ответить
  11. Я имею ввиду вы привели пример говнокода в начале статьи, только пример реализации этих же операций со входом на почту с новым методом не привели, было бы наглядней если вы бы это сделали заранее благодарю

    Ответить
  12. а что включать голову и изучать js должен за вас автор статьи?
    или вам еще и работу с текстом в js разжевать? так вон гугл есть, сотни ресурсов по js и уроки в том числе

    Ответить
  13. какая разница как реализован код. Главное что он работает! Все были новичками и новичкам так понятнее

    Ответить
  14. Добра Вам.
    Подскажите как в текстовый файл сохранить URL групп фейсбук и если скажем она меньше 300 чел то ее URL не вписывать в коец текстового файла. Тоесть я спрашиваю про парсер.

    Ответить
  15. Спасибо, много полезного.
    Вероятно от недостатка внимания, не могу найти
    как сохранить в файл содержимое window.console.log( );

    Ответить
    • Последний в статье листинг, надеюсь заменить переменную на нужное вам, сами догадаетесь как

      Ответить
      • Спасибо, реализовал через вывод в Json, почти сразу после того, как задал вопрос. Надо было отписать.

        Ответить
  16. Здравствуйте! При запуске этого кода, через некоторое время Mozilla начинает потреблять очень много ОЗУ до 2 Гб, комп начинает жестко тормозить:
    var loadFile = function (fileName) {
    var fileDescriptor = imns.FIO.openNode(fileName);
    var text = imns.FIO.readTextFile(fileDescriptor);
    return {
    text: text,
    strings: text.split(‘\r\n’) // стандартные символы перевода каретки и новой строки в текстовых файлах
    };
    }

    var content = loadFile(«C:\\test\\insta.txt»).text;

    for (var i = 0; i SECTION>MAIN>ARTICLE>HEADER>SECTION>DIV>SPAN>SPAN>BUTTON» BUTTON=0
    WAIT SECONDS=30
    `);
    }
    В файле C:\\test\\insta.txt у меня список ссылок на аккаунты инстаграмм.
    Версия imacros 8.9.7, мозила 49
    Подскажите пожалуйста, что можно сделать чтобы этот скрипт не потреблял много памяти?

    Ответить
  17. strings: text.split(‘\r\n’) // стандартные символы перевода каретки и новой строки в текстовых файлах
    };
    }
    Для линуксоидов не стоит забывать, что что там другой перевод строки
    split(‘\n’)

    Ответить

Оставить комментарий