CoderCastrov logo
CoderCastrov
Crontab - Планировщик задач

Запуск пауков Scrapy локально в Cron Job

Запуск пауков Scrapy локально в Cron Job
просмотров
8 мин чтение
#Crontab - Планировщик задач

...или любом другом скрипте

kite surfing in the bay

У меня есть набор пауков, которые запускаются еженочно для обновления классов для инструмента поиска классов в колледже сообщества, который я создал, btp. После того, как я разместил пауки на Scrapinghub на протяжении нескольких лет, у меня возникла ошибка TSL, которая возникает только на Scrapinghub, но не локально (ошибка, с которой столкнулись несколько других пользователей). Я пытался решить эту проблему несколько месяцев, но безуспешно. Чтобы усугубить моё разочарование, Scrapinghub позволяет получать техническую поддержку только платным пользователям, поэтому было непросто попросить помощи - я пытался заплатить за неё, но моя карта продолжала отклонять запрос, их банк находится в Ирландии.

В выходные я решил просто запустить пауки локально на свободном ноутбуке с Ubuntu, который у меня был. В этом руководстве описан этот процесс, но эти шаги можно выполнить на любой Unix-машине - cron-задания работают на моем Mac, например. Это руководство предназначено для людей, у которых уже есть работающие пауки и которым просто нужно их развернуть. Однако это руководство будет работать для любого, кто пытается периодически запускать любой произвольный скрипт или команду. Я использовал pipenv (виртуальная среда Python) и cron/crontab, чтобы выполнить это.

TLDR…просто дай мне код!!!

Вот gist с конечным скриптом и записью в cron.

создайте скрипт для запуска паука

Создайте новый bash-файл crawl.sh со следующим содержимым:

#!/bin/sh
# перейти в директорию паука
cd /Users/michael/repos/spiders
# запустить паука
pipenv run scrapy crawl multi_subject_spider

Этот скрипт переходит в директорию, содержащую проект Scrapy, запускает виртуальное окружение с помощью pipenv и выполняет команду scrapy crawl внутри этого окружения. Я настоятельно рекомендую использовать pipenv, потому что он позволяет делать крутые вещи, подобные этой, не беспокоясь о активации и деактивации виртуальных окружений. pipenv run запускает окружение, выполняет любую команду в нем и выходит из окружения, возвращая вывод команды. Это позволяет передавать аргументы команде, которую вы выполняете, и легко записывать ее вывод.

сделать скрипт исполняемым

По умолчанию новые bash-файлы не являются исполняемыми, и это означает, что cron не сможет запустить их. Вы можете сделать их исполняемыми, выполнив следующую команду.

chmod +x crawl.sh

добавить cron на ваш компьютер

Cron - это программа, которая запускает задания, описанные в crontab. Вы используете команду crontab -e для редактирования списка заданий, а crontab -l для просмотра списка заданий. Запись в cron или crontab - это описание задания, например, 30 1 * * * echo "пора спать!" выполняет echo "пора спать!" в 1:30 утра местного времени каждый день недели.

Если вы используете Mac или Linux, cron должен уже быть на вашем компьютере. Проверьте, открыв терминал и запустив crontab -l, который покажет существующие задания cron. Что насчет Windows? Установите Ubuntu, это бесплатно и просто в использовании. Или запустите его виртуально с помощью VirtualBox.

добавление скрипта в cron

Это можно сделать, запустив crontab -e в терминале. Это откроет редактор терминала, по умолчанию Vi, с помощью которого вы можете редактировать свой список cron. Вы можете передать переменную среды, чтобы установить редактор, например, для nano EDITOR=nano crontab -e.

30 1 * * * /Users/michael/repos/crawl.sh

Эта задача будет выполнять crawl.sh в 1:30 утра местного времени каждый день недели. Вы можете узнать больше о формате времени cron здесь. Выйдите из редактора и сохраните файл, с nano это CTRL+X, а затем Y. Запустите crontab -l, чтобы увидеть новую задачу.

экспорт зависимостей

Работа будет запущена, но скрипт завершится с ошибкой "неизвестная команда". Cron выполняет ваш скрипт в новой оболочке с минимальным набором переменных среды и путей. Библиотеки Python, такие как pipenv и scrapy, не будут доступны в этой оболочке. Мы можем исправить это, жестко закодировав пути к необходимым командам в скрипте или, что лучше, экспортировав путь в скрипт. Для этого нам нужно получить путь к pipenv (вы можете сделать это, запустив which pipenv). Затем отредактируйте задание, запустив crontab -e снова. Измените его на следующее:

30 1 * * * export PIPENV=/Library/Frameworks/Python.framework/Versions/3.6/bin/pipenv && /Users/michael/repos/crawl.sh

Это экспортирует путь pipenv как PIPENV в оболочку, в которой cron будет запускать crawl.sh, а затем выполняет crawl.sh (вы можете объединять команды с помощью оператора "и", &&). Теперь нам нужно отредактировать crawl.sh, чтобы использовать переменную среды PIPENV.

#!/bin/sh
# перейти в каталог со скрапером
cd /Users/michael/repos/spiders# запустить скрапер
$PIPENV run scrapy crawl multi_subject_spider

$ означает, что PIPENV - это переменная, и ее значение будет получено и выполнено. Однако этот скрипт пока не будет работать. Оказывается, что у pipenv есть зависимость [click](https://click.palletsprojects.com/en/7.x/), которая ожидает, что локаль будет доступна в оболочке, в которой выполняется pipenv. Это можно исправить, экспортировав еще одну переменную, на этот раз в crawl.sh.

#!/bin/sh
# перейти в каталог со скрапером
cd /Users/michael/repos/spiders# предотвратить сбой click, от которого зависит pipenv, из-за отсутствия информации о локали [https://click.palletsprojects.com/en/7.x/python3/](https://click.palletsprojects.com/en/7.x/python3/)
export LC_ALL=en_US.utf-8# запустить скрапер
$PIPENV run scrapy crawl multi_subject_spider

Теперь, когда локаль доступна, и мы вызываем pipenv напрямую, скрипт будет работать.

проверка успешного выполнения

Вы можете проверить, успешно ли выполнилась задача, запустив crontab -l после выполнения задачи. В выводе терминала будет дополнительный текст, указывающий на var/mail/USERNAME, где находится вывод ошибок. Если ошибок нет, то этот текст не появится. Более надежный способ проверки статуса задачи - это запись вывода pipenv run. Давайте запишем вывод pipenv run:

#!/bin/sh
# перейти в директорию со скрапером
cd /Users/michael/repos/spiders# предотвратить сбой click, от которого зависит pipenv, из-за отсутствия информации о локали [https://click.palletsprojects.com/en/7.x/python3/](https://click.palletsprojects.com/en/7.x/python3/)
export LC_ALL=en_US.utf-8# получить дату и время
datetime=$(date '+%m_%d_%Y_%H_%M_%S')# запустить скрапер
$PIPENV run scrapy crawl multi_subject_spider -a debug='True' &> "logs/log_${datetime}.txt"

Этот код получает временную метку mm_dd_yyyy_hh_mm_ss и перенаправляет весь вывод $PIPENV run scrapy crawl multi_subject_spider в файл журнала в /Users/michael/repos/spiders/logs. Я добавил временную метку в имя журнала, чтобы каждый журнал был уникальным и избежать перезаписи файлов, а также чтобы облегчить отладку. Оператор &> перезаписывает существующие файлы или создает файл, если он не существует. Он также захватывает как ошибки, так и обычный вывод.

Я передаю аргумент в свой скрапер с помощью -a debug='True', это пользовательский код, который я добавил в свой скрапер для записи всех запросов и ответов.

логирование каждый раз, когда запускается скрапер

Мы можем добавить логирование в саму запись в crontab, чтобы перехватить любые ошибки или другой вывод, который находится вне pipenv run. Это позволит нам добавить время начала и окончания в crawl.sh, что позволит нам получить больше контекста о наших заданиях. Этот новый лог будет фиксировать ошибки в нашем формате времени cron, в том, как мы экспортируем наши переменные, и любой другой вывод, не созданный pipenv run. Внесите следующие изменения в запись в crontab:

30 1 * * * export PIPENV=/Library/Frameworks/Python.framework/Versions/3.6/bin/pipenv && /Users/michael/repos/crawl.sh >> cron_log.txt 2 >& 1

Это регистрирует весь вывод crawl.sh и запись в crontab в файл cron_log.txt, но в режиме добавления, содержимое файла не будет перезаписано. Затем 2 >& 1 перенаправляет ошибки (2) туда, куда отправляется обычный вывод (1), в данном случае в cron_log.txt. Теперь мы можем использовать команду echo для вывода временной метки и нескольких других полезных сообщений в crawl.sh, и они будут отображаться в cron_log.txt.

#!/bin/sh
# получить дату и время начала
start_datetime=$(date '+%m_%d_%Y_%H_%M_%S')
echo "${start_datetime} - начало работы скрапера"
# перейти в каталог со скрапером
cd /Users/michael/repos/spiders
# предотвратить сбой click, от которого зависит pipenv, из-за отсутствия информации о локали [https://click.palletsprojects.com/en/7.x/python3/](https://click.palletsprojects.com/en/7.x/python3/)
export LC_ALL=en_US.utf-8
# запустить скрапер
$PIPENV run scrapy crawl multi_subject_spider -a debug='True' &> "logs/log_${start_datetime}.txt"
# получить дату и время окончания
end_datetime=$(date '+%m_%d_%Y_%H_%M_%S')
echo "${end_datetime} - скрапер успешно завершен"

Теперь у нас есть информация о том, когда наши скраперы начинают и заканчивают работу. Это создаст лог в файле /Users/michael/cron_log.txt, который будет выглядеть следующим образом:

07_25_2020_22_40_00 - начало работы скрапера
07_25_2020_22_40_03 - скрапер успешно завершен
07_25_2020_22_56_00 - начало работы скрапера
07_25_2020_22_56_03 - скрапер успешно завершен
07_25_2020_23_03_00 - начало работы скрапера
07_25_2020_23_03_02 - скрапер успешно завершен

Мы также можем установить путь к файлу журнала, чтобы он был более доступным: >> /Users/michael/repos/spiders/logs/cron_log.txt 2 >& 1

Еще одно улучшение здесь - зарегистрировать имя скрапера и переданные ему аргументы.

улучшение скрипта для большей портативности и других улучшений

Что если вы хотите взять crawl.sh и запустить его на другом компьютере? Возможно, вы хотите передать аргументы пауку? Нам нужно больше переменных среды!

Обновите скрипт с дополнительными переменными:

  • путь к паукам SPIDER_PATH, чтобы сделать скрипт независимым от компьютера
  • паук для запуска SPIDER_NAME, для отладки и гибкости
  • аргументы для передачи пауку, пока только DEBUG
#!/bin/sh
# получить текущую дату и время
start_datetime=$(date '+%m_%d_%Y_%H_%M_%S')
echo "${start_datetime} - запуск паука ${SPIDER_NAME} - отладка: ${DEBUG}"# перейти в директорию с пауками
cd $SPIDER_PATH# предотвратить сбои click, от которого зависит pipenv, из-за отсутствия информации о локали [https://click.palletsprojects.com/en/7.x/python3/](https://click.palletsprojects.com/en/7.x/python3/)
export LC_ALL=en_US.utf-8# запустить паука
$PIPENV run scrapy crawl $SPIDER_NAME -a debug=$DEBUG &> "logs/log_${start_datetime}.txt"# получить конечную дату и время
end_datetime=$(date '+%m_%d_%Y_%H_%M_%S')
echo "${end_datetime} - паук успешно завершил работу"

Обновите запись в cron:

30 1 * * * export PIPENV=/Library/Frameworks/Python.framework/Versions/3.6/bin/pipenv SPIDER_PATH=/Users/michael/repos/spiders SPIDER_NAME=multi_subject_spider DEBUG=True && /Users/michael/repos/crawl.sh  >> /Users/michael/repos/spiders/logs/cron_log.txt 2 >& 1

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

PIPENV=/Library/Frameworks/Python.framework/Versions/3.6/bin/pipenv 
SPIDER_PATH=/Users/michael/repos/spiders 
SPIDER_NAME=multi_subject_spider
DEBUG=True

Итоговый скрипт и запись в cron можно посмотреть здесь.

Заключение

С этим у вас должно быть хорошее начальное положение для парсинга интернета в удобном, прозрачном и предсказуемом формате. Лучше всего это все делать с помощью вашего локального компьютера. Что насчет блокировщиков IP? Чтобы избежать блокировки, я бы порекомендовал вам инвестировать в VPN и поместить своих парсеров за ним. Убедитесь, что VPN открывает каждое соединение с новым IP, и у вас все будет в порядке. Счастливого парсинга!