IT-инфраструктура для бизнеса и творчества
Разработка
NTA

Пауки Scrapy

Рассмотрим парсинг сайтов с использованием Scrapy. Применение такой технологии позволит, например, эффективно находить негативные отзывы клиентов.

Анализ и работа с зонами роста как в уровне сервиса, так и в качестве продуктов повышает лояльность клиентов, а значит, прибыль компании. Вот только как узнать, что именно требует модернизации? Клиенты редко оставляют жалобы напрямую.

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

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

Scrapy — мощный фреймворк, основным принципом которого является гибкая настройка парсеров (пауков, crawlers). Главными особенностями данного фреймворка являются:

  • Запуск всех парсеров в разных процессах (по умолчанию = 16).
  • Автоматическое отслеживание нагрузки на сервер сайта — если в процессе парсинга увеличивается временной интервал между запросом и ответом, то Scrapy для данного паука увеличивает задержку между запросами (delay) до тех пор, пока сервер не будет разгружен и не будет отвечать с ожидаемой задержкой (выставляется в настройках, по умолчанию — 3 секунды).
  • Отслеживание переходов по ссылкам для предотвращения повторных запросов.
  • Удобная структура проекта выделяет слои middleware, pipeline и spider, позволяя гибко добавлять парсеры для различных сайтов и не нарушать принципы DRY.
  • Легко встраивается в такие популярные фреймворки как – Flask, Django. Также разработан специальный фреймворк, выступающий в роли daemon по циклическому запуску парсеров с множеством настроек расписаний – Scrapyd.

Структура проекта выглядит следующим образом:

project/ spiders/ __init__.py spider.py __init__.py items.py middlewares.py pipelines.py settings.py

Перед вами пример парсера, осуществляющий разбор страницы для поиска и сохранения в базе данных информации о сообщении:

1. from project.items import Post 2. from project.common.regexp_template import RegExp 3. from project.settings import SPIDER_URLS 4. from datetime import datetime 5. import scrapy 6. import re 7. 8. 9. class BankiruSpider(scrapy.Spider): 10. # Обязательные переменных класса 11. # Имя парсера, должно быть уникальным по всему проекту 12. name = "bankiru" 13. 14. # Список URL с которых будет начинаться парсинг 15. start_urls = SPIDER_URLS[name] 16. 17. def __init__(self, *args, **kwargs): 18. super().__init__(*args, **kwargs) 19. self.limit_date = kwargs.get("limit_date", None) 20. self.completed = False 21. 22. # Основной метод парсера запускающий разбор страниц 23. def parse(self, response): 24. # Получаем все теги постов (сообщений) на текущей странице сайта 25. main = response.css("main.layout-column-center") 26. posts = main.css("table.resptab") 27. 28. for post in posts: 29. post_link = re.sub(r"#.+$", "", post.css("a.linkNote::attr(href)").get()) 30. # Выполнение парсинга поста в методе self.parse_post 31. yield response.follow(f"{post_link}/", self.parse_post) 32. # Условие для остановки парсинга - достижение поста с определенной датой 33. if self.completed: 34. break 35. 36. # Поиск и формирование ссылки для вледующей страницы сайта 37. current_page = response.url.split("/")[-1] 38. lst_url = current_page.split("=") 39. if len(lst_url) > 2: 40. next_page = f"{lst_url[0]}={lst_url[1]}={int(lst_url[2]) + 1}" 41. else: 42. next_page = f"{current_page}&PAGEN_1=2" 43. 44. # Выполняем парсинг следующей страницы или останавливаем парсинг - возврат пустого словаря 45. yield dict() if self.completed else response.follow(next_page, callback=self.parse) 46. 47. # Метод для парсинга поста 48. def parse_post(self, response): 49. # Флаг остановки парсинга 50. out_of_limit = False 51. post_url = response.url 52. post_id = int(post_url.split("/")[-2]) 53. 54. # Контейнеры 55. post = response.css("table.resptab") 56. author = response.css("table.resptab td.footerline a") 57. 58. # Интересующие нас данные 59. title = RegExp.space.sub(" ", post.css("td.headerline::text").get().strip()) 60. msg = RegExp.space.sub(" ", RegExp.tag.sub(" ", post.css("td.article-text").get() or "").strip()) 61. author_uid = author.attrib["href"].split("=")[-1] 62. author_login = author.css("::text").get() 63. dt = datetime.strptime(response.css("span.color-grey::text").get(), "%d.%m.%Y %H:%M") 64. 65. # Проверка на достижение поста с условной датой 66. if self.limit_date and dt < self.limit_date: 67. self.completed = True 68. out_of_limit = True 69. 70. yield Post() if out_of_limit else Post( 71. post_url=post_url, 72. post_id=post_id, 73. title=title, 74. msg=msg, 75. author_uid=author_uid, 76. author_login=author_login, 77. datetime=dt 78. )

