CoderCastrov logo
CoderCastrov
Парсер

Как занять первое место в рейтинге интерактивных карт Didactalia + вызов для вас

Как занять первое место в рейтинге интерактивных карт Didactalia + вызов для вас
просмотров
10 мин чтение
#Парсер

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

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

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

Примечание: если вам не интересен весь процесс того, как я пришел к решению, вы можете сразу перейти к разделу "Решение".

Анализ проблемы

Первую карту, которую я решил, была Карта стран Европы в формате "Где это?". В настоящее время она доступна по следующему адресу: https://mapasinteractivos.didactalia.net/comunidad/mapasflashinteractivos/recurso/mapa-de-europa-paises/d2bfae17-7658-4d3d-bce3-d4d5f316c52b

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

Постановка задачи

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

пока есть страны:
    1. прочитать название страны
    2. найти страну на карте
    3. нажать на страну

Это всего лишь несколько шагов, поэтому кажется, что это несложно достичь, но прежде чем приступить к работе, мне нужно было решить, как я это буду программировать.

Первая мысль, которая пришла мне в голову, была: "если это игра в вебе, то мне необходим браузер", поэтому у меня возникли следующие варианты:

  • Использовать Selenium (фреймворк, который позволяет управлять браузером как роботом)
  • Использовать JavaScript скрипт

Первый вариант более визуальный, но требует программирования на языке, таком как Python или Java, наличия определенных файлов для каждого браузера и т. д., поэтому он более сложный.

Второй вариант, напротив, позволяет этот скрипт выполнять любому человеку с браузером, что, очевидно, упрощает вещи.

Не задумываясь слишком долго, я выбрал второй вариант.

Программирование сценария

Перед тем, как написать хоть одну строку кода, я первым делом проверил, что карта не была создана с помощью Flash или чего-то, с чем JavaScript не может взаимодействовать. К счастью, это не так.

1. Чтение страны

Было достаточно быстро заметить, что "чтение страны" не представляет никакой сложности, просто взглянув на инспектор.

Для тех, кто знаком с CSS, JavaScript и jQuery, будет ясно, что на веб-странице нам это сделали очень просто, так как название страны, которую мы должны найти, явно помечено классом objetivo.

Таким образом, "чтение страны" будет таким простым, как выполнение:

Шаг 1: Чтение страны

2. "Найти страну"

Если мы проанализируем исходный код, мы увидим, что карта представлена элементом <svg>, а каждая "страна" (на самом деле каждый "полигон") представлена элементом <g>, который в свою очередь содержит элемент <path>.

Paso 2: “Encontrar el país”

На первый взгляд я не смог найти никаких указаний, которые связывали бы каждый элемент <g> с определенной страной. Если бы это было так, например, если бы был класс или атрибут, который указывал на страну, работа была бы такой же простой, как в первом шаге. Однако здесь у меня не было никаких подсказок, которые помогли бы мне связать каждый полигон с названием страны.

После небольшого исследования и обновления браузера я заметил, что порядок этих элементов <g>** всегда один и тот же**: сначала Албания, затем Австрия, затем Бельгия... и так далее. Это стало ключом к определению страны, потому что я мог создать отображение или словарь (в терминологии JavaScript называется объектом), между позицией каждого элемента и названием страны. Вот как это можно сделать:

const mapaPaises = {
  0: "Албания",
  1: "Австрия",
  2: "Бельгия",
  // и так далее
};

const poligono = document.getElementsByTagName("g")[posicion];
const nombrePais = mapaPaises[posicion];

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

3. "Кликнуть по стране"

Я был очень рад, когда смог выполнить первые два шага так быстро, но моя радость не продлилась долго, когда я обнаружил, что при попытке кликнуть на элементы <g> или <path> с помощью JavaScript игра не продвигалась.

Я пробовал много вариантов, чтобы заставить игру реагировать, но не смог добиться успеха. Я пробовал использовать country_element.click(), country_element.trigger('click') и т.д. на выбранном элементе, но это не вызывало никакой реакции. Я перерыл множество веб-страниц (особенно StackOverflow), пытаясь найти способ заставить игру правильно интерпретировать мой клик, но ничто не менялось в игре, даже когда я самостоятельно кликал по ней с помощью указателя и игра реагировала, как положено.

