Предыстория
Честно говоря, никогда ранее не покупал б/у вещи на досках объявлений. Но когда случился очередной экономический кризис и нужда заставила, пришлось обратить свое внимание на Авито.
При осмотре предложений сразу бросилось в глаза, что часть объявлений выглядит сомнительными по ряду признаков: заниженная цена, неточное описание и т.п., по которым складывалось впечатление, что или продают не то, что предлагают, или сами не знают что продают. В памяти сразу возник образ из прошлого — барахолки 90-х годов, с которых можно было вернуться как с покупками, так и с пустыми карманами или порезанной сумкой.
Как известно, Авито не дает информацию о других объявлениях продавца, поэтому в тексте объявления часто используют ключевые слова или хэштеги, которые покупатель может использовать для поиска. Но надо заметить, что администрация Авито не приветствует наличие в тексте объявления любой информации, не относящейся к предмету продажи, о чем говорится в правилах размещения. При нарушении правил размещения Авито может заблокировать как объявление, так и аккаунт продавца.
Поисковик выдал мне несколько сайтов, паразитирующих на Авито и других досках объявлений, преимущественно связанных с подержанными автомобилями. В большинстве своем они предлагают поиск объявлений по номеру телефона продавца в базе предварительно скачанных объявлений. Все они имеют недостатки, немаловажные для меня: замысловатый интерфейс, платность услуги, обновление данных с определенным лагом.
Разделяя мнение Стивена Леви о свободном доступе к информации, было решено проанализировать Авито на предмет разработки собственного сервиса sravnito.ru с блэкджеком и всеми делами, а именно: с простым интерфейсом и бесплатным доступом.
На основании проведенного анализа были определены основные атрибуты объявления:
- название
- дата публикации
- цена
- местонахождение
- телефонный номер (либо изображение номера, либо хэш от него, либо распознанное OCR значение)
Архитектура
Теперь непосредственно об архитектуре программного комплекса, на котором основан сервис поиска всех объявлений продавца:
Хранилище
В качестве хранилища для хранения объявлений и их скриншотов используется MySQL (Maria DB) с движком InnoDB.
В DB1 хранятся объявления с основными атрибутами. Для сокращения объема памяти, занимаемого данными, для текстовых полей используется тип VARCHAR, так как их длина разнится от объявления к объявлению. Ежедневно добавляется примерно полмиллиона строк, среди которых как сами объявления, так и логи и служебная информация. При такой динамике хранилище по праву можно будет относить к Big Data. Из особенностей настройки можно выделить следующие параметры:
max_heap_table_size = 512M
innodb_buffer_pool_size = 3G
Таблицы в куче используются для оптимизации запросов из нескольких больших таблиц, когда сначала данные выбираются во временную таблицу:
CREATE TEMPORARY TABLE _temp_table ENGINE=MEMORY AS (
SELECT field
FROM table
WHERE key = i_key
LIMIT i_limit);
которая потом соединяется с другой:
SELECT table.*
FROM table
JOIN _temp_table
ON table.field = _temp_table.field;
В DB2 хранятся скриншоты объявлений как они выглядят для пользователя браузера. Перед записью скриншоты сжимаются в JPEG с quality = 5, что обеспечивает размер файла с изображением равный примерно 20Кб. Принято считать, что при размере BLOB не более 200Кб, производительность хранения файлов в MySQL ничем не уступает NoSQL-хранилищам, что позволяет оставаться в зоне комфорта реляционной СУБД со всеми ее преимуществами. Настолько сжатый скриншот, несмотря на минимальное качество изображения, позволяет пользователю убедиться, что заданное объявление действительно существовало, и разглядеть хотя бы схематично изображение товара.
Вся логика реализована в хранимых процедурах, чтобы инкапсулировать зависимый от структуры данных код в самой СУБД. Таким образом, клиенты СУБД имеют полномочия только для доступа к хранимым процедурам, которые являются идемпотентными. Как дополнительный плюс получаем отсутствие возможности осуществления SQL-инъекций.
API к хранилищу
Сервер M1 реализован как микросервис на golang и предоставляет RESTful API для сохранения объявлений, скриншотов, а также для чтения данных выводимых на странице сервиса поиска объявлений. Нет причин использовать какие-либо фреймворки или внешние библиотеки для реализации RESTful на golang, поэтому используются только стандартные библиотеки, кроме одной:
import (
"database/sql"
"encoding/json"
"net/http"
_ "github.com/go-sql-driver/mysql"
)
Обрабатываются GET и POST запросы от клиентов и вызываются соответствующие хранимые процедуры БД DB1, DB2.
Загрузчики объявлений
Объявления загружаются с сайта Авито загрузчиками S(1)-S(N), написанными на Java с использованием библиотеки Selenium WebDriver. Получив с Авито атрибуты объявления и скриншот, загрузчик обращается к серверу M1 для передачи данных. Также реализована обратная связь для управления загрузчиками, которые периодически опрашивают сервер M1 на предмет команд, например «стоп», «старт».
Captcha
Для разгадывания капчи, которую иногда запрашивает Авито, существует сервер C1, по аналогии с сервером M1 реализованный на golang и предоставляющий RESTful API. Разгадывание капчи осуществляется двумя способами:
- с помощью сервиса rucaptcha.com
- вручную в приложении для Android
Для связи с rucaptcha.com используется их API. Для ручного разгадывания используется написанное на Java приложение Android, которое выводит изображение и принимает ответ. Сервер C1 принимает решение о перенаправлении капчи на rucaptcha.com или в приложение Android в зависимости от количества запросов, накопившихся в очереди. Получив разгадку капчи, сервер C1 отправляет ответ запросившему его загрузчику.
Мониторинг
По аналогии с приложением Android для разгадывания капчи, существует приложение для мониторинга. Приложение Android для мониторинга обращается к серверу M1, который в свою очередь обращается к БД где агрегируются логи, на основе которых можно судить о количестве загруженных объявлений, сбоях в работе и т.п.
Заключение
Дальнейшее развитие сервиса может быть таким:
- Создание загрузчиков для других досок объявлений, что позволит делать кросс-поиск всех объявлений одного продавца
- Использование комплекса для обработки других данных, например загрузка из соцсетей для поиска всех постов по имени аккаунта
И в том и в другом случае разработке подлежат только загрузчики, которые легко интегрируются с серверами M1 и C1. Доработка других частей системы не потребуется.
Если сделать API серверов M1, C1 публичным с авторизацией и добавить в структуру БД ключевое поле для разделения по клиентам, то можно предоставлять услугу по хранению данных и обработке капчи как SaaS. Данные клиента можно хранить в BLOB в виде JSON, доработав при этом БД с хранимыми процедурами и интерфейс API.
Что можно сказать о выбранных технологиях:
- MySQL — классика жанра, без комментариев
- Java — загрузчики можно запускать на кофеварках и холодильниках
- golang — очень быстро разрабатываются микросервисы, легко деплоится путем копирования единственного бинарника на сервер
Буду признателен за комментарии и обсуждение.
Комментарии (45)
KlimovDm
14.12.2016 17:51При такой динамике хранилище по праву можно будет относить к Big Data.
innodb_buffer_pool_size = 3G
Как то не стыкуется.smplpro
14.12.2016 22:17Разве Big Data определяется именно размером кучи под данные, а не в целом объемами?
KlimovDm
14.12.2016 22:41Я не об определении Big Data, а о настройках mysql сервера, которые вы отдельно выделили. Думаю, что эта старая статья Петра Зайцева более точно пояснит мою мысль. Объем всех ваших данных в innodb таблицах явно больше 3Gb. И оперативной памяти на вашем DB сервере явно больше 4Gb.
smplpro
14.12.2016 22:47Спасибо за ссылку, почитаю и подумаю что ответить.
Данных именно по объявлениям без учета скриншотов в данный момент >50Gb, а оперативки 4 Gb. Понятно, что хранить все данные в оперативке нереально в данный момент.
smplpro
14.12.2016 23:15В части использования пула, такие данные, то есть видно, что
You need buffer pool a bit (say 10%) larger than your data (total size of Innodb TableSpaces) because it does not only contain data pages – it also contain adaptive hash indexes, insert buffer, locks which also take some time.
указанные данные вмещаются в пул с большим запасом
KlimovDm
15.12.2016 08:29В принципе 4Gb оперативной памяти на сервере DB всё объясняет.
Что касается данных, то в пул они как раз не умещаются (при условии, что у вас данные >50Gb). На график особо не глядите, при такой нагрузке использование буфера может увеличиваться очень медленно. Проведите нагрузочное тестирование — получите другую картину.smplpro
15.12.2016 22:23имел в виду эти данные:
You need buffer pool a bit (say 10%) larger than your data (total size of Innodb TableSpaces) because it does not only contain data pages – it also contain adaptive hash indexes, insert buffer, locks which also take some time.
KlimovDm
16.12.2016 00:38Теперь вообще не понимаю, о чем вы. Ну, допустим, индексы/блокировки по размеру помещаются в пул. И что? Вы умеете класть их в пул отдельно от данных? В тексте ясно написано, что размер пула желательно задавать немного больше общего размера данных, так как туда попадают еще индексы и т.д… То, что эти данные по размеру умещаются в пул — вам ничего не дает, кроме теоретической констатации самого факта.
smplpro
16.12.2016 11:32В любом случае не понимаю, где можно взять 100, 200,… 500,… 1000Гб оперативной памяти для размещения всех данных в пуле с учетом их непрерывного поступления. Так что рекомендацию автора также можно считать чисто теоретической в данном случае
KlimovDm
16.12.2016 11:49В данном случае (когда у вас 4 Гб оперативки и отсутствие нагрузки) — конечно. А общем случае — такие задачи решаются не в лоб (добавляя оперативку), а архитектурно. Кластеризация, распределенные данные и т.д. и т.п.
marenkov
14.12.2016 18:43+1Вступительная часть статьи вводит в заблуждение — полный тест имеет мало общего со вступлением. Вы бы хотя бы написали, как будете бороться с мошенниками на своем сайте. Показывать все объявления продавца в этом не поможет.
smplpro
14.12.2016 22:19Смысл в том, что если по ссылке на объявление либо номеру телефона нашлось, допустим 5 топовых видеокарт в разных регионах, то очевидно, что здесь что то не чисто. Если же нашлось много объявлений, например про коллекционирование или детскую одежду,- то похоже на правду
fatum2996
14.12.2016 22:19Вбил ссылку на объявление, ничего не нашлось. Хотя с этим номером телефона есть еще объявления. По номеру телефона нашлось несколько объявлений, но не все.
smplpro
14.12.2016 22:20Для любительского проекта неплохо, что «нашлось несколько объявлений», так как не всегда хватает мощностей, чтобы успевать за всеми объявлениями
smplpro
14.12.2016 22:24Хотелось бы услышать комментарии по архитектуре и технологиям, а не насчет того, что кто-то не нашел все объявления
HellMaster_HaiL
14.12.2016 22:31+1Если честно, ожидал что речь пойдет об каком либо плагине для браузера, который при открытии страницы с объявлением рядом с именем или аватаркой автора большими красными буквами пишет слово «мошенник» и ссылку на пруфы…
smplpro
14.12.2016 22:40Технически плагин к браузеру реализуем, так как все данные есть в БД, но понимать сомнительность объявлений возможно только косвенно непосредственно пользователю, по признакам указанным в комментарии выше
hard_sign
15.12.2016 10:45Неудобно, что при выдаче объявлений нет пометки о том, что объявление снято с продажи. Если у человека 80 объявлений, это подозрительно. Но если из них 5 активных — то это нормально :)
smplpro
15.12.2016 10:50Анализировать объявления повторно на предмет снятия с продажи требует дополнительных мощностей.
В настоящий момент загрузка однократная — берутся аттрибуты и картинка и больше к этому объявлению не возвращаемся
avallac
15.12.2016 10:46А где картинки то?
smplpro
15.12.2016 10:48Картинки есть, но не все.
Это связано с тем, что загрузка картинок писалась после намного позже после запуска загрузчика, а также время от времени останавливалась из-за превышения объема диска. Опытным путем был получен подходящий размер картинок, так что со временем картинки будут для каждого объявления
Hush
15.12.2016 11:15Насколько знаю, авито банит айпишники за особый и массовый интерес к телефонам. Как обходили этот момент? Закупили проксей?
smplpro
15.12.2016 12:37Это тема для отдельной статьи как с помощью API VPS-провайдера (DigitalOcean) удалять и создавать новые сервера взамен старых с забаненными адресами.
В целом вы правы — забаненные адреса уже нельзя использовать.
Прокси не использовал, слишком сложно для меняhakastein
15.12.2016 13:45А поднять тор? Поднимается и настраивается на раз два, а потом юзается как обычная прокся.
mapatka
15.12.2016 16:59Авито, вроде, через тор забанено? Нет?
roversochi
27.03.2017 09:51Достал 28BYJ, запустил. Шумность действительно у него изумительная. Однако маловата скорость и крутящий момент. Попробую переделать его в биполярник, возможно тогда мотора хватит.
rikert
15.12.2016 19:20
> как с помощью API VPS-провайдера (DigitalOcean) удалять и создавать новые сервера взамен старых с забаненными адресами
Когда то такой жемудакхороший человек парсил авито так что его забанили, теперь это адрес моего сервера, что особенное примечательно этот блок действует уже больше года т.е. авито никогда не разбанит этот адрес.smplpro
15.12.2016 22:20Сообщите, пожалуйста:
* ваш IP-адрес (его можно посмотреть на yandex.ru/internet);
* название интернет-провайдера;
* город проживания
и мы с хабра-сообществом подумаем, чем вам можно помочь
capitannemo
16.12.2016 11:09Еще один парсер авито.
Телефон кстати легко получить в текстовом виде.
На 1С такое делал )
ИМХО делать копию авито с более удобным поиском — не потянет движок.
А вот локальную базу выборки объявлений по интересующей области — самое то.
Так что делайте на денвере с открытым кодом и будет вам щастяsmplpro
16.12.2016 11:29Как я уже писал, найденные мной парсеры авито, предлагают какие-то нереальные цены (цитаты с их сайтов):
- -25% на первые 3 месяца,
2400 руб.1800 руб. - Стоимость одной лицензии программы XXX: 550
1350рублей, лицензия безсрочная! - Цена на подключение к API 3000 руб. в месяц (30 дней)
- Продаю навороченный парсер (граббер) объявлений с avito.ru (авито.ру) Цена 1 лицензии — 2500 рублей + комиссия за перевод.
- Автоматическая выгрузка объявлений авито.
Стоимость программы 700р.
Отсюда становится понятен баттхёрт недовольных комментаторов: «как же так? бесплатный конкурент, чёрт бы его побрал»
Также доставляют незатейливые просьбы от открытии, казалось бы тривиального, кода- -25% на первые 3 месяца,
Alexufo
16.12.2016 16:42Кое что про авито. Я не понимаю, почему они банят, при указании телефона как копии оригинала.
Получается они поощряют мошенников.
Так что имейте ввиду, с обьявами еще немного сложнее.
rikert
> собственного сервиса sravnito.ru
Вы хотели сказать «собственного паразитирующего на авито сервиса sravnito.ru».
> с простым интерфейсом и бесплатным доступом
и бесплатной рекламой Google Adwords
smplpro
Паразитируют, когда просят 300 рублей за «пробивку».
Когда все бесплатно с рекламой для поддержки оплаты vps — не считаю что это так
LeReve
И чем же он вас паразитировал? Комментарий вида: лишь бы придраться к чему нибудь.
Aionoff
Что плохого в бесплатной рекламе Google Adwords?