Em maio deste ano, escrevi um post sobre web scraping dos dados da secretaria de segurança pública de São Paulo. Como o título indica, o foco do texto é mostrar como se raspa a página de estatísticas da SSP, mas, mais do que isso, o texto também sugere um roteiro de construção de web scrapers.
Neste post, venho divulgar uma expansão do conteúdo do post passado. Agora é possível acessar os dados da SSP diretamente no R usando o pacote brcrimR
, mas a ideia é que no futuro todas as informações divulgadas por alguma Secretaria de Segurança fiquem disponíveis diretamente no R!
devtools::install_github("abjur/brcrimR")
O brmcrimR
se propõe a resolver três problemas:
- Obter informações criminais brasileiras diretamente no R - Muitas análises interessantes seriam viabilizadas se fosse fácil e rápido carregar informações criminais históricas num
data_frame
. Fazer isso é a motivação principal dobrmcrimR
. - Consolidar tabelas em bases históricas - Assim como em São Paulo, muitas Secretarias de Segurança disponibilizam as informações filtradas por mês ou localidade. A segunda motivação principal do
brcrimR
é iterar por essas páginas. - Padronização - O objetivo menos direto do
brcrimR
é padronizar as informações disponibilizadas. Esse não é um problema simplesmente computacional, mas algumas rotinas de pré-processamento podem ajudar no processo.
Para ilustrar o funcionamento do brcrimR
, vamos olhar o que já está implementado em São Paulo.
Informações agregadas
As tabelas de informações agregadas podem ser obtidas seguindo os passos que descrevi aqui, mas a função brmcrimR::get_summarized_table_sp
faz todo o trabalho por nós.
brcrimR::get_summarized_table_sp(year = '2016', city = '1') %>%
knitr::kable(caption = "Contagem de boletins de ocorrência na cidade de Amparo.") %>%
kableExtra::kable_styling(font_size = 8)
Natureza | Jan | Fev | Mar | Abr | Mai | Jun | Jul | Ago | Set | Out | Nov | Dez | Total | municipio | ano |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
HOMICÍDIO DOLOSO (2) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 2016 |
Nº DE VÍTIMAS EM HOMICÍDIO DOLOSO (3) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 2016 |
HOMICÍDIO DOLOSO POR ACIDENTE DE TRÂNSITO | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2016 |
Nº DE VÍTIMAS EM HOMICÍDIO DOLOSO POR ACIDENTE DE TRÂNSITO | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2016 |
HOMICÍDIO CULPOSO POR ACIDENTE DE TRÂNSITO | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 2 | 1 | 2016 |
HOMICÍDIO CULPOSO OUTROS | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2016 |
TENTATIVA DE HOMICÍDIO | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 3 | 5 | 1 | 2016 |
LESÃO CORPORAL SEGUIDA DE MORTE | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2016 |
LESÃO CORPORAL DOLOSA | 4 | 19 | 13 | 13 | 12 | 15 | 15 | 8 | 16 | 23 | 23 | 10 | 171 | 1 | 2016 |
LESÃO CORPORAL CULPOSA POR ACIDENTE DE TRÂNSITO | 10 | 18 | 21 | 22 | 13 | 15 | 18 | 10 | 17 | 15 | 15 | 17 | 191 | 1 | 2016 |
LESÃO CORPORAL CULPOSA - OUTRAS | 1 | 1 | 2 | 1 | 0 | 3 | 0 | 0 | 0 | 0 | 0 | 1 | 9 | 1 | 2016 |
LATROCÍNIO | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2016 |
Nº DE VÍTIMAS EM LATROCÍNIO | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2016 |
TOTAL DE ESTUPRO (4) | 2 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 6 | 1 | 2016 |
ESTUPRO DE VULNERÁVEL | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 2016 |
TOTAL DE ROUBO - OUTROS (1) | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 4 | 1 | 2016 |
ROUBO DE VEÍCULO | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2016 |
ROUBO A BANCO | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2016 |
ROUBO DE CARGA | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2016 |
FURTO - OUTROS | 17 | 19 | 20 | 22 | 13 | 11 | 11 | 12 | 23 | 27 | 17 | 30 | 222 | 1 | 2016 |
FURTO DE VEÍCULO | 0 | 1 | 1 | 1 | 5 | 1 | 2 | 1 | 0 | 1 | 2 | 1 | 16 | 1 | 2016 |
Para obter os dados históricos, basta usar a função brcrimR::get_historical_summarized_table_sp
. Ela funciona da mesma maneira que brcrimR::get_summarized_table_sp
, mas pode receber vetores como input. Nesse caso, ela organiza os parâmetros num grid e retorna uma tabela com todas as requisições empilhadas.
brcrimR::get_historical_summarized_table_sp(
y = c('2016', '2017'), c = '1', ty = 'ctl00$conteudo$btnMensal') %>%
filter(Natureza == "LESÃO CORPORAL CULPOSA POR ACIDENTE DE TRÂNSITO") %>%
set_names(c('Natureza', 1:12, "Total", "municipio", "ano")) %>%
gather(mes, valor, -municipio, -ano, -Natureza, -Total) %>%
filter(!is.na(valor)) %>%
mutate(data_bo = lubridate::dmy(paste("01", mes, ano, sep = "-"))) %>%
ggplot(aes(x = data_bo, y = valor)) +
geom_bar(stat = 'identity', fill = 'royalblue') +
theme_minimal(15) +
labs(x = 'Mês', y = "Número de BO's")
Informações desagregadas
Além dos dados agregados, a partir do ano passado a SSP de São Paulo também passou a divulgar informações detalhadas sobre os BO’s da capital no portal da trasparência. O José de Jesus começou o scraping desses conteúdos e eu só encapsulei tudo dentro de um pacote. Vejam só:
brcrimR::get_detailed_table_sp(folder = 'btnHomicicio', year = '2017', month = '1', department = '0') %>%
select(NUM_BO, BO_INICIADO, DATAOCORRENCIA, BO_AUTORIA, FLAGRANTE, LATITUDE, LONGITUDE) %>%
distinct(NUM_BO, .keep_all = T) %>%
head(10) %>%
knitr::kable(caption = "Algumas colunas de dez BO's da tabela de boletins de ocorrência de homicídio de janeiro de 2017.") %>%
kableExtra::kable_styling(font_size = 8)
NUM_BO | BO_INICIADO | DATAOCORRENCIA | BO_AUTORIA | FLAGRANTE | LATITUDE | LONGITUDE |
---|---|---|---|---|---|---|
11589 | 31/12/2016 22:02:52 | 31/12/2016 | Conhecida | Não | -23,6027829499999 | -46,516050348 |
1761 | 31/12/2016 21:02:50 | 31/12/2016 | Conhecida | Não | -22,7974702349999 | -45,1786399879999 |
2915 | 31/12/2016 23:33:16 | 31/12/2016 | Conhecida | Sim | ||
3 | 01/01/2017 03:45:42 | 31/12/2016 | Conhecida | Não | -23,4663377281984 | -47,4644919148778 |
2 | 01/01/2017 03:40:13 | 01/01/2017 | Desconhecida | Não | -23,7062770598301 | -46,5997084663396 |
6 | 01/01/2017 04:25:12 | 01/01/2017 | Desconhecida | Não | -23,435339469 | -45,0776321386071 |
1 | 01/01/2017 02:30:11 | 31/12/2016 | Conhecida | Sim | -23,838385412 | -46,5778088709999 |
4 | 01/01/2017 04:53:21 | 01/01/2017 | Desconhecida | Não | -23,6528390081698 | -46,4471844195849 |
859 | 31/12/2016 21:35:30 | 31/12/2016 | Conhecida | Não | -23,6027829499999 | -46,516050348 |
13933 | 31/12/2016 21:09:59 | 31/12/2016 | Conhecida | Sim | -23,6221113323333 | -46,4778365256666 |
É fantástico que a gente tenha acesso a informações tão detalhadas como latitude e longitude de um crime, mas o formato não é lá essas coisas. Como os dados da SSP provavelmente estão armazenados em um banco de dados relacional, a tabela que baixamos parece ser um inner_join
de várias tabelas do banco, pois existem repetições da chave primária NUM_BO
. É claro que quem tem um pouco de experiência com esse tipo de coisa vai tirar de letra, mas esse certamente não é o melhor formato para a população em geral.
De toda forma, mesmo nesse formato, é interessante loopar por todas as páginas rapidamente. Por isso, também implementamos a função get_detailed_table_sp
, que funciona mais ou menos da mesma forma que a get_historical_summarized_table_sp
.
brcrimR::get_historical_detailed_table_sp(f = 'btnHomicicio', y = '2017', m = 1:8, d = '0')
Próximos passos
Encerro aqui a apresentação do pacote, com os seus objetivos e funcionalidades básicas. Ele está longe de ser ideal e tem uma listinha de coisas que queremos implementar no futuro, mas como se trata de dados abertos, não vejo porque não contar com a comunidade para isso! Seguem abaixo algumas ideias:
- Helper functions para os parâmetros das funções de get de São Paulo. Precisa existir uma função que pegue uma especificação de parâmetro do tipo “Homicídio” e transforme em “btnHomicicio”, que é o parâmetro que precisamos passar pro site do tribunal.
- Um “desacoplador” de tabelas do portal da transparência.
- Implementações de funções parecidas para outros estados.
Por hoje é isso, pessoal. Happy coding!