Эта статья была первоначально опубликована в Pythongasm
Вступление
Scrapy-это веб-фреймворк с открытым исходным кодом, и он делает гораздо больше, чем просто библиотека. Он управляет запросами, анализирует веб-страницы HTML, собирает данные и сохраняет их в нужном формате. Следовательно, вам не нужны отдельные библиотеки для каждого второго шага. Вы также можете использовать промежуточное программное обеспечение в scrapy. Промежуточные программы-это своего рода “плагины”, которые добавляют дополнительные функции в scrapy. Существует множество промежуточного программного обеспечения с открытым исходным кодом, которое мы можем прикрепить к scrapy для дополнительных функций. В этой статье вы узнаете, как собирать данные с веб-страниц с помощью scrapy. Более конкретно, мы будем очищать Craigslist и собирать некоторые данные о недвижимости с их веб-страницы. Было бы неплохо иметь некоторый опыт работы с HTML/CSS, но вы можете продолжить, даже если вы не знакомы с HTML, поскольку небольшая часть этой статьи была посвящена HTML.
Установка
Убедитесь, что вы используете Python 3. Откройте терминал и выполните следующую команду:
pip install scrapy
Скребковая оболочка
Вы не захотите отправлять новые запросы каждый раз, когда вам придется вносить небольшие изменения в свой код. Вместо этого более логично “сохранить” веб-страницу локально с помощью одного запроса, а затем посмотреть, как вызывать функции и извлекать данные. Вот почему мы используем scrapy shell для отладки. Это быстро, легко и эффективно.
Выполните эту команду, чтобы запустить оболочку scrapy:
scrapy shell
Мы очищаем раздел недвижимости Craigslist (Нью-Йорк) по адресу: https://newyork.craigslist.org/d/real-estate/search/rea
(Вы можете перейти на newyork.craigslist.com, и выберите недвижимость из списка вариантов)
Мы используем функцию fetch()
для извлечения ответа из URL-адреса.
fetch("https://newyork.craigslist.org/d/real-estate/search/rea")
Это вернет a Html-ответ
объект, хранящийся в переменной response
.
Вы можете просмотреть этот ответ (который сохраняется локально на устройстве) в своем браузере:
view(response)
Разбор HTML
Давайте рассмотрим этот пример:
Чтобы проанализировать веб-страницу, мы находим элементы, используя имя тега и/или класс CSS этого тега. Мы можем поручить нашему пауку найти тег h2
, который имеет “заголовок main” в качестве своего CSS-класса, и он вернет нам этот фрагмент. В этом случае есть только один тег h2
, поэтому нам даже не нужно указывать имя класса, мы можем просто сказать нашему пауку искать тег h2
, и он вернет нам этот фрагмент. Если мы хотим получить тег span
внутри тега h2
, мы можем либо получить его, указав id
, ЛИБО сообщив скребку, что мы ищем тот, который находится внутри тега h2
. Цель состоит в том, чтобы ваш скребок однозначно идентифицировал элемент, который вы ищете.
Теперь давайте вернемся к браузеру и найдем название веб-страницы, которую мы только что загрузили. Щелкните правой кнопкой мыши на веб-странице и выберите Проверить. Заголовки обычно присутствуют внутри тега head
.
Мы используем эту функцию css ()
(также называемую селектором CSS) для выбора элементов веб-страницы. Например, мы можем выбрать тег title
следующим образом:
response.css("title")
[]
функция css()
возвращает список (называемый Selector List
), и каждый элемент этого списка является объектом Selector
. На веб-странице есть только один тег title
, поэтому неудивительно, что длина этого списка равна 1.
Однако это не желаемый результат. Мы ищем текст внутри заголовка, а не весь HTML. Чтобы получить текст внутри тега, мы используем ::text
:
response.css("title::text")
[
Вы можете видеть , что данные в выходных данных теперь изменились на data='new york real estate'
, что является текстом внутри тега title
.
Мы можем получить строковые значения из Селектора
объектов, вызвав для них функцию get ()
.
response.css("title::text")[0].get()
'new york real estate - craigslist'
Интересно, что когда вы вызываете функцию get()
непосредственно в списке ( Список селекторов
), она по-прежнему дает вам тот же результат.
response.css("title::text").get()
'new york real estate - craigslist'
Это связано с тем, что при вызове функции get()
без указания индекса она возвращает строковое значение самого первого элемента в списке SelectorList
. Другими словами, вы можете вызвать get()
в списке селекторов , и вместо того, чтобы выдавать ошибку, он вызывает функцию
get () для первого элемента в списке.
У нас есть еще один метод, называемый get all()
. Эта функция просто вызывает функцию get()
для каждого элемента в списке селекторов . Следовательно, функция
getall() может быть вызвана в списке селекторов
для получения списка строк.
Точно так же, как мы соскребли текст заголовка с веб-страницы, мы можем извлечь другие элементы со страницы, используя эти функции.
Скраповый проект
Выполните следующую команду, чтобы запустить новый проект scrapy:
scrapy startproject craigslist
Это создаст папку “craigslist” в вашем текущем рабочем каталоге со следующей структурой:
Перейдите в каталог пауков. Именно здесь мы будем спасать наших пауков (краулеров). Вы можете создать там новый файл python и начать писать код. Однако мы также можем выполнить следующую команду, чтобы сгенерировать паука с некоторым начальным кодом.
scrapy genspider realestate newyork.craigslist.org/d/real-estate/search/rea
Теперь откройте realestate.py файл в каталоге пауков. Это должно выглядеть так:
import scrapy class RealestateSpider(scrapy.Spider): name = 'realestate' allowed_domains = ['https://newyork.craigslist.org/d/real-estate/search/rea'] start_urls = ['http://https://newyork.craigslist.org/d/real-estate/search/rea/'] def parse(self, response): pass
Когда мы запускаем паука, запрос отправляется на все URL-адреса внутри start_urls
. Это специальная переменная, которая автоматически вызывает функцию parse()
и передает ответ в качестве аргумента.
функция parse ()
– это место, где мы будем писать наш код для анализа ответа. Давайте определим это так:
def parse(self, response): print("\n") print("HTTP STATUS: "+str(response.status)) print(response.css("title::text").get()) print("\n")
Сохраните файл и запустите паука с помощью этой команды:
scrapy crawl realestate
HTTP STATUS 200 new york real estate - craigslist
Теперь давайте извлекем другие элементы на веб-странице. Проверьте HTML-код первого объявления. (Щелкните правой кнопкой мыши > Проверить элемент, который вы хотите проверить):
Вы можете увидеть всю информацию об этом объявлении (название, дата, цена и т.д.) Внутри этого тега p
с именем класса “result-info”
На веб-странице должно быть 120 (или равно количеству списков) таких фрагментов.
allAds = response.css("p.result-info")
Это позволит выбрать все такие фрагменты и составить из них список Селекторов|/.
Давайте попробуем извлечь данные, цену, заголовок объявления и гиперссылку из первого объявления
firstAd = allAds[0]
Дата присутствует внутри тега time
. Поскольку здесь есть только один тег time
, нам не нужно указывать здесь имя класса CSS:
date = firstAd.css("time").get()
Теперь извлеките заголовок и гиперссылку из объявления, которые присутствуют внутри тега a
. Нам нужно удалить пробелы в имени класса CSS с помощью .
:
title = firstAd.css("a.result-title.hdrlnk::text").get()
link = firstAd.css("a.result-title.hdrlnk::attr(href)").get()
Кроме того, как вы можете видеть, мы добавляем ::attr()
в аргумент для извлечения атрибутов, таких как src
, href
из тегов.
Наконец, нам нужно найти цену, которая находится внутри тега span
с именем класса “result-price”:
price = firstAd.css("span.result-price::text").get()
Давайте завершим функцию parse ()
. Нам просто нужно перебрать список Селекторов
и получить подробную информацию из Селектора
объектов внутри него , как мы сделали с первым Селектором
объектом
def parse(self, response): allAds = response.css("p.result-info") for ad in allAds: date = ad.css("time::text").get() title = ad.css("a.result-title.hdrlnk::text").get() price = ad.css("span.result-price::text").get() link = ad.css("a::attr(href)").get() print("====NEW PROPERTY===") print(date) print(title) print(price) print(link) print("\n")
Запустите этого паука еще раз, и вы увидите такой результат:
Мы успешно очистили нужные нам данные из Craigslist. Вы всегда можете включить дополнительную информацию, которую, возможно, захотите извлечь. С небольшими изменениями мы также можем очистить следующие несколько страниц Craigslist (2,3,4 и так Далее).
Экспорт данных
Теперь пришло время структурировать его, как только у нас будет готов скребок, и сделать полезный набор данных. Это довольно легко сделать.
Перейдите в Craigslist > Craigslist > items.py
Вам просто нужно определить новые атрибуты элемента Craigslist
класса (это очень похоже на класс словаря Python):
import scrapy `class CraigslistItem(scrapy.Item): date = scrapy.Field() title = scrapy.Field() price = scrapy.Field() link = scrapy.Field() `
Откройте realestate.py файл и импортируйте элемент Craigslist
класс из items.py:
from ..items import CraigslistItem
Создайте экземпляр этого класса, называемый items
, и назначьте значения точно так же, как вы сделали бы со словарем.
def parse(self, response): allAds = response.css("p.result-info") for ad in allAds: date = ad.css("time::text").get() title = ad.css("a.result-title.hdrlnk::text").get() price = ad.css("span.result-price::text").get() link = ad.css("a.result-title.hdrlnk::attr(href)").get() items = CraigslistItem() items['date'] = date items['title'] = title items['price'] = price items['link'] = link yield items
Если вы не знакомы с ключевым словом yield
, вы можете прочитать об этом здесь . На данный момент вы можете просто предположить, что он выполняет работу , аналогичную return
, но с гораздо большей эффективностью.
Запустите паука с аргументом -o output.csv
вот так:
scrapy crawl realestate -o output.csv
Вы обнаружите, что выходные данные были сохранены в родительском каталоге (Craigslist). Помимо CSV, вы также можете экспортировать данные в другие форматы, такие как JSON, XML и т.д.
Настройки Scrapy
Перейдите в craigslist > craigslist > settings.py. Этот файл в основном позволяет вам настраивать множество вещей.
Например, перед обходом веб-страницы пауки scrapy посещают robots.txt файл для проверки разрешений/ограничений, установленных владельцем сайта для веб-искателей. Если конкретная страница, которую вы хотите очистить, “ограничена” веб-сайтом, scrapy не перейдет на эту страницу. Однако вы можете отключить эту функцию, просто изменив значение ROBOTS TXT_OBEY
на False
в settings.py файл, и ваш искатель перестанет следовать рекомендациям внутри robots.txt.
Аналогично, вы можете настроить пользовательский агент вашего искателя (строки пользовательского агента-это идентификаторы, с помощью которых веб-сайт распознает, какой запрос он получает). Вы можете прочитать больше о user-agent здесь . Допустим, вы хотите подделать пользовательский агент Google Chrome (работающий на macOS). Вы можете назначить строку агента пользователя USER_AGENT
USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36".
Вывод
Это было знакомство со скрэпи. Как мы уже обсуждали, есть много других вещей, которые мы можем сделать с помощью scrapy (или веб-скребка в целом). Существует множество общедоступных веб-сайтов, которые вы, возможно, захотите очистить и преобразовать их содержимое в огромные наборы данных для последующего использования (визуализация, прогнозирование и т. Д.). Например, вы можете очистить все твиты, сделанные пользователем. Надеюсь, вам понравилось читать эту статью.