Análise do site do Smart Voting e da nova API no site do CEC

imagem



Em 13 de setembro de 2020, um único dia de votação foi realizado na Rússia. Em algumas regiões, a oposição usou a estratégia de Votação Inteligente, em que eleitores com mentalidade oposicionista votam em um único candidato com as maiores chances de derrotar um representante das autoridades.



Pelo segundo ano consecutivo, o processo de seleção de candidatos ao “Smart Voting” tem gerado discussões sobre o tema de sua transparência. Além disso, estou pessoalmente confuso com as dificuldades em resumir a estratégia que os analistas independentes podem enfrentar. Os organizadores do UMG não publicam resultados detalhados da estratégia, mas apenas diagramas mostrando quantos candidatos da oposição entraram no parlamento regional.



No site do "Smart Voting"você não pode obter uma lista de candidatos com suporte especificando, por exemplo, cidade e distrito. Se alguém quiser coletar dados sobre a região, terá que fazer o monótono trabalho de selecionar os endereços de cada distrito.



Em nenhum caso eu reprovo os desenvolvedores do site UMG, ele tem todas as funcionalidades necessárias para implementar a estratégia de votação. Mas devido ao fato de que em 2019 ninguém estava envolvido na coleta e publicação de dados detalhados sobre os resultados do UMG (fora das eleições de Moscou), nestas eleições decidi tomar a iniciativa em minhas próprias mãos.



O resultado é uma tabela de resumo como esta . Neste artigo, contarei como o determinado conjunto de dados foi obtido , como as informações foram coletadas nos sites de votação inteligente e no novo serviço da web CEC .



imagem



Site de votação inteligente



Primeiro, vamos ver quais dados podemos extrair do site do Smart Voting. Na página principal do site existe um campo para inserir o endereço de cadastro do usuário. Quando você insere uma string, uma lista de endereços sugeridos aparece no seguinte formato:



imagem




Ao escolher um dos endereços propostos, somos conduzidos à página da assembleia de voto à qual o endereço selecionado está anexado:



imagem


A página lista as campanhas eleitorais que acontecem nesta área. Para cada campanha, há uma lista de candidatos a favor / contra os quais eles podem votar:



imagem


Neste caso, vemos a eleição do governador, para o qual a UMG não indicou candidato da oposição. Isso se deve ao fato de que as eleições de governadores são realizadas em dois turnos e não importa em qual dos candidatos da oposição os eleitores votarão no primeiro turno.

Também vemos três candidatos ao mesmo tempo, para os quais são oferecidos para votar nas eleições para o parlamento da cidade. Isso se deve ao fato de que as eleições em Sochi têm círculos eleitorais com vários membros.

Em todas as outras campanhas eleitorais, envolvidas no UMG este ano, havia apenas constituintes de um único membro.



Vamos examinar o código da página e descobrir que todos os dados descritos são coletados em um formato JSON conveniente. No elemento com id = "__ NEXT_DATA__", que serve para desenhar a página, há informações sobre a mesa de voto, as campanhas eleitorais correspondentes e os candidatos:



Conteúdo do elemento __NEXT_DATA__
{
   "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
}




Para a assembleia de voto, o número (número) do PEC correspondente e o seu identificador são indicados na base de dados do site da UMG. Id = 440834 corresponde ao número encontrado no URL da página (/ candidate / 440834).



Podemos, conhecendo o número e a região do PEC, calcular o identificador de comissão no site da UMG? Não consegui encontrar uma dependência óbvia, pois os identificadores são distribuídos de forma bastante caótica:

Sochi, PEC # 4512 -> id = 440834

Sochi, PEC # 4513 -> id = 441403

Sochi, PEC # 4514 -> id = 1781216



Como coletar uma lista de reflexos de números PEC nas páginas de id? Parece extremamente ineficiente iterar e verificar todos os tipos de identificadores de 1 a 2.000.000; a maioria desses identificadores não está funcionando.



Mas, se tivermos uma lista de endereços, podemos reunir com relativa facilidade uma lista das assembleias de voto relevantes. Quando você insere uma string na tela inicial, uma lista de endereços adequados é retornada do servidor junto com os identificadores de comissão correspondentes:



Pesquisar um site por endereço



