본문 바로가기
코딩 연습/코딩배우기

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

by good4me 2021. 9. 6.

goodthings4me.tistory.com

 

티스토리 포스팅 글, 이미지 백업 (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 파일로 저장하고 해당 포스트에 있는 이미지전체는 이름을 다시 만들어져서 저장된다.

 

 

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

 

 

 

댓글