Запуск импорта WP All Import по cron из командной строки

Иван Никитин и партнерыНовостиWordPressЗапуск импорта WP All Import по cron из командной строки

ВНИМАНИЕ! Данная статья потеряла актуальность. Сейчас запуск любого импорта WP All Import из командной строки делается так:

$ wp all-import run <import id>

Например, запуск импорта 52 выглядит так:

$ wp all-import run 52

Используйте этот способ, а не тот, который описан в старой статье ниже.


Плагин WP All Import — это очень мощный инструмент импорта в WordPress, который может использоваться в самых разных сценариях, например, для импорта или синхронизации товарного каталога WooCommerce с данными, полученными из 1С или из выгрузки поставщика товаров. Однако, этому плагину присущ ряд особенностей:

  1. WP All Import не использует WP Cron в полной мере, а требует явного вызова wp-cron.php со своими параметрами. Фактически он требует двух вызовов: т.н. триггер, запускающий импорт, и шаг итерации импорта (тик), в котором обрабатывается очередная порция данных. Разработчики рекомендуют для второго вызова (тика) ставить время повторения около 2 минут, но на практике этого времени может не хватить. Увеличение же этого времени приводит к увеличению пауз в импорте и росту общего времени импорта. В результате оно реально может вырасти до нескольких часов на больших объемах данных.
  2. Хоть WP All Import и умеет скачивать файлы у поставщика, но WAI работает только с кодировкой UTF-8, а большинство российских поставщиков дают те же CSV файлы в Windows-1251 (привет 1С-программистам!). Загрузки все равно требуют дополнительной обработки.

Всё это явно просится быть перенесённым на уровень серверного ПО, а не выполнять его в админке WP или вызовами по cron wget. Но, к сожалению, WAI не умеет работать ни с WP-CLI, ни в командной строке. И я озадачился поиском решения.

Решение было найдено. Вот оно:

Идея,  реализованная Rolandow, заключается в прямом вызове PHP CLI сначала триггера, а потом последовательном вызове тиков WAI с секундной задержкой до тех пор, пока очередной тик не скажет, что импортировать больше нечего. Плюсы очевидны:

  • Все работает в CLI, поэтому нет никаких лимитов на время исполнения.
  • Веб-сервер не грузится.
  • Тики итераций идут почти впритык друг к другу и выполняются ровно столько, сколько они выполняются.
  • Всё это великолепно вызывается через cron.

Простой эксперимент показал, что тот импорт, который у нас занимал более 25 минут, выполнился за 10 мин.

Итак, вот наше решение, немного упрощенное и переделанное из исходного. Важно в скрипте задать следующие переменные:

  • JOB_ID — номер задания WP All Import (берется в админке WP All Import)
  • KEY — ключ для запуска (берется там же)
  • LOGFILE — имя и расположение файла лога
  • CURLOG — имя временного лога (он жизненно необходим для работы!)
  • ROOT_DIR — путь к руту сайта

Сейчас значения стоят так, как мы обычно делаем на наших серверах, но вы должны их явно определить.

!/bin/bash

SITE="your-site.ru"
JOB_ID=999
KEY="Your-Key"
LOGFILE=/var/www/$SITE/logs/wp-all-import.log
CURLOG=/var/www/$SITE/logs/wp-all-import-$JOB_ID.tmp
ROOT_DIR=/var/www/$SITE/www
DONE=0

function log {
  echo "$(date): $*" >> $LOGFILE
}

echo "!-- WP All Import:" $SITE " job id:" $JOB_ID
cd $ROOT_DIR
log "Start import for job id $JOB_ID"

php -e -r 'parse_str("import_key='$KEY'&import_id='$JOB_ID'&action=trigger", $_GET); include "wp-cron.php";' >>$LOGFILE 2>&1
sleep 1

while [ $DONE -eq 0 ]
do
  php -e -r 'parse_str("import_key='$KEY'&import_id='$JOB_ID'&action=processing", $_GET); include "wp-cron.php";' >$CURLOG 2>&1
  cat $CURLOG >>$LOGFILE
  cat $CURLOG
  echo "---  `date '+%d.%m.%Y %H:%M:%S'` ---"
  DONE=$(grep 'is not triggered' $CURLOG | wc -l)
  sleep 1
done
rm $CURLOG

log "!-- End of import for jobId" $JOB_ID
log ""
  • Если у вас не читается нормально код скрипта — пишите в комментариях, я дам ссылку на исходник.

