Парсинг сайта ГИБДД

Всем привет! Сегодня хочу рассказать вам, как можно извлечь большой объем данных с сайта ГИББД и с помощью какого инструмента. Это может быть полезно для финансовых компаний, которые принимают автомобили в качестве залога.

Итак, мне необходимо было получить информацию о владельцах и периодах владения автомобилями, чтобы определить были ли изменения в конкретном периоде. Данная информация есть на официальном сайте ГИБДД.рф. На входе было дано 70 тысяч VIN номеров автомобилей, по которым и возможно было сделать эту выгрузку.

Парсинг сайта ГИБДД

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

В ходе анализа я обнаружил библиотеку «requests» для передачи POST запросов, но понял, что она не подходит, поскольку на сайте «ГИБДД.РФ» есть элементы JS, а значит, что VIN номер не передать через адресную строку.

В ходе дальнейшего поиска решения для поставленной задачи я обнаружил библиотеку «Selenium», которая позволяет имитировать действия пользователя на сайте, в том числе с элементами JS.

Для ее установки используется команда: - pip install selenium.

При использовании браузера «Google Сhrome» для работы с вышеуказанной библиотекой необходима имеющаяся в свободном доступе программа «Webdriver Сhrome».

Проблемы:

На мой взгляд, особую сложность при парсинге подобных сайтов вызывает изучение вариантов различного поведения самого сайта при отправке запросов. В моём случае – это появление видеорекламы (которое можно закрыть спустя определенное время), либо фоторекламы (которое проходит само), то есть своеобразный аналог капчи. Проблематика заключалась в необходимости анализа периодичности появления рекламы, ее разновидности и способах обхода вышеуказанной рекламы. Алгоритм действий:

  1. Импортируем все необходимые библиотеки:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import pandas as pd import time from bs4 import BeautifulSoup from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC

Задаём опции для webdriver, запускаем его и переходим на нужную нам страницу проверки авто:

option = Options() option.add_argument("--disable-infobars") browser = webdriver.Chrome('C:\webdr\chromedriver.exe',chrome_options=option) # 'https://xn--90adear.xn--p1ai/check/auto' – ГИБДД.РФ browser.get('https://xn--90adear.xn--p1ai/check/auto')
Парсинг сайта ГИБДД

Открывается новое окно webdriver, после чего запускается сайт со следующим содержанием:

Затем осуществляем поиск нужных нам элементов через код страницы (правой кнопкой мыши – посмотреть код). В нашем случае, это элемент с вводом VIN номера и кнопка с запросом.

Парсинг сайта ГИБДД

Находим идентификатор элемента id=”checkAutoVIN” и имя class =”checker”. Осуществляем поиск данных элементов, затем вставляем нужный VIN в соответствующую строку:

elem = browser.find_element(By.ID, 'checkAutoVIN' ) elem.send_keys('5GRGXXXXXXX129289' + Keys.RETURN)

и нажимаем на кнопку:

share = browser.find_element(By.CLASS_NAME, 'checker' ) share.click()

Первая проблема – появляется видеореклама, при просмотре которой через 4 секунды появляется крестик справа.

Парсинг сайта ГИБДД

Находим код этого крестика на странице

Парсинг сайта ГИБДД

С помощью функции “WebDriverWait” ждём появления этого крестика и нажимаем на него, если это видеореклама.

WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.CLASS_NAME, "close_modal_window"))).click()

В случае появления фоторекламы вышеуказанный код выдаст ошибку, которую я решил с помощью конструкции try – except (она пригодится ещё много раз).В случае появления фоторекламы необходимо дождаться ее самопроизвольного закрытия:

Парсинг сайта ГИБДД
time.sleep(4)

В дальнейшем для парсинга страницы необходимо воспользоваться библиотекой “ BeautifulSoup”.

Парсинг сайта ГИБДД
elements = soup.find_all(attrs={"class":{"ownershipPeriods-from", "field vehicle-model", "field vehicle-year", "ownershipPeriods-to", "simplePersonType"}}) elements1=soup.find_all(attrs={"class":{"ownershipPeriods-from"}}) elements2=soup.find_all(attrs={"class":{"ownershipPeriods-to"}}) elements3=soup.find_all(attrs={"class":{"simplePersonType"}}) elements4=soup.find_all(attrs={"class":{"field vehicle-model"}}) elements5=soup.find_all(attrs={"class":{"field vehicle-year"}})

Для заполнения полученной информацией создадим пустую таблицу:

df = pd.DataFrame({'VIN': [], 'С': [], 'По': [], 'Владелец': [], 'Марка или модель':[], 'Год выпуска':[]})

Учитываем, что периодов владений автомобилем может быть несколько. В таком случае в каждом из найденных элементов будет несколько записей (элементы сохраняются в виде списка). Каждый новый период добавим с тем же VIN номером в новую строку:

for j in range(len(elements1)): df1 = df1.append({'VIN': '5GRGXXXXXXX29289', 'Марка или модель':elements4[j].text, 'Год выпуска':elements5[j].text, 'С': elements1[j].text, 'По': elements2[j].text, 'Владелец':elements3[j].text}, ignore_index=True)

На первый взгляд кажется, что для решения поставленной задачи необходимо только запустить цикл по всем VIN номерам, однако, обнаружились следующие проблемы. После проверки 5 VIN номеров сайт начинает сильно затормаживаться. Чтобы это избежать, я установил счетчик и когда он достигает значение 5 – обнуляем его и инициализируем webdriver заново. В ходе данной работы я установил, что для корректной работы цикла важно не закрывать предыдущий webdriver (“browser.quit()”), поскольку при его закрытии в следующем проходе цикла возникает ошибка.

Также счётчик помог мне понять, что реклама появляется при первом запросе, при условии, что VIN номер есть в базе ГИБДД. Учитываем указанную информацию и вводим новую переменную «ermes=»», и при парсинге добавляем условие, что, если эта переменная равна ‘По указанному VIN не найдена информация о регистрации транспортного средства.’, то проделываем операцию с пережиданием или закрытием рекламы.

elementserr=soup.find_all(attrs={"class":{"check-space check-message"}}) if elementserr[0].text=='По указанному VIN не найдена информация о регистрации транспортного средства.': ermes=elementserr[0].text

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

while elementserr[0].text=='При получении ответа сервера произошла ошибка.': share.click() time.sleep(4)

Для успешного завершения поставленной задачи остается лишь взять все VIN номера и сделать по ним цикл:

vin=pd.read_excel(r'C:\Users\grvla\Desktop\Парс.xlsx')

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

sh=0 ermes='' for i in vin.vins: if i in df.VIN.unique(): continue

В качестве ключевых выводов хочу отметить:

Разработанный парсинг не могут забанить по ip, поскольку демонстрируется реклама. Мы же просто имитируем деятельность человека на сайте. Его можно запустить сразу на многих компьютерах, что ускорит результат работы. Количество полученных VIN-номеров в день – 7-8 тысяч. То есть на 10 компьютерах 70 тысяч VIN -номеров можно пропарсить за один день.

Библиотека Selenium позволяет имитировать действия пользователя в браузере. Это помогает автоматизировать сбор данных практически с любого сайта, в котором нет капчи. С её помощью возможна работа с сайтами, в которых есть элементы javascript, с чем не справляются другие библиотеки для парсинга. Достаточно простой для написания код.

1616
28 комментариев

селениум ненужен

10
Ответить

А как же капча?

Ответить

В ходе анализа я обнаружил библиотеку «requests» для передачи POST запросов, но понял, что она не подходит, поскольку на сайте «ГИБДД.РФ» есть элементы JS, а значит, что VIN номер не передать через адресную строку.

Вы знаете, по примерам кода, конечно, можно догадаться, что имеется ввиду библиотека для Python, но всё это далеко неочевидно, особенно тому, кто это видит впервые. В результате ваш опыт парсинга довольно сложно использовать кому-либо ещё.

Насчёт POST-запросов..JS, не JS, но в 99% случаев и запрос, и ответ на него передаются в виде POST или GET HTTP-запросов, наверняка всё это приходит в браузер в каком-нибудь JSON, который легко разобрать. Косвенно это подтверждается тем фактом, что в сети есть сайты, которые предоставляют API для доступа к сайту ГИБДД.

Внезапно, немножко документации по взаимодействию с сайтом гибдд.рф есть...на сайте гибдд.рф. Почитайте. Неохота разбираться, но на первый взгляд там всё просто. На HTTP-запрос определённого формата приходят данные в формате JSON.
https://xn--90adear.xn--p1ai/api

В вашем случае я бы начал просмотра кода сайта и подключённых библиотек на JS на предмет отправки запросов, вроде XMLHttpRequest. Наверняка там есть параметры, которые передаются на сервер (вин-номера, например).
Если не поможет, то посмотрел бы содержание HTTP-запросов к серверу и ответов на них. Наверняка где-то там прячутся необходимые данные в JSON. Во многих браузерах есть такая возможность, или в анализаторах пакетов.
Далее уже всё это вставил бы в свой код на питоне (ну или на чём угодно) и анализировать результат.

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

2
Ответить

Комментарий недоступен

1
Ответить

Комментарий недоступен

1
Ответить

Топовым по версии форбс?

2
Ответить

Если действительно не банят, то это просто недочёт сайта. А так просто необходимо юзать фингерпринты, proxy, разные задержки перед запросами и ещё куча всего. И стоит им начать даже фингерпртинт проверять и всё сломается.
А по поводу Js довольно странное суждение. К чему это я так и не понял

Ответить