https://votesmart.appspot.com/api/v1/cik/addresses?query=ADDRESS


  • ENDEREÇO - endereço, preferencialmente no formato “Assunto, cidade, logradouro, domicílio”. Também é desejável sem as abreviações "rua", "d.", Já que o analisador no servidor não as trata bem


Solicitação de exemplo:



https://votesmart.appspot.com/api/v1/cik/addresses?query= Lenin's Smolensk



Resultado da consulta
{
   "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
         }
      },
...
   ]
}




Onde posso obter uma lista de endereços para extrair dados do site? Enumerar o banco de dados de todos os endereços do país parece uma solução ineficaz, porque para resolver nosso problema, precisamos apenas de um endereço por constituinte.



Cada distrito tem uma média de 2 a 8 distritos. Embora o endereço de uma assembleia de voto, em casos raros, possa não corresponder ao círculo eleitoral a que pertence, proponho a seguinte hipótese: ao consultar os endereços do PEC no site da UMG, pode recolher informações sobre cada círculo eleitoral.



Posteriormente, com a ajuda dessa hipótese, consegui coletar informações sobre quase todos os constituintes. Devido à heterogeneidade do formato dos endereços na base de dados das comissões eleitorais, apenas os endereços de 10 entre 1100 constituintes que tive de selecionar manualmente.



Na Internet, você pode encontrar um banco de dados regularmente atualizado das comissões eleitorais da Federação Russa , contendo informações sobre os endereços e até mesmo a composição dos PECs. Mas para uma maior relevância e fiabilidade dos dados (e também porque não estava satisfeito com o formato de um determinado campo), resolvi recolher eu próprio a lista de endereços, pois, ao que parece, o site do CEC tem todas as funcionalidades necessárias para isso.



Novo serviço da web CEC. Métodos API



GAS "Vybory" é um sistema automatizado desenvolvido em 1995, destinado à preparação e condução de eleições e referendos na Federação Russa.



Se você já se interessou pelo andamento de uma campanha eleitoral, provavelmente já se deparou com este site , que publica informações básicas do sistema GAS "Vybory", incluindo a contagem de votos, antes mesmo da aprovação dos resultados eleitorais:



imagem



E se antes, para recuperar os resultados das eleições, dataminers utilizei este site, durante os dias de votação das alterações à Constituição , apareceu repentinamente um captcha no site . O captcha é muito persistente, ele aparece quando você vai a cada página do site:



imagem


Como você mesmo pode avaliar visualmente, o captcha é muito simples, e certamente alguém já encontrou maneiras de contorná-lo. Em vez de fazer aprendizado de máquina, abri uma nova seção no site do CEC, que muito poucas pessoas conhecem ainda: Serviços digitais



imagem



Esta seção apareceu apenas durante a votação de emendas e contém vários serviços da web que, via HTTP as solicitações se comunicam com a API interna para receber dados do sistema GAS "Vybory". O usuário Habr já prestou atenção a esta funcionalidade. Vamos considerá-lo com mais detalhes.



A seguir está uma descrição das principais solicitações da nova API que foram utilizadas neste projeto:



Cada estrutura de dados no sistema contém uma chave VRN- um identificador único para o objeto, seja um local, campanha, distrito ou candidato .




Informação PEC



http://cikrf.ru/iservices/voter-services/committee/subjcode/SUBJECT_CODE/num/COMMITTEE_NUM




Solicitação de exemplo:



http://cikrf.ru/iservices/voter-services/committee/subjcode/ 01 / num / 2



Resultado da consulta
{
   "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"
   }
}







Informações sobre campanhas eleitorais no site



http://cikrf.ru/iservices/voter-services/vibory/committee/COMMITTEE_VRN


  • COMMITTEE_VRN - identificador PEC


Solicite um exemplo:



http://cikrf.ru/iservices/voter-services/vibory/committee/ 4544028162533



Resultado da consulta
[
   {
      "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"
   }
]





Lista de distritos eleitorais



http://cikrf.ru/iservices/sgo-visual-rest/vibory/CAMPAIGN_VRN/tvd


  • CAMPAIGN_VRN - ID da campanha


Solicite um exemplo:



http://cikrf.ru/iservices/sgo-visual-rest/vibory/ 457422069597 / tvd



