AI SCHOOL/project

[mini project] 동의종료 청원 데이터 수집

moru_xz 2023. 1. 29. 19:40

https://petitions.assembly.go.kr/closed/agreeEnded

 

requests, bs4, pandas, time 불러오기

import time
from bs4 import BeautifulSoup as bs
import pandas as pd
import requests
page_no = 1
url = f'<https://petitions.assembly.go.kr/api/petits?pageIndex={page_no}&recordCountPerPage=8&sort=AGRE_END_DE-&searchCondition=sj&searchKeyword=&petitRealmCode=&sttusCode=PETIT_FORMATN,CMIT_FRWRD,PETIT_END&resultCode=BFE_OTHBC_WTHDRAW,PROGRS_WTHDRAW,PETIT_UNACPT,APPRVL_END_DSUSE,ETC_TRNSF¬InColumn=RESULT_CODE&beginDate=20180101&endDate=20230122&ageCd=>'
response = requests.get(url)
pd.read_json(url, encoding = 'utf-8').info() #98개의 칼럼이 있는 것을 확인
df = pd.read_json(url, encoding = 'utf-8').dropna(axis = 1)
df.info() #45개의 칼럼만 남은 것을 확인
df = df[['petitRealmNm','petitSj', 'resultCodeNm', 'agreBeginDe', 'agreEndDe', 'petitEndDt', 'agreCo', 'petitCn', 'petitId']]
  • 칼럼 이름'petitSj' : 청원분야'agreBeginDe' : 동의 시작일'petitEndDt' : 청원 종료일'petitCn' : 청원 내용
  • 'petitId' : id
  • 'agreCo' : 동의수
  • 'agreEndDe' : 동의 마감일
  • 'resultCodeNm' : 청원결과
  • 'petitRealmNm' : 제목

목록 수집 함수

def get_list(page_no) :
    try :
        url_list = f'<https://petitions.assembly.go.kr/api/petits?pageIndex={page_no}&recordCountPerPage=8&sort=AGRE_END_DE-&searchCondition=sj&searchKeyword=&petitRealmCode=&sttusCode=PETIT_FORMATN,CMIT_FRWRD,PETIT_END&resultCode=BFE_OTHBC_WTHDRAW,PROGRS_WTHDRAW,PETIT_UNACPT,APPRVL_END_DSUSE,ETC_TRNSF¬InColumn=RESULT_CODE&beginDate=20180101&endDate=20230122&ageCd=>'
        df = pd.read_json(url_list, encoding = 'utf-8')
        df = df[['petitRealmNm','petitSj', 'resultCodeNm','agreBeginDe', 'agreEndDe', 'petitEndDt', 'agreCo', 'petitCn', 'petitId']]
        return df
    except Exception as e :
        print(f'이 {page_no}은 없는 페이지 입니다.{e}')

url 뒷부부분 beginDate=20180101&endDate=20230122&ageCd=> 여기가 날짜를 의미하는 것임

그냥 처음 들어갔을 때 그대로 하면 1년간의 데이터만 수집할 수 있어서 날짜를 맞추고 나서 url 을 긁어와야 함

 

 

마지막 페이지 까지 수집하는 함수

from tqdm import trange
import time
last_page = 79
page_list = []
for page_no in trange(1, last_page + 1) :
    result = get_list(page_no)
    page_list.append(result)
    time. sleep(0.01)

# 모든 페이지 합치기
df = pd.concat(page_list) 

ID를 가지고 청원 취지 가져오는 함수

def get_content(petit_id) :
    try :
        content_url = f'<https://petitions.assembly.go.kr/api/petits/{petit_id}?petitId={petit_id}&sttusCode=>'
        response = requests.get(content_url)
        cont = response.json()['petitObjet']
        return cont
    
    except Exception as e :
        print(f'이 {petit_id}는 없는 아이디 입니다.{e}')

모두 적용하는 함수

# 모두 적용
from tqdm.notebook import tqdm
tqdm.pandas()
view = df['petitId'].progress_map(get_content)
view

df['petitObjet'] = view

저장

file_name = "국민동의청원수집.csv"
df.to_csv(file_name, index=False)

시각화 목적

  • 사람들의 동의수를 많이 받은 청원분야는 무엇인가
  • 연도별로 사람들의 많은 관심을 받은 청원 분야가 무엇이었는가
  • 50,000 이상의 동의수를 얻어 성립된 청원들은 어떤 내용들로 구성되어 있는가

