[Python + Crawling] 스파르타 코딩 3주 차 5번째 수업

안영우·2020년 10월 18일
0
post-thumbnail

✏️ 서론

저번 시간에는 Python을 통해 ajax 통신을 하는 과정을 배웠다.

사실 PythonJavaScript보다 문법이 쉬워서 언어를 배우는데 큰 장벽은 없었다.(이제는 오히려 console.log()를 안 써서 어색하다.)

수업 중 리팩토링(Refactoring) 의 개념을 배웠다.

간단하게 말해 코드를 정리하는 것 인데
똑같은 결과값을 내는 긴 코드를 짧은 코드로 압축하면 더 효율적이라는 말이다.

아직 리팩토링을 잘 하지 못하지만 대략적으로 이런 긴 코드를 한 줄로 요약해봤다.

// Refactoring 전
let cityAir = response["RealtimeCityAir"]["row"]
for (let i = 0; i < cityAir.length; i++) {
let data = cityAir[i]
let mise = data["PM10"]
let guName = data["MSRSTE_NM"]

// Refactoring 후
let cityAir = response["RealtimeCityAir"]["row"]
for (let i = 0; i < cityAir.length; i++) {
let{data, mise, guName} = cityAir

이런식으로 리팩토링(Refactoring)을 사용하면 5줄을 3줄로 요약가능하다.


✏️ 본론

이제 웹 크롤링에 대해 배워보자.
" [추석특집] 파이썬 혼자 놀기" 과정에서 했던 내용과 비슷하다.

설명은 생략하고 코드부터 배워보자.

사용할 패키지는 requests BeautifulSoup4 이다.

requests : HTTP 통신을 할 때 필요한 패키지
BeautifulSoup4 : 크롤링 할 때 코드를 HTML 구조로 변경해주어 편리하게 사용할 수 있다.

🍯 Tips

1. 문제분석(화면 분석)
➡️ 내가 출력하고 싶은 데이터가 어디에 있는지 확인하는게 중요하다. 
  .selector를 복사해서 가져오기 전에 페이지의 구성을 살펴보자
  
2. 부모요소로부터 접근
➡️ 내가 원하는 값의 부모요소부터 접근해서 가져오자
   ex) table > tbody / table > tbody > tr
   
3. 값 출력
➡️ 원하는 데이터를 얻기 위해 주기적으로 print하는 습관을 기르자.

본격적인 크롤링에 앞서 requests BeautifulSoup4 를 불러온 코드는 다음과 같다.

import requests
from bs4 import BeautifulSoup4

# headers는 user-Agent로 어떤 브라우저에서 보냈는지 확인하는 코드이다. 
사이트마다 다르지만 일부 사이트는 크롤링기술을 막아놓은 사이트도 있다.(ex) investing.com) 
headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}

# '해당 URL'에서 HTML값을 받아온다. ' .json()' 을 붙여 JSON 형식으로 출력도 가능하다.
data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20200716',headers=headers)

# HTML을 BeautifulSoup이라는 라이브러리를 활용해 검색하기 용이한 상태로 만듦
# soup이라는 변수에 "파싱 용이해진 html"이 담긴 상태가 됨
# 이제 코딩을 통해 필요한 부분을 추출하면 된다.
soup = BeautifulSoup(data.text, 'html.parser')

이제 해당 URL을 통해 데이터를 가져오자.

import requests
from bs4 import BeautifulSoup4

headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20200716',headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

# 해당 selector안에 있는 내용 전체를 긁어온다. 
# 이때, tbody는 뼈대이므로 내용을 포함하고 있는 'tr' 까지 추가한다.
movie = soup.select('#old_content > table > tbody > tr')

# 여기서 print를 걸면, ' tbody > tr '의 전체 HTML값이 출력된다.
# print(movie)
# 하지만, 나는 영화 순위제목을 알고 싶으니까 자세한 경로를 작성하자.

# 반복문을 통해 하나씩 제목을 출력한다. 올바른 선택자를 작성하자.
for movies in movie:
  title = movies.select_one('td.title > div > a')

# HTML코드 중 none을 빼고 출력하라는 코드이다. 
# 나중에 .text로 반환할때 none형태가 있으면 반환이 안되므로 꼭 넣어주자.
  if title is not None:
     print(title.text)