Resultado da consulta
{
   "_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 é o número do condado. O número zero geralmente é responsável pelos resultados de um único distrito. Por exemplo, se as eleições são realizadas em um sistema misto, o "eleitorado zero" é responsável por votar em um sistema proporcional. O restante dos constituintes é composto por um ou vários membros.



Como você pode ver, a estrutura de dados também contém um link que pode ser usado para descobrir os resultados das eleições. O link é gerado antes mesmo da publicação do resultado da votação.






Lista de candidatos que participam da campanha eleitoral



http://cikrf.ru/iservices/sgo-visual-rest/vibory/CAMPAIGN_VRN/candidates/?page=PAGE_NUM&numokr=NUMTVD


  • CAMPAIGN_VRN - ID da campanha
  • PAGE_NUM - número da página da lista
  • NUMTVD - número do condado (opcional)


Solicitar exemplo:



http://cikrf.ru/iservices/sgo-visual-rest/vibory/ 4674220125616 / candidate /? Page = 1 & numokr = 11



Resultado da consulta
{
   "_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
   }
}




A estrutura da página contém o número total de páginas, ela pode ser usada para determinar quando você atingiu a última página (ou por uma lista vazia retornada do servidor).






A API contém outros métodos, principalmente para obter mais informações sobre eleições / candidatos. Se necessário, você pode rastrear facilmente as solicitações necessárias. Agora você pode começar a enviar dados.



Baixando dados do site do CEC



Antes de fazer o download dos dados necessários, foi necessário compilar uma lista de campanhas eleitorais que utilizaremos no projeto. O fato é que o “Smart Voting” não ocorreu em todos os lugares, mas nas eleições:

— ,

— ,

— ( 200 )

( 4 ).

//


Decidi ignorar as eleições parciais para a Duma Estadual, por causa da insignificância desses dados. O artigo da Wikipedia sobre o dia da eleição ajudou a compilar a lista de eleições para os conselhos locais , porque apenas listou as eleições nas grandes cidades.



Dirigindo-me a um amigo (que me ajudou a implementar este projeto fazendo o trabalho manual necessário), pedi-lhe que compilasse uma lista de URLs para as respetivas campanhas eleitorais, retirando-as da página principal do clássico site do CEC . O fato é que o URL contém os identificadores de região e campanha de que precisaremos para uma análise posterior.



vybory.izbirkom.ru/region/izbirkom?action=show&vrn=21120001136916&
region=11&prver=1&pronetvd=1


Como resultado, a lista foi composta por 43 campanhas eleitorais. No total, mais de 9000 campanhas eleitorais distintas foram realizadas no Dia Único de Eleições para os órgãos de vários níveis.



Agora, com a lista de opções e os métodos de API listados anteriormente, o download dos dados foi fácil. Tendo escrito um script Python , fazendo solicitações regulares usando o módulo de solicitações , salvei os dados sobre candidatos e assembleias de voto no formato JSON original.



O principal a considerar ao baixar informações sobre as assembleias de voto: não é suficiente percorrer todos os números possíveis a partir de 1 até que o servidor retorne um valor vazio. O fato é que a numeração dos PECs na região pode ser interrompida, e ir, por exemplo, da seguinte forma:

... # 1001 - # 1016, # 1101 - # 1136, 1138 ...

ou:

# 0 - # 700, # 900 - # 1002, 1004 ...

Para determinar o número máximo de PEC na região e não fazer solicitações desnecessárias, coletei os dados da seguinte maneira: tentei fazer upload de dados nos primeiros 1000 números e depois verifiquei se eu + 1, i + 5, i + 100, i + 500, i + 1000 números correspondem a qualquer PEC (nesse caso, o download continuado).



Além disso, recomendo manter o número do PEC de onde você baixou os dados sobre a delegacia. O fato é que os dados retornados não contêm o número da PEC, mas apenas o nome no formulário: “Comissão Eleitoral da Delegacia nº 100” . O processo de obtenção do número PEC original, com o qual tive que lidar mais tarde, resultou em bugs de curto prazo e frustração. Como se viu, a numeração em nome dos PECs em algumas regiões tem um formato diferente.



Por exemplo, em Udmurtia, o nome do PEC tinha a seguinte numeração: “№1 / 01, №1 / 02, №1 / 03" , na região de Lipetsk: "№01-01, №01-02, №01-03" . Na região de Orenburg, encontrei um verdadeiro exótico: foi a única região onde várias comissões eleitorais receberam o nome de alguém. Por exemplo, "Comissão Eleitoral de Distrito No. 1696 com o nome de" Irmãos Pustovitov "