라이브러리 및 데이터 불러오기

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.pyplot as plt
plt.rc('font', family='NanumBarunGothic') # 폰트 설정
plt.rc('axes', unicode_minus=False) # 마이너스 폰트 설정
%config InlineBackend.figure_format='retina' # 그래프 글씨 뚜렷
df = pd.read_csv('/content/drive/MyDrive/petitions.assembly.csv')

청원분야별 청원수

df[['청원 분야', '동의수']].groupby(df['청원 분야'], as_index = True).count()['청원 분야'].plot.bar(figsize = (12, 6))

청원 결과 한눈에 보기

pd.crosstab(df['청원 분야'], df['청원 결과'], margins = True).style.background_gradient(cmap='summer_r')

연도별 동의수가 많은 주제 알아보기

# 동의 시작 날짜 2020~2022 
df_2022 = df[df['동의 시작 날짜'].str.contains('2022')]
df_2021 = df[df['동의 시작 날짜'].str.contains('2021')]
df_2020 = df[df['동의 시작 날짜'].str.contains('2020')]
agr2022 = df_2022[['청원 분야', '동의수']]
agr2022 = agr2022.groupby(agr2022['청원 분야'], as_index = True).sum()

agr2021 = df_2021[['청원 분야', '동의수']]
agr2021 = agr2021.groupby(agr2021['청원 분야'], as_index = True).sum()

agr2020 = df_2020[['청원 분야', '동의수']]
agr2020 = agr2020.groupby(agr2020['청원 분야'], as_index = True).sum()
data = pd.concat([agr2020, agr2021, agr2022], axis = 1)
data.columns = '2020', '2021', '2022'

# 연도별 동의수가 많은 카테고리
data.plot(kind = 'bar', figsize = (20, 10), title = '연도별', fontsize = 12)
plt.show()
  • 사람들의 동의수를 많이 받은 청원분야는 무엇인가
  • 연도별로 사람들의 많은 관심을 받은 청원 분야가 무엇이었는가
  • 50,000 이상의 동의수를 얻어 성립된 청원들은 어떤 내용들로 구성되어 있는가

라이브러리 및 데이터 불러오기

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.pyplot as plt
plt.rc('font', family='NanumBarunGothic') # 폰트 설정
plt.rc('axes', unicode_minus=False) # 마이너스 폰트 설정
%config InlineBackend.figure_format='retina' # 그래프 글씨 뚜렷
df = pd.read_csv('/content/drive/MyDrive/petitions.assembly.csv')

청원분야별 청원수

df[['청원 분야', '동의수']].groupby(df['청원 분야'], as_index = True).count()['청원 분야'].plot.bar(figsize = (12, 6))

청원 결과 한눈에 보기

pd.crosstab(df['청원 분야'], df['청원 결과'], margins = True).style.background_gradient(cmap='summer_r')

연도별 동의수가 많은 주제 알아보기

# 동의 시작 날짜 2020~2022 
df_2022 = df[df['동의 시작 날짜'].str.contains('2022')]
df_2021 = df[df['동의 시작 날짜'].str.contains('2021')]
df_2020 = df[df['동의 시작 날짜'].str.contains('2020')]
agr2022 = df_2022[['청원 분야', '동의수']]
agr2022 = agr2022.groupby(agr2022['청원 분야'], as_index = True).sum()

agr2021 = df_2021[['청원 분야', '동의수']]
agr2021 = agr2021.groupby(agr2021['청원 분야'], as_index = True).sum()

agr2020 = df_2020[['청원 분야', '동의수']]
agr2020 = agr2020.groupby(agr2020['청원 분야'], as_index = True).sum()
data = pd.concat([agr2020, agr2021, agr2022], axis = 1)
data.columns = '2020', '2021', '2022'

# 연도별 동의수가 많은 카테고리
data.plot(kind = 'bar', figsize = (20, 10), title = '연도별', fontsize = 12)
plt.show()
  • 사람들의 동의수를 많이 받은 청원분야는 무엇인가
  • 연도별로 사람들의 많은 관심을 받은 청원 분야가 무엇이었는가
  • 50,000 이상의 동의수를 얻어 성립된 청원들은 어떤 내용들로 구성되어 있는가

