본문 바로가기

파이썬 프로그래밍/파이썬 크롤링

[Python] Pillow를 활용한 이미지 썸네일/다운로드 처리 크롤링

들어가기전

크롤링할 대상

- HTML문서 + JSON
- 이미지 (이번 포스트에서 다룰 파트)
- PDF, EXCEL등 여러가지 정적 파일

웹에서 자주 쓰이는 이미지 포맷

- jpg: 주로 이미지 저장할 때
이미지 품질 옵션이 있으며 0~100 까지 있음
- gif: 움직이는 이미지. 저품질 (저품질인 이유는 gif에서 지원하는 팔레트가 적기 때문에)
- png: 투명지원되는 이미지 포맷

이미지 크롤링 순서(크게보기)

1. 이미지 다운받기

2. 고화질 이미지를 다운받더라도, 경우에 따라 작은 용량으로 줄일 필요가 있음

-> 이것을 썸네일 처리라고 함

3. 대개 여러개의 파일로 나뉘어져있음

-> 이럴경우 이미지 합치기

Ex) 웹툰 (웹툰의 이미지가 하나의 파일일 경우, 로딩에 긴 시간이 필요하므로 나뉘어서 올려짐)

4. 이미지를 다른 포맷으로 변환하기(jpg, png 등)


파이썬 이미지 라이브러리

- PIL: Python Image Library (마지막업데이트가 2009년) 비추천
- Pillow: PIL Fork (PIL과 똑같아 PIL코드를 그대로 사용 가능)
- PILkit PIL: 유틸리티 컬렉션
- Wand: ImageMagick 파이썬 바인딩 (세팅이 번거로움)

Pillow란?

- PIL프로젝트의 대체 프로젝트로 PIL의 라이브러리와 완전 호환되고 있음
- 이미지 썸네일 생성, 다수 이미지 합성, 다른 이미지 포맷으로 변환, 회전하기 등의 활용하는데 사용
- 장고에서는 models.ImageField필드를 사용할 떄 Pillow가 필수적으로 설치되어 있어야함

Pillow 설치

pip3 install pillow


실습

1. 이미지 다운받기

코드
1
2
3
4
5
6
7
8
9
10
11
12
13
#이미지 다운받기
import os
from PIL import Image
import requests
 
image_url = ('https://ee5817f8e2e9a2e34042-3365e7f0719651e5b'
             '8d0979bce83c558.ssl.cf5.rackcdn.com/python.png')
image = requests.get(image_url).content #서버 응답을 받아 파일내용 획득. content는 응답받은 RawData
filename = os.path.basename(image_url)  #URL에서 파일명 획득. 뒷부분의 python.png란 파일명만 저장
with open(filename, 'wb') as f: #wb: 쓰기 파이러니
    f.write(image)#파일 저장
    
##다운만 받으므로 실행시켜도 아무일이 일어나지 않음
cs

1-1.이미지 확인

코드
1
2
3
#이미지 확인
from IPython.display import Image
Image(filename='python.png')
cs


실행

코드를 실행하면 이미지가 잘 나옵니다.



2. 이미지 품질낮추기 & 다른 포맷으로 변경

2-1. 이미지 품질 낮추고 포맷 변경

