Пишу статью для тех, у кого появилась похожая задача, но они не знают как ее решать. Не уверен что мой способ работает везде (недостаточно опытен), но считаю, что если бы я увидел эту статью, загуглив "как парсить 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 и тд. Я понял, что иду в правильном направлении.
Поизучав все приходящие с самого начала загрузки страницы запросы, я нашел один выделяющийся:
Он в аргументах содержал абсолютно точный 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.
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)
Alexandroppolus
24.11.2021 14:14+3Довольно классический сюжет парсинга. Комфортный - все данные оказались в отдельном json. Бывает хуже, когда всё зашито в здоровенный исходный html, особенно если там вокруг хреновая верстка с inline-стилями вместо классов, нагромождениями div, и т.д.
На одной из прошлых работ как раз такими вещами занимался.
unibasil
24.11.2021 15:18Преобразованный json, в котором каждый элемент это координата точки маршрута (с превеликой точностью в пару метров)
Вы не правы, семь знаков после запятой — это околосантиметровая точность (различается по широте). Поэтому даже в Google Maps координаты с шестью знаками после запятой (а в отдельных задачах — например, polyline'ах — даже пять).
И как выше справедливо заметили, формат называется GeoJSON, и файл в таком формате можно непосредственно сразу подключить в виде слоя в любых распространённых движках карт.
Zickam Автор
24.11.2021 20:24На счет околосантиметровой точности у вас правильный вывод, но я, видимо, не совсем правильно выразился. Я имел в виду расстояние между двумя ближайшими точками маршрута, которые мы можем спарсить
Nehc
24.11.2021 15:56Автору читать статью Как спарсить любой сайт, что бы не наступать на те же грабли, и успехов изысканиях! )
zartdinov
Формат называется GeoJSON, я бы его даже не парсил, а дальше везде использовал