라이브러리 및 데이터 불러오기

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.pyplot as plt
plt.rc('font', family='NanumBarunGothic') # 폰트 설정
plt.rc('axes', unicode_minus=False) # 마이너스 폰트 설정
%config InlineBackend.figure_format='retina' # 그래프 글씨 뚜렷
df = pd.read_csv('/content/drive/MyDrive/petitions.assembly.csv')

청원분야별 청원수

df[['청원 분야', '동의수']].groupby(df['청원 분야'], as_index = True).count()['청원 분야'].plot.bar(figsize = (12, 6))

청원결과 한눈에 보기

pd.crosstab(df['청원 분야'], df['청원 결과'], margins = True).style.background_gradient(cmap='summer_r')

연도별 동의수가 많은 주제 알아보기

# 동의 시작 날짜 2020~2022 
df_2022 = df[df['동의 시작 날짜'].str.contains('2022')]
df_2021 = df[df['동의 시작 날짜'].str.contains('2021')]
df_2020 = df[df['동의 시작 날짜'].str.contains('2020')]
agr2022 = df_2022[['청원 분야', '동의수']]
agr2022 = agr2022.groupby(agr2022['청원 분야'], as_index = True).sum()

agr2021 = df_2021[['청원 분야', '동의수']]
agr2021 = agr2021.groupby(agr2021['청원 분야'], as_index = True).sum()

agr2020 = df_2020[['청원 분야', '동의수']]
agr2020 = agr2020.groupby(agr2020['청원 분야'], as_index = True).sum()
data = pd.concat([agr2020, agr2021, agr2022], axis = 1)
data.columns = '2020', '2021', '2022'

# 연도별 동의수가 많은 카테고리
data.plot(kind = 'bar', figsize = (20, 10), title = '연도별', fontsize = 12)
plt.show()

워드클라우드

목적

  • 사람들의 동의수를 많이 받은 청원분야는 무엇인가
  • 연도별로 사람들의 많은 관심을 받은 청원 분야가 무엇이었는가
  • 50,000 이상의 동의수를 얻어 성립된 청원들은 어떤 내용들로 구성되어 있는가

라이브러리 및 데이터 불러오기

!sudo apt-get install -y fonts-nanum
!sudo fc-cache -fv
!rm ~/.cache/matplotlib -rf  #colab 사용
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.pyplot as plt
plt.rc('font', family='NanumBarunGothic') # 폰트 설정
plt.rc('axes', unicode_minus=False) # 마이너스 폰트 설정
%config InlineBackend.figure_format='retina' # 그래프 글씨 뚜렷
df = pd.read_csv('/content/drive/MyDrive/petitions.assembly.csv')

연도별 성립된 청원 데이터 정리

agr = df[df['청원 결과'] != '동의만료폐기']
# 동의 시작 날짜가 object고 날짜 데이터가 따로 필요하지 않아서 str.contains로 찾았습니다
pass_2022 = agr[agr['동의 시작 날짜']. str.contains('2022')].reset_index(drop = True)
pass_2021 = agr[agr['동의 시작 날짜']. str.contains('2021')].reset_index(drop = True)
pass_2020 = agr[agr['동의 시작 날짜']. str.contains('2020')].reset_index(drop = True)

pass_2022.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 24 entries, 0 to 23
Data columns (total 8 columns):
 #   Column    Non-Null Count  Dtype 
---  ------    --------------  ----- 
 0   청원 분야     24 non-null     object
 1   청원 주제     24 non-null     object
 2   청원 취지     24 non-null     object
 3   청원 결과     24 non-null     object
 4   동의수       24 non-null     int64 
 5   청원 내용     24 non-null     object
 6   동의 시작 날짜  24 non-null     object
 7   동의 종료 날짜  24 non-null     object
dtypes: int64(1), object(7)
memory usage: 1.6+ KB

데이터를 확인해 보니 양이 많지 않아 내용을 word cloud 사용하여 정리하기로 함

텍스트만 뽑기

content_22 = ''
for i in range(len(pass_22['청원 내용'])) :
               content_22 = content_22 + ' ' + pass_22['청원 내용'].loc[i]

content_21 = ''
for i in range(len(pass_21['청원 내용'])) :
               content_21 = content_21 + ' ' + pass_21['청원 내용'].loc[i]

content_20 = ''
for i in range(len(pass_20['청원 내용'])) :
               content_20 = content_20 + ' ' + pass_20['청원 내용'].loc[i]

단어 전처리

