졸업프로젝트-Spring2021 (1)

dudtls11444·2021년 3월 5일
0

졸업프로젝트

목록 보기
2/2

강의 분석을 위한 웹크롤링

본 조의 주제는 사용자 맞춤형 시간표/커리큘럼 생성 웹/앱이다.
따라서 사용자 맞춤형 시간표를 제작하기 위해 강의계획서를 토대로 강의방식과 학습평가방식, 중간고사/기말고사 일정을 분석해 사용자의 취향에 맞는 강의를 추천할 예정이다. 이를 위해 이번 학기 열린 대략 34000여개의 강의의 강의계획서를 분석해야 했다. 모든 과목의 강의계획서의 정보를 직접 수기로 입력 할 수 없기에 강의계획서를 크롤링하는 방식으로 필요한 정보만 얻었다.

조금 덧붙이자면 '크롤링' 자체가 상업적 활용인 된다면 불법으로 작용할 수 있다. 2020년에 '여기어때'가 '야놀자'의 정보를 크롤링 한 것이나 '사람인'이 '잡코리아'의 정보를 크롤링 한 부분에 유죄를 선고받은 사례를 보았을 때 크롤링을 통한 무단 사용은 DB권 침해 행위이며 불법이다.

하지만 2019년 미국 판례를 보았을 때는 로그인 필요없는 정보는 공공재로 인식하며 따라서 크롤링은 불법이 아니게 된다. 즉, 로그인이 필요없는 정보는 퍼블릭 데이터이며 퍼블릭 데이터는 게시자가 원하지 않을 때도 승인없이 가져갈 수 있다. (https://reason.com/2019/09/09/scraping-a-public-website-doesnt-violate-the-cfaa-ninth-circuit-mostly-holds/?fbclid=IwAR2vCBAEXNt8W9rGMDOumzJv0mmnmnhS0ZyimtqeW_SyJBRUGY1Z65403-w)

이런 사소한 부분에서도 기술 발전의 속도에 못미치는 공학 윤리나 법적 제도의 발걸음이 보인다는 생각을 잠시 했다. 다행히(?) 대부분의 사이트는 사이트 루트 경로 아래에 robots.txt라는 파일을 두고 크롤링에 대한 규약과 크롤링을 허용하거나 허용하지 않는 페이지들을 적어놓는다. 사실 이도 의무가 아닌 권고사항이므로 반드시 지킬 필요는 없지만 robots.txt 파일이 생긴 이유가 있는 만큼 지키는게 좋다고 생각한다.
학교 강의 계획서에는 robots 파일이 없기에 본 조는 학교 측에 문의해 크롤링을 허락받았다.

잡담이 길었다. 다시 본론으로 돌아와서

강의 계획서 중 일부의 모습이다. 이 중 저 %에 해당하는 내용만을 크롤링 해야한다.

구체적인 크롤링 방법은 다음과 같다.

1. BeautifulSoup로 웹사이트 크롤링

크롤링에 몇몇 방식이 있지만 제일 보편적인 BeatifulSoup4 패키지를 이용하였다.

1) 페이지 읽어온 후 BeautifulSoup 객체로 만들기

from bs4 import BeautifulSoup
import urllib.request
target_url = ''
html = urllib.request.urlopen(target_url).read()
soup = BeautifulSoup(html, 'html.parser')

2) 원하는 태그, style 찾기

BeautifulSoup의 find() 함수는 soup 객체 아래 특정 조건에 맞는 엘레멘트를 가져온다. 해당 페이지가 어떤 tag 들로 어떻게 구성되어있는지 보고싶다면 print(soup.prettify()) 혹은 개발자도구로 보면 된다.
예를 들어 아래의 코드는 class 속성 값이 grid인

태그를 가져온다.

table = soup.find('table', {'class': 'grid'})
print(table)

div tag 를 가진 항목을 list 로 뽑아내고자 한다면

res = soup.find_all('div', 'tit5')
print(res)

다양한 BeautifulSoup 라이브로리의 API를 살펴보면

if __name__  == "__main__":
    soup = BeautifulSoup(html_doc, 'html.parser')
    print('1. ', soup.title)
    print('2. ', soup.title.name)
    print('3. ', soup.title.string)
    print('4. ', soup.title.parent.name)
    print('5. ', soup.p)
    print('6. ', soup.p['class'])
    print('7. ', soup.a)
    print('8. ', soup.find_all('a'))
    print('9. ', soup.find(id="link3"))

즉, 이런식으로 원하는 데이터를 파싱하면 된다. 어렵지않다!

2. CSV 파일 이용하기

csv 파일에서 분반과 학수번호를 읽어 이에 맞게 데이터를 파싱해야하고 이를 다시 csv 파일에 저장할 목적이므로 csv 파일을 읽고 써야한다.

1) CSV 파일

CSV란 Comma-separated values의 약자로서 CSV 파일은 각 라인의 컬럼들이 콤마로 분리된 텍스트 파일 포맷이다. 가장 간단한 형태의 CSV 파일은 문자열을 콤마로 Split 하여 처리하면 되지만, 간혹 컬럼 데이타에 콤마가 있을 경우 이중인용부호로 감싸서 데이타 내의 콤마를 Escape하기 (예: "Lee, Alex") 때문에, 파이썬에 내장된 csv 모듈을 사용하여 .csv 파일을 처리하는 것이 좋다.

