자유/대외 활동

패스트캠퍼스 프로그래밍첫걸음 학습후기 ) #13.11주차 학습후기, 파이썬 웹크롤러 만들기

Chipmunks 2019. 5. 27.
728x90




패스트캠퍼스 프로그래밍 첫걸음, 열 세 번째 학습 후기


이번 진도에서 드디어 여러 파이썬 라이브러리를 사용해본다.


말뭉치를 분석해주는 konlpy 라이브러리,


크롤링을 하기 위한 BeautifulSoup, requests 라이브러리


들을 실습한다.


파이썬 패키지 매니저의 편리함과


라이브러리의 간편함과 유용함을 체험할 수 있다.


다양한 과제로 차근차근 파이썬 웹 크롤링 프로그램을 만든다.


웹 크롤링, 말뭉치 분석, 엑셀 파일 저장, 워드 클라우드를 활용하여


파이썬으로 얼마나 간단하게 자동화할 수 있는지 느낄 수 있다.


앞서 웹 시간에 배운 내용이 파이썬 프로그램에도 이용될 수 있다는 점이 신기했다.


어느 언어를 배우든 언젠가 꼭 유용하게 이용될 수 있을 것 같다.


11주차 진도에서는

11. [PYTHON] 파이썬 말뭉치 처리
과제1. 말뭉치 분석하고 정렬하기
12. [PYTHON] 파이썬 크롤러 개발 1 - 크롤링할 사이트 분석
과제2. 선택자 문법으로 크롤링 할 태그 찾기
13. [PYTHON] 파이썬 크롤러 개발 2 - BeautifulSoup, requests 라이브러리
과제3 .불러와진 웹 자원 콘솔로 확인하기
14. [PYTHON] 파이썬 크롤러 개발 3 - 원하는 데이터 추출
과제4. 특정 컨텐츠만 추출하기
15. [PYTHON] 파이썬 크롤러 개발 4 - 데이터 분석 및 엑셀파일 저장
과제5. 크롤링 데이터 엑셀 파일로 저장하기
16. [PYTHON] 파이썬 마무리
[PYTHON] 과제영상1
[PYTHON] 과제영상2

를 학습했다.

이번 진도는 쉽지 않았다.

코드 줄이 많아지고 새로운 라이브러리들이 많이 나오기 때문에

모르는 부분을 검색하는 데에 시간을 오래 썼다.

그래도 웹 크롤러 프로젝트를 잘 마무리 했다.

이제 배운 내용들을 꼼꼼히 정리해보자

11. [PYTHON] 파이썬 말뭉치 처리

말뭉치(Corpus)란 자연어 처리를 위한 분류된 언어의 표본 집합이다.

pip3 install konlpy

konlpy 라이브러리를 설치한다.

konlpy 라이브러리는 한국어 말뭉치 처리를 위한 패키지 모음이다.

Okt (Open Korean Text) 주요 메소드는 다음과 같다.

1. pos : (문자열, 품사)를 튜플로 반환 
2. nouns : 문자열의 명사 리스트로 반환

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from konlpy.tag import Okt
 
ok = Okt()
test = "안녕하세요. 저는 다람쥐입니다."
 
# for i in ok.pos(test, norm=True):
#    print(i)
 
'''
('안녕하세요', 'Adjective')
('.', 'Punctuation')
('', 'Noun')
('', 'Josa')
('다람쥐', 'Noun')
('입니다', 'Adjective')
('.', 'Punctuation')
'''
 
hash_tag = "#패스트캠퍼스 #패캠 #인강 #프로그래밍첫걸음 #컴퓨터공학 #Pythton"
 
# for i in ok.pos(hash_tag):
#    print(i)
 
'''
('#패스트캠퍼스', 'Hashtag')
('#패캠''Hashtag')
('#인강''Hashtag')
('#프로그래밍첫걸음''Hashtag')
('#컴퓨터공학''Hashtag')
('#Pythton''Hashtag')
'''
test = "패스트캠퍼스 패캠 다람쥐 코딩 프로그래밍"
for i in ok.nouns(test):
    print(i)
'''
패스트
캠퍼스
패캠
다람쥐
코딩
프로그래밍
'''
cs


앞에 #이 붙어있다면 해쉬태그로 인식한다.

[ QUIZ ]

1. '말뭉치'는 자연어 처리를 위해 분류된 언어 표본 집합이다.
2. 'konlpy' 라이브러리를 통해 말뭉치 처리를 할 수 있다.
3. Twitter 패키지의 'pos' 메소드는 품사 정보를 제공한다.

과제1. 말뭉치 분석하고 정렬하기

리스트에 저장된 명사들을 무작위로 생성해 그 빈도수를 출력하는 과제다.

딕셔너리 개념과 딕셔너리를 값으로 정렬하는 방법이 관건이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from random import randint
 