Работа парсера начинается с вызова метода «parse», с передачей единственного параметра response, который содержит структурированный экземпляр страницы.

В процессе разбора страницы можно выделить два основных метода:

  • response.css(<tag>) — выполняет поиск указанного тега или последовательности вложенности тегов с параметрами.
  • response.follow(,) — выполняет загрузку следующей страницы, разобранную структуру которого передает в качестве параметра функции «parse»..

Данный рекурсивный процесс выполняется до тех пор, пока функция «parse» не вернет пустую структуру данных (или пустой словарь). Для парсеров, запускаемых на регулярной основе необходимо предусмотреть условие прерывания.

В данном примере используется ограничение по достижении определенной даты поста. Команда для запуска парсера будет выглядеть следующим образом:

1. from scrapy.utils.project import get_project_settings 2. from scrapy.crawler import CrawlerProcess 3. from project.spiders.bankiru import BankiruSpider 4. 5. 6. limit_date = datetime.strptime("29.05.2020", "%d.%m.%Y") 7. process = CrawlerProcess(get_project_settings()) 8. process.crawl(BankiruSpider, limit_date=limit_date) 9. process.start()

Код сохранения полученных данных со страниц следует выносить в pipeline.py, а именно в класс ParserPipeline(object).

Каждый вызов «yield response.follow(f»{post_link}/», self.parse_post)» парсера передает результаты обратным вызовом функции ParserPipeline.process_item, внутри которой следует описывать работу с сохранением данных.

Подключение к БД следует описывать в ParserPipeline.from_crawler, который вызывается при создании экземпляра класса фреймворком Scrapy.

1. from project.common.db_manager import DB, DBParams 2. 3. 4. class NewsParserPipeline(object): 5. def __init__(self, params: DBParams): 6. self._params = params 7. self.db = None 8. 9. @classmethod 10. def from_crawler(cls, crawler): 11. db_params = crawler.settings.getdict("DB_PARAMS") 12. return cls(DBParams( 13. host=db_params["host"], 14. port=db_params["port"], 15. name=db_params["name"], 16. user=db_params["user"], 17. password=db_params["password"] 18. )) 19. 20. def open_spider(self, spider): 21. self.db = DB(self._params) 22. self._source_id = int(self._get_source_id(spider.name)) 23. self._post_types = self._get_types_id() 24. spider.set_last_post_id(self._get_last_post_id()) 25. 26. def close_spider(self, spider): 27. self.db.disconnect() 28. 29. def process_item(self, item, spider): 30. self.db.insert.posts({ 31. "p_post_url": item.post_url, 32. "p_post_id": item.post_id, 33. "p_title": item.title, 34. "p_msg": item.msg, 35. "p_author_uid": item.author_uid, 36. "p_datetime": item.datetime 37. }) 38. self.db.commit()

Отметим также, что если использовать Docker-контейнер, то полученную систему можно быстро и легко развернуть на любой машине.

Подробнее ознакомиться с классами фреймворка можно из официальной документации проекта Scrapy.

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

{ "author_name": "NTA", "author_type": "editor", "tags": ["\u043f\u0430\u0440\u0441\u0438\u043d\u0433","scrapy"], "comments": 2, "likes": 5, "favorites": 28, "is_advertisement": false, "subsite_label": "dev", "id": 138132, "is_wide": true, "is_ugc": false, "date": "Tue, 30 Jun 2020 07:09:25 +0300", "is_special": false }
(function () { let cdnUrl = `https://specialsf378ef5-a.akamaihd.net/SelectelBranding/images/` let previousArticleNumber = null let currentArticleNumber = 0 let platform = 'Desktop' let articles = [ // { // name: 'camera', // url: `${cdnUrl}CameraCat`, // text: 'умную камеру для\u00A0наблюдения за\u00A0котиками', // link: '1', // }, { name: 'chill', url: `${cdnUrl}ChillCat`, text: 'трекер, который подскажет, когда пора отдохнуть', link: 'https://vc.ru/promo/288561-eye-tracker', }, // { // name: 'cloud', // url: `${cdnUrl}CloudCat`, // text: 'котика: даёшь ему «пять», а\u00A0он делает бэкап в облако', // link: '3', // } ] let buttonCycle = document.querySelector('.button--cycle') let textField = document.querySelector('.selectel-footer-subtitle') let imageAgent = document.querySelector('.image--agent') let banner = document.querySelector('.selectel-footer') buttonCycle.addEventListener('click', cycleClick) let media = window.matchMedia("(max-width: 570px)") media.addEventListener('change', matchMedia) function matchMedia() { if (media.matches) { platform = 'Mobile' } else { platform = 'Desktop' } update() } matchMedia() function cycleClick(event) { if (event) { event.preventDefault() event.stopPropagation() } window.open('https://vc.ru/tag/selectelDIY', '_blank') //cycle(event) } function cycle(event) { // incrementArticleNumber() textField.innerHTML = generatedText() imageAgent.src = articles[currentArticleNumber].url + platform + '.svg?5' imageAgent.setAttribute("class", "") imageAgent.classList.add('image--agent', articles[currentArticleNumber].name) banner.href = articles[currentArticleNumber].link } function update() { banner.href = articles[currentArticleNumber].link imageAgent.src = articles[currentArticleNumber].url + platform + '.svg?5' textField.innerHTML = generatedText() } function incrementArticleNumber() { previousArticleNumber = currentArticleNumber if (currentArticleNumber >= articles.length - 1) { currentArticleNumber = 0 } else { currentArticleNumber++ } } function generatedText() { let defaultText if (platform === 'Desktop') { defaultText = `Мы тут собрали %text%. Хотите почитать?` } else { defaultText = `Мы тут собрали %text%.` } return defaultText.replace('%text%', articles[currentArticleNumber].text) } function getRandom(min, max) { min = Math.ceil(min) max = Math.floor(max) return Math.floor(Math.random() * (max - min + 1)) + min } (function create() { currentArticleNumber = getRandom(0, articles.length - 1) cycle() let page = document.querySelector('.page--entry') if (page) { function insertAfter() { let parents = page.querySelectorAll('[data-id="7"]') let referenceNode = parents[0] referenceNode.parentNode.insertBefore(banner, referenceNode.nextSibling); loaded() } setTimeout(() => insertAfter(), 0) } }()) function loaded() { banner.classList.add('loaded') } loadImages([ `${cdnUrl}CameraCatDesktop.svg`, `${cdnUrl}ChillCatDesktop.svg`, `${cdnUrl}CloudCatDesktop.svg`, `${cdnUrl}CameraCatMobile.svg`, `${cdnUrl}ChillCatMobile.svg`, `${cdnUrl}CloudCatMobile.svg`, ]) function loadImages(urls) { return Promise.all(urls.map(function (url) { return new Promise(function (resolve) { var img = document.createElement('img'); img.onload = resolve; img.onerror = resolve; img.src = url; }); })); } }())
0
2 комментария
Популярные
По порядку

Познавательно. Спасибо

1

а не проще программным роботом парсить? Или это другая тема? https://vc.ru/marketing/149696-parsing-eto-polezno-i-ne-stydno-osobenno-esli-dannye-sobiraet-robot-rpa-a-otchety-pokazyvaet-bi

0
Читать все 2 комментария
Что такое сложный процент. Рассказывает персональный брокер

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

Старинный пруд в Москве очистят при помощи водной мяты

Систему природной очистки воды при поддержке крупного французского фитокосметического бренда презентовали в Ботаническом саду МГУ «Аптекарский огород». Рассказываем, как устроена технология и в чем выгода использования растений.

фото: пресс-служба "Аптекарского огорода"
Mozilla обвинила Google в слежке за пользователями в новой версии браузера Chrome Статьи редакции

Критику API поддержали и разработчики движка WebKit.

Вторая часть пивозавро-стикеров для IT

Первая часть пивозавро-стикеров зашла очень хорошо. Мы попали на первую строчку в популярном, и видим, что вы их юзаете) Поэтому выкатываем вторую часть!

Как сделать карьеру из незакрытого со школы гештальта

История создания самого успешного в России университетского мобильного приложения

90 самых полезных статей на vc.ru для предпринимателей

Привет, это команда Логомашины. За несколько лет мы прочитали тысячи материалов на vc.ru, а сегодня делимся подборкой из 90 статей, которые нам кажутся самыми полезными для тех, кто создаёт или развивает свой бизнес.

Предсказал будущее интернета в 90-ых и пропал, когда его прогнозы стали сбываться: история Филипа Агре Статьи редакции

Исследователь рассказал об опасности сбора данных ещё до запуска Google и Amazon, поставил под сомнение этичность работы над искусственным интеллектом и предсказал китайскую систему рейтинга.

Филип Агре NPR
Еврокомиссия официально предложила сделать USB-C единым зарядным портом для смартфонов и других устройств Статьи редакции

Принятие проекта, вероятно, сильнее всего повлияет на Apple.

Volvo показала в соцсетях обновлённый логотип — он появится на автомобилях с 2023 года Статьи редакции

Теперь он плоский.

Обновлённый логотип Volvo
Банк России утвердил порядок тестирования неквалифицированных инвесторов

Банк России утвердил в новой редакции стандарт, который устанавливает порядок тестирования неквалифицированных инвесторов для допуска к совершению сделок со сложными финансовыми инструментами.

«Сбер» запустил первую в России наружную 3D-рекламу с CGI-персонажами Чебурашкой и крокодилом Геной Статьи редакции

Видео длиной семь минут создавали около четырёх тысяч часов.

3D-рекламу с CGI-персонажами «Сбер»
null