Вот, собственно, и все. Комментарии и вопросы приветствуются.

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

  1. Здравствуйте. Не могли бы вы опубликовать пошаговую инструкцию реализации данной идеи? Для менее опытных пользователей. Очень хотелось бы попробовать реализовать ваш вариант, а то у меня по стандартной схеме mysql падает(

    • Артем, для этого нужно установить WP CLI (http://wp-cli.org/). Там на сайте есть инструкция. Далее, скопируйте код скрипта в любую папку и файл с любым именем и расширением sh, поставьте ему разрешения 775, замените в нем переменные на свои данные и попробуйте запустить.

      Но если MySQL при импорте падает — то это однозначно мало памяти!
      Что выводится по команде?
      free -m

  2. Иван у меня 3Гб оперативной памяти на VPS. Вручную импорты проходят без проблем, всего их 60 штук на сайте. Настроил через cron все эти импорты по стандартной схеме. Тригеры в течении дня по очереди стартуют, а итерации постоянно все работают с шагом 2 минуты. В итоге mysql переодически падает. В isp manager в мониторинге перегрузок процессора и оперативной памяти не происходит, максимум оба параметра на 70% бывали загружены.

  3. Может что подскажете по этому поводу? Могу скинуть настройки конфигурации mysql. Сегодня попробую реализовать вашу схему. Спасибо.

    • Попробуйте этот скрипт. Скорее всего, импорты накладываются друг на друга потому и падает MySQL

      • $JOB_ID — это переменная. Скрипт туда подставит номер задания.

      • Например, скрипт лежит в домашней папке. Запустите для пробы его из консоли командой (предположим, он называется all-import.sh)
        ~/all-import.sh

        ~ — это домашняя директория пользователя в Linux
        Если все работает, добавляейте его в cron

        crontab -e

  4. Иван большое спасибо за вашу наработку и реально гражданскую так скажем позицию. То что вы выкладываете очень важные решения и безвозмездно это редкость и говорит о благородстве. Плагин wp all import реально лучшее решение на сегодняшний день для серьезного магазина, но его тонкие настройки требуют реального опыта работы с этим плагином, ваша статья очень многим поможет. И мне она реально помогла снизить время импорта с 0 в два раза а обновления почти в 3 раза. Спасибо из Новосибирска!!

  5. Подскажите под каждый файл выгрузки отдельный файл .sh создавать?

      • Спасибо за быстрый ответ. Создал файлы логов с именами wp-all-import.log и wp-all-import-$JOB_ID.tmp указал пути к ним. В консоле получаю:
        /wp-content/uploads/wpallimport/logs/wp-all-import-76.tmp: No such file or directory
        /wp-content/uploads/wpallimport/logs/wp-all-import.log: No such file or directory

        • Вообще-то, $JOB_ID — это переменная, которая подставится в имя файла. Файлы логово не надо создавать, они сами создадутся

          • Спасибо. Я намудрил с путями, вроде заработало. Спасибо за пост, очень помог!!!

  6. Подскажите еще, пожалуйста, у меня выгрузка с 6000 товарами обновляется уже около 8-ми часов, обновление идет это видно в менеджере импорта: Records Processed 4969. В ручном режиме обновлялось примерно за час. Есть другие выгрузки с гораздо меньшим количеством товаров. Что если параллельно запускать маленькие файлы на протяжении выполнения большого файла. Не будет зависать?

    • Думаю, это зависит от самого сервера. Если у него достаточно ресурсов — почему бы и нет.
      А по поводу долгих выгрузок, попробуйте фотки грузить отдельно! То есть, скачали у поставщика архив фоток, развернули его в папку и оттуда уже берем для выгрузки.

  7. Спасибо.
    И все так и я оставляю файлы в папке сайта.
    Какая разница Linux откуда запускать файл?
    Запускаю командой
    sh /home/iproject/public_html/wp-content/cron-lib/script.sh
    Получаю в wp-all-import.log и в wp-all-import-3.tmp
    Error in argument 2, char 2: option not found r
    Почему так?

    • Класть шеловские скрипты в папках веб-сервера, то есть «заходи кто хочешь, бери что хочешь» — как бы очень-очень неправильно и небезопасно!

      Далее — переменные. Они специфичны для сервера

      SITE=»your-site.ru» — у нас на большинстве наших серверов сайты лежат в папках /var/www/САЙТ/www. Эта переменная просто поставляется в этот путь для разных сайтов

      JOB_ID=999 — Это номер задания WP All Import, берется из его страницы в админке
      KEY=»Your-Key» — ключ, берется там же
      LOGFILE=/var/www/$SITE/logs/wp-all-import.log — это лог работы. В любом месте, где есть права на запись. Желательно не в папке сайта. В нашем случае, сюда подставляется имя сайта из переменной $SITE, вы можете просто путь и файл прописать
      CURLOG=/var/www/$SITE/logs/wp-all-import-$JOB_ID.tmp — текущий лог. То же самое
      ROOT_DIR=/var/www/$SITE/www — полный путь к руту сайта. Мы подставляем $SITE в наш стандартный путь, вы можете просто путь прописать
      DONE=0 — не меняем ее!

  8. Иван, спасибо за ваш скрип, он работает, но у меня выдает 2 ошибки
    function not found и log not found
    Как быть?

      • Вот все строки:
        root@170299:~# sh p5s.sh
        p5s.sh: 11: p5s.sh: function: not found
        p5s.sh: 13: p5s.sh: Syntax error: «}» unexpected
        root@170299:~#

        вот код
        #!/bin/bash  

        SITE=»some.site»
        JOB_ID=1
        KEY=»keykey»
        LOGFILE=/root/wp-all-import.log
        CURLOG=/root/wp-all-import-$JOB_ID.tmp
        ROOT_DIR=/usr/share/nginx/$SITE
        DONE=0

        function log {
        echo «$(date): $*» >>$LOGFILE
        }

        echo «WP All Import:» $SITE » job id:» $JOB_ID
        cd $ROOT_DIR
        log «Start import for job id $JOB_ID»

        php -e -r ‘parse_str(«import_key=’$KEY’&import_id=’$JOB_ID’&action=trigger», $_GET); include «wp-cron.php»;’ >>$LOGFILE 2>&1
        sleep 1

        while [ $DONE -eq 0 ]
        do
        php -e -r ‘parse_str(«import_key=’$KEY’&import_id=’$JOB_ID’&action=processing», $_GET); include «wp-cron.php»;’ >$CURLOG 2>&1
        cat $CURLOG >>$LOGFILE
        DONE=$(grep ‘is not triggered’ $CURLOG | wc -l)
        sleep 1
        done
        rm $CURLOG

        log «End of import for jobId» $JOB_ID
        log «»
        log «»

        • Тогда выбросьте эту функцию, она сервисная

          #!/bin/bash

          SITE="some.site"
          JOB_ID=1
          KEY="keykey"
          LOGFILE=/root/wp-all-import.log
          CURLOG=/root/wp-all-import-$JOB_ID.tmp
          ROOT_DIR=/usr/share/nginx/$SITE
          DONE=0

          echo "WP All Import:" $SITE " job id:" $JOB_ID
          cd $ROOT_DIR

          php -e -r 'parse_str("import_key='$KEY'&import_id='$JOB_ID'&action=trigger", $_GET); include "wp-cron.php";' >>$LOGFILE 2>&1
          sleep 1

          while [ $DONE -eq 0 ]
          do
          php -e -r 'parse_str("import_key='$KEY'&import_id='$JOB_ID'&action=processing", $_GET); include "wp-cron.php";' >$CURLOG 2>&1
          cat $CURLOG >>$LOGFILE
          DONE=$(grep 'is not triggered' $CURLOG | wc -l)
          sleep 1
          done
          rm $CURLOG

  9. Добрый день!
    Тоже мучаюсь с этим кодом….
    Взял последнюю версию кода из Вашего комментария.
    Запускаю и выдает ошибку
    [root@ ~]# sh ~/import.sh
    job id: 8rt: http://www.ru
    : No such file or directory: cd: /var/www/cscart
    : ambiguous redirectline 11: 1
    sleep: invalid time interval ‘1\r’
    Try ‘sleep —help’ for more information.
    /root/import.sh: line 21: syntax error: unexpected end of file

    хотя последняя строка в коде rm $CURLOG под номером 20 и рут сайта /var/www/cscart. У меня centos 7.
    Помогите что делаю не так?

    • Видимо, при копировании переносы строк остались в формате Windows.
      Скопируйте в Notepad++ и выберите Формат конца строк — Unix и сохраните файл

  10. Иван, добрый день!
    В моем случае приходится импортировать файл объемом 100-200Мб. Через некоторое время выдает mySQL дисконнект. Загрузка RAM 100% (при 2 ГБ памяти). т.е. сервер в дауне. А каким образом он заказчивает данные в базу, поэтапно или сразу? Может увеличить интервалы работы крона, и sleep?

    • Он закачивает порциями. Это определяется в настройках wpAllImport, там можно изменить число записей.
      Таймауты на выполнение скрипта — в настройках /etc/php/…/cli/php.ini. То есть, скрипт работает в командной строке, не на сервере, по умолчанию там бесконечное время на выполнение. Интервал крона конечно же увеличивайте!

  11. Здравствуйте. Борюсь с медленной загрузкой сайта, особенно ощущаемой при обновлении товаров. Очень улучшились показатели скорости загрузки сайта при использовании объектного кэширования Redis , но вот скорость импорта и обновления товаров сошла на нет (20 в час!). Подскажите пожалуйста, если использовать метод, как описали Вы, проблема может решиться? Заранее благодарю за любой совет.

    • Думаю, да. Поскольку импорт выполняется в консоли, а не на веб-сервере

  12. Здравствуйте. Возникла такая проблема: при импорте по вашей схеме через крон после нажатия по кнопке «купить» в карточке товара браузер mozilla firefox выдает «Информация, введённая вами на этой странице, будет отправлена по незащищённому соединению и может быть прочитана третьей стороной. Вы уверены, что хотите отправить эту информацию?» Если запускаешь импорт вручную такой проблемы не возникает. Что это может быть?

    • Еще добавлю. Используется WP All Import — Link Cloaking Add-on, после нажатия по кнопке «купить» переход по партнерской ссылке идет на основной магазин. В браузере Chrome такой проблемы нет.

    • URL страницы с импортированным товаром можете привести?

    • Проблема осталась, все товары с импортом через скрипт выдают такое предупреждение

      • Ну здесь дело вообще не в скипте! Там вообще нет указаний на http или https — скорее, это такие настройки самого импорта у вас…

    • Дело как то связанно со скриптом или автоматической составляющей (планировщик, сервер или еще что), так как при импорте вручную (run import) все ссылки формируются через https, а через планиовщик http.

      • Мне здесь сложно что либо сказать, не видя вашего импорта. В самом скрипте нет упоминаний https или http. Более того, мы сами его используем на десятке серверов, но с вашей проблемой не сталкивались. Может быть имеет смысл обратиться к разработчикам WpAllImport? Они нормально отвечают. Мы много раз с ними общались…

      • Вот-вот. Я про это и говорил. Это просто настройки конкретного сайта и сервера, а не скрипта. Поэтому у нас проблема не возникала ни разу, на наших серверах мы обычно вписываем http —> https переадресации по дефолту прямо в конфиги nginx.

  13. Идея отличная и очень нужная и актуальная как никогда, а то уже импорт идет около 12 часов, а полное обновление сайта может занимать 5 дней. Просидел весь день. Знаний мало. Пришлось сначала гуглить как установить PHP на локальную машину. потом пытался соединится с удаленным сайтом по SSH. пробовал устанавливать far manager и putty. Оказалось все бесполезно и вообще не нужно. Ковырял админ панель isp manager нашел shell клиент который оказывается то что нужно. Провел все шаги из этой статьи, немного танцев с бубном и Импорт запустился и уже обновляет. Дай божечка здоровья автору. Всем бы быть такими умными.

    • Только вот обновил Records Processed 1839 из 40 000 товров и все, дальше пишет last activity 10 минут ago.
      Неужели это потому что я закрыл админ панел shell клиента, хотя после этого еще обновилось 500 товаров примерно. Не знаю что делать. Подскажите пожалуйста

      • Конечно же! Скрипт в консоли остановился!
        Вообще-то, лучше всего этот скрипт запускать по крону

        • Оказывается можно добавить в конце & и тогда скрип в фоновом режиме будет работать до конца) Теперь все ок.

          • Ну да, или так.

  14. При запуске скрипта выскакивает ошибка:

    Error in argument 2, char 2: option not found r

    если я удаляю «-r» — то не работает, что делать, подскажите, пожалуйста?

  15. Добрый день! Спасибо за ваш скрипт. Мучаюсь с такой проблемой, запускаю скрипт из консоли, в логах бесконечная ошибка: ./imp.sh: line 23: $’parse_str(«import_key=InHwQycirgY&import_id=5&action=processing», \n$_GET); include «wp-cron.php»;’: command not found

    вот текстскрипта в моем случае:

    #!/bin/bash
    SITE=»СКРЫТО»
    JOB_ID=5
    KEY=»InHwQycirgY»
    LOGFILE=/var/www/user145283/data/www/$SITE/logs/wp-all-import.log
    CURLOG=/var/www/user145283/data/www/$SITE/logs/wp-all-import-$JOB_ID.tmp
    ROOT_DIR=/var/www/user145283/data/www/$SITE/
    DONE=0
    function log {
    echo «$(date): $*» >>$LOGFILE
    }
    echo «WP All Import:» $SITE » job id:» $JOB_ID
    cd $ROOT_DIR
    log «Start import for job id $JOB_ID»
    php -e -r ‘parse_str(«import_key=’$KEY’&import_id=’$JOB_ID’&action=trigger», $_GET); include «wp-cron.php»;’ >>$LOGFILE 2>&1
    sleep 1
    while [ $DONE -eq 0 ]
    do
    php -e -r ‘parse_str(«import_key=’$KEY’&import_id=’$JOB_ID’&action=processing», $_GET); include «wp-cron.php»;’ >$CURLOG 2>&1
    cat $CURLOG >>$LOGFILE
    DONE=$(grep ‘is not triggered’ $CURLOG | wc -l)
    sleep 1
    done
    rm $CURLOG
    log «End of import for jobId» $JOB_ID
    log «»
    log «»

    • Наберите в командной строке и приведите что получится?
      php -v

  16. -bash-4.2$ php -v
    PHP 5.4.16 (cli) (built: Oct 30 2018 05:43:23)
    Copyright (c) 1997-2013 The PHP Group
    Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies
    with the ionCube PHP Loader (enabled) + Intrusion Protection from ioncube24.com (unconfigured) v10.2.4, Copyright (c) 2002-2018, by ionCube Ltd.

    • Все вроде правильно. Проверьте кавычки в этой строке. Они должны быть одинарные или двойные. Обычные. Не обратные или что-то в этом духе…

  17. Добрый день. Скрипт теперь выглядит вот так: https://yadi.sk/i/Rc4zMPzD-Y49Mg
    Ошибка следущая: PHP Warning: include(0php): failed to open stream: No such file or directory in Command line code on line 1
    PHP Warning: include(0php): failed to open stream: No such file or directory in Command line code on line 1
    PHP Warning: include(): Failed opening ‘0php’ for inclusion (include_path=’.:/usr/share/pear:/usr/share/php’) in Command line code on line 1

    • Кавычка ПЕРЕД parse_str. Вот как должно быть
      php -e -r 'parse_str("import_key='$KEY'&import_id='$JOB_ID'&action=trigger", $_GET); include "wp-cron.php";' >>$LOGFILE 2>&1

    • Проверьте еще раз код. Вот код с боевого сайта. Кроме начальных переменных
      function log {
      echo "$(date): $*" >>$LOGFILE
      }

      echo "------ [ " $START ": WP All Import:" $SITE " job id:" $JOB_ID " ]---------"
      cd $ROOT_DIR
      log "Start import for job id $JOB_ID"

      php -e -r 'parse_str("import_key='$KEY'&import_id='$JOB_ID'&action=trigger", $_GET); include "wp-cron.php";' >>$LOGFILE 2>&1
      sleep 1

      while [ $DONE -eq 0 ]
      do
      echo "Iteration: $COUNT"
      php -e -r 'parse_str("import_key='$KEY'&import_id='$JOB_ID'&action=processing", $_GET); include "wp-cron.php";' >$CURLOG 2>&1
      cat $CURLOG >>$LOGFILE
      DONE=$(grep 'is not triggered' $CURLOG | wc -l)
      sleep 1
      COUNT=$((COUNT + 1))
      done
      rm $CURLOG

      log "--- End of import for jobId" $JOB_ID ". ---"

  18. не понял из статьи, данный скрипт подхватывает кодировку 1251 и конвертирует в утф8 или нет?

  19. подскажите, можно ли как то ускорить скрипт? может выдать ему лишних ресурсов?

    • Только общей оптимизацией WP. Скрипт и так максимально выжимает ресурсы WP All Import.

      • то что он 2500 обновлений цен грузит 15 мин это нормально на выделенном сервере?

        • В значительной степени скорость определяется не скриптом, а окружением wp. Смотря какие плагины грузятся и что делают… Я же и говорю: нужно оптимизировать wp. Но вообще у вас нормальная скорость. У нас на ряде решений медленнее. На одном сайте 35000 товаров он грузит несколько часов.

Добавить комментарий