CoderCastrov logo
CoderCastrov
Парсер

Как создать проект парсинга с помощью Scrapy и MongoDB

Как создать проект парсинга с помощью Scrapy и MongoDB
просмотров
9 мин чтение
#Парсер

Стань настоящим "человеком-пауком" в Python

Scrapy - это универсальный и мощный фреймворк для парсинга веб-сайтов и извлечения структурированных данных с веб-страниц. Он подходит для больших проектов парсинга, в которых требуется парсить много веб-сайтов или много страниц.

В этой статье мы продемонстрируем, как использовать Scrapy для парсинга всего веб-сайта с цитатами и извлечения всех цитат всех авторов. Извлеченные данные будут сохранены в MongoDB, поскольку извлеченные теги представляют собой списки строк переменной длины, которые лучше всего подходят для документ-ориентированной базы данных, такой как MongoDB.


Перед началом работы нам нужно установить необходимые пакеты для этого руководства. Рекомендуется создать виртуальное окружение и установить пакеты в нем, чтобы они не повредили системные библиотеки. Для простоты мы будем использовать conda для создания виртуального окружения. Нам нужно установить пакет Scrapy, который используется для парсинга веб-сайтов, а также pymongo, который является библиотекой с инструментами для работы с MongoDB.

(base) $ **conda create --name scrapy python=3.10**
(base) $ **conda activate scrapy**
(scrapy) $ **pip install -U Scrapy==2.5.1 pymongo==4.0.1**

Поскольку мы будем сохранять извлеченные данные в MongoDB, нам нужен доступный сервер MongoDB. На практике у вас обычно есть выделенный сервер MongoDB, такие как те, которые предоставляет MongoDB Atlas. В этой работе мы просто запустим сервер MongoDB в контейнере Docker:

$ docker network create mongo-net$ docker run --detach --network  mongo-net  --name mongo-server \
    --env MONGO_INITDB_ROOT_USERNAME=admin \
    --env MONGO_INITDB_ROOT_PASSWORD=pass \
    --env  MONGO_INITDB_ROOT_DATABASE=admin \
    --volume mongo-data:/data/db \
    --publish 27017:27017 \
     mongo:5.0.6

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


Мы будем использовать нашего старого друга quotes.toscrape.com для демонстрации в этой статье. Мы можем использовать другие сайты для демонстрации, но это не будет более интересно, потому что нас интересуют техники парсинга, а не сами веб-сайты. quotes.toscrape.com очень прост и использовался в наших предыдущих статьях. Поэтому мы должны быть более знакомы с структурой веб-сайта и с тегами и XPath, которые будут использоваться.

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

$ **scrapy startproject scraping_proj**

Как указано в консоли, мы можем перейти в папку проекта и затем создать паука:

$ **cd scraping_proj**
$ **scrapy genspider quotes quotes.toscrape.com**
  • quotes — имя создаваемого паука.
  • quotes.toscrape.com — веб-сайт, который будет парситься пауком.

Вы можете дать любое имя своему пауку. Чтобы соблюдать хорошие практики, оно должно быть описательным и отражать сайт и элементы, которые вы парсите.

Теперь давайте проверим структуру папки проекта с помощью команды tree в Linux:

$ **tree . -I __pycache__**

Опция -I игнорирует файлы, соответствующие указанному шаблону. Здесь мы хотим игнорировать папку __pycache__, содержащую скомпилированный байт-код Python. Вот структура папки проекта:

Сейчас нам не нужно трогать большинство файлов, но полезно иметь общее представление о них:

  • scrapy.cfg — файл конфигурации проекта. Обычно он содержит конфигурации для определения расположения файла настроек и способа развертывания проекта. Каталог, в котором находится файл scrapy.cfg, известен как корневой каталог проекта.
  • scraping_proj — В корневом каталоге проекта находится папка проекта (здесь scraping_proj), в которой расположены все модули, связанные с парсингом.
  • spiders — Важно, что в папке проекта есть папка spiders, в которой находятся все пауки.
  • items.py — Модуль, определяющий формат извлекаемых данных. Это необязательно, но хорошей практикой является определение типов элементов для извлекаемых данных, чтобы данные можно было стандартизировать и они не содержали случайных полей.
  • pipelines.py — Модуль, определяющий, как должны обрабатываться извлеченные элементы. Может быть несколько конвейеров, которые будут обрабатывать элементы в определенном порядке.
  • middlewares.py — Модуль, определяющий фреймворк хуков, которые могут добавлять пользовательскую функциональность для запросов и ответов Scrapy.
  • settings.py — Файл настроек проекта. Мы можем определить промежуточное программное обеспечение и конвейеры в этом файле. Кроме того, мы можем определить некоторые переменные, относящиеся к окружению или системе, которые могут использоваться всеми пауками или конвейерами.

Мы обновим items.py, pipelines.py и settings.py позже, когда будем записывать извлеченные данные в MongoDB. А пока добавим код для нашего первого паука в quotes.py:

Если вы не знакомы с оператором-валрусом (:=) в Python, вам может быть полезен этот пост. Кроме того, ознакомьтесь с этим постом, если вы хотите узнать немного больше о веб-сайте quotes.toscrape.com и используемых HTML-элементах и XPath.

Теперь мы можем запустить этот паук с помощью scrapy crawl:

$ **scrapy crawl quotes**

Ура! Данные успешно извлечены и выведены в консоль. Если мы хотим сохранить данные в файл JSON, мы можем добавить опцию -o в командной строке:

$ **scrapy crawl quotes -o quotes_all.json**

Извлеченные данные теперь будут сохранены в quotes_all.json. Однако данные по-прежнему выводятся в консоль, что довольно шумно сейчас. Если вы внимательно проверите журналы парсинга, вы заметите, что извлеченные данные регистрируются в режиме DEBUG модуля регистрации. Мы можем использовать опцию -L, чтобы изменить уровень регистрации. Давайте изменить уровень регистрации на INFO:

$ **scrapy crawl quotes -o quotes_all.json -L INFO**

Обратите внимание, что и -L, и INFO должны быть в верхнем регистре. На этот раз данные не выводятся в консоль, но данные по-прежнему сохраняются в quotes_all.json. Если вы не верите, вы можете удалить этот файл и повторно выполнить указанную выше команду.

Как вы заметили, в методе start_requests() мы проверяем, есть ли у паука свойство tag, и если да, мы будем парсить только цитаты с определенным тегом. В командной строке свойство можно передать пауку с помощью опции -a. Вы можете передавать любые свойства пауку. Однако, как эти пользовательские свойства будут использоваться, зависит от вас.

Давайте спарсим все цитаты с тегом love:

$ **scrapy crawl quotes -o quotes_love.json -L INFO -a tag=love**

Пользовательские свойства указываются в формате -a NAME=VALUE. Вы можете повторять -a NAME=VALUE несколько раз, чтобы указать несколько свойств. В вышеприведенной команде должны парситься только цитаты с тегом love.


Выше мы успешно спарсили цитаты и сохранили их в JSON-файле. В этом разделе мы узнаем, как сохранить спарсенные данные в MongoDB, очень популярной NoSQL-базе данных для хранения документов, которыми обычно являются JSON-объекты или словари в Python.

Поскольку спарсенные цитаты в основном содержат тексты, а не числовые данные, удобнее хранить их в NoSQL-базе данных, а не в реляционной базе данных, такой как MySQL. Кроме того, поле tags, в котором разное количество строк для разных цитат, делает MongoDB еще более подходящим решением, потому что MongoDB хорошо подходит для хранения неструктурированных данных.

Давайте сначала создадим класс Item для цитаты. Классы Item определяются в модуле items.py, как было указано выше. С определенным классом Item будет ясно, какие поля будут спарсены:

А затем нам нужно обновить паука, чтобы он возвращал данные в виде QuoteItem, а не словаря:

Полный модуль quotes.py можно найти здесь.

Как класс Item, QuoteItem работает как обычный класс, и нам нужно создать и возвращать экземпляр с извлеченными данными в качестве свойств.

Если вы снова запустите паука, вы увидите, что он работает точно так же, как и раньше. Вы можете попробовать все различные опции командной строки, продемонстрированные выше, и результаты будут такими же.