2) csv파일 읽기

CSV 파일을 읽기 위해서는 먼저 파이썬에 기본 내장된 csv 모듈을 import 한다. 다음 .csv 파일을 오픈하고 파일객체를 csv.reader(파일객체) 에 넣으면 된다. csv.reader() 함수는 Iterator 타입인 reader 객체를 리턴하므로 for 루프를 돌며 한 라인씩 가져올 수 있다. 이때 리턴되는 각 라인은 컬럼들을 나열한 리스트(list) 타입이다.

아래 예제는 data.csv 라는 CSV 파일을 읽어 각 라인을 출력하는 예이다.

import csv
 
f = open('data.csv', 'r', encoding='utf-8')
rdr = csv.reader(f)
for line in rdr:
    print(line)
f.close() 

3) csv 파일 쓰기

CSV 파일을 쓰기 위해서는 .csv 파일을 쓰기모드로 오픈하고 파일객체를 csv.writer(파일객체) 에 넣으면 된다. CSV writer는 writerow() 라는 메서드를 통해 list 데이타를 한 라인 추가하게 된다. 윈도우즈의 경우 csv 모듈에서 데이타를 쓸 때 각 라인 뒤에 빈 라인이 추가되는 문제가 있는데, 이를 없애기 위해 (파이썬 3 에서) 파일을 open 할 때 newline='' 와 같은 옵션을 지정한다.

아래 예제는 output.csv 라는 CSV 파일에 2개 라인을 추가하는 예이다.

import csv    
f = open('output.csv', 'w', encoding='utf-8', newline='')
wr = csv.writer(f)
wr.writerow([1, "김정수", False])
wr.writerow([2, "박상미", True])
f.close()

3. 프로젝트에 적용하기

이제까지 기본 공부를 끝냈으니 이제 적용해보자. 모든 수업의 학수번호와 분반은 아래와 같이 csv 파일에 정리해놓았다.
가린건 교수님 성함이다.

강의 계획서 url을 살펴보면 SUBJECT_CD id로 학수번호가 CLASS_NUM id로 분반이 들어가므로 url은 저 부분만 바꿔주면 된다.

url="https://ewha_secret?YEAR=2021&TERM_CD=10&GROUP_CD=20&SUBJECT_CD="
    url = url + class_id
    url = url + "&CLASS_NUM=0"
    url = url+class_num
    webpage = requests.get(url)

이런식으로

강의 계획서 중 %에 해당하는 내용만을 크롤링 해야하므로 페이지소스를 살펴보자.

페이지 소스의 모습니다. %에 해당하는 내용은 class가 "GV_TL_C"이며 style이 "height: 15px"로 되어있는 td이다. 따라서 이에 해당하는 td들 중 그 내용이 %를 포함하는 텍스트만 가져오고자 하였다.

table = soup.find_all(attrs={'class':'GV_TL_C', 'style':'height:15px'})

이렇게 간단하게...

최종코드는 다음과 같다.

import requests
from bs4 import BeautifulSoup
import csv

f = open('Class.csv','r',encoding = 'utf-8-sig')
csvReader = csv.reader(f)
for row in csvReader:
  class_id = row[1]
  class_num = row[3]
  print(class_id, class_num)
  url="https://ewha_secret?YEAR=2021&TERM_CD=10&GROUP_CD=20&SUBJECT_CD="
  url = url + class_id
  url = url + "&CLASS_NUM=0"
  url = url+class_num
  webpage = requests.get(url)
  soup = BeautifulSoup(webpage.content, "html.parser") 
  table = soup.find_all(attrs={'class':'GV_TL_C', 'style':'height:15px'})
  percent_list = []
  for percent in table:
      if '%' in percent.get_text():
          percent_list.append(percent.get_text())
  with open('Class_percent.csv','a', newline='') as a: 
      makewrite = csv.writer(a) 
      makewrite.writerow([percent_list])
      a.close()

f.close()

csv 파일에서 학수번호와 분반을 불러와서 필요한 data를 파싱하고 다시 이를 csv에 저장하였다. 몇몇 부분을 추가로 설명하자면 단순히 class가 'GV_TL_C'이고 style이 'height:15px'인 부분을 찾으면 필요없는 데이터까지 파싱된다. 필요한 데이터는 모두 %를 포함하고 있으므로 class가 'GV_TL_C'이고 style이 'height:15px'인 데이터를 table에 담고 이를 하나씩 살피며 %를 포함하고 있는지 확인한 후 %를 포함하고 있는 경우에만 리스트에 추가하며 정보를 파싱했다.


이런식으로 결과는 매우 잘 나왔다. (각 % 데이터를 각 셀에 따로 담지 않은 것은 excel에서 쉽게 할 수 있는 부분이기 때문이다.) 하지만 문제는 교수님들... 왜 강의방식과 학습평가방식을 다 0%로 표시하시는 건가요ㅠㅠㅠㅠㅠㅠ 결과가 좋지 않아 프로젝트에 적용할 수 있을지 모르겠다! 이만 자야지!

0개의 댓글