코드
1
2
3
4
#이미지 품질 낮추기
from PIL import Image
with Image.open('python.png') as im:
    im.save('python_quality_40.jpg', quality=40#quality는 jpg포맷만 유효
cs

저장만 했기 때문에 실행해도 화면출력은 안됩니다.


코드

1
2
3
#이미지 품질 낮추기 
from IPython.display import Image
Image(filename='python_quality_40.jpg')
cs

확인

티가 안날수도 있지만 안좋아지긴 했습니다.

검정배경이 된 이유: 원래 png는 배경이 투명배경이였지만 jpg로 바꾸면서 투명이 검정색으로 바뀌어서 그렇습니다.


2-2. 배경색 흰색으로 변경하기

2-1코드를 그대로 변경하셔서 하시면 됩니다.

코드

1
2
3
4
5
6
7
8
9
#이미지 저장
from PIL import Image as PILImage
 
with PILImage.open('python.png') as im:
    #im.save('python_quality_40.jpg', quality=40) #quality는 jpg포맷만 유효
    
    with PILImage.new('RGBA', im.size, (255255255)) as canvas:
        marged_im = PILImage.alpha_composite(canvas, im)
        marged_im.save('python_bg_white.jpg')
cs

코드

1
2
3
#이미지 출력
from IPython.display import Image
Image(filename='python_bg_white.jpg')
cs

실행



3. 이미지 용량 줄이기

- resize(size, resample=0)

- 리사이징된 Image 복사본 생성

- 원본의 가로/세로 비율 무시, 지정 크기로 강제 리사이징

- thumbnail(size, resample=3)

- 원본 Image객체를 변경

- 원본 가로/세로 비율을 유지하면서 지정 크기로 리사이징


이미지 크기를 줄이거나 늘리거나 약간의 변경도 모두 손실


thumbnail사용

코드

1
2
3
4
5
6
#이미지 크기 변경 (가로세로 크기 줄이기)
 
#image thumnail 사용
with PILImage.open('python.png') as im:
    im.thumbnail((300300))#원본을 300 by 300 변경
    im.save('python_300_300.png')
cs

확인
1
Image(filename='python_300_300.png')
cs

이미지 크기가 줄은것을 확인하실 수 있습니다.




연습문제) 이미지가 나눠진 웹툰의 이미지를 다운로드하고, 그 이미지를 하나로 붙여보십시오. 

여러개인 이미지를 하나로 이어 붙이는 실습입니다.

이미지 붙이기 순서

(1)첫번째 이미지를 열고

(2)두번째 이미지를 열어서 이어붙이기
(3)두개를 이어붙일 방향(세로로 가로로)을 정하기
(4)붙일방향에 sum(붙여야 하니까)을 나머지는 max로 지정
Ex) 세로로 붙인다면 width = max(), weight = sum()
(5)사이즈 만들기
(6)사이즈, 색등을 조정하여 빈 캔버스 만들기
(7)paste를 사용하여 이미지 위치 정하기 box=(num,num)은 이미지의 좌표(ex 0, 0은 가장 좌측 최상)
두번째 붙일 이미지(0, 이미지.height)
(8)저장


가져올 이미지 주소
http://comic.naver.com/webtoon/detail.nhn?titleId=20853&no=1093&weekday=tue


1. 이미지 url 획득

이런식으로 이미지 url을 획득했습니다.

http://imgcomic.naver.net/webtoon/20853/1093/20170529163407_0ed8a697d896451fee4bc3642fb46db8_IMAG01_3.jpg

http://imgcomic.naver.net/webtoon/20853/1093/20170529163407_0ed8a697d896451fee4bc3642fb46db8_IMAG01_4.jpg



2. 이미지 다운

1
2
3
4
5
6
7
8
# 이미지 url 정의
img1_url = "http://imgcomic.naver.net/webtoon/20853/1093/20170529163407_0ed8a697d896451fee4bc3642fb46db8_IMAG01_3.jpg"
img2_url = "http://imgcomic.naver.net/webtoon/20853/1093/20170529163407_0ed8a697d896451fee4bc3642fb46db8_IMAG01_4.jpg"
 
#이미지 다운
import requests
img1_data = requests.get(img1_url).content
img2_data = requests.get(img2_url).content
cs


실행하여 다운 확인

이미지가 다운받아 지지 않고 html문서로 되어있습니다.

이럴때 헤더의 값이 주어지지 않아 그러므로 헤더값을 기존코드에서 추가하겠습니다.



기존코드에서 헤더값 추가하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 이미지 url 정의
img1_url = "http://imgcomic.naver.net/webtoon/20853/1093/20170529163407_0ed8a697d896451fee4bc3642fb46db8_IMAG01_3.jpg"
img2_url = "http://imgcomic.naver.net/webtoon/20853/1093/20170529163407_0ed8a697d896451fee4bc3642fb46db8_IMAG01_4.jpg"
 
#이미지 다운
import requests
 
#헤더 지정
headers = {
    'Referer''http://comic.naver.com/webtoon/detail.nhn?titleId=20853&no=1093&weekday=tue'
}
 
img1_data = requests.get(img1_url, headers=headers).content
img2_data = requests.get(img2_url, headers=headers).content
cs

원래 네이버 웹툰에서 이미지를 받으려면 키값이 필요하므로 그렇습니다.


실행하여 확인

성공적으로 이미지데이터가 출력됩니다.