NOUNS = ['세종''한글''한글날']
 
def get_rand():
    random_list = []
    for i in range(11):
        random_list.append(NOUNS[randint(02)])
    return random_list
 
def get_rank(random_list):
    result = {}
    for n in random_list:
        if not n in result:
            result[n] = 0
        result[n] += 1
    result = sorted(result.items(), key=lambda t: t[1], reverse=True)
 
    return result
 
if __name__ == "__main__":
    result = get_rank(get_rand())
    for k, v in result:
        print("{}({})".format(k, v))
 
 
'''
코드 실행 예
세종(5)
한글(4)
한글날(2)
'''
cs

get_rank() 함수에서 해당 명사가 얼만큼 카운팅 되었는지 저장하기 위해 딕셔너리 자료형을 이용했다. 

sorted() 함수에 reverse 옵션을 True로 주어서 내림차순 정렬을 했다.

딕셔너리의 items() 메소드로 키와 값이 튜플로 짝지어진 요소들로 정렬 작업을 시작한다.

정렬할 때 대소 비교할 키(Key)로 튜플의 두 번째 요소인 '값'으로 설정했다.

12. [PYTHON] 파이썬 크롤러 개발 1 - 크롤링할 사이트 분석

크롤링(Crawling)은 웹 상에 데이터를 긁어 모으는 작업을 말한다.

보통 크롤링 작업을 할 때 다음 일련의 과정이 필요하다.

1. 개발자 도구를 통해 웹 사이트 분석
2. 크롤링 코드 작성
3. 파일 저장 및 데이터 분석

웹 데이터를 가져오기 위해 다음의 패키지들이 필요하다.

pip3 install requests : 웹 자원을 요청하기 위한 라이브러리

pip3 install bs4 : 가지고 온 HTML 코드를 파싱하기 위한 라이브러리

1
2
3
4
5
6
7
8
9
import requests
from bs4 import BeautifulSoup as bs
 
URL = "https://www.fastcampus.co.kr/dev_online_introp/#row_course"
 
rq = requests.get(URL)
soup = bs(rq.content, 'html.parser')
 
print(soup)
cs

설치한 라이브러리들을 불러와

위 코드로 HTML 코드를 간편하게 불러올 수 있다.

[ QUIZ ]

1. 개발자 도구의 'Copy 기능'을 이용해 선택자 문법을 확인할 수 있다.
2. 'requests' 라이브러리를 통해 웹 자원을 요청한다.
3. 'bs4' 라이브러리를 통해 html 코드를 파싱한다.

과제2. 선택자 문법으로 크롤링 할 태그 찾기

선택자 문법으로 크롤링 할 태그를 찾는 것이 과제다.

깃허브 문서로 기본 내용부터 과제까지 차근차근 따라할 수 있게 정리했다.

첫 번째 주제로 검색 엔진과 robots.txt 를 이야기 한다.

포털 사이트와 여러 검색 엔진은 특정 키워드로 검색 봇이 알고리즘에 따라 키워드에 맞는 웹 페이지들을 탐색한다.

이 때 검색 엔진에게 보여주고 싶은 페이지나 디렉토리가 있을 수도 있고 반대로 수집되기 싫은 페이지나 디렉토리가 있을 수도 있다.

robots.txt 로 검색봇에게 이런 사항들을 알릴 수 있다. 즉 노출 허용, 노출 거부 등을 설정할 수 있는 텍스트 파일이다. 이를 로봇 배제 표준이라고 한다.

대다수의 웹사이트가 robots.txt 파일을 최상위 도메인 주소에 접근할 수 있다.

https://fastcampus.co.kr/robots.txt 로봇 배제 표준 텍스트 파일 >


robots.txt 표준은 권고안이고 로봇 파일에 접근 방지 내용을 작성하였다 하더라도 다른 사람들이 여전히 접근이 가능할 수도 있다.

위 권고안을 지키지 않을 경우에 웹 사이트에서 접근 IP를 강제 차단할 수도 있다.

크롤링을 진행할 때 robots.txt 파일에 명시된 내용들을 준수하는 것이 올바르다.

공격적이고 악의적인 목적의 크롤링은 지양해야 한다.

두 번째로 국가과학기술센터 사이트로 크롤링 테스트 사이트로 이용하는 실습이다.


키워드로 파이썬을 입력해보고 검색 버튼을 누른다.



크롤링 작업으로 논문의 제목과 초록을 가져오고 싶다.


스크롤을 맨 아래까지 내려보자.



보통 게시판 같은데서 자주 보이는 페이징 기능이다.


정보들을 잘라 여러 페이지로 나눠 보이게 해준다.



개발자 도구를 열어 Network 탭을 열어 현재 보고있는 웹 페이지의 정보를 확인해본다.


웹 페이지가 어떻게 구성되었는지 알아야 한다.



