Думаю, у вас возникали ситуации, когда вы написали скрипт на iMacros, который несколько часов путешествует по сайтам и собирает информацию, как вдруг происходит что-то ужасное, приводящее к остановке скрипта, например выключается свет или аварийно завершается работа браузера. И вам приходится по новой запускать скрипт и ждать черт знает сколько, пока он с самого начала будет собирать данные, даже те, которые уже были собраны. Сегодня я вам на простом примере покажу, как запомнить состояние бота, и продолжить работу скрипта с момента остановки. В этом нам поможет Javascript и мы будем использовать JSON-формат в качестве хранилища.
CRUD-операции с JSON в iMacros+JS
CRUD(Create, Read, Update, Delete) — так называются четыре базовые функции(создание, чтение, редактирование, удаление), которые используются для работы с данными. Подобная концепция используется повсеместно в разработке сайтов, различных веб-приложений и программ. В нашем случае, мы будем использовать массив объектов в формате JSON и выполнять над ним эти операции.
Разберем простой пример:
Нам нужно сделать скрипт, который заходит на 10 сайтов и в случае, если мы закроем браузер после 5 сайта, то при следующем запуске скрипта, он начнет свою работу с 6 сайта, а не с первого.
Модель данных будет выглядеть так:
var data = [ { url: "Ссылка на страницу города", status: "not started", id: 1 } ];
Это массив data
, внутри которого будут объекты. В каждом объекте будет ссылка на страницу, которую нужно посетить, а в статусе мы будем указывать была ли посещена страница или если возникла ошибка.
Функция Create
Для начала нам понадобиться зайти на главную страницу avito.ru, получить оттуда ссылки на все города и области и загнать их в массив, после чего сохранить на диск в формате JSON. Тут мы как раз и будем использовать функцию Create.
// Наша, пока что еще пустая модель var data = []; // определяем функцию Create function create (params) { data.push(params); } // Вызов функции Create create({ url: "Название города", status: "not started", id: 1 });
Вам может показаться бессмысленным, что ради одной строки кода мы определили целую функцию. Но, это очень простой пример, на практике вам будет необходимо совершать больше различных манипуляций. Поэтому данная абстракция здесь к месту. Помимо ссылки и статуса, мы добавим поле id
. Оно нам понадобиться, для того, чтобы мы смогли корректно обновлять и удалять объекты.
id
должно быть уникальным, чтобы мы случайно не изменили не тот объект.
Функция Update
Давайте рассмотрим функцию Update. Ее логика в том, чтобы найти объект по каким-либо признакам и изменить в нем данные. Мы будем искать по id
и это будет необходимым входным параметром, но никто нам не мешает искать по адресу или статусу.
// определяем функцию Update function update (params) { for (var i = 0; i < data.length; i++) { // Ищем совпадение по Id if (data[i].id == params.id) { // Обновляем все ключи, присутствующие в params for (var item in params) { data[i][item] = params[item]; } } } } // Вызов функции Update update({ status: "completed", id: 1 });
В этом примере мы находим в массиве объект с id=1
, и меняем его статус на «completed», что будет означать, что по этой ссылке бот уже прошел. Я частенько люблю совмещать Create и Update, код от этого измениться не очень сильно.
Функция Delete
Данная функция в этой задаче нам не понадобиться, но точно понадобиться в какой-нибудь другой. Поэтому о ней я тоже расскажу. По логике, нам необходимо найти элемент массива по параметру id
и удалить весь объект из массива, выглядеть это будет так:
// определяем функцию Delete function remove (params) { for (var i = 0; i < data.length; i++) { // Ищем совпадение по Id if (data[i].id == params.id) { data.splice(i, 1); } } } // Вызов функции Delete remove({id: 1});
Обратите внимание, в названии функции я использовал remove, потому что delete — это ключевое зарезервированное слово в javascript и его нельзя использовать в названиях функций и переменных. Метод для удаления элемента массива splice()
принимает в качестве первого аргумента индекс элемента в массиве, а в качестве второго аргумента — количество удаляемых элементов.
Функция Read
Иногда, нам требуется прочитать какие-либо данные из объекта. Тут похожая логика, мы ищем элемент массива по параметру id
и возвращаем объект с данными:
// определяем функцию Read function read (params) { for (var i = 0; i < data.length; i++) { // Ищем совпадение по Id if (data[i].id == params.id) { return data[i]; } } } // Вызов функции Read window.console.log( read({id: 1}) );
В результате работы функции, мы получаем объект со всеми-всеми параметрами.
Теперь у нас есть все необходимое, чтобы написать скрипт, который будет сохранять свое состояние даже после аварийной остановки Firefox.
Финальный скрипт для перехода по страницам avito, запоминающий свое состояние
Алгоритм:
- Заходим на avito.ru
- Парсим все ссылки на города
- Забиваем ссылки в массив
- Сохраняем массив ссылок на диск в JSON-формате
- Проходим по каждой ссылке
- Проверяем, что у ссылки
status="Not started"
и тогда переходим по ссылке - Обновляем статус на
"Completed"
- Снова сохраняем обновленный массив на диск
В результате такого алгоритма, бот всегда будет знать, где он остановился в прошлый раз. В коде ниже, я использовал материалы этого урока и немного кода из прошлого урока для реализации чтения и записи файлов.
Полностью код с комментариями:
// Default reset iimPlayCode('SET !EXTRACT_TEST_POPUP NO'); iimPlayCode('SET !ERRORIGNORE YES'); iimPlayCode('SET !TIMEOUT_STEP 0'); iimPlayCode('TAB T=1'); var APP = { _fileName: "C:\\BOTS\\avito.json", // Read file and return our model or empty array _data: function () { window.console.log(this); try { var fileDescriptor = imns.FIO.openNode(this._fileName); return JSON.parse(imns.FIO.readTextFile(fileDescriptor)); } catch (e) { return []; } }, // Create/Update write: function (params) { var isUpdate = false; // Update for (var i = 0; i < this._data.length; i++) { // Ищем совпадение по Id if (this._data[i].id == params.id) { // Обновляем все ключи, присутствующие в params for (var item in params) { this._data[i][item] = params[item]; } isUpdate = true; } } // Create if (!isUpdate) { this._data.push(params); } // Write file on disk var fileDescriptor = imns.FIO.openNode(this._fileName); imns.FIO.writeTextFile( fileDescriptor, JSON.stringify(this._data, null, 4) ); }, // Get city list and fill array getCityList: function () { iimPlayCode('URL GOTO=https://avito.ru/'); var cities = window.document.querySelectorAll('.js-index-cities-item'); for (var i = 0; i < cities.length; i++) { this.write({ url: cities[i].getAttribute('href'), status: "not started", id: cities[i].getAttribute('id') }); } }, // Main scenario run: function () { // Load data from file if exsists this._data = this._data(); if (this._data.length == 0) { this.getCityList(); } for (var i = 0; i < this._data.length; i++) { // Check if url was opened if (this._data[i].status == "not started") { var resultCode = iimPlayCode('SET !TIMEOUT 5\nURL GOTO=' + this._data[i].url); // set timeout for loading page 5 seconds iimPlayCode('WAIT SECONDS=1'); // Any action with page can placed here // ... // Update status if (resultCode >= 0 || resultCode ==-802) { // -802 = stop loading by timeout(5 sec.) this.write({ id: this._data[i].id, status: "completed" }); } } } } } // Start script APP.run();
Как вы можете видеть, мы создали достаточно удобную структуру с данными и функциями, которая запускается одной командой APP.run();
Здесь мы используем Create\Update, но я заменил их на одну функцию write()
. Это базовая заготовка для iMacros, которая демонстрирует принцип сохранения и обновления статусов. Этот пример можно бесконечно усложнять, добавлять вложенность, любые дополнительные действия и обработку данных. Очень важно уметь сохранять текущее состояние бота, это может сэкономить кучу времени и избавит вас от необходимости выполнять двойную работу.
Пишите ваши вопросы и комментарии.
Ищу работу))) А щас много предлагают работы постить объявления на авито. Правда, их еще составить надо, а это уже не автоматом.
добрый день. помогите пожалуйста. как прописать чтобы выбирал просто первого человека списке здесь https://m.facebook.com/friends/center/requests/outgoing/#friends_center_main. а то при записи получается только имя конкретного человека.
вот так:
VERSION BUILD=844 RECORDER=CR
URL GOTO=https://m.facebook.com/friends/center/requests/outgoing/#friends_center_main
TAG POS=1 TYPE=A ATTR=TXT:MariaBekasova
TAG POS=1 TYPE=A ATTR=TXT:Еще
TAG POS=1 TYPE=A ATTR=TXT:Заблокировать
TAG POS=1 TYPE=BUTTON FORM=ACTION:/privacy/touch/block/id/?bid=606607564&ret_cancel&source=profile&
У меня вопрос к блоку // Default reset
В предыдущих уроках вы говорили, что такой вариант записи не будет работать целостно, то есть ни одна из этих команд не будет влиять на выполнения макроса в дальнейшем. Зачем тогда их писать?
Алексей, в блоке Default reset, в-основном, мы устанавливаем глобальные переменные, которые будут актуальны и в дальнейшем. В прошлых уроках я писал, что некоторые команды в совокупности с другими командами не будут корректно выполняться если их разделить. В данном блоке команды могут спокойно работать независимо друг от друга.
FILTER TYPE=IMAGES STATUS=OFF
TAB CLOSEALLOTHERS
SET !VAR1 API-CAPTCHA-KEY
SET !VAR2 адрес криптовалюты
SET !VAR3 image.jpg
SET !VAR4 http://imacros2.rucaptcha.com/new/
SET !VAR5 getcapcha.php
URL GOTO=http://topfan.info/
SET !EXTRACT NULL
WAIT SECONDS=1
ONDOWNLOAD FOLDER=C:\CAPCH\ FILE=image.jpg
WAIT SECONDS=1
TAG POS=1 TYPE=DIV ATTR=ID:adcopy-puzzle-image CONTENT=EVENT:SAVE_ELEMENT_SCREENSHOT
WAIT SECONDS=1
TAB OPEN
TAB T=2
URL GOTO={{!VAR4}}
TAG POS=1 TYPE=INPUT:TEXT FORM=ACTION:{{!VAR5}} ATTR=NAME:key CONTENT={{!VAR1}}
TAG POS=1 TYPE=INPUT:FILE FORM=ACTION:{{!VAR5}} ATTR=NAME:file CONTENT=C:\CAPCH\{{!VAR3}}
TAG POS=1 TYPE=INPUT:CHECKBOX FORM=ACTION:{{!VAR5}} ATTR=NAME:calc CONTENT=NO
TAG POS=1 TYPE=INPUT:TEXT FORM=ACTION:{{!VAR5}} ATTR=NAME:soft_id CONTENT=677
TAG POS=1 TYPE=INPUT:SUBMIT FORM=ACTION:{{!VAR5}} ATTR=*
TAG POS=1 TYPE=* ATTR=TXT:* EXTRACT=TXT
WAIT SECONDS=1
FRAME F=0
TAB CLOSE
TAB T=1
WAIT SECONDS=1
TAG POS=1 TYPE=INPUT:TEXT FORM=ACTION:#about ATTR=ID:adcopy_response CONTENT={{!EXTRACT}}
WAIT SECONDS=1
TAG POS=1 TYPE=BUTTON FORM=ACTION:#about ATTR=TXT:Getareward
WAIT SECONDS=3600
Подскажите что делать…вроде норма работает но иногда 1)решенную капчу не ставляет в окно для отпрвки …или 2)капча ставить но не кликает на GET A REVARD///
что прописать после таких ошибок???
скажу так. сайт может глючить из-за того, что не успевает прогружаться.
по крайней мере я всё время вынужден приостанавливать джаву, чтобы сайт поспевал за ним.
Спасибо огромное, за урок! С нетерпением жду продолжения. Очень помогли в учебе. За пару недель, благодаря Вам начал что-то писать сам. Но, у меня загвоздка. В моем случае сбор // Get city list and fill array должен происходить по нескольким страницам поочередно. То есть, собрал на одной занес в массив и перешел на другую, по коду iimPlayCode(‘TAG POS=1 TYPE=SPAN ATTR=TXT:Ещерезультаты’);. Или, собрал на одной странице, выполнил блок команд // Any action with page can placed here далее выполнил код перехода iimPlayCode(‘TAG POS=1 TYPE=SPAN ATTR=TXT:Ещерезультаты’); и повтор блока // Get city list and fill array…
Добился, что код перехода выполняет iimPlayCode(‘TAG POS=1 TYPE=SPAN ATTR=TXT:Ещерезультаты’), но после, сразу выбрасывает…
Разобрался. Еще раз, спасибо.
так себе пост
для меня вообще 0 смысловой нагурзки
пример скрипта на авито пфф
кому сдался авито твой
Спасибо за комментарий) В тебе очень много негатива — это верный путь стать неудачником по жизни. Ноль смысловой нагрузки — в твоих предложениях без запятых.
Чувак, это ПРИМЕР!
По аналогии делаешь то, что тебе надо.
Тут нет готовых решений.
Спасибо за статью