[빅 리더 AI] 크롤러 자습

KrTeaparty·2022년 7월 9일
0

빅 리더 AI

목록 보기
6/7

국민 청원 크롤링

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
import urllib.request
import urllib
import time
import pandas as pd    
import os
import math

print('='*50)
print(" 국민청원 정보 추출하기 ")
print('='*50)

department = ['정치/선거/국회운영','수사/법무/사법제도','재정/세제/금융/예산',
             '소비자/공정거래','교육','과학기술/정보통신','외교/통일/국방/안보',
             '재난/안전/환경','행정/지방자치','문화/체육/관광/언론','농업/임업/수산업/축산업',
             '산업/통상','보건의료','복지/보훈','국토/해양/교통','인권/성평등/노동',
             '저출산/고령화/아동/청소년/가족','기타','카테고리미선택']
for i, w in enumerate(department):
    print('{}.{}'.format(i+1, w), end=' ')
    if (i+1) % 3 == 0:
        print('\n')

print('\n')
dep_no = int(input('1.위 분야 중에서 자료를 수집할 분야의 번호를 선택하세요: '))
cnt = int(input('2.크롤링 할 건수는 몇 건입니까?: '))
page_cnt = math.ceil(cnt/8) # 한 페이지에 8개
f_dir = 'C:/Users/HJK/Desktop/data_kyungnam/crawler/소스코드/result/'


# 폴더, 파일명 설정
dep_name = department[dep_no-1].replace('/','_')
query_txt = '국민청원'

n = time.localtime()
s1 = '%04d-%02d-%02d-%02d-%02d-%02d' %(n.tm_year, n.tm_mon, n.tm_mday, n.tm_hour, n.tm_min, n.tm_sec)

os.makedirs(f_dir+s1+'-'+query_txt+'-'+dep_name)
os.chdir(f_dir+s1+'-'+query_txt+'-'+dep_name)

ff_dir=f_dir+s1+'-'+query_txt+'-'+dep_name
ff_name=f_dir+s1+'-'+query_txt+'-'+dep_name+'\\'+s1+'-'+query_txt+'-'+dep_name+'.txt'
fc_name=f_dir+s1+'-'+query_txt+'-'+dep_name+'\\'+s1+'-'+query_txt+'-'+dep_name+'.csv'
fx_name=f_dir+s1+'-'+query_txt+'-'+dep_name+'\\'+s1+'-'+query_txt+'-'+dep_name+'.xls'

# 접속
options = webdriver.ChromeOptions()
options.add_argument('headless')
args = ["hide_console",]
options.add_argument("disable-infobars");

s = Service("C:/Users/HJK/Desktop/data_kyungnam/crawler/chrome_driver/chromedriver.exe")
driver = webdriver.Chrome(service=s, options=options, service_args=args)

query_url = 'https://petitions.assembly.go.kr/status/onGoing'
driver.get(query_url)
driver.maximize_window()
time.sleep(5)

if dep_no != 19:
    driver.find_element(By.XPATH, '//*[@id="contentsbody"]/div/div/div[3]/div/div[1]').click()
    time.sleep(1)
    driver.find_element(By.XPATH, '//*[@id="contentsbody"]/div/div/div[3]/div/div[2]/ul/li[{}]'.format(dep_no)).send_keys(Keys.ENTER)
    time.sleep(1)

total = int(driver.find_element(By.XPATH, '//*[@id="contentsbody"]/div/div/div[4]/div[2]/div[1]/div/span').text)
if total < cnt:
    cnt = total
# 수집할 내용: 카테고리, 제목, 동의 수, 목표 동의 달성율, 동의 기간, 남은 시간
# 본문에 들어가서 수집할 내용: 청원의 취지, 청원의 내용
category2=[]
title2=[]
agree2=[]
ratio2=[]
date2=[]
dday2=[]
opinion2=[]
content2=[]

count = 1