**👉🏽 결과  **
그린 북
가버나움
베일리 어게인
원더
포드 V 페라리
아일라
주전장
당갈
쇼생크 탈출
터미네이터 2:오리지널
보헤미안 랩소디
덕구
월-E
나 홀로 집에
아이즈 온 미 : 더 무비
라이언 일병 구하기
사운드 오브 뮤직
살인의 추억
매트릭스
인생은 아름다워
헬프
빽 투 더 퓨쳐
포레스트 검프
위대한 쇼맨
클래식
글래디에이터
센과 치히로의 행방불명
토이 스토리 3
타이타닉
알라딘
어벤져스: 엔드게임
안녕 베일리
죽은 시인의 사회
레옹
집으로...
헌터 킬러
반지의 제왕: 왕의 귀환
동주
아이 캔 스피크
캐스트 어웨이
히든 피겨스
굿바이 마이 프랜드
여인의 향기
굿 윌 헌팅
서유기 2 - 선리기연
클레멘타인
주토피아
쉰들러 리스트
달링
모노노케 히메

크롤링을 사용하면 이처럼 원하는 내용들을 출력할 수 있다.
다음으로 ' 순위, 제목, 평점 순 ' 으로 출력하는 크롤링 코드를 작성해보자.

import requests
from bs4 import BeautifulSoup4

headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20200716',headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

movie = soup.select('#old_content > table > tbody > tr')

# 주의 할 점은 movie_tag라는 공통 코드로 'if movie_tag is not None:'을 사용하면 
# rank, title, score를 모두 쓸수 있다. title은 더 이상 들어갈 선택자가 없으므로 movie_tag.text를 사용한다.
for movies in movie:
   movie_tag = movies.select_one('td.title > div > a')
   if movie_tag is not None:
      rank = movie.select_one('td:nth-child(1) > img')['alt']
      title = movie_tag.text
      score = movie.select_one('td.point').text

**👉🏽 결과  **
102주차 네이버 영화 순위, 제목, 평점 순입니다.
01 그린 북 9.59
02 가버나움 9.59
03 베일리 어게인 9.53
04 원더 9.49
05 포드 V 페라리 9.49
06 아일라 9.49
07 주전장 9.48
08 당갈 9.47
09 쇼생크 탈출 9.44
010 터미네이터 2:오리지널 9.44
11 보헤미안 랩소디 9.42
12 덕구 9.42
13-E 9.41
14 나 홀로 집에 9.41
15 아이즈 온 미 : 더 무비 9.41
16 라이언 일병 구하기 9.40
17 사운드 오브 뮤직 9.40
18 살인의 추억 9.40
19 매트릭스 9.40
20 인생은 아름다워 9.40
21 헬프 9.40
22 빽 투 더 퓨쳐 9.40
23 포레스트 검프 9.40
24 위대한 쇼맨 9.40
25 클래식 9.39
26 글래디에이터 9.39
27 센과 치히로의 행방불명 9.39
28 토이 스토리 3 9.38
29 타이타닉 9.38
30 알라딘 9.38
31 어벤져스: 엔드게임 9.38
32 안녕 베일리 9.37
33 죽은 시인의 사회 9.37
34 레옹 9.37
35 집으로... 9.37
36 헌터 킬러 9.37
37 반지의 제왕: 왕의 귀환 9.37
38 동주 9.37
39 아이 캔 스피크 9.37
40 캐스트 어웨이 9.37
41 히든 피겨스 9.36
42 굿바이 마이 프랜드 9.36
43 여인의 향기 9.36
44 굿 윌 헌팅 9.36
45 서유기 2 - 선리기연 9.35
46 클레멘타인 9.35
47 주토피아 9.35
48 쉰들러 리스트 9.35
49 달링 9.35
50 모노노케 히메 9.35

beautifulsoup4 내부의 selector에 미리 정의된 다른 함수도 알아보자.

# 선택자를 사용하는 방법 (copy selector)
soup.select('태그명')
soup.select('.클래스명')
soup.select('#아이디명')

soup.select('상위태그명 > 하위태그명 > 하위태그명')
soup.select('상위태그명.클래스명 > 하위태그명.클래스명')

# 태그와 속성값으로 찾는 방법
soup.select('태그명[속성="값"]')

# 한 개만 가져오고 싶은 경우
soup.select_one('위와 동일')

✏️ 예제(3문제) ✏️

1. 2018년 3월 27일 기준 네이버 영화 순위, 제목, 평점 순으로 출력하는 코드를 작성하세요!

import requests
from bs4 import BeautifulSoup

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://movie.naver.com/movie/sdb/rank/rmovie.nhn?sel=pnt&date=20170327', headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