Baixando dados do site do “Smart Voting”



Agora, para cada endereço PEC coletado, vamos baixar os dados da votação do site da UMG. Antes disso, vale a pena considerar várias características (que aprendi no processo):



Primeiro, é preciso levar em consideração que os endereços na base de dados CEC têm um formato diferente, às vezes até em certas regiões das regiões. Tive que remover as abreviaturas “d.”, “G.” e “st.”, Já que o site de “Smart Voting” não foi de forma alguma capaz de dar conta da busca de endereços para tais consultas. Também recomendo remover o código postal do endereço, bem como o prefixo "Federação Russa" às vezes encontrado.



Em segundo lugar, o site UMG possui forte proteção contra ataques DDoS, e mesmo que você faça cem requisições com intervalo de 0,3 segundos, seu IP será banido. Seria possível usar um conjunto de proxies pagos, mas pessoalmente usei apenas proxies gratuitos e solicitações alternadas do meu próprio IP e de um IP de terceiros. Para não ser banido, havia um intervalo de cerca de 0,7 segundo entre as solicitações. Como resultado, o download de todos os dados demorou cerca de um dia.



Usando as consultas do primeiro capítulo, o algoritmo é o seguinte:



  1. Formatando o endereço PEC
  2. Fazer um pedido de uma lista de endereços adequados
  3. Recebemos uma lista contendo IDs de página de site
  4. Verificamos se já baixamos os dados sobre o site por este identificador
  5. Carregue a página HTML do site para este identificador
  6. Extraímos o elemento "__NEXT_DATA__" e salvamos os dados no formato JSON


A página foi analisada usando a biblioteca beautifulsoup4 .



Este processo não é perfeito: normalmente o roteiro não encontra uma dúzia de assembleias de voto na região no site, ou no endereço de um PEC você encontra informações sobre um PEC completamente diferente.



Isso não é um problema, pois para cada distrito, precisamos apenas encontrar pelo menos uma página correspondente no site.



Para validar a integridade dos dados, escrevemos um script simples que verifica se o conjunto de dados baixado do site UMG contém informações sobre cada constituinte. Se algo estiver faltando, nós reabastecemos o conjunto de dados manualmente. Novamente, houve menos de 10 dessas situações excepcionais em 1.100 distritos.



Combinando dados de sites UMG e CEC



Nesta fase, recolhemos uma estrutura de dados conveniente , com informações sobre cada candidato por distrito: ID do candidato, nome completo, partido, uma etiqueta com informação sobre se é apoiado pela UMG.



Exemplo de um conjunto de dados candidato coletado
{
    "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
        }
    ],
    ...
}




O algoritmo é bastante simples:



  1. Com base na matriz de dados do site da UMG, criamos uma lista de candidatos apoiados para cada distrito
  2. Usando a matriz de dados do site do CEC, criamos uma lista filtrada de candidatos admitidos para cada constituinte
  3. Em cada distrito, pelo nome completo, calculamos a correspondência Candidate-UMG-Candidate-CEC


É claro que esse algoritmo simples deve levar em conta muitas situações de problema em potencial.



Primeiro, há uma chance de que em um distrito haja candidatos com nomes completamente idênticos. Felizmente, entre 5.000 candidatos, tal situação ocorreu em apenas um caso, e nenhum dos candidatos foi apoiado pelo UMG.



Em segundo lugar, deve-se levar em conta que podem haver erros na base de dados do site da CEC. O erro mais comum: quebras de linha e espaços extras no nome completo. Além disso, na coleta de dados sobre o resultado da votação, houve uma situação em que a letra "" no sobrenome foi substituída por "e".



Terceiro, a relevância dos dados deve ser levada em consideração. Os dados no site do CEC e do UMG foram alterados e atualizados até sábado: alguns candidatos foram removidos / reintegrados, em alguns distritos o apoio do UMG mudou.



Para validar as listas UMG, foi escrito um script simples que faz uma solicitação por distrito (afinal, o conjunto de dados que coletamos agora nos permite identificar de forma única a página dedicada a cada distrito) e verifica se os nomes correspondem aos que recebemos anteriormente.



Uma tarefa interessante foi identificar os partidos pelo nome de suas filiais. Este ponto poderia ser ignorado, mas decidi fazer isso para unificar as informações. O problema é que os candidatos de um partido podem ter nomes diferentes no banco de dados CEC. Por exemplo, no caso do KPRF, havia mais de 40 opções:



  ()    "   " 