3. 이미지 열기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 이미지 url 정의
img1_url = "http://imgcomic.naver.net/webtoon/20853/1093/20170529163407_0ed8a697d896451fee4bc3642fb46db8_IMAG01_3.jpg"
img2_url = "http://imgcomic.naver.net/webtoon/20853/1093/20170529163407_0ed8a697d896451fee4bc3642fb46db8_IMAG01_4.jpg"
 
#이미지 다운
import requests
 
#헤더 지정
headers = {
    'Referer''http://comic.naver.com/webtoon/detail.nhn?titleId=20853&no=1093&weekday=tue'
}
 
img1_data = requests.get(img1_url, headers=headers).content
img2_data = requests.get(img2_url, headers=headers).content
 
#이미지 열고 wb로 써주기
with open('img1.jpg''wb') as f:
    f.write(img1_data)
with open('img2.jpg''wb') as f:
    f.write(img2_data)
    
from IPython.display import Image
Image(filename='img1.jpg')
cs

16~23번째줄 추가입니다.

22번째줄은 이미지를 화면에 출력하기 위해 선언했습니다


실행

이미지가 잘 열렸습니다.



4. 이미지 붙이기

이미지 붙이기 순서

(1)첫번째 이미지를 열고

(2)두개를 이어붙일 방향(세로로 가로로)을 정하기
(3)사이즈 만들기. 붙일방향에 sum(붙여야 하니까)을 나머지는 max로 지정
              Ex) 세로로 붙인다면 width = max(), weight = sum()
(4)사이즈, 색등을 조정하여 빈 캔버스 만들기
(5)paste를 사용하여 이미지 위치 정하기 box=(num,num)은 이미지의 좌표(ex 0, 0은 가장 좌측 최상)
두번째 붙일 이미지(0, 이미지.height)
(6)저장

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
# 이미지 url 정의
img1_url = "http://imgcomic.naver.net/webtoon/20853/1093/20170529163407_0ed8a697d896451fee4bc3642fb46db8_IMAG01_3.jpg"
img2_url = "http://imgcomic.naver.net/webtoon/20853/1093/20170529163407_0ed8a697d896451fee4bc3642fb46db8_IMAG01_4.jpg"
 
#이미지 다운
import requests
 
#헤더 지정
headers = {
    'Referer''http://comic.naver.com/webtoon/detail.nhn?titleId=20853&no=1093&weekday=tue'
}
 
img1_data = requests.get(img1_url, headers=headers).content
img2_data = requests.get(img2_url, headers=headers).content
 
#이미지 열고 wb로 써주기
with open('img1.jpg''wb') as f:
    f.write(img1_data)
with open('img2.jpg''wb') as f:
    f.write(img2_data)
    
from IPython.display import Image
#Image(filename='img1.jpg')
 
#이미지 붙이기
from PIL import Image as PILImage
with PILImage.open('img1.jpg') as im1:
    with PILImage.open('img2.jpg') as im2:
        width = max(im1.width, im2.width)
        height = sum((im1.height, im2.height))
        with PILImage.new('RGB', (width, height), (255255255))as canvas:
            canvas.paste(im1, box=(0,0))
            canvas.paste(im2, box=(0,im1.height))
            canvas.save('img_merged.jpg')
cs

25~34번째줄 추가


추가부분 설명 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#이미지 붙이기
from PIL import Image as PILImage
 
#(1)이미지 오픈 하여 im1, im2로 지정
with PILImage.open('img1.jpg') as im1:
    with PILImage.open('img2.jpg') as im2:
        
        #(2)열은 이미지 가로 최대크기 지정
        width = max(im1.width, im2.width)
 
        #(3)열은 이미지 세로 최대크기 지정(세로로 붙일 것이기 때문에 두개를 더함.
        height = sum((im1.height, im2.height))
        
        #(4)캔버스 만들고 가로 세로 크기는 위에서 선언해준대로, RGB 255,255,255의 흰색바탕
        with PILImage.new('RGB', (width, height), (255255255))as canvas:
            
            #(5)이미지 위치 정하기
            canvas.paste(im1, box=(0,0))#1번이미지 좌표는 0,0
            canvas.paste(im2, box=(0,im1.height))#2번 이미지는 가장좌측0, 이미지1이 끝나는 지점
            
            #(6)이미지 저장
            canvas.save('img_merged.jpg')
cs


확인

잘린부분이 잘 붙었습니다.




본 포스트는 nomade.kr의 크롤링 강의를 참고하여 작성한 글입니다.