Headers 탭에서 스크롤을 맨 아래로 내려가보면 Query String Parameters 항목을 확인할 수 있다.


현재 쿼리 스트링 정보들을 보기 좋게 정리해준다.


페이지를 옮길 때 마다 GET 파라미터에 page 값이 달라지는 것을 확인할 수 있다.



개발자 도구로 커서 버튼으로 논문 제목에 해당하는 부분으로 마우스로 옮겨


해당 HTML 코드를 확인한다.


이 때 마우스 오른쪽 버튼을 눌러 Copy -> Copy selector 버튼으로 선택자를 얻을 수 있다.


#result_list > li:nth-child(1) > div.list_con > p.title > a



마찬가지로 초록 내용도 선택자를 구할 수 있다.


#result_list > li:nth-child(1) > div.list_con > div:nth-child(4) > p:nth-child(2)


선택자 문법 안에 nth-child() 선택자가 있으면 HTML 태그를 가져올 때 까다로울 수 있다.

눈 여겨볼 점은 클래스 속성이 list_con 인 div 태그 안에서 논문 제목과 초록 내용이 있다는 것을 확인할 수 있다.

과제는 위처럼 임의의 사이트에서 크롤링 테스트 코드를 작성하는 것이다.

첫 번째 사이트는 다양한 사진을 무료로 제공하는 https://wallpaperscraft.com/ 사이트이다.


다람쥐(Chipmunk)를 검색해 보았다.


내가 가져올 태그와 값들은 이미지 태그 안에 있는 이미지 주소 값,


해상도 값과 태그 값들이다.


개발자 도구를 열어 요소 커서 기능으로 이미지 선택자를 불러와보자.



body > div > div.l-body > div.l-layout.l-layout_wide > div > div.content-main > div.wallpapers.wallpapers_zoom.wallpapers_main > ul > li:nth-child(1) > a > span.wallpapers__canvas > img


이미지 태그의 선택자다.


body > div > div.l-body > div.l-layout.l-layout_wide > div > div.content-main > div.wallpapers.wallpapers_zoom.wallpapers_main > ul > li:nth-child(1) > a > span:nth-child(2)


해상도를 감싸는 태그의 선택자다.


body > div > div.l-body > div.l-layout.l-layout_wide > div > div.content-main > div.wallpapers.wallpapers_zoom.wallpapers_main > ul > li:nth-child(1) > a > span:nth-child(3)


해쉬태그를 감싸는 태그의 선택자다.


모두 클래스 속성이 wallpapers.wallpapers_zoom.wallpapers.main 인 div 태그를 공통으로 갖고 있다.


span 태그를 구별하여 내가 원하는 값들을 가져올 수 있다.


두 번째 사이트는 패스트 캠퍼스 사이트로부터 크롤링 작업을 해보는 것이다.

크롤링 작업으로 이 달의 베스트 셀러 온라인 강의 목록을 가져오고 싶다.


개발자 도구로 알아낸 선택자는 다음과 같다.


div#text-block-22 > div.top_box > div > h1


다른 베스트 셀링 강의의 선택자는 다음과 같다.


div#text-block-23 > div.top_box > div > h1



13. [PYTHON] 파이썬 크롤러 개발 2 - BeautifulSoup, requests 라이브러리

이제 두 번째 단계인 크롤링 코드 작성 단계다.

BeautifulSoup 의 주요 메소드는 다음과 같다.

find : 하나의 태그 찾기
find_all : 여러 태그 찾기 (리스트)
select : 선택자 문법으로 여러 태그 찾기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import requests
from bs4 import BeautifulSoup as bs
 
URL = "https://www.fastcampus.co.kr/dev_online_introp/#row_course"
 
rq = requests.get(URL)
soup = bs(rq.content, 'html.parser')
 
# (class, id, ...), attr = {key: value, ...}
li_list = soup.find_all('div', class_ = 'vc_toggle_content')
 
for li in li_list:
    ul = li.select('ul > li')
    for l in ul:
        print(l.get_text())
 
