Web Scraping. Часть 1

Введение


Всем привет. Недавно у меня возникла идея о том, чтобы поделиться с интересующимся кругом лиц о том как пишутся скраперы. Так как большинству аудитории знаком Python все дальнейшие примеры будут написаны на нём.


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


print('Part 1. Get started')

Инструменты


  • Язык программирования и соответствующие библиотеки
    Конечно, без него никуда. В нашем случае будет использован Python. Данный язык является довольно сильным инструментом для написания скраперов, если уметь правильно пользоваться им и его библиотеками: requests, bs4, json, lxml, re.
  • Инструменты разработчика
    Каждый современный браузер имеет данную утилиту. Лично мне удобно пользоваться Google Chrome или Firefox. Если вы пользуетесь другим браузерами, рекомендую попробовать один из вышеперечисленных. Здесь нам понадобятся практически все инструменты: elements, console, network, application, debuger.
  • Современная IDE
    Здесь выбор остаётся за вами, единственное, что хотелось бы посоветовать — наличие компилятора, debuger'a и статического анализатора в вашей среде разработке. Я отдаю своё предпочтение PyCharm от JetBrains.

Немного о формате


Для себя я выделяю два принципа извлечения и анализа данных: frontend, backend.


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


Backend. В данном случае мы получаем информацию от backend api веб-приложения в формате json или xml. Сразу хочу сказать, что не у всех сайтов возможно получить доступ к api, иногда даже невозможно, если анализировать только сайт и приходится анализировать протокол мобильного приложения. Однако если у нас получается найти способы и точки для обращения к api веб-приложения, то мы получаем структурированную информацию и хорошую производительность нашего скрапера.


Работа с Frontend сайта


Здесь мы работаем с селекторами, чтобы получить нужные нам элементы. Для этого для начала нам нужно подключить библиотеку requests и сделать запрос. Отдельное внимание стоит уделить headers, т.к. с их помощью сервер анализирует запрос и возвращает вам результат в зависимости от того, что было в них указано, настоятельно рекомендую найти информацию про стандартные хэддеры и их значения. Для примеря я выбрал сайт: Kith


import requests

headers = {
    'authority': 'www.kith.com',
    'cache-control': 'max-age=0',
    'upgrade-insecure-requests': '1',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36',
    'sec-fetch-dest': 'document',
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
    'sec-fetch-site': 'same-origin',
    'sec-fetch-mode': 'navigate',
    'sec-fetch-user': '?1',
    'accept-language': 'en-US,en;q=0.9',
}

session = requests.session()

response = session.get("https://kith.com/collections/mens-footwear", headers=headers)

if response.status_code == 200:
    print("Success")
else:
    print("Bad result")

Далее нам необходимо забрать элементы. С помощью инструментов разработчика(Ctrl+Shift+I) мы можем посмотреть селекторы элементов, который нас интересуют.
И выведем интересующие нас параметры: название, цена, ссылка на продукт.


soup = BeautifulSoup(response.text, 'html.parser')

for element in soup.find_all('li', class_='collection-product'):
    name = element.find('h1', class_="product-card__title").text.strip()
    price = element.find('span', class_="product-card__price").text.strip()
    link = "https://kith.com/" + element.find('a').get('href')

Для удобного взаимодействия с продуктами создадим класс


class Prodcut:
    name = str
    price = str
    link = str

    def __init__(self, name, price, link):
        self.name = name
        self.price = price
        self.link = link

    def __repr__(self):
        return str(self.__dict__)

Теперь наш скрипт готов, остаётся его выполнить и вывести результат


import requests
from bs4 import BeautifulSoup

class Prodcut:
    name = str
    price = str
    link = str

    def __init__(self, name, price, link):
        self.name = name
        self.price = price
        self.link = link

    def __repr__(self):
        return str(self.__dict__)

headers = {
    'authority': 'www.yeezysupply.com',
    'cache-control': 'max-age=0',
    'upgrade-insecure-requests': '1',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36',
    'sec-fetch-dest': 'document',
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
    'sec-fetch-site': 'same-origin',
    'sec-fetch-mode': 'navigate',
    'sec-fetch-user': '?1',
    'accept-language': 'en-US,en;q=0.9',
}

session = requests.session()

response = session.get('https://kith.com/collections/mens-footwear', headers=headers)

soup = BeautifulSoup(response.text, 'html.parser')

for element in soup.find_all('li', class_='collection-product'):
    name = element.find('h1', class_="product-card__title").text.strip()
    price = element.find('span', class_="product-card__price").text.strip()
    link = "https://kith.com/" + element.find('a').get('href')

    prodcut = Prodcut(name, price, link)

    print(prodcut.__repr__())

Заключение