import re
content_20 = re.sub("[^가-힣ㄱ-ㅎㅏ-ㅣ]", " ",  content_20)
content_21 = re.sub("[^가-힣ㄱ-ㅎㅏ-ㅣ]", " ",  content_21)
content_22 = re.sub("[^가-힣ㄱ-ㅎㅏ-ㅣ]", " ",  content_22)
!pip install matplotlib-venn
!apt-get -qq install -y libfluidsynth1
!curl -s <https://raw.githubusercontent.com/teddylee777/machine-learning/master/99-Misc/01-Colab/mecab-colab.sh> | bash
# colab에서 konlpy 쓰기 위해
!pip install customized_konlpy # colab에서 Tiwtter 쓰기 위해
from konlpy.tag import Okt
okt = Okt()
nouns20_txt = okt.nouns(content_20)
nouns20_txt

from ckonlpy.tag import Twitter # 기존의 단어 품사 바꾸는 건 안 됨
Twitter = Twitter()

Twitter.add_dictionary('낙태허용', 'Noun')
Twitter.add_dictionary('낙태 허용', 'Noun')
Twitter.add_dictionary('심장박동', 'Noun')
Twitter.add_dictionary('심장 박동', 'Noun')
Twitter.add_dictionary('약물처방', 'Noun')
Twitter.add_dictionary('약물 처방', 'Noun')
Twitter.add_dictionary('계획안', 'Noun')
Twitter.add_dictionary('계획 안', 'Noun')
Twitter.add_dictionary('대통령 특별 담화 요구', 'Noun')
Twitter.add_dictionary('대통령특별담화요구', 'Noun')
Twitter.add_dictionary('초 중등교육', 'Noun')
Twitter.add_dictionary('초중등교육육', 'Noun')
Twitter.add_dictionary('교육기관', 'Noun')
Twitter.add_dictionary('교육 기관', 'Noun')
Twitter.add_dictionary('불법낙태', 'Noun')
Twitter.add_dictionary('불법 낙태', 'Noun')
Twitter.add_dictionary('약 처방', 'Noun')
Twitter.add_dictionary('약처방', 'Noun')
Twitter.add_dictionary('임신중단', 'Noun')
Twitter.add_dictionary('임신 중단', 'Noun')
Twitter.add_dictionary('임신중단시술', 'Noun')
Twitter.add_dictionary('임신 중단 시술술', 'Noun')
Twitter.add_dictionary("n번방", "Noun")
Twitter.add_dictionary("n 번방", "Noun")
Twitter.add_dictionary("텔레그램", "Noun")
Twitter.add_dictionary("텔레 그램", "Noun")
Twitter.add_dictionary("법 개정", "Noun")
Twitter.add_dictionary("생계 보호", "Noun")
Twitter.add_dictionary("생계보호호", "Noun")
Twitter.add_dictionary("사업추진", "Noun")
Twitter.add_dictionary("사업 추진진", "Noun")
Twitter.add_dictionary("근로제한", "Noun")
Twitter.add_dictionary("근로 제한한", "Noun")
Twitter.add_dictionary("국가 지자체", "Noun")
Twitter.add_dictionary("국가지자체체", "Noun")
Twitter.add_dictionary("계약 체결", "Noun")
Twitter.add_dictionary("계약체결결", "Noun")
Twitter.add_dictionary("법률 개정안", "Noun")
Twitter.add_dictionary("법률개정안", "Noun")
okt = Okt()
nouns20_txt = okt.nouns(content_20)
nouns21_txt = okt.nouns(content_21)
nouns22_txt = okt.nouns(content_22)

워드클라우드 라이브러리 불러오기

!pip install wordcloud
from wordcloud import WordCloud
from PIL import Image
!apt-get update -qq
!apt-get install fonts-nanum* -qq
!pip install matplotlib-venn
!apt-get -qq install -y libfluidsynth1
import matplotlib.font_manager as fm
sys_font = fm.findSystemFonts()
[f for f in sys_font if 'Nanum' in f]
img_path = '/content/drive/MyDrive/images_bluespot9_post_cd03caf8-d809-4b9f-b4d2-7a932e696932_cloud.png'

2020년 워드클라우드

from collections import Counter
count20 = Counter(nouns20_txt)
count20
rank_text20 = count20.most_common()
rank_text20 = dict(rank_text20)
count20_len = 4 #4번 미만 나온 단어들은 빼기기
temp_dic20 = {}
for key, value in rank_text20.items():
    if value > count20_len:
        temp_dic20[key] = value