-   ""
  
     "   "
...


A situação se transforma em um problema de análise interessante, quando há 25 lotes e quase cada um tem uma grafia diferente para cada região. Felizmente, com a ajuda do meu amigo, que me ajudou em todo o trabalho manual, fizemos uma lista de palavras-chave pelas quais o partido do candidato é determinado de forma única.



Carregando os resultados da eleição do site do CEC



O conjunto de dados coletados foi suficiente para atingir o objetivo inicial do projeto - compilamos listas de candidatos UMG-2020 para cada constituinte. Mas se existe uma oportunidade técnica de obter os resultados das eleições, por que não aproveitá-la?






Resultados da eleição distrital



http://cikrf.ru/iservices/sgo-visual-rest/vibory/CAMPAIGN_VRN/results/DISTRICT_VRN/major


  • CAMPAIGN_VRN - ID da campanha
  • DISTRICT_VRN - ID do distrito


Exemplo de solicitação:

http://cikrf.ru/iservices/sgo-visual-rest/vibory/ 457422069597 / results / 457422069602 / major



Resultado da consulta
{
   "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"
   }
}




Como você pode ver, os resultados são devolvidos na forma de protocolo da comissão regional. Cada região difere no formato do protocolo e no número de linhas introdutórias nele, portanto, uma validação cuidadosa dos dados extraídos deve ser realizada.






Quando o GAS "Vybory" começou a publicar resultados preliminares, fiquei um pouco desapontado. Descobriu-se que por meio da API você pode obter dados apenas sobre os resultados que são aprovados oficialmente. Os resultados preliminares ainda podem ser vistos no antigo site da comissão eleitoral, mas não por meio dos novos serviços da web.



Um dia depois, conheciam-se os resultados de 50% e, no final da semana, foram somados os resultados de quase todas as eleições, algumas regiões ainda se recusavam a aprovar os resultados. No momento em que este artigo foi escrito, 7 dias se passaram e os resultados das eleições em Tambov ainda não foram aprovados. Além disso, em alguns distritos há uma recontagem, razão pela qual esses resultados também não estão disponíveis por meio da API.



Conclusão: os métodos de API atualmente não são adequados para o recebimento imediato dos resultados da votação. Você terá que esperar mais de uma semana para que os resultados sejam aprovados ou terá que analisar o antigo site da comissão eleitoral, encontrando uma maneira de contornar o captcha.



Estou cansado de esperar que as eleições sejam aprovadas em cerca de 30 dos 1100 constituintes, então escrevi um script usando a biblioteca de selênio que baixa dados do site clássico da comissão eleitoral e me pede para resolver manualmente o captcha de cada solicitação. Com um número tão pequeno de solicitações, não demora muito para resolver manualmente um captcha.



Como resultado, eu coletei os dados sobre os resultados da votação na seguinte estrutura :



Exemplo de resultados de votação de condado
{
...
"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
    },
...
}




Para cada distrito, salvei o número total de eleitores nas listas (para calcular a participação), o número de votos válidos e inválidos. A estrutura contém um dicionário: Identificador do candidato -> O número de votos que ele digitou.



Publicação dos resultados do UMG-2020



Primeiro, publiquei os dados coletados no formato JSON no GitHub . Os dados serão atualizados até que os resultados sejam validados em todos os distritos.



Em segundo lugar, para chamar a atenção para o projeto, decidi gerar uma planilha Google, que contém todos os dados coletados de uma forma conveniente para análise visual.



Não vou entrar em detalhes, nenhuma dificuldade (exceto para estudar a API do Google Sheets) deve surgir. Eu recomendo este artigo , que detalha a interação com a API do Planilhas Google em Python.



imagem



Como resultado, obtivemos a seguinte tabela, que contém:





Posfácio



A ideia deste mini-projeto surgiu 3 dias antes do dia da votação, e estou pessoalmente satisfeito com a forma como consegui estudar e implementar tudo no menor tempo possível (embora o código tenha se revelado péssimo).



Não vou tirar conclusões sobre os resultados da estratégia do Smart Voting, apenas forneci ferramentas para fãs de estatísticas eleitorais. Tenho certeza de que haverá alguns entre vocês e em breve veremos estudos maravilhosos, com gráficos e diagramas interessantes.



All Articles