Сканеры для поиска на нескольких страницах

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

Что такое Scrapy?

Из Википедии:

Scrapy (произносится как skray-pee) [1] - это бесплатный фреймворк для веб-сканирования с открытым исходным кодом, написанный на Python. Первоначально разработанный для веб-скрейпинга, он также может использоваться для извлечения данных с помощью API-интерфейсов или в качестве поискового робота общего назначения. [2] В настоящее время его обслуживает Scrapinghub Ltd., компания, занимающаяся разработкой и предоставлением услуг веб-скрейпинга.

Создание проекта

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

Adnans-MBP:ScrapyCrawlers AdnanAhmad$ scrapy startproject olx
New Scrapy project 'olx', using template directory '//anaconda/lib/python2.7/site-packages/scrapy/templates/project', created in:
    /Development/PetProjects/ScrapyCrawlers/olx

You can start your first spider with:
    cd olx
    scrapy genspider example example.com

Создание вашего краулера

Я выполнил команду scrapy startproject olx, которая создаст проект с именем olx и полезной информацией для ваших следующих шагов. Вы переходите во вновь созданную папку, а затем выполняете команду для создания первого паука с именем и доменом сайта, который нужно сканировать:

Adnans-MBP:ScrapyCrawlers AdnanAhmad$ cd olx/
Adnans-MBP:olx AdnanAhmad$ scrapy genspider electronics  www.olx.com.pk
Created spider 'electronics' using template 'basic' in module:
  olx.spiders.electronics

Я сгенерировал код своего первого паука с именем electronics, так как я захожу в раздел электроники OLX. Вы можете называть своего паука как хотите.

Окончательная структура проекта будет примерно такой, как в примере ниже:

Как видите, отдельная папка есть только для пауков. Вы можете добавить несколько пауков в один проект. Давайте откроем electronics.py файл с пауком. Когда вы его откроете, вы увидите что-то вроде этого:

Как видите, ElectronicsSpider является подклассом scrapy.Spider. Свойство name на самом деле является именем паука, которое было дано в команде генерации паука. Это имя поможет при запуске самого краулера. Свойство allowed_domains сообщает нам, какие домены доступны для этого поискового робота, а start_urls - это место для упоминания исходных URL-адресов, к которым необходимо получить доступ в первую очередь. Помимо файловой структуры, это хорошая функция для определения границ вашего поискового робота.

Метод parse, как следует из названия, анализирует содержимое страницы, к которой осуществляется доступ. Поскольку я хочу написать краулер, который просматривает несколько страниц, я внесу несколько изменений.

Чтобы поисковый робот переходил на несколько страниц, я создал подкласс своего поискового робота от поискового робота вместо scrapy.Spider. Этот класс упрощает сканирование многих страниц сайта. Вы можете сделать что-то подобное с сгенерированным кодом, но вам нужно будет позаботиться о рекурсии для навигации по следующим страницам.

Следующим шагом является установка переменной правил. Здесь вы упоминаете правила навигации по сайту. LinkExtractor фактически принимает параметры для рисования границ навигации. Здесь я использую параметр restrict_css, чтобы установить класс для страницы NEXT. Если перейти на эту страницу и проверить элемент, можно найти что-то вроде этого:

pageNextPrev - это класс, который будет использоваться для получения ссылок на следующие страницы. Параметр call_back сообщает, какой метод использовать для доступа к элементам страницы. Мы скоро поработаем над этим методом.

Помните, что вам нужно изменить имя метода с parse() на parse_item() или другое имя, которое вы выберете, чтобы избежать переопределения базового класса, иначе ваше правило не будет работать, даже если вы установите follow=True.

Все идет нормально; давайте протестируем краулер, который я сделал до сих пор. Перейдите в свой терминал в каталог вашего проекта и введите:

scrapy crawl electronics

Третий параметр - это фактически имя паука, которое было установлено ранее в свойстве name класса ElectronicsSpiders. В вашем Терминале вы найдете много полезной информации, которая поможет отладить ваш сканер. Вы можете отключить отладчик, если не хотите видеть отладочную информацию. Команда будет аналогична переключателю --nolog.

scrapy crawl --nolog  electronics

Если вы запустите сейчас, он напечатает что-то вроде:

Adnans-MBP:olx AdnanAhmad$ scrapy crawl --nolog  electronics
Processing..https://www.olx.com.pk/computers-accessories/?page=2
Processing..https://www.olx.com.pk/tv-video-audio/?page=2
Processing..https://www.olx.com.pk/games-entertainment/?page=2
Processing..https://www.olx.com.pk/computers-accessories/
Processing..https://www.olx.com.pk/tv-video-audio/
Processing..https://www.olx.com.pk/games-entertainment/
Processing..https://www.olx.com.pk/computers-accessories/?page=3
Processing..https://www.olx.com.pk/tv-video-audio/?page=3
Processing..https://www.olx.com.pk/games-entertainment/?page=3
Processing..https://www.olx.com.pk/computers-accessories/?page=4
Processing..https://www.olx.com.pk/tv-video-audio/?page=4
Processing..https://www.olx.com.pk/games-entertainment/?page=4
Processing..https://www.olx.com.pk/computers-accessories/?page=5
Processing..https://www.olx.com.pk/tv-video-audio/?page=5
Processing..https://www.olx.com.pk/games-entertainment/?page=5
Processing..https://www.olx.com.pk/computers-accessories/?page=6
Processing..https://www.olx.com.pk/tv-video-audio/?page=6
Processing..https://www.olx.com.pk/games-entertainment/?page=6
Processing..https://www.olx.com.pk/computers-accessories/?page=7
Processing..https://www.olx.com.pk/tv-video-audio/?page=7
Processing..https://www.olx.com.pk/games-entertainment/?page=7

Поскольку я установил follow=True, поисковый робот проверит правило для СЛЕДУЮЩЕЙ страницы и продолжит навигацию до тех пор, пока не попадет на страницу, где правило не означает, обычно последнюю страницу списка.

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

Теперь я собираюсь написать код, который будет извлекать ссылки на отдельные элементы со страниц списков. Я собираюсь изменить код в моем parse_item методе.

Здесь я получаю ссылки, используя метод ответа .css. Как я уже сказал, вы также можете использовать xpath - решать вам. В данном случае это довольно просто:

Якорная ссылка имеет класс detailsLink. Если я использую только response.css('.detailsLink'), то он будет выбирать повторяющиеся ссылки из одной записи из-за повторения ссылок в тегах img и h3. Я также обратился к родительскому классу large, чтобы получить уникальные ссылки. Я использовал ::attr(href), чтобы извлечь href часть, которая является самой ссылкой. Затем я использую метод theextract().

Причина использования этого метода заключается в том, что .css и .xpath возвращают SelectorList объект, аextract() помогает вернуть фактическую DOM для дальнейшей обработки. Наконец, я yield добавляю ссылки в scrapy.Request с помощью обратного вызова. Я не проверял внутренний код Scrapy, но, скорее всего, они используют yield вместо return, потому что вы можете получить несколько элементов. Поскольку поисковому роботу необходимо одновременно обрабатывать несколько ссылок, то yield - лучший выбор в данном случае.

Метод parse_detail_page, как следует из названия, анализирует индивидуальную информацию со страницы сведений. Итак, что на самом деле происходит:

  • Вы получите список записей в parse_item.
  • Вы передаете их в методе обратного вызова для дальнейшей обработки.

Так как это был только двухуровневый траверс, я смог добраться до самого нижнего уровня двумя способами. Если бы я собирался начать сканирование с главной страницы OLX, мне пришлось бы написать здесь три метода: первые два для получения подкатегорий и их записей, а последний - для анализа фактической информации. Понятно?

Наконец, я собираюсь проанализировать фактическую информацию, которая доступна в одной из записей вроде эта.

Анализ информации с этой страницы ничем не отличается, но вот кое-что, что делается для хранения проанализированной информации. Нам нужно определить модель для наших данных. Это означает, что нам нужно сообщить Scrapy, какую информацию мы хотим сохранить для дальнейшего использования. Давайте отредактируем файл theitem.py, который ранее был сгенерирован Scrapy.

OlxItem - это класс, в котором я буду устанавливать обязательные поля для хранения информации. Я собираюсь определить три поля для своего модельного класса.

Я собираюсь сохранить заголовок сообщения, цену и сам URL.

Вернемся к классу сканера и изменим parse_detail_page.

Теперь один из способов - начать писать код, протестировать, запустив весь поисковый робот, и посмотреть, на правильном ли вы пути, но есть еще один замечательный инструмент, предоставляемый Scrapy.