rank_text20 = temp_dic20

[rank_text20.pop(key) for key in ['법', '수', '등', '것', '제', '대한', '의사', '조', '년', '이',
                                  '일', '명', '위', '그', '위해', '및', '월', '정', '자', '관', '만', '의',
                                  '중', '로', '전', '초']]
 
import wordcloud
flower_mask = np.array(Image.open(img_path))
wordcloud = WordCloud(font_path =  '/usr/share/fonts/truetype/nanum/NanumGothic.ttf',
                      background_color = 'white', mask = flower_mask)
wc = wordcloud.generate_from_frequencies(rank_text20)
plt.figure(figsize = (8,15))
plt.imshow(wc)
plt.imshow(wc, interpolation= 'bilinear')
plt.axis('off')
plt.show()

2021년 워드 클라우드

nouns21_txt = okt.nouns(content_21)
nouns21_txt

okt = Okt()
nouns21_txt = okt.nouns(content_21)
nouns21_txt

from collections import Counter
count21 = Counter(nouns21_txt)
count21
rank_text21 = count21.most_common()
rank_text21

rank_text21 = dict(rank_text21)
count21_len = 4
temp_dic21 = {}
for key, value in rank_text21.items():
    if value > count21_len:
        temp_dic21[key] = value
rank_text21 = temp_dic21

[rank_text21.pop(key) for key in ['것', '수', '등', '법', '년', '제', '위', '이', '및', '말', '위해', '명', '의',
                                  '저', '그', '함', '만', '전', '조', '중']]

import wordcloud
flower_mask = np.array(Image.open(img_path))
wordcloud = WordCloud(font_path =  '/usr/share/fonts/truetype/nanum/NanumGothic.ttf',
                      background_color = 'white', mask = flower_mask)
wc = wordcloud.generate_from_frequencies(rank_text21)
plt.figure(figsize = (8,15))
plt.imshow(wc)
plt.imshow(wc, interpolation= 'bilinear')
plt.axis('off')
plt.show()

 

2022년 워드 클라우드

nouns22_txt = okt.nouns(content_22)
nouns22_txt

okt = Okt()
nouns22_txt = okt.nouns(content_22)
nouns22_txt

from collections import Counter
count22 = Counter(nouns22_txt)
count22
rank_text22 = count22.most_common()
rank_text22

rank_text22 = dict(rank_text22)
count22_len = 4
temp_dic22 = {}
for key, value in rank_text22.items():
    if value > count22_len:
        temp_dic22[key] = value
rank_text22 = temp_dic22
rank_text22

[rank_text22.pop(key) for key in ['것', '수', '년', '등', '제', '법', '기관', '이', '위', '조', '일', '및', '월',
                                  '그', '회', '위해', '또한', '안', '명', '해당', '때문', '대해', '함', '모든',
                                  '때', '더', '항', '전', '투', '중', '사항', '뿐', '정', '다른', '내']]
 
import wordcloud
flower_mask = np.array(Image.open(img_path))
wordcloud = WordCloud(font_path =  '/usr/share/fonts/truetype/nanum/NanumGothic.ttf',
                      background_color = 'white', mask = flower_mask)
wc = wordcloud.generate_from_frequencies(rank_text22)
plt.figure(figsize = (8,15))
plt.imshow(wc)
plt.imshow(wc, interpolation= 'bilinear')
plt.axis('off')
plt.show()


 

  • 좋았던 것(Liked) : 새로운 라이브러리를 사용해봤다. (word cloud) / 강사님 도움 없이 처음으로 한 제이터 
  • 배운 것(Learned) : 일단 해보자라는 마음을 배운 것 같다. 그 전까지만 해도 안 배운 부분을 어떻게 혼자하지 라는 생각을 했는데 구글링해서 찾아본 것을 바탕으로 일단 해보면 완성하지는 못하더라도 그 과정을 알아가며 성장할 수 있었다. 
  • 부족했던 것(Lacked) : 시각화 실력. matplolib 과 seaborn을 아주 조금 다룰 수 있어서 그걸 바탕으로 그래프를 만들다보니 한계가 있었다. 그리고 수박 겉핥기식 배움이라 한 번 각 잡고 기초부터 다져야겠다. 
  • 바라는 것(Longed for) : 노력