Как я спарсил WebGL карту с Федерального сайта +1




Пишу статью для тех, у кого появилась похожая задача, но они не знают как ее решать. Не уверен что мой способ работает везде (недостаточно опытен), но считаю, что если бы я увидел эту статью, загуглив "как парсить webgl" или что-то типо такого, я бы потратил на несколько часов жизни меньше на поиск решения.

Задача (и как у меня НЕ получилось ее решить)

Мне пришел заказ от знакомого: он мне отправляет QR код, я парсю ссылку с куарки, и при переходе по этой ссылке в конце страницы будет карта. На ней нарисован маршрут.

Мне надо спарсить этот маршрут так, чтобы заказчик его мог использовать при составлении идентичного маршрута в своем специальном приложении-навигаторе (или что-то такое).

Парсинг QR-ки был реализован очень легко:

def qrParser(pathToImage):
    decoded = decode(Image.open(pathToImage))
    url = (decoded[0].data).decode("utf-8")
    return url

А дальше пошла жара.


Я сначала вообще представить не мог как это сделать. Так как это WebGL приложение, в полном подгруженном HTML-JS коде ничего не было. Там это выглядит просто как обьект типа холст (canvas), в который никаких ссылок, яваскриптов не передается. Даже проштудировав многие parent элементы, а также поискав по ключевым классам, ничего путного найдено не было.

В подгружаемых js скриптах тоже ничего понятного обнаружено не было

Сидел я так часа 2-3.

Решение (которое сработало в моем случае)

Далее я решил поискать в network. Это та вкладочка, где показываются по идее все get запросы.

Перезагрузил страницу, долистал до карты. Сбросил историю всех завершенных запросов. И начал отдалять и водить картой туда-сюда. Появились новые запросы, которые должны были подгружать данные карты. Я посмотрел что там: и да, о чудо! В пришедших json'ах были переданы всякие объекты по типу water, grass, road и тд. Я понял, что иду в правильном направлении.

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

Исходная ссылка: "https://urm.safe-route.ru/check?uuid=ID_HERE"
Исходная ссылка: "https://urm.safe-route.ru/check?uuid=ID_HERE"

Он в аргументах содержал абсолютно точный id, который передавался в исходной ссылке, а также имел ключевое слово api, которое также меня зацепило и я решил посмотреть что же интересного мне возвращает этот запрос. БИНГО. Он возвращал json, в глубинах которого передавались точные координаты (вида Долгота/Широта) всех начальных и конечных точек всех линий, из которых строился графически маршрут, что нам и нужно было.

Вот и все, задача сводится к отправке запроса по ссылке "https://urm.safe-route.ru/api/claim/resolution/check?uuid=ID_HERE". Мы получаем json, в котором передается много всяких параметров, но мы выцепляем нужный нам раздел ["geom"]["features"][номеркоординаты]["geometry"]["coordinates"] и забираем из него списки списков координат, переворачиваем координаты, так как они передаются в виде Долгота/Широта, а правильно будет Широта/Долгота и дампим в json

Вот и все, задача сводится к отправке запроса по ссылке "https://urm.safe-route.ru/api/claim/resolution/check?uuid=ID_HERE". Мы получаем json, в котором передается много всяких параметров, но мы выцепляем нужный нам раздел ["geom"]["features"][номеркоординаты]["geometry"]["coordinates"] и забираем из него списки списков координат, переворачиваем координаты, так как они передаются в виде Долгота/Широта, а правильно будет Широта/Долгота и дампим в json.

Исходный response, который нам приходит (там сверху еще много параметров, нам не нужных)
Исходный response, который нам приходит (там сверху еще много параметров, нам не нужных)
Преобразованный json, в котором каждый элемент это координата точки маршрута (с превеликой точностью в пару метров)
Преобразованный json, в котором каждый элемент это координата точки маршрута (с превеликой точностью в пару метров)
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0"}
response = requests.get(url, headers=headers, verify=False) 
#url мы здесь передаем уже вида "https://urm.safe-route.ru/api/claim/resolution/check?uuid=ID_HERE"
#ведь именно он возвращает нам json с координатами
count = 0
answer = {}

for i in range(len(response.json()["geom"]["features"]) - 1, 0, -1):
  geometries = (response.json()["geom"]["features"][i]["geometry"]["coordinates"])
  for j in geometries:

    j = j[::-1]
    answer.setdefault(count, j)
    count += 1

json_object = json.dumps(answer, indent=4)
with open("coords.json", "w+") as file:
  file.write(json_object)

verify=False при get запросе был обязателен, без этого сайт не пропускал по причине отсутствия SSL сертификата у python-инициатора запроса.

Надеюсь, кому-то это сэкономило часы жизни.

P.S. Любые замечания и вопросы приветствуются и будут просмотрены автором.




Комментарии (5):