movies = soup.select('#old_content > table > tbody > tr')
print("3월 4주차 네이버 영화 순위, 제목, 평점 순입니다.")
for movie in movies:
    movie_tag = movie.select_one('td.title > div > a')
    if movie_tag is not None:
        movie_title = movie_tag.text
        movie_rank = movie.select_one('td:nth-child(1) > img')['alt']
        movie_score = movie.select_one('td.point').text
        print(movie_rank, movie_title, movie_score)

**👉🏽 결과  **
34주차 네이버 영화 순위, 제목, 평점 순입니다.
01 히든 피겨스 9.43
02 쇼생크 탈출 9.40
03 터미네이터 2 9.39
04 매트릭스 9.38
05 레옹 9.38
06 인생은 아름다워 9.38
07 빽 투 더 퓨쳐 9.38
08 죽은 시인의 사회 9.37
09 포레스트 검프 9.37
010-E 9.37
11 나 홀로 집에 9.37
12 센과 치히로의 행방불명 9.37
13 토이 스토리 3 9.36
14 반지의 제왕: 왕의 귀환 9.36
15 살인의 추억 9.36
16 라이언 일병 구하기 9.36
17 사운드 오브 뮤직 9.36
18 굿바이 마이 프랜드 9.36
19 동주 9.35
20 에이리언 2 9.35
21 글래디에이터 9.35
22 자백 9.35
23 헬프 9.35
24 주토피아 9.35
25 빌리 엘리어트 뮤지컬 라이브 9.35
26 여인의 향기 9.35
27 울지마 톤즈 9.34
28 반지의 제왕: 두 개의 탑 9.34
29 미세스 다웃파이어 9.34
30 뚜르: 내 생애 최고의 499.34
31 클래식 9.34
32 세 얼간이 9.34
33 아마데우스 9.34
34 패왕별희 9.34
35 그대를 사랑합니다 9.34
36 언터처블: 1%의 우정 9.33
37 오페라의 유령 : 25주년 특별 공연 9.33
38 서유기 2 - 선리기연 9.33
39 드래곤 길들이기 9.33
40 캐스트 어웨이 9.33
41 쉰들러 리스트 9.33
42 모노노케 히메 9.32
43 굿 윌 헌팅 9.32
44 라푼젤 9.32
45 집으로... 9.32
46 사랑은 비를 타고 9.32
47 지금, 만나러 갑니다 9.32
48 바이센테니얼 맨 9.32
49 아이언 자이언트 9.32
50 알라딘 9.31

2. 네이버 한국 야구 페이지에서 팀 순위 정보를 스크래핑하여 출력하세요.

import requests
from bs4 import BeautifulSoup

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://sports.news.naver.com/kbaseball/record/index.nhn?category=kbo', headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

baseballs = soup.select('#regularTeamRecordList_table > tr')

for baseball in baseballs:
    rank = baseball.select_one('th > strong').text
    team = baseball.select_one('td > div > span').text
    print(rank, team)

**👉🏽 결과  **
1 NC
2 KT
3 LG
4 두산
5 키움
6 롯데
7 KIA
8 삼성
9 SK
10 한화

3. 네이버 한국 야구 페이지에서 팀, 순위, 승률(0.5미만)을 출력하시오.

import requests
from bs4 import BeautifulSoup

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get('https://sports.news.naver.com/kbaseball/record/index.nhn?category=kbo', headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

baseballs = soup.select('#regularTeamRecordList_table > tr')

for baseball in baseballs:
    rank = baseball.select_one('th > strong').text
    team = baseball.select_one('td > div > span').text
    victory = baseball.select_one('td > strong').text

# victory 변수에 float선언을 해주지 않으면 이런 오류가 뜬다.
# TypeError: '>' not supported between instances of 'str' and 'float'
    if float(victory) > 0.5:
        print(rank, team, victory)

**👉🏽 결과  **
1 NC 0.609
2 KT 0.565
3 LG 0.561
4 두산 0.558
5 키움 0.555
6 롯데 0.519
7 KIA 0.515

이와 같은 분야에서 Web_scraping 기능이 사용될 수 있다.


✏️ 결론

오늘은 Python을 활용하여 Web_scraping을 다뤘다.
Web_scraping의 장점은 내가 원하는 데이터를 출력 할 수 있다.
다만, 원하는 데이터에 맞게 가공하는 과정이 쉽지 않기 때문에 많은 노력이 필요하다.

오늘 배운과정을 통해 내가 어떤분야에 응용 할 수 있는지 떠올리며 오늘 복습을 마치겠다.

mongoDB를 활용한 AJAX 통신은 2편에서 다루겠다.

profile
YW_Tech

0개의 댓글