코딩 연습/코딩배우기

티스토리 포스팅 글, 이미지 백업 (with 파이썬)

good4me 2021. 9. 6. 23:44

 

티스토리 포스팅 글, 이미지 백업 (with 파이썬)

티스토리(tistory) 백업이 필요해서 파이썬(스크래핑)으로 블로그 포스팅 글과 이미지를 PC에 저장하기 도전

 

왜, 이 짓을 했을까..?

티스토리(Tistory) 블로그를 한 지 1년이 지나면서, 가끔 데이터 백업을 어떻게 하나 고민을 한 적이 있었는데 이제 파이썬을 어느 정도 공부를 했으니 도전해볼 때가 된 것 같아서 이런 짓(?)을 했다.

 

포스팅 글에 대해 아무것도 모르고 시작하던 1년 전에는 PC에 원본을 저장하기도 했었는데, 그것도 잠시…
시간이 지나다보니 포스팅한 후 원본은 어디에 팔아먹었는지 보이지가 않는다.

 

티스토리도 옛날에는 백업 기능이 있었다고 하던데…

그랬으면 이런 수고(?)를 안해도 되었을텐데... 

 

포스팅 글이 그렇게 좋은 건 아니지만,

한 땀 한 땀 써 놓은 내 글과 이미지가 내 PC에 저장되어 있는 것도 좋을 것 같아서 파이썬 공부삼아 고민해가면서 하나씩 시도를 좀 해봤다. (글은 텍스트(.txt)로, 이미지는 올린 크기로 받기)

 

일단 코딩한 것이 동작하는 환경과 그 내역을 살펴보면,

-북클럽(Book Club) 스킨에서 카테고리 7~8개 만들고 포스팅 중
-포스트 주소는 숫자로 설정해서 사용 중임

 

여기서, 초보자의 기술적 한계로 인해 포스트 주소가 문자로 설정된 경우는 이후에 해결할 문제로 남겨둘 수 밖에 없었다. (나중에 다시 시도를 할 목표가 생김)

 

개발자도구(F12)에서 html 코드를 보고

-requests, BeautifulSoup를 통해 스크래핑 진행했으며,

-PIL Image를 통해 이미지 다운로드 시 안보이는 확장자 문제를 해결해봤다. (맞는지는 모름… 나오긴 함)

 

이미지는 src에 확장자(.jpg .png)까지 정확하게 된 것도 있었지만, 다음과 같은 형태로 포함된 URL을 가지고 있는 것도 있었다.

<img srcset="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=http%3A%2F%2Fcfile1.uf.tistory.com%2Fimage%2F993693465F20BB0F1FAFB6" src="https://t1.daumcdn.net/cfile/tistory/993693465F20BB0F1F" 

//i1.daumcdn.net/thumb/C176x120/?fname=https://t1.daumcdn.net/cfile/tistory/99400A3F5F21057413

PIL Image로 이미지 정보를 찾으면,

img_url: https://t1.daumcdn.net/cfile/tistory/992895395F2040A804
img_format: PNG
imge_size: (830, 1019)
len(이미지): 41568

 

good4me.co.kr

 

소스 코드는

from bs4 import BeautifulSoup
import requests
import os
from PIL import Image


def tistory_backup(post_num):

    for num in range(1, post_num + 1):
        url = 'https://본인의 티스토리 URL/' + str(num)
        response = requests.get(url)
        soup = BeautifulSoup(response.text, 'lxml')
        
        ### 포스팅 글 제목
        titles = soup.select_one('#content > div.inner > div.post-cover > div > h1')
        
        ### 등록일
        date = soup.select_one('#content > div.inner > div.post-cover > div > span.meta > span.date')
        
        if not titles or not date:
            continue
        
        print(titles.text)    
        print(date.text)
        
        ### 포스팅 내용
        entry_content = soup.find('div', {'class':'entry-content'})
        print(entry_content.get_text())
        
        res = requests.get(url)
        soup_img = BeautifulSoup(res.content, 'lxml')
        imgs = soup_img.select('img[src^=https]')  # https 로 시작하는 src, '//'로 시작하는 src 제외시킴
        print(f'이미지 수 : {len(imgs)}')
        # print(imgs)
        
        # 저장 디렉토리 만들기
        if not os.path.exists('tistoryBackup'):
            os.mkdir('tistoryBackup')
        if not os.path.exists('tistoryBackup/post_' + str(num)):
            os.makedirs('tistoryBackup/post_' + str(num))
        
        cnt = 1
        for img in imgs:
            img_url = img['src']
            
            ## pillow.Image로 이미지 format 알아내기
            imageObj = Image.open(requests.get(img_url, stream=True).raw)
            img_format = imageObj.format
            imge_size = imageObj.size
            print(f'img_url: {img_url}')
            print(f'img_format: {img_format}')
            print(f'imge_size: {imge_size}')
            print(f'os.path.basename(img_url): {os.path.basename(img_url)}')
            
            res_img = requests.get(img_url).content
            print(f'len(이미지): {len(res_img)}')  # requests의 .content는 bytes 타입을 리턴함
            
            if img_url.split('.')[-1] in ['png', 'jpg']:
                img_name = str(num) + '_' + str(cnt) + '_' + os.path.basename(img_url)
            else:
                img_name = str(num) + '_' + str(cnt) + '_' + 'no_filename_img.' + img_format
            
            print(img_name)
            
            if len(res_img) > 100:  # 이미지 용량이 00 bytes 이상인 것만
                with open('./tistoryBackup/post_' + str(num) + '/' + img_name, 'wb') as f:
                    f.write(res_img)
                cnt += 1
        
        title_content = titles.text + '\n' + date.text +  '\n' + entry_content.get_text()
        filename = str(num) + '_tistory_title_content.txt'
        with open('./tistoryBackup/post_' + str(num) + '/' + filename, 'w', encoding='utf-8') as f:
            f.write(title_content)
        
tistory_backup(20)

tistory_backup(20) 실행 시, 20은 포스트 주소의 숫자.
즉, https://abc4u.tistory.com/1 ~ https://abc4u.tistory.com/20 까지의 포스트 url을 대상으로 추출한다는 의미이며, 본인의 최근 포스팅 번호를 넣으면 1번 부터 최근 번호까지 전체가 추출됨

티스토리 백업

 

소스코드를 실행하면 위 탐색기 이미지처럼 폴더를 생성하고, 글은 .txt 파일로 저장하고 해당 포스트에 있는 이미지전체는 이름을 다시 만들어져서 저장된다.

 

 

티스토리, 다음, 네이버 블로그 하나씩 다운로드하는 프로그램