Оболочка Scrapy

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

Adnans-MBP:olx AdnanAhmad$ scrapy shell https://www.olx.com.pk/item/asus-eee-pc-atom-dual-core-4cpus-beautiful-laptops-fresh-stock-IDUVo6B.html#4001329891

Теперь я могу легко протестировать код, не обращаясь к одному и тому же URL снова и снова. Я получил название следующим образом:

In [8]: response.css('h1::text').extract()[0].strip()
Out[8]: u"Asus Eee PC Atom Dual-Core 4CPU's Beautiful Laptops fresh Stock"

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

И я получаю цену следующим образом:

In [11]: response.css('.pricelabel > strong::text').extract()[0]
Out[11]: u'Rs 10,500'

Для получения URL ничего делать не нужно, поскольку response.url возвращает текущий доступный URL.

Теперь, когда весь код проверен, пора включить его в parse_detail_page:

title = response.css('h1::text').extract()[0].strip()
price = response.css('.pricelabel > strong::text').extract()[0]
item = OlxItem()
item['title'] = title
item['price'] = price
item['url'] = response.url
yield item

После анализа необходимой информации создается OlxItem экземпляр и устанавливаются свойства. Теперь, когда пришло время запустить поисковый робот и сохранить информацию, есть небольшое изменение в команде:

scrapy crawl  electronics -o data.csv -t csv

Я передаю имя файла и формат файла для сохранения данных. После запуска он сгенерирует для вас CSV. Легко, правда? В отличие от краулера, который вы пишете самостоятельно, вам нужно написать свою собственную процедуру для сохранения данных.

Но ждать! На этом все не заканчивается - вы даже можете получать данные в формате JSON; все, что вам нужно сделать, это передать json с переключателем -t.

Scrapy предоставляет вам еще одну функцию. Передача фиксированного имени файла не имеет смысла в реальных сценариях. Как я могу иметь возможность генерировать уникальные имена файлов? Что ж, для этого вам нужно изменить файл settings.py и добавить эти две записи:

FEED_URI = 'data/%(name)s/%(time)s.json'
FEED_FORMAT = 'json'

Здесь я привожу образец моего файла, %(name)% - это имя самого краулера, а time - это временная метка. Подробнее об этом вы можете узнать здесь. Теперь, когда я запускаю scrapy crawl --nolog electronics или scrapy crawl electronics, он сгенерирует файл JSON в папке data, например:

[
{"url": "https://www.olx.com.pk/item/acer-ultra-slim-gaming-laptop-with-amd-fx-processor-3gb-dedicated-IDUQ1k9.html", "price": "Rs 42,000", "title": "Acer Ultra Slim Gaming Laptop with AMD FX Processor 3GB Dedicated"},
{"url": "https://www.olx.com.pk/item/saw-machine-IDUYww5.html", "price": "Rs 80,000", "title": "Saw Machine"},
{"url": "https://www.olx.com.pk/item/laptop-hp-probook-6570b-core-i-5-3rd-gen-IDUYejF.html", "price": "Rs 22,000", "title": "Laptop HP Probook 6570b Core i 5 3rd Gen"},
{"url": "https://www.olx.com.pk/item/zong-4g-could-mifi-anlock-all-sim-supported-IDUYedh.html", "price": "Rs 4,000", "title": "Zong 4g could mifi anlock all Sim supported"},
...
]

Написание скребков - интересное путешествие, но вы можете упасть, если сайт блокирует ваш IP. Вы также не можете позволить себе дорогие прокси. Scraper API предоставляет вам доступный и простой в использовании API, который позволит вам очищать веб-сайты без каких-либо проблем. Вам не нужно беспокоиться о блокировке, потому что Scraper API по умолчанию использует прокси для доступа к веб-сайтам. Кроме того, вам не нужно беспокоиться о Selenium, поскольку Scraper API также предоставляет возможность безголового браузера. Я также написал пост о том, как его использовать.

Нажмите здесь, чтобы зарегистрироваться по моей реферальной ссылке, или введите промокод adnan10, вы получите 10% скидку на него. Если вы не получите скидку, просто дайте мне знать по электронной почте на моем сайте, и я обязательно вам помогу.

Ресурсы

Репозиторий GitHub для этой статьи