[API, Big Data, Data Mining, Интерфейсы, Открытые данные] Парсинг сайта Умного Голосования и новый API на сайте ЦИК
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
13 сентября 2020 года в России прошёл единый день голосования. В некоторых регионах оппозицией была применена стратегия «Умного Голосования», заключающаяся в том, что оппозиционно настроенные избиратели голосуют за единого кандидата, имеющего наивысшие шансы победить представителя от властей.
Процесс отбора кандидатов для «Умного Голосования» уже второй год вызывает дискуссии на тему своей прозрачности. Кроме того, лично меня смущают сложности с подведением итогов стратегии, с которыми могут столкнуться независимые аналитики. Организаторы УмГ не публикуют подробные итоги стратегии, а лишь диаграммы, демонстрирующие сколько оппозиционных кандидатов прошло в региональный парламент.
На сайте «Умного Голосования» нельзя получить список поддержанных кандидатов, указав, например, город и округ. Если кто-то захочет собрать данные по региону, ему предстоит монотонная работа по подбору адресов для каждого округа.
Ни в коем случае не упрекаю разработчиков сайта УмГ, он имеет весь требуемый функционал для реализации стратегии голосования. Но в связи с тем, что в 2019 году никто не занимался сбором и публикацией подробных данных по итогам УмГ (вне московских выборов), на этих выборах я решил взять инициативу в свои руки.
В итоге получилась вот такая сводная таблица. В данной статье я расскажу, как был получен приведённый набор данных, как собиралась информация с сайтов Умного Голосования и нового веб-сервиса ЦИК.
Сайт Умного Голосования
Для начала посмотрим, какие данные мы можем извлечь с сайта «Умного Голосования». На главной странице сайта есть поле для ввода адреса регистрации пользователя. При вводе строки появляется список предложенных адресов в следующем формате:
При выборе одного из предложенных адресов, мы перемещаемся на страницу избирательного участка, к которому прикреплён выбранный адрес:
На странице перечислены выборные кампании, которые проходят на данном участке. Для каждой кампании приведён список кандидатов, за/против которых предлагают проголосовать:
В данном случае мы видим выборы губернатора, для которых УмГ не указало кандидата от оппозиции. Связанно это с тем, что выборы губернаторов проходят в два тура и не имеет значения, за кого из оппозиционных кандидатов проголосуют избиратели.
Также мы видим сразу трёх кандидатов, за которых предлагают проголосовать на выборах в городской парламент. Связанно это с тем, что на выборах в Сочи многомандатные округа.
На всех остальных выборных кампаниях, задействованных УмГ в этом году, были только одномандатные округа.
Заглянем в код страницы и обнаружим, что все описанные данные, собраны в удобном JSON-формате. В элементе с id="__NEXT_DATA__", который используется для отрисовки страницы, есть информация об избирательном участке, о соответствующих выборных кампаниях и кандидатах:
Содержимое __NEXT_DATA__ элемента
SPL
{
"props":{
"pageProps":{
"id":"440384",
"settings":{
"id":1,
"share_photo":"/ganimed-media/share_photo/smartvote_sharepic_1200x628.jpg",
"video_on_main_page":"https://youtu.be/w8gapDGwWMY",
"fake_mode":false,
"title_share":"Объединяемся, чтобы победить Единую Россию",
"text_share":"Мы разные, но у нас одна политика — мы против монополии «Единой России». Всё остальное — математика.",
"telegram_bot_link":"https://tlinks.run/smartvotebot",
"viber_bot_link":"viber://public?id=smartvote",
"facebook_bot_link":"https://facebook.com/umnoegolosovanie/",
"alice_link":null,
"vk_bot_link":null
},
"serverData":{
"commission":{
"id":440384,
"number":"4317",
"address":"354340, Краснодарский край, город Сочи, Адлерский район, улица Богдана Хмельницкого, 24",
"descr":"здание средней школы № 49 им. Н.И. Кондратенко",
"lat":"43.425923",
"lon":"39.920152",
"region_id":26,
"region_intid":"135637827259064320000372513"
},
"campaigns":[
{
"id":26,
"code":"krasnodar-gub-2020",
"title":"Выборы губернатора Краснодарского края",
"is_regional":true,
"ready_date":null,
"district":{
"id":458,
"code":"oik-0",
"name":"0",
"leaflet":""
},
"candidates":[
{
"id":998,
"name":"Кондратьева Вениамина Ивановича",
"share_image":"/elections-api-media/share/26/998.png",
"anticandidate":true,
"self_nominated":false,
"has_won":false,
"has_second_round":false,
"party":{
"title":"Единая Россия",
"antiparty":true
}
}
]
},
{
"id":28,
"code":"krasnodar-sochi-gorduma-2020",
"title":"Выборы в городское собрание Сочи",
"is_regional":false,
"ready_date":null,
"district":{
"id":526,
"code":"oik-2",
"name":"2",
"leaflet":"/elections-api-media/28/526-1334-1335-5385.pdf"
},
"candidates":[
{
"id":1334,
"name":"Киров Сабир Рафаилович",
"share_image":"/elections-api-media/share/28/1334.png",
"anticandidate":false,
"self_nominated":true,
"has_won":false,
"has_second_round":false,
"party":null
},
{
"id":1335,
"name":"Мукаелян Марине Айковна",
"share_image":"/elections-api-media/share/28/1335.png",
"anticandidate":false,
"self_nominated":true,
"has_won":false,
"has_second_round":false,
"party":null
},
{
"id":5385,
"name":"Рябцев Виктор Александрович",
"share_image":"/elections-api-media/share/28/5385.png",
"anticandidate":false,
"self_nominated":false,
"has_won":false,
"has_second_round":false,
"party":{
"title":"КПРФ",
"antiparty":false
}
}
]
}
]
},
"error":null,
"currentUrl":"https://votesmart.appspot.com/candidates/440384"
}
},
"page":"/candidates/[id]",
"query":{
"id":"440384"
},
"buildId":"U8hjaoxZw8TINu-DU_Ixw",
"runtimeConfig":{
"HOST":"https://votesmart.appspot.com"
},
"isFallback":false,
"customServer":true,
"gip":true
}
Для избирательного участка указан номер (number) соответствующей УИК и её идентификатор в базе данных сайта УмГ. Id = 440834 соответствует номеру, который содержится в URL-адресе страницы (/candidates/440834).
Можем ли мы, зная номер УИК и регион, вычислить идентификатор комиссии на сайте УмГ? Я не смог найти очевидную зависимость, так как идентификаторы распределены достаточно хаотично:
Сочи, УИК №4512 -> id = 440834
Сочи, УИК №4513 -> id = 441403
Сочи, УИК №4514 -> id = 1781216
Каким образом собрать список отражений номеров УИК в id страниц? Перебирать и проверять всевозможные идентификаторы от 1 до 2000000 звучит крайне неэффективно, большинство из этих идентификаторов нерабочие.
Но, если у нас есть список адресов, мы можем относительно легко собрать список соответствующих избирательных участков. При вводе строки на начальном экране, с сервера возвращается перечень подходящих адресов вместе с соответствующими идентификаторами комиссий:
Поиск участка по адресу
https://votesmart.appspot.com/api/v1/cik/addresses?query=ADDRESS
- ADDRESS — адрес, желательно в формате «Субъект, город, улица, дом». Также желательно без сокращений «ул.», «д.», так как парсер на сервере плохо с ними справляется
Пример запроса:
https://votesmart.appspot.com/api/v1/cik/addresses?query=Смоленск ленина
Результат запроса
SPL
{
"suggestions":[
{
"value":"Смоленская область, город Смоленск, Промышленный район, Ленина улица",
"data":{
"fullname":"Смоленская область, город Смоленск, Промышленный район, Ленина улица",
"level":"7",
"region_id":69,
"commission_id":null,
"intid":"138474570115456000000347353",
"path":"135637827259064320000359815,135637827259064320000359819,135637827259064320000359820,138474570115456000000347353",
"snippet":"Смоленская область, город <em>Смоленск</em>, Промышленный район, <em>Ленина</em> улица",
"score":118.84238
}
},
{
"value":"Смоленская область, город Смоленск, Ленинский район, Ленина улица, 12А",
"data":{
"fullname":"Смоленская область, город Смоленск, Ленинский район, Ленина улица, 12А",
"level":"8",
"region_id":69,
"commission_id":1124357,
"intid":"135659820348349440000359937",
"path":"135637827259064320000359815,135637827259064320000359819,135637827259064320000359822,135659820348349440000359708,135659820348349440000359937",
"snippet":"Смоленская область, город <em>Смоленск</em>, Ленинский район, <em>Ленина</em> улица, 12А",
"score":115.14931
}
},
...
]
}
Где взять список адресов для извлечения данных с сайта? Перебор базы данных всех адресов страны кажется неэффективным решением, ведь для решения нашей задачи нужен лишь один адрес на избирательный округ.
На каждый избирательный округ приходится в среднем от 2 до 8 участков. Даже не смотря на то, что адрес избирательного участка, в редких случаях, может не соответствовать округу к которому он принадлежит, я выдвинул следующую гипотезу: перебрав адреса УИК на сайте УмГ, можно собрать информацию о каждом округе.
В дальнейшем, при помощи данной гипотезы мне удалось собралось информацию почти по всем избирательным округам. Из-за неоднородности формата адресов в базе данных избирательных комиссий, лишь адреса 10 округов из 1100 мне пришлось подбирать вручную.
В интернете можно найти регулярно обновляющуюся базу данных избирательных комиссий РФ, содержащую информацию об адресах и даже составах УИК. Но для большей актуальности и надежности данных (а также по причине того, что меня не устраивал формат определенного поля) я решил собрать список адресов сам, ведь, как оказалось, на сайте ЦИК имеется весь нужный для этого функционал.
Новый веб-сервиса ЦИК. Методы API
ГАС «Выборы» — автоматизированная система, разработанная в 1995 году, предназначенная для подготовки и проведения выборов и референдумов в РФ.
Если вы когда-либо интересовались ходом выборной кампании, то наверняка сталкивались с данным сайтом, на котором публикуется основная информация из системы ГАС «Выборы», в том числе ход подсчёта голосов, ещё до утверждения результатов выборов:
И если раньше для извлечения результатов выборов датамайнеры пользовались этим сайтом, в дни проведения Голосования по поправкам в Конституцию на сайте внезапно появилась капча. Капча очень настойчивая, появляется при переходе на каждую страницу сайта:
Как вы сами можете визуально оценить, капча конечно очень простая, и наверняка кто-то уже нашел способы её обходить. Я же, вместо того чтобы заняться машинным обучением, обратился к новому разделу на сайте ЦИК, о котором пока мало кто знает: Цифровые сервисы
Данный раздел появился как раз во время Голосования по поправкам и содержит в себе несколько веб-сервисов, которые через POST-запросы общаются с внутренним API для получения данных из системы ГАС «Выборы». Пользователь Хабра уже обратил внимание на данный функционал. Рассмотрим же его подробнее.
Далее приведено описание основных запросов нового API, которые использовались в данном проекте:
Каждая структура данных в системе содержит ключ VRN — уникальный идентификатор объекта, будь то участок, кампания, округ или кандидат.
Информация об УИК
http://cikrf.ru/iservices/voter-services/committee/subjcode/SUBJECT_CODE/num/COMMITTEE_NUM
- SUBJECT_CODE — Код субъекта РФ
- COMMITTEE_NUM — Номер УИК
Пример запроса:
http://cikrf.ru/iservices/voter-services/committee/subjcode/01/num/2
Результат запроса
SPL
{
"vrn":"4014001117979",
"name":"Участковая избирательная комиссия №2",
"subjCode":"01",
"numKsa":"01T001",
"vid":"5",
"address":{
"address":"385200, Республика Адыгея, городской округ Адыгейск, город Адыгейск, проспект имени В.И.Ленина, 16",
"descr":"здание МБОУ СОШ№1",
"phone":"8-87772-9-23-72",
"lat":"44.882893",
"lon":"39.187187"
},
"votingAddress":{
"address":"385200, Республика Адыгея, городской округ Адыгейск, город Адыгейск, проспект имени В.И.Ленина, 16",
"descr":"здание МБОУ СОШ№1",
"phone":"8-87772-9-23-72",
"lat":"44.882893",
"lon":"39.187187"
}
}
Информация о выборных кампаниях на участке
http://cikrf.ru/iservices/voter-services/vibory/committee/COMMITTEE_VRN
- COMMITTEE_VRN — идентификатор УИК
Пример запроса:
http://cikrf.ru/iservices/voter-services/vibory/committee/4544028162533
Результат запроса
SPL
[
{
"vrn":"100100163596966",
"date":"2020-07-01",
"name":"Общероссийское голосование по вопросу одобрения изменений в Конституцию Российской Федерации",
"subjCode":"0",
"pronetvd":null,
"vidvibref":"0"
},
{
"vrn":"25420001876696",
"date":"2020-09-13",
"name":"Выборы депутатов Законодательного Собрания Новосибирской области седьмого созыва",
"subjCode":"54",
"pronetvd":"0",
"vidvibref":"2"
},
{
"vrn":"4544220183446",
"date":"2020-09-13",
"name":"Выборы депутатов Совета депутатов города Новосибирска седьмого созыва ",
"subjCode":"54",
"pronetvd":null,
"vidvibref":"2"
}
]
Перечень округов выборной кампании
http://cikrf.ru/iservices/sgo-visual-rest/vibory/CAMPAIGN_VRN/tvd
- CAMPAIGN_VRN — идентификатор выборной кампании
Пример запроса:
http://cikrf.ru/iservices/sgo-visual-rest/vibory/457422069597/tvd
Результат запроса
SPL
{
"_embedded":{
"tvdDtoList":[
{
"vrn":457422069601,
"namtvd":"Муниципальная избирательная комиссия города Орла",
"namik":"Муниципальная избирательная комиссия города Орла",
"numtvd":"0",
"vidtvd":"ROOT",
"_links":{
"results":{
"href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/457422069597/results/457422069601/proportion"
}
}
},
{
"vrn":457422069602,
"namik":"Окружная избирательная комиссия № 1",
"numtvd":"1",
"vidtvd":"OIK",
"_links":{
"results":{
"href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/457422069597/results/457422069602/major"
}
}
},
...
]
},
"_links":{
"self":{
"href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/457422069597/tvd"
}
}
}
NUMTVD — номер округа. Нулевой номер обычно отвечает за результаты по единому округу. Например, если проходят выборы по смешанной системе, «нулевой избирательный округ» отвечает за голосование по пропорциональной системе. Остальные округа — одномандатные, либо многомандатные.
Как видите, структура данных содержит и ссылку, по которой можно будет узнать результаты выборов. Ссылка генерируется ещё до публикации итогов голосования.
Список кандидатов, участвующих в выборной кампании
http://cikrf.ru/iservices/sgo-visual-rest/vibory/CAMPAIGN_VRN/candidates/?page=PAGE_NUM&numokr=NUMTVD
- CAMPAIGN_VRN — идентификатор выборной кампании
- PAGE_NUM — номер страницы списка
- NUMTVD — номер округа (необязательный параметр)
Пример запроса:
http://cikrf.ru/iservices/sgo-visual-rest/vibory/4674220125616/candidates/?page=1&numokr=11
Результат запроса
SPL
{
"_embedded":{
"candidateDtoList":[
...
{
"index":50,
"vrn":4674020270868,
"fio":"Трофименко Владимир Карпович",
"datroj":"23.04.1964 00:00:00",
"vidvig":"выдвинут",
"registr":"зарегистрирован",
"vrnio":4674220132098,
"namio":"Региональное отделение Политической партии "Российская партия пенсионеров за социальную справедливость" в Смоленской области",
"numokr":11,
"tekstat2":"1",
"_links":{
"self":{
"href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/4674220125616/candidates/4674020270868"
}
}
},
{
"index":56,
"vrn":4674020269642,
"fio":"Божедомов Евгений Эдуардович",
"datroj":"15.02.1986 00:00:00",
"vidvig":"выдвинут",
"registr":"отказ в регистрации",
"namio":"Самовыдвижение",
"numokr":11,
"tekstat2":"1",
"_links":{
"self":{
"href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/4674220125616/candidates/4674020269642"
}
}
},
{
"index":105,
"vrn":4674020271181,
"fio":"Трифоненко Владислав Андреевич",
"datroj":"15.07.1994 00:00:00",
"vidvig":"выдвинут",
"registr":"зарегистрирован",
"vrnio":4674220134054,
"namio":"Смоленское городское отделение политической партии "КОММУНИСТИЧЕСКАЯ ПАРТИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ"",
"numokr":11,
"tekstat2":"1",
"_links":{
"self":{
"href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/4674220125616/candidates/4674020271181"
}
}
},
...
]
},
"_links":{
"self":{
"href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/4674220125616/candidates?page=1&numokr=11"
}
},
"page":{
"size":20,
"totalElements":9,
"totalPages":1,
"number":1
}
}
Структура page содержит общее количество страниц, по ней можно определить когда вы достигните последней страницы (либо по пустому списку, вернувшемуся с сервера).
API содержит и другие методы, в основном чтобы узнать дополнительную информацию о выборах/кандидатах. Если понадобится, вы легко можете отследить нужные запросы. А теперь, можно приступить к выгрузке данных.
Выгрузка данных с сайта ЦИК
Прежде чем приступить к скачиванию нужных данных, нужно было составить список выборных кампаний, которые мы задействуем в проекте. Дело в том, что «Умное Голосование» проходило не везде, а именно на выборах:
— в законодательные собрания регионов,
— в городские советы региональных центров,
— в городские советы крупных городов (с населением больше 200 тысяч человек)
(А также довыборы в Госдуму по 4 округам).
// Леонид Волков
Довыборы в Госдуму я решил проигнорировать, из-за незначительности этих данных. Составить перечень выборов в местные советы помогла статья в Википедии о дне голосования, ведь в ней как раз были перечислены выборы в крупных городах.
Обратившись к товарищу (который помогал мне реализовывать данный проект, выполняя требуемую ручную работу), я попросил его составить список URL-адресов для соответствующих выборных кампаний, взяв их с главной страницы классической системы ЦИК. Дело в том, что в URL-адресе содержится идентификаторы региона и кампании, которые как раз понадобятся нам для дальнейшего парсинга.
vybory.izbirkom.ru/region/izbirkom?action=show&vrn=21120001136916&
region=11&prver=1&pronetvd=1
В итоге, список состоял из 43 выборных кампаний. Всего в Единый день голосования прошло более 9000 отдельных выборных кампаний в органы разного уровня.
Теперь, имея на руках список выборов и перечисленные ранее методы API, скачать данные не составило никакого труда. Написав скрипт на python, делая обычные запросы про помощи requests модуля, я сохранил данные о кандидатах и избирательных участках в исходном JSON-формате.
Главное, что стоит учесть при скачивании информации об избирательных участках: недостаточно перебирать всевозможные номера начиная с 1, до тех пор пока сервер не вернет пустое значение. Дело в том, что нумерация УИК в регионе может прерываться, и идти, например, в таком виде:
...№1001 — №1016, №1101 — №1136, 1138 ...
либо:
№0 — №700, №900 — №1002, 1004...
Чтобы определить максимальный номер УИК в регионе и не делать лишние запросы, я собирал данные следующим образом: пробовал выгрузить данные по первым 1000 номерам, а затем проверял если i+1,i+5,i+100,i+500,i+1000 номера соответствуют какому-либо УИКу (в случае чего продолжал скачивание).
Также, рекомендую сохранять номер УИК, по которому вы скачали данные об участке. Дело в том, что возвращаемые данные не содержат номер УИК, а только название в виде: «Участковая Избирательная Комиссия №100». Процесс получения исходного номера УИК, с которым мне позже пришлось столкнуться, привёл к кратковременным багам и фрустрации. Как оказалось, нумерация в названии УИК в некоторых регионах имеет разный формат.
К примеру, в Удмуртии в названии УИК была следующая нумерация: «№1/01, №1/02, №1/03», в Липецкой области: «№01-01, №01-02, №01-03». В Оренбургской области я столкнулся с настоящей экзотикой: это был единственный регион, где ряд избирательных комиссий были названы в честь кого-то. Например «Участковая избирательная комиссия №1696 имени «Братьев Пустовитовых»
Выгрузка данных с сайта «Умного Голосования»
Теперь, по каждому собранному адресу УИК мы собираемся скачать данные о голосовании с сайта УмГ. Перед этим стоит учесть несколько особенностей (о которых я узнал уже в процессе):
Во первых, надо учесть что адреса в базе данных ЦИК имеют различный формат, порой даже в отдельных областях регионов. Мне пришлось убирать сокращения „д.“, „г.“ и „ул.“, так как сайт «Умного Голосования» совсем не справлялся с поиском адресов по таким запросам. Ещё рекомендую убирать почтовый индекс из адреса, а также, встречающийся иногда префикс „Российская Федерация“.
Во вторых, сайт УмГ имеет жёсткую защиту от DDoS атак, и даже если вы сделаете сотню запросов с интервалом в 0.3 секунды — ваш IP получит бан. Можно было бы использовать набор из платных прокси, но лично я просто воспользовался бесплатными прокси и чередовал запросы со своего и стороннего IP. Чтоб уж точно не получить бан, между запросами был интервал примерно в 0.7 секунд. В итоге, скачивание всех данных заняло примерно сутки.
С использованием запросов из первой главы, алгоритм получился следующим:
- Форматируем адрес УИК
- Делаем запрос на список подходящих адресов
- Получаем список, содержащий идентификаторы страниц сайта
- Проверяем если уже скачали данные об участке по данному идентификатору
- Загружаем HTML-страницу сайта по данному идентификатором
- Извлекаем элемент „__NEXT_DATA__“ и сохраняем данные в JSON-формате
Парсинг страницы происходил при помощи библиотеки beautifulsoup4.
Данный процесс не безупречен: обычно скрипт не находит на сайте десяток избирательных участков в регионе, либо по адресу одного УИК вы находите информацию о совершенно другом УИК.
Это не беда, ведь для каждого округа, нам достаточно найти хоть одну соответствующую страницу на сайте.
Для валидации полноты данных мы пишем простой скрипт, который проверяет если в скачанном с сайта УмГ набора данных содержится информация о каждом избирательном округе. Если чего-то не хватает — пополняем набор вручную. Опять же, таких исключительных ситуаций было менее 10 на 1100 округов.
Объединение данных с сайтов УмГ и ЦИК
На данном этапе, мы собираем удобную структуру данных, с информацией о каждом кандидате по округам: идентификатор кандидата, ФИО, партия, метка с информацией о том, подержан ли он УмГ.
Пример собранного набора данных о кандидатах
SPL
{
"33": [
{
"name": "Бекенева Любовь Александровна",
"vrn": 4444032121758,
"birthdate": "05.05.1958 00:00:00",
"party": "ЕР",
"smart_vote": 0
},
{
"name": "Крохичев Павел Александрович",
"vrn": 4444032122449,
"birthdate": "16.11.1977 00:00:00",
"party": "КПРФ",
"smart_vote": 0
},
{
"name": "Ростовцев Михаил Павлович",
"vrn": 4444032122782,
"birthdate": "27.02.1996 00:00:00",
"party": "ЛДПР",
"smart_vote": 0
},
{
"name": "Морозов Максим Сергеевич",
"vrn": 4444032123815,
"birthdate": "20.11.1991 00:00:00",
"party": "Яблоко",
"smart_vote": 1
},
{
"name": "Захарова Алина Сергеевна",
"vrn": 4444032124060,
"birthdate": "21.07.1996 00:00:00",
"party": "КПКР",
"smart_vote": 0
},
{
"name": "Афанасов Александр Николаевич",
"vrn": 4444032123597,
"birthdate": "21.05.1974 00:00:00",
"party": "СР",
"smart_vote": 0
}
],
...
}
Алгоритм достаточно прямолинейный:
- По массиву данных с сайта УмГ создаем список поддержанных кандидатов для каждого округа
- По массиву данных с сайта ЦИК создаем отфильтрованный список допущенных кандидатов для каждого округа
- В каждом округе по ФИО вычисляем соответствие Кандидат-УмГ—Кандидат-ЦИК
Конечно, такой простой алгоритм должен учесть множество потенциальных проблемных ситуаций.
Во первых, есть шанс что в одном округе будут кандидаты с полностью совпадающими ФИО. Благо, среди 5000 кандидатов, такая ситуация была лишь в одном случае, причём ни один из кандидатов не был поддержан УмГ.
Во вторых, надо учесть, что в базе данных сайта ЦИКа могут быть ошибки. Самая частая ошибка: переносы строк и лишние пробелы в ФИО. Также, при сборе данных об итогах голосования попадалась ситуация, при которых буква „ё“ в фамилии заменялась на „е“.
В третьих, надо учитывать актуальность данных. Данные на сайте ЦИКа и УмГ изменялись и обновлялись вплоть до субботы: каких-то кандидатов снимали/восстанавливали, в каких-то округах менялась поддержка УмГ.
Для валидации списков УмГ был написан простой скрипт, который делает по одному запросу на округ (ведь собранный нами набор данных теперь позволяет однозначно определить страницу, посвященную каждому округу) и проверяет соответствуют ли имена тем, что мы получали ранее.
Интересной задачей была идентификация партий по названию их отделений. Данный пункт можно было бы пропустить, но я решил заняться этим для унификации информации. Проблема заключается в том, что у кандидатов от одной партии может различаться её название в базе ЦИК. Например, в случае КПРФ встречалось более 40 вариантов:
Ивановское городское (местное) отделение Политической партии "Коммунистическая партия Российской Федерации"
Ямало-Ненецкое ОО ПП "КПРФ"
ЧОО ПП КПРФ
КАЛУЖСКОЕ РЕГИОНАЛЬНОЕ ОТДЕЛЕНИЕ политической партии "КОММУНИСТИЧЕСКАЯ ПАРТИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ"
...
Ситуация превращается в интересную задачу по синтаксическому анализу, когда партий 25 штук и почти у каждой разное написание каждом регионе. Благо, про помощи моего товарища, который помогал мне со всей ручной работой, мы составили список ключевых слов, по которым однозначно определяется партия кандидата.
Выгрузка результатов выборов с сайта ЦИК
Собранного набора данных хватило для достижения первоначальной цели проекта — мы составили списки кандидатов УМГ-2020 для каждого избирательного округа. Но если есть техническая возможность получить результаты выборов, почему бы не воспользоваться ею?
Результаты выборов в округе
http://cikrf.ru/iservices/sgo-visual-rest/vibory/CAMPAIGN_VRN/results/DISTRICT_VRN/major
- CAMPAIGN_VRN — идентификатор выборной кампании
- DISTRICT_VRN — идентификатор округа
Пример запроса:
http://cikrf.ru/iservices/sgo-visual-rest/vibory/457422069597/results/457422069602/major
Результат запроса
SPL
{
"report":{
"tvd":"",
"date_sign":"none",
"vrnvibref":"457422069597",
"line":[
{
"txt":"число избирателей на момент окончания голосования",
"kolza":"8488",
"index":"1"
},
{
"txt":"число бюллетеней, полученных участковой комиссией",
"kolza":"6700",
"index":"2"
},
...
{
"txt":"число недействительных бюллетеней",
"kolza":"65",
"index":"9"
},
{
"txt":"число действительных бюллетеней",
"kolza":"1948",
"index":"10"
},
...
{
"delimetr":"1"
},
{
"txt":"Авдеев Максим Юрьевич",
"numsved":"1",
"kolza":"112",
"index":"11",
"namio":"ПАРТИЯ ПЕНСИОНЕРОВ в Орловской области",
"perza":"5.56",
"numsvreestr":"4574030258379"
},
{
"txt":"Жуков Александр Александрович",
"numsved":"2",
"kolza":"186",
"index":"12",
"namio":"Орловское региональное отделение Партии СПРАВЕДЛИВАЯ РОССИЯ",
"perza":"9.24",
"numsvreestr":"4574030258723"
},
{
"txt":"Жуков Родион Вячеславович",
"numsved":"3",
"kolza":"54",
"index":"13",
"namio":"Самовыдвижение",
"perza":"2.68",
"numsvreestr":"4574030258555"
},
...
],
"data_gol":"13.09.2020 00:00:00",
"is_uik":"0",
"type":"423",
"version":"0",
"sgo_version":"5.6.0",
"isplann":"0",
"podpisano":"1",
"versions":{
"ver":{
"current":"true",
"content":"0"
}
},
"vibory":"Выборы депутатов Орловского городского Совета народных депутатов шестого созыва",
"repforms":"1",
"generation_time":"14.09.2020 07:59:21",
"nazv":"Результаты выборов по одномандатному (многомандатному) округу",
"datepodp":"14.09.2020 05:44:00"
}
}
Как видите, результаты возвращаются в виде протокола областной комиссии. В каждом регионе различается формат протокола и количество вступительных строк в нём, поэтому надо проводить внимательную валидацию извлечённых вами данных.
Когда в ГАС «Выборы» начали публиковать предварительные результаты, я столкнулся с небольшим разочарованием. Оказалось, что через API можно получить данные только по тем результатам, которые официально утвердили. С предварительными результатами всё ещё можно ознакомиться на старом сайте избиркома, но нельзя через новые веб-сервисы.
Спустя сутки были известны результаты по 50%, а к концу недели были подведены итоги почти всех выборов, некоторые регионы всё ещё отказывались утверждать результаты. На момент написания статьи, прошло уже 7 дней, а результаты выборов в Тамбове всё ещё не утверждены. К тому же, в некоторых округах происходит пересчёт голосов, из-за чего эти результаты также недоступны через API.
Вывод: методы API на данный момент не подходят для оперативного получения результатов голосования. Вам либо придётся ждать более недели, когда же утвердят результаты, либо придётся парсить старый сайт избиркома, найдя способ обойти капчи.
Мне же надоело ждать когда в ~30 округах из 1100 утвердят выборы, поэтому я написал скрипт, при помощи selenium библиотеки, который выгружает данные с классического сайта избиркома и просит меня вручную решить капчу при каждом запросе. С таким небольшим числом запросов, вручную решать капчу не занимает много времени.
В результате, данные об итогах голосования я собрал в следующую структуру:
Пример результатов голосования в округе
SPL
{
...
"33": {
"candidate_total": {
"4444032121758": 880,
"4444032122449": 236,
"4444032122782": 143,
"4444032123597": 152,
"4444032123815": 149,
"4444032124060": 72
},
"is_final": 1,
"non_valid_votes": 132,
"registered_voters": 6928,
"valid_votes": 1632
},
...
}
Для каждого округа я сохранил суммарное число избирателей в списках (для подсчёта явки), число действительных и недействительных бюллетеней. В структуре содержится словарь: Идентификатор кандидата -> Набранное им число голосов.
Публикация итогов УмГ-2020
Во первых, собранные данные в JSON-формате я опубликовал на GitHub. Данные будут обновляться, пока результаты не утвердят во всех округах.
Во вторых, для привлечения внимания к проекту, я решил сгенерировать Google Таблицу, в которой, в удобном для визуального анализа виде, приведены все собранные данные.
Вдаваться в подробности не буду, никаких сложностей (кроме изучения Google Sheets API) возникнуть не должно. Рекомендую данную статью, в которой подробно рассказано взаимодействие с Google Sheets API на Python.
В итоге получилась такая таблица, в которой собраны:
- Перечень задействованных выборов
- Перечень задействованных партий
- Список кандидатов по округам
- Результаты выборов по округам
- Сухие сводные данные
Послесловие
Идея данного мини-проекта возникла за 3 дня до дня голосования и лично я доволен тем, как успел изучить и реализовать всё в кратчайшие сроки (хотя код получился ужасным).
Я не собираюсь делать какие-либо выводы об итогах стратегии «Умного Голосования», я лишь предоставил инструменты для любителей электоральной статистики. Уверен, среди вас найдутся таковые и скоро мы увидим замечательные исследования, с интересными графиками и диаграммами :)
===========
Источник:
habr.com
===========
Похожие новости:
- [Финансы в IT] Хакеры остановили торги на бирже в Новой Зеландии
- [Python, Высокая производительность, Распределённые системы, Финансы в IT] Собираем данные AlphaVantage с Faust. Часть 1. Подготовка и введение
- [JavaScript, Node.JS] Подключение и настройка TradingView графиков
- [Python, Программирование, Визуальное программирование] Опыт проведения городской школьной олимпиады по программированию
- [Big Data, Искусственный интеллект, Машинное обучение] Временные сверточные сети – революция в мире временных рядов (перевод)
- [Google Cloud Platform, Python, R, Профессиональная литература] Учимся обращаться к данным и запрашивать их при помощи Google BigQuery. С примерами на Python и R (перевод)
- [Машинное обучение, Конференции, Искусственный интеллект] Data Fest 2020 — полностью в Online уже завтра
- [Разработка веб-сайтов, CSS, JavaScript, HTML, Accessibility] Делаем модальные окна для сайта. Заботимся об удобстве и доступности
- [Big Data, Data Mining, Open source, Python] Crime, Race and Lethal Force in the USA — Part 2 (перевод)
- [Open source, Python, Data Mining] Crime, Race and Lethal Force in the USA — Part 1 (перевод)
Теги для поиска: #_api, #_big_data, #_data_mining, #_interfejsy (Интерфейсы), #_otkrytye_dannye (Открытые данные), #_golosovanie (голосование), #_parsing (парсинг), #_python, #_json, #_html, #_vybory (выборы), #_navalnyj (навальный), #_tsik_rf (цик рф), #_sbor_dannyh (сбор данных), #_http, #_api, #_big_data, #_data_mining, #_interfejsy (
Интерфейсы
), #_otkrytye_dannye (
Открытые данные
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 06:19
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
13 сентября 2020 года в России прошёл единый день голосования. В некоторых регионах оппозицией была применена стратегия «Умного Голосования», заключающаяся в том, что оппозиционно настроенные избиратели голосуют за единого кандидата, имеющего наивысшие шансы победить представителя от властей. Процесс отбора кандидатов для «Умного Голосования» уже второй год вызывает дискуссии на тему своей прозрачности. Кроме того, лично меня смущают сложности с подведением итогов стратегии, с которыми могут столкнуться независимые аналитики. Организаторы УмГ не публикуют подробные итоги стратегии, а лишь диаграммы, демонстрирующие сколько оппозиционных кандидатов прошло в региональный парламент. На сайте «Умного Голосования» нельзя получить список поддержанных кандидатов, указав, например, город и округ. Если кто-то захочет собрать данные по региону, ему предстоит монотонная работа по подбору адресов для каждого округа. Ни в коем случае не упрекаю разработчиков сайта УмГ, он имеет весь требуемый функционал для реализации стратегии голосования. Но в связи с тем, что в 2019 году никто не занимался сбором и публикацией подробных данных по итогам УмГ (вне московских выборов), на этих выборах я решил взять инициативу в свои руки. В итоге получилась вот такая сводная таблица. В данной статье я расскажу, как был получен приведённый набор данных, как собиралась информация с сайтов Умного Голосования и нового веб-сервиса ЦИК. Сайт Умного Голосования Для начала посмотрим, какие данные мы можем извлечь с сайта «Умного Голосования». На главной странице сайта есть поле для ввода адреса регистрации пользователя. При вводе строки появляется список предложенных адресов в следующем формате: При выборе одного из предложенных адресов, мы перемещаемся на страницу избирательного участка, к которому прикреплён выбранный адрес: На странице перечислены выборные кампании, которые проходят на данном участке. Для каждой кампании приведён список кандидатов, за/против которых предлагают проголосовать: В данном случае мы видим выборы губернатора, для которых УмГ не указало кандидата от оппозиции. Связанно это с тем, что выборы губернаторов проходят в два тура и не имеет значения, за кого из оппозиционных кандидатов проголосуют избиратели. Также мы видим сразу трёх кандидатов, за которых предлагают проголосовать на выборах в городской парламент. Связанно это с тем, что на выборах в Сочи многомандатные округа. На всех остальных выборных кампаниях, задействованных УмГ в этом году, были только одномандатные округа. Заглянем в код страницы и обнаружим, что все описанные данные, собраны в удобном JSON-формате. В элементе с id="__NEXT_DATA__", который используется для отрисовки страницы, есть информация об избирательном участке, о соответствующих выборных кампаниях и кандидатах: Содержимое __NEXT_DATA__ элементаSPL{
"props":{ "pageProps":{ "id":"440384", "settings":{ "id":1, "share_photo":"/ganimed-media/share_photo/smartvote_sharepic_1200x628.jpg", "video_on_main_page":"https://youtu.be/w8gapDGwWMY", "fake_mode":false, "title_share":"Объединяемся, чтобы победить Единую Россию", "text_share":"Мы разные, но у нас одна политика — мы против монополии «Единой России». Всё остальное — математика.", "telegram_bot_link":"https://tlinks.run/smartvotebot", "viber_bot_link":"viber://public?id=smartvote", "facebook_bot_link":"https://facebook.com/umnoegolosovanie/", "alice_link":null, "vk_bot_link":null }, "serverData":{ "commission":{ "id":440384, "number":"4317", "address":"354340, Краснодарский край, город Сочи, Адлерский район, улица Богдана Хмельницкого, 24", "descr":"здание средней школы № 49 им. Н.И. Кондратенко", "lat":"43.425923", "lon":"39.920152", "region_id":26, "region_intid":"135637827259064320000372513" }, "campaigns":[ { "id":26, "code":"krasnodar-gub-2020", "title":"Выборы губернатора Краснодарского края", "is_regional":true, "ready_date":null, "district":{ "id":458, "code":"oik-0", "name":"0", "leaflet":"" }, "candidates":[ { "id":998, "name":"Кондратьева Вениамина Ивановича", "share_image":"/elections-api-media/share/26/998.png", "anticandidate":true, "self_nominated":false, "has_won":false, "has_second_round":false, "party":{ "title":"Единая Россия", "antiparty":true } } ] }, { "id":28, "code":"krasnodar-sochi-gorduma-2020", "title":"Выборы в городское собрание Сочи", "is_regional":false, "ready_date":null, "district":{ "id":526, "code":"oik-2", "name":"2", "leaflet":"/elections-api-media/28/526-1334-1335-5385.pdf" }, "candidates":[ { "id":1334, "name":"Киров Сабир Рафаилович", "share_image":"/elections-api-media/share/28/1334.png", "anticandidate":false, "self_nominated":true, "has_won":false, "has_second_round":false, "party":null }, { "id":1335, "name":"Мукаелян Марине Айковна", "share_image":"/elections-api-media/share/28/1335.png", "anticandidate":false, "self_nominated":true, "has_won":false, "has_second_round":false, "party":null }, { "id":5385, "name":"Рябцев Виктор Александрович", "share_image":"/elections-api-media/share/28/5385.png", "anticandidate":false, "self_nominated":false, "has_won":false, "has_second_round":false, "party":{ "title":"КПРФ", "antiparty":false } } ] } ] }, "error":null, "currentUrl":"https://votesmart.appspot.com/candidates/440384" } }, "page":"/candidates/[id]", "query":{ "id":"440384" }, "buildId":"U8hjaoxZw8TINu-DU_Ixw", "runtimeConfig":{ "HOST":"https://votesmart.appspot.com" }, "isFallback":false, "customServer":true, "gip":true } Для избирательного участка указан номер (number) соответствующей УИК и её идентификатор в базе данных сайта УмГ. Id = 440834 соответствует номеру, который содержится в URL-адресе страницы (/candidates/440834). Можем ли мы, зная номер УИК и регион, вычислить идентификатор комиссии на сайте УмГ? Я не смог найти очевидную зависимость, так как идентификаторы распределены достаточно хаотично: Сочи, УИК №4512 -> id = 440834 Сочи, УИК №4513 -> id = 441403 Сочи, УИК №4514 -> id = 1781216 Каким образом собрать список отражений номеров УИК в id страниц? Перебирать и проверять всевозможные идентификаторы от 1 до 2000000 звучит крайне неэффективно, большинство из этих идентификаторов нерабочие. Но, если у нас есть список адресов, мы можем относительно легко собрать список соответствующих избирательных участков. При вводе строки на начальном экране, с сервера возвращается перечень подходящих адресов вместе с соответствующими идентификаторами комиссий: Поиск участка по адресу https://votesmart.appspot.com/api/v1/cik/addresses?query=ADDRESS
Пример запроса: https://votesmart.appspot.com/api/v1/cik/addresses?query=Смоленск ленина Результат запросаSPL{
"suggestions":[ { "value":"Смоленская область, город Смоленск, Промышленный район, Ленина улица", "data":{ "fullname":"Смоленская область, город Смоленск, Промышленный район, Ленина улица", "level":"7", "region_id":69, "commission_id":null, "intid":"138474570115456000000347353", "path":"135637827259064320000359815,135637827259064320000359819,135637827259064320000359820,138474570115456000000347353", "snippet":"Смоленская область, город <em>Смоленск</em>, Промышленный район, <em>Ленина</em> улица", "score":118.84238 } }, { "value":"Смоленская область, город Смоленск, Ленинский район, Ленина улица, 12А", "data":{ "fullname":"Смоленская область, город Смоленск, Ленинский район, Ленина улица, 12А", "level":"8", "region_id":69, "commission_id":1124357, "intid":"135659820348349440000359937", "path":"135637827259064320000359815,135637827259064320000359819,135637827259064320000359822,135659820348349440000359708,135659820348349440000359937", "snippet":"Смоленская область, город <em>Смоленск</em>, Ленинский район, <em>Ленина</em> улица, 12А", "score":115.14931 } }, ... ] } Где взять список адресов для извлечения данных с сайта? Перебор базы данных всех адресов страны кажется неэффективным решением, ведь для решения нашей задачи нужен лишь один адрес на избирательный округ. На каждый избирательный округ приходится в среднем от 2 до 8 участков. Даже не смотря на то, что адрес избирательного участка, в редких случаях, может не соответствовать округу к которому он принадлежит, я выдвинул следующую гипотезу: перебрав адреса УИК на сайте УмГ, можно собрать информацию о каждом округе. В дальнейшем, при помощи данной гипотезы мне удалось собралось информацию почти по всем избирательным округам. Из-за неоднородности формата адресов в базе данных избирательных комиссий, лишь адреса 10 округов из 1100 мне пришлось подбирать вручную. В интернете можно найти регулярно обновляющуюся базу данных избирательных комиссий РФ, содержащую информацию об адресах и даже составах УИК. Но для большей актуальности и надежности данных (а также по причине того, что меня не устраивал формат определенного поля) я решил собрать список адресов сам, ведь, как оказалось, на сайте ЦИК имеется весь нужный для этого функционал. Новый веб-сервиса ЦИК. Методы API ГАС «Выборы» — автоматизированная система, разработанная в 1995 году, предназначенная для подготовки и проведения выборов и референдумов в РФ. Если вы когда-либо интересовались ходом выборной кампании, то наверняка сталкивались с данным сайтом, на котором публикуется основная информация из системы ГАС «Выборы», в том числе ход подсчёта голосов, ещё до утверждения результатов выборов: И если раньше для извлечения результатов выборов датамайнеры пользовались этим сайтом, в дни проведения Голосования по поправкам в Конституцию на сайте внезапно появилась капча. Капча очень настойчивая, появляется при переходе на каждую страницу сайта: Как вы сами можете визуально оценить, капча конечно очень простая, и наверняка кто-то уже нашел способы её обходить. Я же, вместо того чтобы заняться машинным обучением, обратился к новому разделу на сайте ЦИК, о котором пока мало кто знает: Цифровые сервисы Данный раздел появился как раз во время Голосования по поправкам и содержит в себе несколько веб-сервисов, которые через POST-запросы общаются с внутренним API для получения данных из системы ГАС «Выборы». Пользователь Хабра уже обратил внимание на данный функционал. Рассмотрим же его подробнее. Далее приведено описание основных запросов нового API, которые использовались в данном проекте: Каждая структура данных в системе содержит ключ VRN — уникальный идентификатор объекта, будь то участок, кампания, округ или кандидат. Информация об УИК http://cikrf.ru/iservices/voter-services/committee/subjcode/SUBJECT_CODE/num/COMMITTEE_NUM
Пример запроса: http://cikrf.ru/iservices/voter-services/committee/subjcode/01/num/2 Результат запросаSPL{
"vrn":"4014001117979", "name":"Участковая избирательная комиссия №2", "subjCode":"01", "numKsa":"01T001", "vid":"5", "address":{ "address":"385200, Республика Адыгея, городской округ Адыгейск, город Адыгейск, проспект имени В.И.Ленина, 16", "descr":"здание МБОУ СОШ№1", "phone":"8-87772-9-23-72", "lat":"44.882893", "lon":"39.187187" }, "votingAddress":{ "address":"385200, Республика Адыгея, городской округ Адыгейск, город Адыгейск, проспект имени В.И.Ленина, 16", "descr":"здание МБОУ СОШ№1", "phone":"8-87772-9-23-72", "lat":"44.882893", "lon":"39.187187" } } Информация о выборных кампаниях на участке http://cikrf.ru/iservices/voter-services/vibory/committee/COMMITTEE_VRN
Пример запроса: http://cikrf.ru/iservices/voter-services/vibory/committee/4544028162533 Результат запросаSPL[
{ "vrn":"100100163596966", "date":"2020-07-01", "name":"Общероссийское голосование по вопросу одобрения изменений в Конституцию Российской Федерации", "subjCode":"0", "pronetvd":null, "vidvibref":"0" }, { "vrn":"25420001876696", "date":"2020-09-13", "name":"Выборы депутатов Законодательного Собрания Новосибирской области седьмого созыва", "subjCode":"54", "pronetvd":"0", "vidvibref":"2" }, { "vrn":"4544220183446", "date":"2020-09-13", "name":"Выборы депутатов Совета депутатов города Новосибирска седьмого созыва ", "subjCode":"54", "pronetvd":null, "vidvibref":"2" } ] Перечень округов выборной кампании http://cikrf.ru/iservices/sgo-visual-rest/vibory/CAMPAIGN_VRN/tvd
Пример запроса: http://cikrf.ru/iservices/sgo-visual-rest/vibory/457422069597/tvd Результат запросаSPL{
"_embedded":{ "tvdDtoList":[ { "vrn":457422069601, "namtvd":"Муниципальная избирательная комиссия города Орла", "namik":"Муниципальная избирательная комиссия города Орла", "numtvd":"0", "vidtvd":"ROOT", "_links":{ "results":{ "href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/457422069597/results/457422069601/proportion" } } }, { "vrn":457422069602, "namik":"Окружная избирательная комиссия № 1", "numtvd":"1", "vidtvd":"OIK", "_links":{ "results":{ "href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/457422069597/results/457422069602/major" } } }, ... ] }, "_links":{ "self":{ "href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/457422069597/tvd" } } } NUMTVD — номер округа. Нулевой номер обычно отвечает за результаты по единому округу. Например, если проходят выборы по смешанной системе, «нулевой избирательный округ» отвечает за голосование по пропорциональной системе. Остальные округа — одномандатные, либо многомандатные. Как видите, структура данных содержит и ссылку, по которой можно будет узнать результаты выборов. Ссылка генерируется ещё до публикации итогов голосования. Список кандидатов, участвующих в выборной кампании http://cikrf.ru/iservices/sgo-visual-rest/vibory/CAMPAIGN_VRN/candidates/?page=PAGE_NUM&numokr=NUMTVD
Пример запроса: http://cikrf.ru/iservices/sgo-visual-rest/vibory/4674220125616/candidates/?page=1&numokr=11 Результат запросаSPL{
"_embedded":{ "candidateDtoList":[ ... { "index":50, "vrn":4674020270868, "fio":"Трофименко Владимир Карпович", "datroj":"23.04.1964 00:00:00", "vidvig":"выдвинут", "registr":"зарегистрирован", "vrnio":4674220132098, "namio":"Региональное отделение Политической партии "Российская партия пенсионеров за социальную справедливость" в Смоленской области", "numokr":11, "tekstat2":"1", "_links":{ "self":{ "href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/4674220125616/candidates/4674020270868" } } }, { "index":56, "vrn":4674020269642, "fio":"Божедомов Евгений Эдуардович", "datroj":"15.02.1986 00:00:00", "vidvig":"выдвинут", "registr":"отказ в регистрации", "namio":"Самовыдвижение", "numokr":11, "tekstat2":"1", "_links":{ "self":{ "href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/4674220125616/candidates/4674020269642" } } }, { "index":105, "vrn":4674020271181, "fio":"Трифоненко Владислав Андреевич", "datroj":"15.07.1994 00:00:00", "vidvig":"выдвинут", "registr":"зарегистрирован", "vrnio":4674220134054, "namio":"Смоленское городское отделение политической партии "КОММУНИСТИЧЕСКАЯ ПАРТИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ"", "numokr":11, "tekstat2":"1", "_links":{ "self":{ "href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/4674220125616/candidates/4674020271181" } } }, ... ] }, "_links":{ "self":{ "href":"http://cikrf.ru/iservices/sgo-visual-rest/vibory/4674220125616/candidates?page=1&numokr=11" } }, "page":{ "size":20, "totalElements":9, "totalPages":1, "number":1 } } Структура page содержит общее количество страниц, по ней можно определить когда вы достигните последней страницы (либо по пустому списку, вернувшемуся с сервера). API содержит и другие методы, в основном чтобы узнать дополнительную информацию о выборах/кандидатах. Если понадобится, вы легко можете отследить нужные запросы. А теперь, можно приступить к выгрузке данных. Выгрузка данных с сайта ЦИК Прежде чем приступить к скачиванию нужных данных, нужно было составить список выборных кампаний, которые мы задействуем в проекте. Дело в том, что «Умное Голосование» проходило не везде, а именно на выборах: — в законодательные собрания регионов,
— в городские советы региональных центров, — в городские советы крупных городов (с населением больше 200 тысяч человек) (А также довыборы в Госдуму по 4 округам). // Леонид Волков Довыборы в Госдуму я решил проигнорировать, из-за незначительности этих данных. Составить перечень выборов в местные советы помогла статья в Википедии о дне голосования, ведь в ней как раз были перечислены выборы в крупных городах. Обратившись к товарищу (который помогал мне реализовывать данный проект, выполняя требуемую ручную работу), я попросил его составить список URL-адресов для соответствующих выборных кампаний, взяв их с главной страницы классической системы ЦИК. Дело в том, что в URL-адресе содержится идентификаторы региона и кампании, которые как раз понадобятся нам для дальнейшего парсинга. vybory.izbirkom.ru/region/izbirkom?action=show&vrn=21120001136916& region=11&prver=1&pronetvd=1 В итоге, список состоял из 43 выборных кампаний. Всего в Единый день голосования прошло более 9000 отдельных выборных кампаний в органы разного уровня. Теперь, имея на руках список выборов и перечисленные ранее методы API, скачать данные не составило никакого труда. Написав скрипт на python, делая обычные запросы про помощи requests модуля, я сохранил данные о кандидатах и избирательных участках в исходном JSON-формате. Главное, что стоит учесть при скачивании информации об избирательных участках: недостаточно перебирать всевозможные номера начиная с 1, до тех пор пока сервер не вернет пустое значение. Дело в том, что нумерация УИК в регионе может прерываться, и идти, например, в таком виде: ...№1001 — №1016, №1101 — №1136, 1138 ... либо: №0 — №700, №900 — №1002, 1004... Чтобы определить максимальный номер УИК в регионе и не делать лишние запросы, я собирал данные следующим образом: пробовал выгрузить данные по первым 1000 номерам, а затем проверял если i+1,i+5,i+100,i+500,i+1000 номера соответствуют какому-либо УИКу (в случае чего продолжал скачивание). Также, рекомендую сохранять номер УИК, по которому вы скачали данные об участке. Дело в том, что возвращаемые данные не содержат номер УИК, а только название в виде: «Участковая Избирательная Комиссия №100». Процесс получения исходного номера УИК, с которым мне позже пришлось столкнуться, привёл к кратковременным багам и фрустрации. Как оказалось, нумерация в названии УИК в некоторых регионах имеет разный формат. К примеру, в Удмуртии в названии УИК была следующая нумерация: «№1/01, №1/02, №1/03», в Липецкой области: «№01-01, №01-02, №01-03». В Оренбургской области я столкнулся с настоящей экзотикой: это был единственный регион, где ряд избирательных комиссий были названы в честь кого-то. Например «Участковая избирательная комиссия №1696 имени «Братьев Пустовитовых» Выгрузка данных с сайта «Умного Голосования» Теперь, по каждому собранному адресу УИК мы собираемся скачать данные о голосовании с сайта УмГ. Перед этим стоит учесть несколько особенностей (о которых я узнал уже в процессе): Во первых, надо учесть что адреса в базе данных ЦИК имеют различный формат, порой даже в отдельных областях регионов. Мне пришлось убирать сокращения „д.“, „г.“ и „ул.“, так как сайт «Умного Голосования» совсем не справлялся с поиском адресов по таким запросам. Ещё рекомендую убирать почтовый индекс из адреса, а также, встречающийся иногда префикс „Российская Федерация“. Во вторых, сайт УмГ имеет жёсткую защиту от DDoS атак, и даже если вы сделаете сотню запросов с интервалом в 0.3 секунды — ваш IP получит бан. Можно было бы использовать набор из платных прокси, но лично я просто воспользовался бесплатными прокси и чередовал запросы со своего и стороннего IP. Чтоб уж точно не получить бан, между запросами был интервал примерно в 0.7 секунд. В итоге, скачивание всех данных заняло примерно сутки. С использованием запросов из первой главы, алгоритм получился следующим:
Парсинг страницы происходил при помощи библиотеки beautifulsoup4. Данный процесс не безупречен: обычно скрипт не находит на сайте десяток избирательных участков в регионе, либо по адресу одного УИК вы находите информацию о совершенно другом УИК. Это не беда, ведь для каждого округа, нам достаточно найти хоть одну соответствующую страницу на сайте. Для валидации полноты данных мы пишем простой скрипт, который проверяет если в скачанном с сайта УмГ набора данных содержится информация о каждом избирательном округе. Если чего-то не хватает — пополняем набор вручную. Опять же, таких исключительных ситуаций было менее 10 на 1100 округов. Объединение данных с сайтов УмГ и ЦИК На данном этапе, мы собираем удобную структуру данных, с информацией о каждом кандидате по округам: идентификатор кандидата, ФИО, партия, метка с информацией о том, подержан ли он УмГ. Пример собранного набора данных о кандидатахSPL{
"33": [ { "name": "Бекенева Любовь Александровна", "vrn": 4444032121758, "birthdate": "05.05.1958 00:00:00", "party": "ЕР", "smart_vote": 0 }, { "name": "Крохичев Павел Александрович", "vrn": 4444032122449, "birthdate": "16.11.1977 00:00:00", "party": "КПРФ", "smart_vote": 0 }, { "name": "Ростовцев Михаил Павлович", "vrn": 4444032122782, "birthdate": "27.02.1996 00:00:00", "party": "ЛДПР", "smart_vote": 0 }, { "name": "Морозов Максим Сергеевич", "vrn": 4444032123815, "birthdate": "20.11.1991 00:00:00", "party": "Яблоко", "smart_vote": 1 }, { "name": "Захарова Алина Сергеевна", "vrn": 4444032124060, "birthdate": "21.07.1996 00:00:00", "party": "КПКР", "smart_vote": 0 }, { "name": "Афанасов Александр Николаевич", "vrn": 4444032123597, "birthdate": "21.05.1974 00:00:00", "party": "СР", "smart_vote": 0 } ], ... } Алгоритм достаточно прямолинейный:
Конечно, такой простой алгоритм должен учесть множество потенциальных проблемных ситуаций. Во первых, есть шанс что в одном округе будут кандидаты с полностью совпадающими ФИО. Благо, среди 5000 кандидатов, такая ситуация была лишь в одном случае, причём ни один из кандидатов не был поддержан УмГ. Во вторых, надо учесть, что в базе данных сайта ЦИКа могут быть ошибки. Самая частая ошибка: переносы строк и лишние пробелы в ФИО. Также, при сборе данных об итогах голосования попадалась ситуация, при которых буква „ё“ в фамилии заменялась на „е“. В третьих, надо учитывать актуальность данных. Данные на сайте ЦИКа и УмГ изменялись и обновлялись вплоть до субботы: каких-то кандидатов снимали/восстанавливали, в каких-то округах менялась поддержка УмГ. Для валидации списков УмГ был написан простой скрипт, который делает по одному запросу на округ (ведь собранный нами набор данных теперь позволяет однозначно определить страницу, посвященную каждому округу) и проверяет соответствуют ли имена тем, что мы получали ранее. Интересной задачей была идентификация партий по названию их отделений. Данный пункт можно было бы пропустить, но я решил заняться этим для унификации информации. Проблема заключается в том, что у кандидатов от одной партии может различаться её название в базе ЦИК. Например, в случае КПРФ встречалось более 40 вариантов: Ивановское городское (местное) отделение Политической партии "Коммунистическая партия Российской Федерации" Ямало-Ненецкое ОО ПП "КПРФ" ЧОО ПП КПРФ КАЛУЖСКОЕ РЕГИОНАЛЬНОЕ ОТДЕЛЕНИЕ политической партии "КОММУНИСТИЧЕСКАЯ ПАРТИЯ РОССИЙСКОЙ ФЕДЕРАЦИИ" ... Ситуация превращается в интересную задачу по синтаксическому анализу, когда партий 25 штук и почти у каждой разное написание каждом регионе. Благо, про помощи моего товарища, который помогал мне со всей ручной работой, мы составили список ключевых слов, по которым однозначно определяется партия кандидата. Выгрузка результатов выборов с сайта ЦИК Собранного набора данных хватило для достижения первоначальной цели проекта — мы составили списки кандидатов УМГ-2020 для каждого избирательного округа. Но если есть техническая возможность получить результаты выборов, почему бы не воспользоваться ею? Результаты выборов в округе http://cikrf.ru/iservices/sgo-visual-rest/vibory/CAMPAIGN_VRN/results/DISTRICT_VRN/major
Пример запроса: http://cikrf.ru/iservices/sgo-visual-rest/vibory/457422069597/results/457422069602/major Результат запросаSPL{
"report":{ "tvd":"", "date_sign":"none", "vrnvibref":"457422069597", "line":[ { "txt":"число избирателей на момент окончания голосования", "kolza":"8488", "index":"1" }, { "txt":"число бюллетеней, полученных участковой комиссией", "kolza":"6700", "index":"2" }, ... { "txt":"число недействительных бюллетеней", "kolza":"65", "index":"9" }, { "txt":"число действительных бюллетеней", "kolza":"1948", "index":"10" }, ... { "delimetr":"1" }, { "txt":"Авдеев Максим Юрьевич", "numsved":"1", "kolza":"112", "index":"11", "namio":"ПАРТИЯ ПЕНСИОНЕРОВ в Орловской области", "perza":"5.56", "numsvreestr":"4574030258379" }, { "txt":"Жуков Александр Александрович", "numsved":"2", "kolza":"186", "index":"12", "namio":"Орловское региональное отделение Партии СПРАВЕДЛИВАЯ РОССИЯ", "perza":"9.24", "numsvreestr":"4574030258723" }, { "txt":"Жуков Родион Вячеславович", "numsved":"3", "kolza":"54", "index":"13", "namio":"Самовыдвижение", "perza":"2.68", "numsvreestr":"4574030258555" }, ... ], "data_gol":"13.09.2020 00:00:00", "is_uik":"0", "type":"423", "version":"0", "sgo_version":"5.6.0", "isplann":"0", "podpisano":"1", "versions":{ "ver":{ "current":"true", "content":"0" } }, "vibory":"Выборы депутатов Орловского городского Совета народных депутатов шестого созыва", "repforms":"1", "generation_time":"14.09.2020 07:59:21", "nazv":"Результаты выборов по одномандатному (многомандатному) округу", "datepodp":"14.09.2020 05:44:00" } } Как видите, результаты возвращаются в виде протокола областной комиссии. В каждом регионе различается формат протокола и количество вступительных строк в нём, поэтому надо проводить внимательную валидацию извлечённых вами данных. Когда в ГАС «Выборы» начали публиковать предварительные результаты, я столкнулся с небольшим разочарованием. Оказалось, что через API можно получить данные только по тем результатам, которые официально утвердили. С предварительными результатами всё ещё можно ознакомиться на старом сайте избиркома, но нельзя через новые веб-сервисы. Спустя сутки были известны результаты по 50%, а к концу недели были подведены итоги почти всех выборов, некоторые регионы всё ещё отказывались утверждать результаты. На момент написания статьи, прошло уже 7 дней, а результаты выборов в Тамбове всё ещё не утверждены. К тому же, в некоторых округах происходит пересчёт голосов, из-за чего эти результаты также недоступны через API. Вывод: методы API на данный момент не подходят для оперативного получения результатов голосования. Вам либо придётся ждать более недели, когда же утвердят результаты, либо придётся парсить старый сайт избиркома, найдя способ обойти капчи. Мне же надоело ждать когда в ~30 округах из 1100 утвердят выборы, поэтому я написал скрипт, при помощи selenium библиотеки, который выгружает данные с классического сайта избиркома и просит меня вручную решить капчу при каждом запросе. С таким небольшим числом запросов, вручную решать капчу не занимает много времени. В результате, данные об итогах голосования я собрал в следующую структуру: Пример результатов голосования в округеSPL{
... "33": { "candidate_total": { "4444032121758": 880, "4444032122449": 236, "4444032122782": 143, "4444032123597": 152, "4444032123815": 149, "4444032124060": 72 }, "is_final": 1, "non_valid_votes": 132, "registered_voters": 6928, "valid_votes": 1632 }, ... } Для каждого округа я сохранил суммарное число избирателей в списках (для подсчёта явки), число действительных и недействительных бюллетеней. В структуре содержится словарь: Идентификатор кандидата -> Набранное им число голосов. Публикация итогов УмГ-2020 Во первых, собранные данные в JSON-формате я опубликовал на GitHub. Данные будут обновляться, пока результаты не утвердят во всех округах. Во вторых, для привлечения внимания к проекту, я решил сгенерировать Google Таблицу, в которой, в удобном для визуального анализа виде, приведены все собранные данные. Вдаваться в подробности не буду, никаких сложностей (кроме изучения Google Sheets API) возникнуть не должно. Рекомендую данную статью, в которой подробно рассказано взаимодействие с Google Sheets API на Python. В итоге получилась такая таблица, в которой собраны:
Послесловие Идея данного мини-проекта возникла за 3 дня до дня голосования и лично я доволен тем, как успел изучить и реализовать всё в кратчайшие сроки (хотя код получился ужасным). Я не собираюсь делать какие-либо выводы об итогах стратегии «Умного Голосования», я лишь предоставил инструменты для любителей электоральной статистики. Уверен, среди вас найдутся таковые и скоро мы увидим замечательные исследования, с интересными графиками и диаграммами :) =========== Источник: habr.com =========== Похожие новости:
Интерфейсы ), #_otkrytye_dannye ( Открытые данные ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 06:19
Часовой пояс: UTC + 5