'''
HTML이란 무엇일까?
클라이언트와 서버의 차이는 무엇일까?
개발환경과 브라우저에 대해 이해하기
HTML의 구조에 대해 알아보기
HTML의 속성(Attribute) 알아보기
블럭, 인라인 요소에 대해 이해하기
자주 사용되는 태그(table, ul) 써보기
데이터 전송 방식(Get, Post) 이해하기
컨트롤(Form, Input, Button) 이해하기
CSS란 무엇일까?
CSS의 기초 학습하기
CSS 스타일 학습하고 활용하기
부트스트랩(BootStrap) 맛보기
CSS의 핵심 기능 정리하기
자바스크립트(JavaScript)란 무엇일까?
구글의 파이어베이스(Firebase)에 대해 이해하고, 프로젝트 생성하기
자바스크립트의 기초와 구조 알아보기
변수/연산자에 대해 알아보기
조건문 if/switch에 대해 알아보고 실습하기
반복문 for/while에 대해 알아보고 실습하기
자바스크립트의 객체에 대해 개념 이해하기
함수 선언 및 호출에 대해 학습하기
내장함수 호출에 대해 학습하기
자바스크립트 마우스/키보드 이벤트 활용하기
파이어베이스 배포하기
웹의 기초에 대해 정리하기
파이썬은 어떤 프로그래밍 언어일까?
파이썬의 객체 개념 이해하고 클래스 선언하기
모듈과 패키지 이해하기
문자열 처리/함수/예외 처리/엑셀 처리 실습하기
파이썬 말뭉치 처리하기
파이썬을 활용한 크롤러 개발해보기
내가 크롤링하고 싶은 사이트 분석하기
다양한 라이브러리 활용해보기
내가 원하는 데이터 마음껏 추출해보기
추출한 데이터 분석하고 엑셀파일로 저장하기
파이썬 학습내용 정리하기
프론트엔드 개발 – 이웅모 강사
백엔드 개발 – 송종근 강사
iOS 개발 – 이봉원 강사
Android 개발 – 박성완 강사
'''
cs

이렇게 패스트캠퍼스 프로그래밍 첫걸음 사이트에서

강의 목록을 크롤링 해봤다.

[ QUIZ ]

1. BeautifulSoup 모듈의 'find' 메소드로 하나의 태그를 찾는다.
2. find_all 메소드는 BeautifulSoup 객체를 'list' 로 반환한다.
3. select 메소드를 이용해서 '선택자 문법'으로 태그를 찾을 수 있다.

과제3 .불러와진 웹 자원 콘솔로 확인하기

문서를 참고해 크롤링 관련 라이브러리를 자세히 공부한다.

1
2
3
4
5
6
7
8
import requests
 
params = {'key1':1'key2':2}
= requests.get('https://www.fastcampus.co.kr/dev_online_introdev/', params=params) # HTTP GET 요청
 
print(r.encoding) # UTF-8
print(r.url) # https://www.fastcampus.co.kr/dev_online_introdev?key1=1&key2=2
print(r.text) # 응답 받은 콘텐츠를 해석할 수 있는 인코딩 형태로 만들어줍니다.
cs

requests 라이브러리로 웹 자원을 요청할 때 get() 메소드에 딕셔너리 형태의 파라미터 정보들을 입력할 수 있다.

paramas 인자에 딕셔너리형의 파라미터 데이터를 전달한다.

응답 받은 객체에서 인코딩 정보 같은 여러 정보들도 가져올 수 있다.

1
2
3
4
import requests
 
data = {'key1':1'key2':2}
= requests.post('https://www.fastcampus.co.kr/dev_online_introdev/', data=data)
cs

requests 라이브러리에 get() 메소드 뿐 아니라 post() 방식으로도 웹 요청을 보낼 수 있다.

마찬가지로 딕셔너리로 파라미터 값들을 전달한다.

get() 메소드와는 달리 data 인자에 딕셔너리형의 파라미터 데이터를 전달한다.