for p in range(1, page_cnt+1):
    html = driver.page_source
    soup = BeautifulSoup(html, 'html.parser')
    
    item_list = soup.find('div','ListDiv').find('ul').find_all('li')
    no = 1
    for item in item_list:
        if cnt < count:
                break
        try:
            title = item.find('a').find('dl').find('dd').get_text().replace('\n','').strip()
        except:
            continue
        else:
            f = open(ff_name, 'a', encoding='UTF-8')
            f.write("-"*30+'\n')
            print('%s번째 정보 수집' %count)
            print('-'*30)

            # 카테고리
            try:
                category = item.find('a').find('dl').find('dt').get_text().replace('\n','').strip()
            except:
                category = ''
                print('-----카테고리 예외 발생-----')
            print('1.카테고리:', category)
            f.write('1.카테고리:' + category + '\n')

            # 제목
            print('2.제목:', title)
            f.write('2.제목:'+title+'\n')

            # 동의수
            try:
                item.find('div','agreegraph').find('p','hidden').decompose()
                agree = item.find('div','agreegraph').find('li').get_text().replace('\n','').strip()
            except:
                agree = ''
                print('-----동의자수 예외-----')
            print('3.동의자수:',agree)
            f.write('3.동의자수:'+agree+'\n')

            # 동의 달성률
            try:
                ratio = item.find('div','agreegraph').find('ul').find_all('li')[1].get_text().replace('\n','').strip()
            except:
                ratio = ''
                print('-----동의달성률 예외 발생-----')
            print('4.동의 달성률:', ratio)
            f.write('4.동의 달성률:'+ratio+'\n')

            # 동의기간
            try:
                date = item.find('div','agreeDate').find('dl').find('dd').get_text().replace('\n','').replace(' ','').strip()
            except:
                date = ''
                print('-----동의기간 예외 발생-----')
            print('5.동의기간:', date)
            f.write('5.동의기간:'+date+'\n')

            # 남은 기간
            try:
                dday = item.find('div','dDay').get_text().replace('\n','').strip()
            except:
                dday = ''
                print('-----디데이 예외 발생-----')
            print('6.남은기간:',dday)
            f.write('6.남은기간:'+dday+'\n')

            # 본문 진입
            driver.find_element(By.XPATH, '//*[@id="contentsbody"]/div/div/div[4]/div[2]/div[2]/ul/li[{}]/div/a'.format(no)).send_keys(Keys.ENTER)
            time.sleep(2)
            html2 = driver.page_source
            soup2 = BeautifulSoup(html2, 'html.parser')

            # 청원 취지
            try:
                opinion = soup2.find('div','petitionTb').find('dd','pre').get_text().strip()
            except:
                opinion = ''
                print('-----청원 취지 예외 발생-----')
            print('7.청원의 취지:\n',opinion)
            f.write('7.청원의 취지:'+opinion+'\n')

            # 청원 내용
            try:
                content = soup2.find('dd', 'pre contentTxt').get_text().strip()
            except:
                content = ''
                print('-----청원 내용 예외 발생-----')
            print('8.청원의 내용:\n',content)
            f.write('8.청원의 내용:'+content+'\n')

            print('-'*30)

            f.close()
            driver.back()
            time.sleep(2)

            category2.append(category)
            title2.append(title)
            agree2.append(agree)
            ratio2.append(ratio)
            date2.append(date)
            dday2.append(dday)
            opinion2.append(opinion)
            content2.append(content)

            count += 1
            no += 1
    p += 1
    if cnt < count:
        break
        
    try:
        driver.find_element(By.LINK_TEXT, '%s' %p).click()
    except:
        driver.find_element(By.LINK_TEXT, '다음').click()
    time.sleep(2)
    
driver.close()

# 요약
print('='*30)
print('총 %s건의 정보가 수집되었습니다.' %(count-1))
print('='*30)

# csv, xls 형태로 저장
chungwon = pd.DataFrame()
chungwon['카테고리'] = pd.Series(category2)
chungwon['제목'] = pd.Series(title2)
chungwon['동의자수'] = pd.Series(agree2)
chungwon['동의달성률'] = pd.Series(ratio2)
chungwon['동의기간'] = pd.Series(date2)
chungwon['남은기간'] = pd.Series(dday2)
chungwon['취지'] = pd.Series(opinion2)
chungwon['내용'] = pd.Series(content2)

chungwon.to_csv(fc_name, encoding='utf-8-sig',index=False)
chungwon.to_excel(fx_name, index=False, engine='openpyxl')

국민 청원 사이트에서 원하는 카테고리의 청원 정보를 읽어온다.

읽어오는 정보는 카테고리, 제목, 동의수, 동의달성률, 동의기간, 남은기간, 청원의 취지, 청원의 내용이다.

국민 청원 홈페이지의 경우 아마존처럼 스크롤이 필요한 구간도 없고, 이미지를 저장할 것도 없어서 아주 간단하게 만들 수 있었다.

profile
데이터를 접하는 중

0개의 댓글