Мы рассмотрели основные технологии и принципы, с которыми нам предстоит познакомиться в следующих частях. Рекомендую самостоятельно попробовать выполнить задачи с перечисленными библиотеками, а также буду рад выслушать ваши предпочтения по поводу сайта, который будет выбран в качестве примера. Далее мы разберём как искать точки backend api и взаимодействовать с ними.

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 23

    0
    Почему именно kith? Вы искали худи стоимостью в 0.5 зарплаты, чтобы рассекать по Лиговке, или это просто перевод статьи?
      0
      Т.к. уже работал с этим сайтом и платформой Shopify
        0
        все зависит от зарплаты :D
      0

      Как поживает скрейпинг фротенда при вашем подходе, когда всё содержимое страницы загружается AJAX скриптами? Нам приходится целым браузером ходить по страницам

        0
        Возможно вы не до конца поняли, что я хотел донести в этой статье. Что вам не нравится и какие есть предложения?
          +3

          Я то понял. Просто вы описываете простой случай, когда вам сервер отдаёт страницу с данными. А у нас чаще встречаются ситуации, когда сервер отдаёт практически пустой Html с пачкой JS скриптов, которые асинхронно начинают подтягивать данные на страницу и отдельная проблема даже понять, когда эта страница "полностью прогрузилась и готова к использованию". Имитировать логику всех этих JS вручную ооочень трудозатратно.

            0
            В этом случае мы смотрим какие точки backend api тоскают данные js файлы и собираем воедино картину. Зачастую именно грамотный поиск эндпоинтов освобождает от использование хэдлес браузеров. Да и статья расчитана исключительно как вводная.
              0
              А вот это интересно. Буду признателен, если расскажете поподробнее. Спасибо
                0
                Это все хорошо до тех пор, пока не сталкиваешься:
                1. С хорошей, нестандартной captcha
                1. С защитой от ботов с помощью сервисов типа perimeterX, Netacea, dataDome и прочих.
                3. С защитами на основе user fingerprinting.
                И тогда порой уже без полноценного браузера на базе chrome не обойтись. Даже headless режим детектируется при известной сноровке (отрисовка определенных картинок с помощью движка, триггер по использованию мышки, подключенные плагины, быстро закрытая вкладка и прочие варианты).
                Как итог, приходишь к созданию эдакого фреймворка типа scrapy, который бы позволял работать с удаленными сайтами с помощью различных вариантов подключения: requests, selenium, headless, chrome.
                А уж что говорить когда необходимо обеспечивать поиск по данным в реальном времени, тут и каждая секунда дорога, и проблем бывает выше крыши.
                  0
                  За ссылочку спасибо не знал про такие отпечатки. Но так вы правы любой более менее большой скрапер это 98% requests и 2% (selenium или браузер + те или иные способы обхода/решения капчи и периметра) правда распределение полезной информации на эти подходы увы скорее диаметрально противопложное. Кейсов в реальном времени увы(или к счастью) пока не встречал в практике.
                    +1
                    Имею огромный опыт в парсинге. Пару раз встречал капчу, и то, по жадности. +100500 защит используют наверное экзотические малоизвестные сайты. Какой смысл навешать много защит от парсинга, если их все равно обойдут. К тому же, если навешать на сайт много защит, они со 100% вероятностью будут ложно срабатывать. А это раздражает обычных пользователей. Пользователи просто уйдут из сайта. Это как я яндексом. Есть у них сервис, в котором можно посмотреть сколько запросов в месяц по ключевым словам. На каждый запрос, нужно ввести капчю, которая 1 из десяти понятна. Теперь я не пользуюсь яндексом, пусть сами вводят свою капчю.
                    image
                0

                Попробуйте вашим способом получить список документов вот с этой, например, страницы:
                https://www.mos.ru/authority/documents/

                  +2
                  Дёргаем www.mos.ru/api/documents/v1/json/list.php?filter=%7B%7D&page=1
                  В ответе видим красивый json, а листаем страницы меняя значение page=

                  Получение документа по id — www.mos.ru/api/documents/v1/json/detail.php?id=43437220

                  Работать с подобными сайтами даже проще и приятнее. Не нужно возиться с парсингом html, потому что как правило там json в ответе.
                  А enpoint'ы находятся через DevTools максимум за минуту.
                0
                Бывают случаи когда и целым браузером приходится ходить, но это редкость, обычно просто запросов достаточно. Ходячий браузер с сервером тоже просто запросами общается, но иногда повтор логики javascript банально не соотносится с объёмом и частотой добываемых данных, и проще использовать браузер. Вопрос больше в плоскости необходимых усилий.
                  0
                  В своих задачах я не испльзую браузер для скрапинга, т.к. это замедляет выполнение задач для нашей мониторинговой системы, в которой важна каждая секунда, чтобы быть быстрее конкурентов. Поэтому я отдельное внимание уделяю поиску необходимых эндпоинтов.
                    0
                    Ну у вас есть смысл и в js'e и в куках переменные нужные искать и регулярки использовать вместо html парсера. А так есть целый пул кейсов, где данные нужно обновить только раз в день, или js уж больно замудрённый и проще в лямде селениум развернуть, или фантом там.
                    Если ценность в скорости и экономии ресурсов конечно разберёшь весь фронт по полочкам и ещё и старые едпоинты найдёшь. =)

                    P.S.
                    Учитывая что вас спрашивают выше, то совет, по подробнее остановится на хром инструментарии видимо использование вызывает у людей сложности. )
              0

              Хотелось бы узнать причину отклонения комментария.
              В последнем абзаце автор предлагает написать в комментах сайт для примера — я предлагаю reformagkh.ru.

                –4
                Проанализировав ваш профиль я пришёл к выводу, что возможно вы являетесь фэйком. А ваше предложение основано на личных интересах для дальнейше монитизации, не более. Если я не прав, то прошу прощения. Но это моё мнение
                  +1
                  Если необходимо предлагать только то, что не «основано на личных интересах» — стоит писать об этом прямо. Потому что из контекста это не следует.
                  «Дальнейшая монетизация» это зло?
                  Мой проект скрапинга бесплатен. И он на js. Велкам.
                    0
                    Такой парсер пишется за 3 часа. Какая «монитизация»?
                      0
                      Любая работа стоит денег так что не надо перегибать. То что в данном случае сайт максимально просто отдаёт все данные не значит что это всегда так работает. А монетизация зависит от фантазии и лени людей в поиске данных.

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

                Самое читаемое