1
2
3
4
5
import requests
headers = {
'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
r = requests.get('https://www.fastcampus.co.kr/dev_online_introdev/', headers=headers)
cs

requests 라이브러리로 웹 자원을 요청할 때 헤더 정보도 함께 보낼 수 있다.

headers 인자로 값을 넣는다.

User-Agent 헤더는 기기 정보와 브라우저 정보를 담는다.

개발자 도구의 Network 탭에서 헤더들을 또한 확인할 수 있다.

만약 User-Agent 헤더의 값이 비어있다면 웹 사이트에서 크롤링 프로그램이 요청했다는 것을

알고 접근을 막는 경우도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
from bs4 import BeautifulSoup as bs
 
= requests.get('https://www.fastcampus.co.kr/dev_online_introdev/')
soup = bs(r.text, 'html.parser')
 
# 전체 웹 HTML 코드
print(soup) 
 
# 찾은 태그를 하나만 반환합니다.
soup.find('div')
soup.find(id = 'id')
soup.find('div', id ='id')
soup.find('div', class_ ='class')
# 찾은 태그를 리스트로 반환합니다.
soup.find_all('div', attrs={'id':'id'})
soup.find_all('div', attrs={'class':'class'})
soup.select('css  selector'
# 속성에 해당하는 값을 반환합니다.
href = soup['href']
name = soup['name']
value = soup['value']
# 태그가 담고 있는 콘텐츠를 가져옵니다.
print(soup.get_text())
cs

그 다음은 BeautifulSoup 라이브러리에 대한 설명이다.

앞서 강의에서 배웠던 대로 find, find_all, select 메소드를 간단하게 알아봤다.

또한 찾은 태그의 속성까지 접근이 가능하다.

강의에서 언급했던 공공 사이트에서 크롤링 작업을 해보자.

문서에 나와있던 코드는 단순히 페이지마다 HTML 소스 코드만을 불러왔는데

BeautifulSoup 메소드를 적극 활용하여 논문의 제목과 초록을 가져와보자!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import requests
from bs4 import BeautifulSoup as bs
URL = "http://www.ndsl.kr/ndsl/search/list/article/articleSearchResultList.do?page={}&query=%ED%8C%8C%EC%9D%B4%EC%8D%AC&prefixQuery=&collectionQuery=&showQuery=%ED%8C%8C%EC%9D%B4%EC%8D%AC&resultCount=10&sortName=RANK&sortOrder=DESC&colType=scholar&colTypeByUser=&filterValue="
 
def get_link():
    result = []
 
    for page in range(12):
        req = requests.get(URL.format(str(page)))
        soup = bs(req.text, 'html.parser')
        
        # #result_list > li:nth-child(1) > div.list_con > p.title > a
        # #result_list > li:nth-child(1) > div.list_con > div:nth-child(4) > p:nth-child(2)
 
        # #result_list > li:nth-child(2) > div.list_con > p.title > a
        documents = soup.select("#result_list > li")
 
        for li in documents:
            title = li.select("div.list_con > p.title > a")[0].get_text(strip=True)
            abstraction = li.select("div.list_con > div:nth-child(4) > p:nth-child(2)")[0].get_text(strip=True)
 
            result.append((title, abstraction))
    
    return result
 
if __name__ == "__main__":
    result = get_link()
 
    for title, abstraction in result:
        print("제목 : {}".format(title))
        print(abstraction)
        print("\n")
cs


get_text() 메소드에 strip 옵션에 True 값을 입력하면, 공백문자(whitespace)는 모두 제거된다.


strip 옵션을 주지 않으면 공백 문자로 값이 이상하게 나온다.


이렇게 손 쉽게 파이썬으로 웹 크롤링 작업을 할 수 있다.


현재 위 URL은 http 로 시작되고 있다.


만약 requests 모듈로 https 프로토콜에 요청을 할 시 SSLError 오류가 발생할 수 있다.


get 메소드의 인자로 verify=False 를 추가해야 한다.


14. [PYTHON] 파이썬 크롤러 개발 3 - 원하는 데이터 추출

HTTP 상태(응답) 코드 (Status Code) 로 웹 응답을 받았을 때

어떤 상태 코드인지에 따라 어느 결과인지 파악할 수 있다.

링크는 HTTP 상태 코드가 상세히 기술된 문서다.

requests 모듈에서 get(), post() 메소드로 반환된 응답 객체의 status_code 속성으로

어떤 상태 코드인지 확인이 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import requests
from bs4 import BeautifulSoup as bs
from konlpy.tag import Hannanum
 
URL = "https://www.fastcampus.co.kr/dev_online_introp/#row_course"
 
def get_tags():
    tags_list = []
    rq = requests.get(URL)
    print(rq.status_code)
 
    if rq.status_code == 200# OK
        soup = bs(rq.content, 'html.parser')
 
        # (class, id, ...), attr = {key: value, ...}
        li_list = soup.find_all('div', class_ = 'vc_toggle_content')
 
        for li in li_list:
            ul = li.select('ul > li')
            for l in ul:
                tags_list.append(l.get_text(strip=True))
        return tags_list
    else:
        raise Exception('응답 코드가 200번이 아닙니다.')
 
def get_rank():
    rank = {}
    tags_list = get_tags()
    hn = Hannanum()
    for tag in tags_list:
        noun = hn.nouns(tag)
        for n in noun:
            if not (n in rank):
                rank[n] = 0
            rank[n] += 1
    
    rank = sorted(rank.items(), key = lambda x:x[1], reverse = True)
    for k, v in rank:
        print("{}({})".format(k, v))
 
if __name__ == "__main__":
    get_rank()
cs


현재 프로그래밍 첫걸음 페이지가 리뉴얼 되어서 강의 결과와 다르게 나온다.

예전에는 강의 목록이 그대로 나왔지만, 리뉴얼 된 웹 페이지는

그대로 나오지 않고 순화해서 나온다.

그리고 Konlypy 패키지에 Okt 모듈 말고 Hannanum, Kkma, Komoran 모듈들이 존재한다. 

Okt 모듈로 명사를 추출할 때 '대해' 를 명사로 인식해서

명사로 인식하지 않는 다른 모듈을 사용했다.

테스트 결과 Hannanum 모듈이 대해를 명사로 인식하지 않고 명사 결과도 깔끔하게 나오는 것 같아 선택했다.

사용 방법은 Okt 모듈과 동일하다.

[ QUIZ ]

1. 웹 자원 요청 결과를 숫자로 나타낸 것을 'HTTP 상태(응답) 코드' 라고 한다.
2. 웹자원 요청 시 '200'번 응답 코드가 돌아오면 요청 정상 처리이다.

과제4. 특정 컨텐츠만 추출하기

문서를 참고해 앞서 살펴본 논문 제목과 초록을 크롤링 해본다.

앞서 만들어 봤기에 따로 만들지 않고 강사님 코드에서 배울 점을 찾아봤다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import requests
from bs4 import BeautifulSoup as bs
URL = "http://www.ndsl.kr/ndsl/search/list/article/articleSearchResultList.do?page={}&query=%ED%8C%8C%EC%9D%B4%EC%8D%AC&prefixQuery=&collectionQuery=&showQuery=%ED%8C%8C%EC%9D%B4%EC%8D%AC&resultCount=10&sortName=RANK&sortOrder=DESC&colType=scholar&colTypeByUser=&filterValue="
 
def get_link():
    for page in range(111):
        req = requests.get(URL.format(str(page)))
        code = req.status_code
        if code == 200:
            soup = bs(req.text, 'html.parser')
            div = soup.find_all('div', attrs= {'class':'list_con'}) 
            # 하나의 페이지에서 찾은 제목과 초록의 리스트 만큼 반복합니다.
            for data in div:
                # title 속성이 상세화면인 a 태그를 찾습니다.
                a_tag = data.find('a', attrs={'title''상세화면'}) 
                a_tag = a_tag.get_text() # 가져온 태그의 정보 중에 콘텐츠만 추출하여 문자열로합니다.    
                # class 속성이 box_st1인 div 태그를 찾습니다.    
                box_st1 = data.find('div', attrs = {'class':'box_st1'}) 
                box_st1 = box_st1.get_text() # 가져온 태그의 정보 중에 콘텐츠만 추출하여 문자열로 반환합니다.       
                print(a_tag.replace('\n''').replace('\t'''))
                print(box_st1)
                print('------------------------------------------------------')
        else:
            print('HTTP Error:', code)
if __name__ == "__main__":
    get_link()
cs


강사님은 select() 메소드로 선택자 문법으로 찾지 않고

find_all 메소드로 찾고 attrs 딕셔너리 인자로 해당 class 값을 찾았다.

마찬가지로 find 메소드로 attrs 인자를 활용하여 제목과 초록을 찾았다.

그리고 문자열 replcae 메소드로 적절히 결과들을 다듬었다.

그리고 HTTP 상태 코드를 활용한 것을 확인할 수 있다.

15. [PYTHON] 파이썬 크롤러 개발 4 - 데이터 분석 및 엑셀파일 저장

이제 마지막으로 파일저장과 데이터 분석이다.

openpyxl 라이브러리로 엑셀 파일에 저장해보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import requests
from bs4 import BeautifulSoup as bs
from konlpy.tag import Hannanum
from openpyxl import load_workbook as load
 
URL = "https://www.fastcampus.co.kr/dev_online_introp/#row_course"
SAVE_DIR = "rank.xlsx"
 
def save_excel(rank):
    wb = load(SAVE_DIR)
    ws = wb.create_sheet('rank')
    idx = 1
    try:
        for k, v in rank[:15]:
            ws['A' + str(idx)] = "{}({})".format(k, v)
            idx += 1
        
        wb.save(SAVE_DIR)
    except Exception as e:
        print(e)
    finally:
        wb.close()
 
def get_tags():
    tags_list = []
    rq = requests.get(URL)
    print(rq.status_code)
 
    if rq.status_code == 200# OK
        soup = bs(rq.content, 'html.parser')
 
        # (class, id, ...), attr = {key: value, ...}
        li_list = soup.find_all('div', class_ = 'vc_toggle_content')
 
        for li in li_list:
            ul = li.select('ul > li')
            for l in ul:
                tags_list.append(l.get_text(strip=True))
        return tags_list
    else:
        raise Exception('응답 코드가 200번이 아닙니다.')
 
def get_rank():
    rank = {}
    tags_list = get_tags()
    hn = Hannanum()
    for tag in tags_list:
        noun = hn.nouns(tag)
        for n in noun:
            if not (n in rank):
                rank[n] = 0
            rank[n] += 1
    
    rank = sorted(rank.items(), key = lambda x:x[1], reverse = True)
    save_excel(rank)
 
if __name__ == "__main__":
    get_rank()
cs

과제5. 크롤링 데이터 엑셀 파일로 저장하기

과제는 강사님의 문서를 읽고 공부하는 것이다.

엑셀 데이터에 모두 저장해보았다.

이제 저장한 데이터를 활용하는 법을 배워보자.

우선 이 코드를 실행해 엑셀 데이터를 만든다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import requests
from bs4 import BeautifulSoup as bs
from openpyxl import load_workbook as load
 
URL = "http://www.ndsl.kr/ndsl/search/list/article/articleSearchResultList.do?page={}&query=%ED%8C%8C%EC%9D%B4%EC%8D%AC&prefixQuery=&collectionQuery=&showQuery=%ED%8C%8C%EC%9D%B4%EC%8D%AC&resultCount=10&sortName=RANK&sortOrder=DESC&colType=scholar&colTypeByUser=&filterValue="
 
def save_excel(a_links, b_links, title = 'test'):
    wb = load('test.xlsx')
    ws = wb.create_sheet(title)
    try:
        for i in range(len(a_links)):
            ws['A' + str(i + 1)] = a_links[i]
        for j in range(len(b_links)):
            ws['B' + str(j + 1)] = b_links[j]
        wb.save('test.xlsx')
    except Exception as e:
        print(e)
    finally:
        wb.close()
 
def get_link():
    a_links = []
    b_links = [] 
    for page in range(111):
        req = requests.get(URL.format(str(page)))
        code = req.status_code
        print(code)
        if code == 200:
            soup = bs(req.text, 'html.parser')
            div = soup.find_all('div', attrs= {'class':'list_con'})
            for data in div:
                a_tag = data.find('a', attrs={'title''상세화면'})
                a_tag = a_tag.get_text()        
                a_tag = a_tag.replace('\n''').replace('\t''')
                box_st1 = data.find('div', attrs = {'class':'box_st1'})
                box_st1 = box_st1.get_text()
                a_links.append(a_tag)
                b_links.append(box_st1)
        else:
            print('HTTP Error:', code)
    return a_links, b_links
 
if __name__ == "__main__":
    a_links, b_links = get_link()
    save_excel(a_links, b_links, title='content')
cs


이제 워드 클라우드를 만드는 코드를 실행시켜보자


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
from konlpy.tag import Hannanum
import matplotlib as mpl
mpl.use('TkAgg')
import matplotlib.font_manager as fm
from matplotlib import pyplot as plt
from wordcloud import WordCloud
import requests
from bs4 import BeautifulSoup as bs
from openpyxl import load_workbook as load
# import 구문이 추가로 필요하다는 점에 주의하세요!
 
URL = "http://www.ndsl.kr/ndsl/search/list/article/articleSearchResultList.do?page={}&query=%ED%8C%8C%EC%9D%B4%EC%8D%AC&prefixQuery=&collectionQuery=&showQuery=%ED%8C%8C%EC%9D%B4%EC%8D%AC&resultCount=10&sortName=RANK&sortOrder=DESC&colType=scholar&colTypeByUser=&filterValue="
PATH = '/Library/Fonts/NanumGothic.ttf'
 
def get_wc(content, title):
    ok = Hannanum()
    result = {}
    for c in content:
        temp = ok.pos(c)
        for t in temp:
            if t[1== 'N' and len(t[0]) > 1:
                if not (t[0in result):
                    result[t[0]] = 0
                result[t[0]] += 1
    result = sorted(result.items(), key = lambda x:x[1], reverse = True)
    print(dict(result))
    wc = WordCloud(font_path = PATH, background_color = 'white', width = 800, height = 600)
    plt.axis('off')
    plt.imshow(wc.generate_from_frequencies(dict(result)))
    plt.savefig(title + '.jpg')
 
 
def save_excel(a_links, b_links, title = 'test'):
    wb = load('test.xlsx')
    ws = wb.create_sheet(title)
    try:
        for i in range(len(a_links)):
            ws['A' + str(i + 1)] = a_links[i]
        for j in range(len(b_links)):
            ws['B' + str(j + 1)] = b_links[j]
        wb.save('test.xlsx')
    except Exception as e:
        print(e)
    finally:
        wb.close()
 
def get_link():
    a_links = []
    b_links = [] 
    for page in range(1021):
        req = requests.get(URL.format(str(page)))
        code = req.status_code
        print(code)
        if code == 200:
            soup = bs(req.text, 'html.parser')
            div = soup.find_all('div', attrs= {'class':'list_con'})
            for data in div:
                a_tag = data.find('a', attrs={'title''상세화면'})
                a_tag = a_tag.get_text()        
                a_tag = a_tag.replace('\n''').replace('\t''')
                box_st1 = data.find('div', attrs = {'class':'box_st1'})
                box_st1 = box_st1.get_text()
                a_links.append(a_tag)
                b_links.append(box_st1)
        else:
            print('HTTP Error:', code)
    return a_links, b_links
 
if __name__ == "__main__":
    a_links, b_links = get_link()
    save_excel(a_links, b_links, title='content')
    get_wc(a_links, 'python제목')
    get_wc(b_links, 'python초록')
cs


이 전에 pip3 install wordcloud 로 word cloud 를 만들어주는 라이브러리를 설치해야 한다.


강사님의 문서와 다른 점은 태그 패키지를 Okt 대신 Hannanum 을 썼다는 점이다.


Hannanum 모듈의 pos 메소드는 명사를 'N' 으로 반환한다. 따라서 수정이 필요하


같은 폴더에 워드클라우드 이미지 파일이 생성된다.



논문 제목과 논문 초록의 문장 성분들을 분석해서 워드 클라우드로 만들어봤다.


마지막 코드는 직접 검색어와 시트 명을 입력할 수 있게 했다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from konlpy.tag import Hannanum
import matplotlib as mpl
mpl.use('TkAgg')
import matplotlib.font_manager as fm
from matplotlib import pyplot as plt
from wordcloud import WordCloud
import requests
from bs4 import BeautifulSoup as bs
from openpyxl import load_workbook as load
 
URL = "http://www.ndsl.kr/ndsl/search/list/article/articleSearchResultList.do?page={}&query={}&prefixQuery=&collectionQuery=&showQuery=%ED%8C%8C%EC%9D%B4%EC%8D%AC&resultCount=10&sortName=RANK&sortOrder=DESC&colType=scholar&colTypeByUser=&filterValue="
PATH = '/Library/Fonts/NanumGothic.ttf'
 
def get_wc(content, title):
    ok = Hannanum()
    result = {}
    for c in content:
        temp = ok.pos(c)
        for t in temp:
            if t[1== 'N' and len(t[0]) > 1:
                if not (t[0in result):
                    result[t[0]] = 0
                result[t[0]] += 1
    result = sorted(result.items(), key = lambda x:x[1], reverse = True)
    print(dict(result))
    wc = WordCloud(font_path = PATH, background_color = 'white', width = 800, height = 600)
    plt.axis('off')
    plt.imshow(wc.generate_from_frequencies(dict(result)))
    plt.savefig(title + '.jpg')
 
 
def save_excel(a_links, b_links, title = 'test'):
    wb = load('test.xlsx')
    ws = wb.create_sheet(title)
    try:
        for i in range(len(a_links)):
            ws['A' + str(i + 1)] = a_links[i]
        for j in range(len(b_links)):
            ws['B' + str(j + 1)] = b_links[j]
        wb.save('test.xlsx')
    except Exception as e:
        print(e)
    finally:
        wb.close()
 
def get_link(keyword):
    a_links = []
    b_links = [] 
    for page in range(1021):
        req = requests.get(URL.format(str(page), keyword))
        code = req.status_code
        print(code)
        if code == 200:
            soup = bs(req.text, 'html.parser')
            div = soup.find_all('div', attrs= {'class':'list_con'})
            for data in div:
                a_tag = data.find('a', attrs={'title''상세화면'})
                a_tag = a_tag.get_text()        
                a_tag = a_tag.replace('\n''').replace('\t''')
                box_st1 = data.find('div', attrs = {'class':'box_st1'})
                box_st1 = box_st1.get_text()
                a_links.append(a_tag)
                b_links.append(box_st1)
        else:
            print('HTTP Error:', code)
    return a_links, b_links
 
if __name__ == "__main__":
    try:
        while True:
            keyword = input('검색어를 입력해주세요.')
            if keyword == 'exit':
                print('종료되었습니다.')
                break
            a_links, b_links = get_link(str(keyword))
            title = input('엑셀 시트명을 입력해주세요')
            save_excel(a_links, b_links, title=title)
            a, b = input('이미지 이름을 입력해주세요. (1: 제목, 2: 초록)').split()
            get_wc(a_links, a)
            get_wc(b_links, b)
    except Exception as e:
        print(e)
cs

이번에 '파이썬' 대신에 '자바' 검색어를 입력했다.

시트 명으로 'java', 이미지 이름을 '제목사진 초록사진' 으로 입력했다.




이제 자동으로 크롤링 작업으로 논문의 제목과 초록을 가져오고

엑셀 파일에 저장하고

워드 클라우드로 한 눈에 보기 쉽게 이미지를 만들어준다.

이 모든 것이 파이썬과 다양한 패키지 라이브러리로 손 쉽게 가능하다는 것이다.





이번 진도로 패스트 캠퍼스 올인원 패키지 프로그래밍 첫걸음 파이썬 강의를 마무리 하였다.


다음 진도는 이제 첫 걸음을 땠으니 다음 발 걸음을 떼기 위해


직접 조언을 해주는 영상 콘텐츠들로 준비되어 있다.


다음 포스팅은 간단하게 리뷰하는 형식으로 준비할 예정이다.


좋은 강의를 듣게해 준 패스트 캠퍼스 대학생 서포터즈, 패스트 캠퍼스 관계자 분들에게 감사의 인사를 하고 싶다!


조만간 패스트 캠퍼스 프로그래밍 첫걸음과 관련해서 학습 콘텐츠를 제작할 예정인데


좋은 콘텐츠가 나왔으면 좋겠다!









댓글