Если мы хотим обрабатывать и сохранять спарсенные данные по-разному, мы должны создать пользовательский конвейер элементов. Каждый спарсенный элемент будет отправлен в конвейер элементов и обработан там. В этом руководстве мы создадим только один конвейер. В следующем руководстве мы создадим несколько конвейеров и посмотрим, как они будут обрабатывать элементы последовательно с указанным порядком.

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

Пожалуйста, проверьте докстринги в pipelines.py для объяснения специальных методов конвейера, требуемых Scrapy. Пожалуйста, не запускайте паука сейчас, так как мы еще не закончили. Нам нужно добавить конвейер элементов в settings.py. Кроме того, как вы видели в коде выше в методе класса from_crawler(), нам также нужно определить некоторые настройки для MongoDB в settings.py:

Примечания к settings.py:

  • Некоторые поля автоматически генерируются Scrapy, такие как BOT_NAME, SPIDER_MODULES и т. д., и не требуют изменений.
  • В settings.py есть много комментариев, которые предоставляют полезное руководство и ссылки для настройки конфигураций. Их можно безопасно удалить, если вы предпочитаете более краткий модуль.
  • Мы добавляем созданный конвейер элементов в словарь ITEM_PIPELINES. Ключом является относительный путь к классу конвейера элементов MongoDBPipeline, а значением является порядок, в котором конвейеры выполняются: элементы проходят от классов с более низким значением к классам с более высоким значением. Поскольку у нас здесь только один конвейер, не имеет значения, какое значение вы ему даете. 300 - это значение по умолчанию в комментарии и поэтому оно сохраняется по умолчанию.
  • Мы также определили некоторые глобальные настройки для регистрации, включая уровень регистрации по умолчанию, формат регистрации и файл журнала.
  • Наконец, в settings.py также указаны некоторые настройки для MongoDB, чтобы их можно было удобно импортировать при необходимости. Все классы, реализующие метод класса from_crawler(), могут иметь доступ к этим настройкам.

Теперь давайте снова запустим паука:

$ **scrapy crawl quotes**

На этот раз паук парсится молча, без журналов в консоли вообще. Это потому, что журналы теперь сохраняются в файле журнала, который находится по адресу /tmp/scrapy.log. Пожалуйста, проверьте этот файл журнала и убедитесь, что в нем нет ошибок. Если есть, вы можете обновить код соответствующим образом на основе ошибок. Вам может быть полезна PDB для отладки пауков.

Теперь давайте проверим, были ли данные сохранены в MongoDB. Запустите следующий код Python в консоли с активированной средой scrapy:

Ура! Данные успешно сохранены в MongoDB.

Обратите внимание, что если вы запустите паука несколько раз, количество возвращаемых документов будет увеличиваться несколько раз. Это происходит потому, что в этом примере у нас нет уникального ключа для документов. На практике обычно есть уникальный идентификатор источника, который можно использовать для однозначной идентификации спарсенного элемента. Если его нет, вы можете хешировать спарсенные данные и создать уникальный ключ для документа. Затем вы можете использовать метод [update_one](https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/) с опцией upsert, чтобы вставить или обновить документ. В будущей статье будет более подробно рассмотрено, как использовать MongoDB.


Поздравляю, вы добрались до этого момента. Вы узнали, как создать проект парсинга с помощью Scrapy и как сохранить данные в MongoDB. Теперь вы можете использовать Scrapy для парсинга интересующих вас веб-сайтов. Обратите внимание, что вы можете парсить только веб-страницы с обычным HTML-кодом. Для тех, которые рендерятся динамически с помощью кода JavaScript, вам потребуются некоторые специальные инструменты, такие как ProxyCrawl или Selenium. Пожалуйста, ознакомьтесь с соответствующими статьями для получения ссылок. В следующих статьях мы расскажем о некоторых более продвинутых темах, таких как использование прокси для парсинга и кэширование ответов, что может быть полезно в ваших приложениях.

Репозиторий GitHub для кода, продемонстрированного в этой статье, можно клонировать здесь.


Связанная статья: