В этой статье, на примере моей задачи, рассмотрим, как можно извлечь большой объем данных с сайта ГИББД и с помощью какого инструмента. Это может быть полезно для финансовых компаний, которые принимают автомобили в качестве залога. Итак, мне необходимо было получить информацию о владельцах и периодах владения автомобилями, чтобы определить были ли изменения в конкретном периоде. Данная информация есть на официальном сайте ГИБДД.рф. На входе было дано 70 тысяч VIN номеров автомобилей, по которым и возможно было сделать эту выгрузку.
Поскольку я ранее не занимался парсингом, то решил проанализировать различные Интернет-ресурсы для поиска необходимого алгоритма, однако, для поставленной мне задачи отсутствовало готовое решение, в связи с чем мне пришлось делать всё самому. В ходе анализа я обнаружил библиотеку «requests» для передачи POST запросов, но понял, что она не подходит, поскольку на сайте «ГИБДД.РФ» есть элементы JS, а значит, что VIN номер не передать через адресную строку.
В ходе дальнейшего поиска решения для поставленной задачи я обнаружил библиотеку «Selenium», которая позволяет имитировать действия пользователя на сайте, в том числе с элементами JS.
Для ее установки используется команда: -pip install selenium
.
При использовании браузера «Google Сhrome» для работы с вышеуказанной библиотекой необходима имеющаяся в свободном доступе программа «Webdriver Сhrome».
Проблемы:
На мой взгляд, особую сложность при парсинге подобных сайтов вызывает изучение вариантов различного поведения самого сайта при отправке запросов. В моём случае – это появление видеорекламы (которое можно закрыть спустя определенное время), либо фоторекламы (которое проходит само), то есть своеобразный аналог капчи. Проблематика заключалась в необходимости анализа периодичности появления рекламы, ее разновидности и способах обхода вышеуказанной рекламы.
Алгоритм действий:
Импортируем все необходимые библиотеки:
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, с чем не справляются другие библиотеки для парсинга. Достаточно простой для написания код.
Комментарии (8)
igorzakhar
21.03.2022 15:53Открывается новое окно webdriver, после чего запускается сайт со следующим содержанием:
После этого предложения, возможно, должен быть какой-то скриншот, но в статье его нет.
DrunkenMasrer
22.03.2022 10:52Скриншот перед текстом, видимо, съехал
igorzakhar
22.03.2022 10:57Кстати, да, упустил этот момент. Уже исправлено.
eliseev_pv
23.03.2022 19:30requests - не получиться, на сайте reCaptcha v3 которое и при каждом запросе анализируются отправленные данные и если score < 0.7 то в ответе будет информация что вы не прошли проверку. Так что за 10 минут распарсить 70 тысяч винов я уверен что будет не так просто и не за 10 мин тем более...
kalombo
Могут. Достаточно посмотреть, что с одного ip очень много запросов на разные VIN-ы
Зайдите в браузер, откройте инструементы разработчика, вкладку сеть, нажмите на кнопку проверки VIN. Увидите, что отправляется запрос с url https://сервис.гибдд.рф/proxy/check/auto/history и телом {vin: 'vin', checkType: 'history'} Повторяете такой же запрос с помощью requests или может scrapy, распараллелив запросы при этом, чтобы они запускались одновременно, и проверяете все 70 тысяч VIN-ов за 10 минут, а не за:
NewTechAudit Автор
По ip не забанили, думаю им не выгодно, так как я просматриваю рекламу при каждом её появлении. Может позже это пофиксят. Через request вполне могут забанить, но стоит попробовать, спасибо!