После долгого времени (более 1 часа, чтобы быть точным) я заметил, что класс элемента <path> был leaflet-clickable. Я решил найти информацию о Leaflet в интернете и обнаружил, что это библиотека JavaScript для создания интерактивных карт. Тогда мой поиск немного изменился, и я начал искать более конкретно, как симулировать клик по элементу Leaflet.

Поиск был долгим, и после прочтения нескольких веб-страниц, благодаря которым я немного понял, как работает Leaflet, я наконец-то нашел вопрос на StackOverflow, который искал: https://stackoverflow.com/questions/11421127/how-to-trigger-events-on-leaflet-map-polygons

Ни один из ответов не отвечал точно на мой вопрос, но из нескольких ответов я понял несколько вещей: во-первых, в Leaflet есть объект JavaScript с именем map, который содержит layers (слои), и, вероятно, именно эти слои реагируют на события клика. Кроме того, для доступа к этим слоям я должен использовать map._layers, и, наконец, для "кликанья по стране" я должен использовать .fire('click') на соответствующем слое.

Я провел несколько тестов и убедился в том, что 1) слои начинаются с номера 23, 2) порядок стран не полностью алфавитный (по крайней мере на испанском языке) и 3) некоторые страны состоят из нескольких полигонов или слоев (например, Великобритания, Россия, Италия...).

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

“mouseover” на слое

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

После проверки каждого слоя я получил полностью функциональный код, подобный этому:

С его помощью я мог выполнить 3 шага: "прочитать страну", "найти страну" и "кликнуть по стране".

Мне пришлось сделать еще несколько тестов, обращая внимание на то, что выводится в консоль, так как некоторые страны были записаны по-другому, и мне пришлось их изменить. Например, Ватикан был записан как Государство Ватикан и Сербия как Республика Сербия.

0. "Повторение процесса"

Существует несколько способов повторить процесс в JavaScript. Логичным вариантом было бы использовать событие onChange для элемента, содержащего название страны, чтобы код повторялся при изменении этого значения. Однако гораздо более простым вариантом является повторение процесса через определенный интервал времени с использованием функции setInterval(). Это позволяет изменять скорость выполнения скрипта и решать карту за более короткое или длинное время.

Решение

Обновление 18/11/2020: Похоже, что_ сайт изменился _и теперь слои не всегда начинаются с одного и того же номера. К счастью, это не так сложно исправить ;) Мы просто должны извлечь первый допустимый слой и обновить отображение стран. Решение выглядит так:

Теперь, начиная с этого момента, предыдущий контент:

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

С помощью этих строк кода можно автоматически решать карты в рекордно короткое время. Чтобы использовать его, достаточно вставить код в консоль браузера и начать игру.

Применение решения к другим случаям

Тот же код работает для любой карты на веб-сайте типа "Где это?" с изменением только значений переменной solutions. Так, для решения карты Балеарских островов достаточно заменить значение solutions на следующее:

solutions = {
    "Mallorca": "mallorca",
    "Menorca": "menorca",
    "Ibiza": "ibiza",
    "Formentera": "formentera"
}

Для решения карты автономных сообществ Испании значения будут следующими:

solutions = {
    "Андалусия": "andalucia",
    "Арагон": "aragon",
    "Астурия": "asturias",
    "Балеарские острова": "baleares",
    "Канарские острова": "canarias",
    "Кантабрия": "cantabria",
    "Кастилия и Леон": "castilla_y_leon",
    "Кастилия-Ла Манча": "castilla_la_mancha",
    "Каталония": "cataluna",
    "Эстремадура": "extremadura",
    "Галисия": "galicia",
    "Мадрид": "madrid",
    "Мурсия": "murcia",
    "Наварра": "navarra",
    "Страна Басков": "pais_vasco",
    "Ла Риоха": "la_rioja",
    "Валенсия": "valencia",
    "Эстремадура": "extremadura",
    "Кантабрия": "cantabria",
    "Кастилия и Леон": "castilla_y_leon",
    "Кастилия-Ла Манча": "castilla_la_mancha",
    "Каталония": "cataluna",
    "Эстремадура": "extremadura",
    "Галисия": "galicia",
    "Мадрид": "madrid",
    "Мурсия": "murcia",
    "Наварра": "navarra",
    "Страна Басков": "pais_vasco",
    "Ла Риоха": "la_rioja",
    "Валенсия": "valencia"
}

А для карты стран Северной Америки решение будет следующим:

solutions = {
    "Канада": "canada",
    "США": "estados_unidos",
    "Мексика": "mexico",
    "Багамы": "bahamas",
    "Куба": "cuba",
    "Ямайка": "jamaica",
    "Гаити": "haiti",
    "Доминиканская Республика": "republica_dominicana",
    "Гватемала": "guatemala",
    "Белиз": "belice",
    "Сальвадор": "el_salvador",
    "Гондурас": "honduras",
    "Никарагуа": "nicaragua",
    "Коста-Рика": "costa_rica",
    "Панама": "panama",
    "Колумбия": "colombia",
    "Венесуэла": "venezuela",
    "Гайана": "guyana",
    "Суринам": "surinam",
    "Эквадор": "ecuador",
    "Перу": "peru",
    "Бразилия": "brasil",
    "Боливия": "bolivia",
    "Чили": "chile",
    "Аргентина": "argentina",
    "Уругвай": "uruguay",
    "Парагвай": "paraguay"
}

Заключение

У меня возникает много способов подойти к этому проекту. Я мог бы попытаться обмануть веб-сайт, подделав вызовы к API, чтобы время шло медленнее или вообще не двигалось и т. д. Но с самого начала я поставил перед собой цель быть законопослушным и не взламывать систему, обманывая ее или используя обратную инженерию. Вместо этого я решил решить саму игру, выбирая на карте страны, которые мне требуются, с той лишь разницей, что это делает компьютер.

Также стоит отметить, что возможно решить игру за меньшее время, изменив значение миллисекунд в setInterval, чтобы делать шаги быстрее. В случае с картой Балеарских островов, например, можно достичь времени "0" секунд. Однако я несколько раз заметил, что, делая это, мое время не регистрировалось в рейтинге, вероятно, потому что значение было слишком низким или из-за другого внутреннего механизма игры. В любом случае, мне было достаточно иметь время ниже первых позиций на карте стран Европы.

Также стоит отметить, что, увидев, что это возможно сделать гораздо быстрее, чем остальные участники, это может означать несколько вещей:

В заключение, хотелось бы отметить, что, как было описано в предыдущем разделе "Применение решения к другим случаям", существуют более простые случаи, такие как страны Северной Америки или Балеарские острова. Было бы более разумно начать с этих случаев, так как они требуют меньше времени и проще, но осознав это, я предпочел с самого начала решить карту, которая породила все остальное.

Твоя очередь

После того, как я занял первое место на карте Европы, я решил решить другие типы карт, в частности карты, на которых показана выбранная страна/область, и нужно выбрать название страны/области. На сайте Didactalia они классифицируются как "Как это называется?".

Я предлагаю тебе попробовать решить следующую карту: "Автономные области Испании. Как это называется?_", и занять одно из первых мест: https://mapasinteractivos.didactalia.net/comunidad/mapasflashinteractivos/recurso/comunidades-autonomas-de-espaa/9d65ad7b-1679-44ad-951f-e0a275288cde

Я объяснил тебе, шаг за шагом, как я сделал это для другого типа карты, и гарантирую, что это возможно, потому что я сам написал скрипт, способный занять первое место.

Решение_“Автономные области Испании. Как это называется?”

Обновление: Поскольку никто не отправил мне электронное письмо, и никто не поставил "лайк" (хотя были некоторые публикации в Linkedin), я думаю, что интерес к этому вызову отсутствует 😅. Возможно, я написал слишком длинную статью или недостаточно хорошо ее продвигал. В любом случае, я предпочитаю не беспокоиться о публикации кода, который его решает. Я не думаю, что это случится, но если кому-то это нужно, он может оставить комментарий или связаться со мной напрямую. Всем привет!

Чтобы это не было вызовом, который просто исчезнет, я установлю срок: 29 февраля 2020 года, и после истечения срока вы сможете увидеть мое решение здесь.

Я приглашаю всех, кто хочет попробовать, чтобы, если они смогут, поделиться своим решением только со мной (по крайней мере, если срок еще не истек), отправив электронное письмо на hackthatmap@gmail.com. Все, кто сможет, получат упоминание в этом посте, небольшой сюрприз и, самое главное, удовлетворение от достижения этой цели.

Ты смеешься?


Переведи введенный markdown код с английского на русский. Сохрани оригинальную разметку markdown и изображения на своих местах. Вместо слов скрэпинг/скрапинг/скрапер/скрэпер используй парсинг/парсер/парсить. Возвращай только markdown код, без дополнительного текста