################################################################# ---
## 엑셀파일 유권자 투개표 : 국회의원 ##
## 개발자: 이광춘 ##
## 최종수정일: 2024-04-17 ##
################################################################# ---
# 제21대 국회의원 -------------
library(tidyverse)
library(readxl)
library(testthat)
library(here)
library(httr)
library(rvest)
##---------------------------------------------------------------- --
## 제22대 국회의원 --
##---------------------------------------------------------------- --
# 1. 데이터 -------------
## 1.1. 스크립트 --------
# 설정된 URL
url <- "http://info.nec.go.kr/electioninfo/electionInfo_report.xhtml"
# POST 요청을 위한 바디 데이터
body <- list(
electionId = "0020240410",
requestURI = "/electioninfo/0020240410/vc/vccp09.jsp",
topMenuId = "VC",
secondMenuId = "VCCP09",
menuId = "VCCP09",
statementId = "VCCP09_#2",
electionCode = "2",
cityCode = "1100",
sggCityCode = "2110101",
townCode = "-1",
sggTownCode = "0",
x = "52",
y = "20"
)
# POST 요청 실행
response <- POST(url, body = body, encode = "form")
# 응답으로 받은 HTML 확인
content_data <- content(response, as = "text", encoding = "UTF-8")
# HTML을 파싱하여 원하는 데이터 추출
html_data <- read_html(content_data)
# 예시: 특정 HTML 요소 선택 및 데이터 추출 (태그 이름, 클래스 등에 따라 변경 필요)
extracted_tbl <- html_data %>%
html_nodes("table") %>%
html_table() %>%
.[[1]]
v_contents <- extracted_tbl |>
janitor::clean_names(ascii = FALSE) |>
slice(3:n()) |>
filter(구시군명 != "")
v_varnames <- extracted_tbl |>
janitor::clean_names(ascii = FALSE) |>
slice(2) |>
pivot_longer(cols = everything()) |>
mutate(value = if_else(value == "", name, value)) |>
pull(value) |> dput()
v_tbl <- v_contents |>
set_names(v_varnames)
## 1.2. 함수 --------
get_votes <- function(cityCode = "1100", sggCityCode = "2110101") {
# 설정된 URL
url <- "http://info.nec.go.kr/electioninfo/electionInfo_report.xhtml"
# POST 요청을 위한 바디 데이터
body <- list(
electionId = "0020240410",
requestURI = "/electioninfo/0020240410/vc/vccp09.jsp",
topMenuId = "VC",
secondMenuId = "VCCP09",
menuId = "VCCP09",
statementId = "VCCP09_#2",
electionCode = "2",
cityCode = cityCode,
sggCityCode = sggCityCode,
townCode = "-1",
sggTownCode = "0",
x = "52",
y = "20"
)
# POST 요청 실행
response <- POST(url, body = body, encode = "form")
# 응답으로 받은 HTML 확인
content_data <- content(response, as = "text", encoding = "UTF-8")
# HTML을 파싱하여 원하는 데이터 추출
html_data <- read_html(content_data)
# 예시: 특정 HTML 요소 선택 및 데이터 추출 (태그 이름, 클래스 등에 따라 변경 필요)
extracted_tbl <- html_data %>%
html_nodes("table") %>%
html_table() %>%
.[[1]]
v_contents <- extracted_tbl |>
janitor::clean_names(ascii = FALSE) |>
slice(3:n()) |>
filter(구시군명 != "")
v_varnames <- extracted_tbl |>
janitor::clean_names(ascii = FALSE) |>
slice(2) |>
pivot_longer(cols = everything()) |>
mutate(value = if_else(value == "", name, value)) |>
pull(value) |> dput()
v_tbl <- v_contents |>
set_names(v_varnames)
return(v_tbl)
}
get_votes("1100", "2110402")
# 2. 데이터 저장 -------------
## 2.1. 시도 시군구 코드 --------
city_codes <- tribble(
~cityCode, ~cityName,
"1100", "서울특별시",
"2600", "부산광역시",
"2700", "대구광역시",
"2800", "인천광역시",
"2900", "광주광역시",
"3000", "대전광역시",
"3100", "울산광역시",
"5100", "세종특별자치시",
"4100", "경기도",
"5200", "강원특별자치도",
"4300", "충청북도",
"4400", "충청남도",
"5300", "전북특별자치도",
"4600", "전라남도",
"4700", "경상북도",
"4800", "경상남도",
"4900", "제주특별자치도"
)
print(city_codes)
## 2.2. 시군구 코드 --------
# 설정된 URL
url <- "http://info.nec.go.kr/electioninfo/electionInfo_report.xhtml"
# POST 요청을 위한 바디 데이터
body <- list(
electionId = "0020240410",
requestURI = "/electioninfo/0020240410/vc/vccp09.jsp",
topMenuId = "VC",
secondMenuId = "VCCP09",
menuId = "VCCP09",
statementId = "VCCP09_#2",
electionCode = "2",
cityCode = "1100",
sggCityCode = "2110101",
townCode = "-1",
sggTownCode = "0",
x = "52",
y = "20"
)
# POST 요청 실행
response <- POST(url, body = body, encode = "form")
# 응답으로 받은 HTML 확인
content_data <- content(response, as = "text", encoding = "UTF-8")
# HTML을 파싱하여 원하는 데이터 추출
html_data <- read_html(content_data)
sgg_name <- html_data |>
html_nodes(xpath = '//*[@id="sggCityCode"]/option') |>
html_text()
sgg_code <- html_data |>
html_nodes(xpath = '//*[@id="sggCityCode"]/option') |>
html_attr("value")
<- tibble(sgg_name, sgg_code) |>
filter(str_detect(sgg_name, "^[가-힣]"))
## 2.3. 선거구 함수 -------------------------------------------------------------
get_sgg_code <- function(cityCode = "1100") {
url <- "http://info.nec.go.kr/electioninfo/electionInfo_report.xhtml"
# POST 요청을 위한 바디 데이터
body <- list(
electionId = "0020240410",
requestURI = "/electioninfo/0020240410/vc/vccp09.jsp",
topMenuId = "VC",
secondMenuId = "VCCP09",
menuId = "VCCP09",
statementId = "VCCP09_#2",
electionCode = "2",
cityCode = cityCode,
sggCityCode = cityCode,
townCode = "-1",
sggTownCode = "0",
x = "52",
y = "20"
)
# POST 요청 실행
response <- POST(url, body = body, encode = "form")
# 응답으로 받은 HTML 확인
content_data <- content(response, as = "text", encoding = "UTF-8")
# HTML을 파싱하여 원하는 데이터 추출
html_data <- read_html(content_data)
sggCityName <- html_data |>
html_nodes(xpath = '//*[@id="sggCityCode"]/option') |>
html_text()
sggCityCode <- html_data |>
html_nodes(xpath = '//*[@id="sggCityCode"]/option') |>
html_attr("value")
sgg_tbl <- tibble(sggCityCode, sggCityName) |>
filter(str_detect(sggCityName, "^[가-힣]"))
return(sgg_tbl)
}
get_sgg_code()
## 2.4. 시도 선거구 코드 ---------------------------------------------------
city_sgg_raw <- city_codes |>
mutate(data = map(cityCode, get_sgg_code))
city_sgg_tbl <- city_sgg_raw |>
unnest(data)
city_sgg_tbl |>
write_csv("data/제22대_총선_NEC_선거구코드.csv")
# 3. 투개표 ------------------------------------------------------------------
city_sgg_tbl <-
read_csv("data/제22대_총선_NEC_선거구코드.csv")
# 3.1. 투개표 원데이터 -----------------------------------------------------------
get_votes_verbose <- function(cityCode = "1100", sggCityCode = "2110101") {
cat("=============================\n")
cat("시도 코드: ", cityCode, "\n")
cat("선거구 코드: ", sggCityCode, "\n")
cat("=============================\n")
# 설정된 URL
url <- "http://info.nec.go.kr/electioninfo/electionInfo_report.xhtml"
# POST 요청을 위한 바디 데이터
body <- list(
electionId = "0020240410",
requestURI = "/electioninfo/0020240410/vc/vccp09.jsp",
topMenuId = "VC",
secondMenuId = "VCCP09",
menuId = "VCCP09",
statementId = "VCCP09_#2",
electionCode = "2",
cityCode = cityCode,
sggCityCode = sggCityCode,
townCode = "-1",
sggTownCode = "0",
x = "52",
y = "20"
)
# POST 요청 실행
response <- POST(url, body = body, encode = "form")
# 응답으로 받은 HTML 확인
content_data <- content(response, as = "text", encoding = "UTF-8")
# HTML을 파싱하여 원하는 데이터 추출
html_data <- read_html(content_data)
# 예시: 특정 HTML 요소 선택 및 데이터 추출 (태그 이름, 클래스 등에 따라 변경 필요)
extracted_tbl <- html_data %>%
html_nodes("table") %>%
html_table() %>%
.[[1]]
if(cityCode == "5100") {
v_contents <- extracted_tbl |>
janitor::clean_names(ascii = FALSE) |>
slice(3:n()) |>
filter(시도명 != "")
v_varnames <- extracted_tbl |>
janitor::clean_names(ascii = FALSE) |>
slice(2) |>
pivot_longer(cols = everything()) |>
mutate(value = if_else(value == "", name, value)) |>
pull(value) |> dput()
} else {
v_contents <- extracted_tbl |>
janitor::clean_names(ascii = FALSE) |>
slice(3:n()) |>
filter(구시군명 != "")
v_varnames <- extracted_tbl |>
janitor::clean_names(ascii = FALSE) |>
slice(2) |>
pivot_longer(cols = everything()) |>
mutate(value = if_else(value == "", name, value)) |>
pull(value) |> dput()
}
v_tbl <- v_contents |>
set_names(v_varnames)
return(v_tbl)
}
votes_raw <- city_sgg_tbl |>
mutate(data = map2(cityCode, sggCityCode,
safely(get_votes_verbose, otherwise = NA_character_)))
votes_raw |>
write_rds("data/제22대_총선_NEC_투개표.rds")
# 4. 후처리 -----------------------------------------------------------------
## 4.1. 스크립트 ------------------------------------------------------------
votes_raw <-
read_rds("data/제22대_총선_NEC_투개표.rds")
precinct <- votes_raw |>
mutate(result = map(data, "result")) |>
slice(1) |>
pull(result) %>%
.[[1]]
common_tbl <- precinct |>
select( which(names(precinct) %in% c("시도명", "구시군명", "선거인수", "투표수", "계",
"무효투표수", "기권자수", "개표율")))
party_candidate <- precinct |>
select((which(names(precinct) == "투표수") + 2):which(names(precinct) == "계") -1) |>
names() |> dput()
precint_raw <- precinct |>
pivot_longer(cols = party_candidate,
names_to = "정당_후보", values_to = "득표수")
precinct_tbl <- precint_raw |>
select(which(names(precint_raw) %in% c("시도명", "구시군명", "선거인수", "투표수", "계",
"무효투표수", "기권자수", "개표율", "정당_후보", "득표수")))
precinct_tbl
## 4.2. 함수 ------------------------------------------------------------
clean_nec_raw <- function(precinct) {
common_tbl <- precinct |>
select( which(names(precinct) %in% c("시도명", "구시군명", "선거인수", "투표수", "계",
"무효투표수", "기권자수", "개표율")))
party_candidate <- precinct |>
select((which(names(precinct) == "투표수") + 2):which(names(precinct) == "계") -1) |>
names() |> dput()
precint_raw <- precinct |>
pivot_longer(cols = party_candidate,
names_to = "정당_후보", values_to = "득표수")
precinct_tbl <- precint_raw |>
select(which(names(precint_raw) %in% c("시도명", "구시군명", "선거인수", "투표수", "계",
"무효투표수", "기권자수", "개표율", "정당_후보", "득표수")))
precinct_tbl
}
vote_tbl <- votes_raw |>
mutate(result = map(data, "result")) |>
mutate(clean_data = map(result, clean_nec_raw)) |>
select(cityName, sggCityName, clean_data) |>
unnest(clean_data)
precinct_type <- vote_tbl |>
group_by(cityName, sggCityName) |>
summarise(구시군명 = paste0(구시군명, collapse = ", ")) |>
ungroup() |>
mutate(선거구구분 = if_else( str_detect(구시군명, "소계"), "복합", "단일"))
general_2024 <- vote_tbl |>
left_join(precinct_type |> select(-구시군명), by = c("cityName", "sggCityName")) |>
mutate(구분자 = case_when(선거구구분 == "복합" & 구시군명 == "소계" ~ "추출",
선거구구분 == "단일" ~ "추출",
TRUE ~ "제거")) |>
filter(구분자 == "추출") |>
# 세종
mutate(구시군명 = if_else(is.na(구시군명), 시도명, 구시군명)) |>
select(-c(선거구구분, 구분자, 시도명)) |>
relocate(정당_후보, .before = 선거인수) |>
mutate(across(선거인수:득표수, parse_number)) |>
# 정당, 후보
mutate(정당 = case_when(str_detect(정당_후보, "국민의힘") ~ "국민의힘",
str_detect(정당_후보, "더불어민주당") ~ "더불어민주당",
str_detect(정당_후보, "무소속") ~ "무소속",
str_detect(정당_후보, "개혁신당") ~ "개혁신당",
str_detect(정당_후보, "새로운미래") ~ "새로운미래",
str_detect(정당_후보, "진보당") ~ "진보당",
str_detect(정당_후보, "녹색정의당") ~ "녹색정의당",
str_detect(정당_후보, "자유통일당") ~ "자유통일당",
str_detect(정당_후보, "한국국민당") ~ "한국국민당",
str_detect(정당_후보, "내일로미래로") ~ "내일로미래로",
str_detect(정당_후보, "소나무당") ~ "소나무당",
str_detect(정당_후보, "우리공화당") ~ "우리공화당",
str_detect(정당_후보, "자유민주당") ~ "자유민주당",
str_detect(정당_후보, "한국농어민당") ~ "한국농어민당",
str_detect(정당_후보, "가락특권폐지당") ~ "가락특권폐지당",
str_detect(정당_후보, "기독당") ~ "기독당",
str_detect(정당_후보, "기후민생당") ~ "기후민생당",
str_detect(정당_후보, "노동당") ~ "노동당",
str_detect(정당_후보, "대한국민당") ~ "대한국민당",
str_detect(정당_후보, "민중민주당") ~ "민중민주당",
str_detect(정당_후보, "새진보연합") ~ "새진보연합")) |>
mutate(후보 = str_remove(정당_후보, 정당)) |>
select(-정당_후보) |>
relocate(c(정당, 후보), .before = 선거인수) |>
rename(시도명 = cityName, 선거구 = sggCityName)
# mutate(후보 = str_sub(정당_후보, -3, end = -1)) |>
# mutate(정당 = str_remove(정당_후보, 후보)) |>
general_2024 |>
filter(str_detect(선거구, "분당")) |>
select(시도명, 선거구, 구시군명, 정당, 후보)
### 데이터 정합성 확인
test_that("국회선거 2024 성남시분당구을 후보득표검증", {
congress_check_df <- general_2024 %>%
filter(시도명 == "경기도" & 선거구 == "성남시분당구을") |>
select(후보, 득표수) |>
pivot_wider(names_from = 후보, values_from = 득표수)
expect_that( congress_check_df$`김병욱`, equals(66196))
expect_that( congress_check_df$`김은혜`, equals(69259))
})
# 5. 내보내기 ----------------------------------------------------------------
general_2024 <- krvote::clean_varnames(general_2024)
# general_2024 <- general_2024 %>%
# mutate(data = map(data, krvote::clean_varnames))
usethis::use_data(general_2024, overwrite = TRUE)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.