본문 바로가기
Project/Auto Upload

[파이썬] 뉴스 크롤링 티스토리에 자동 업로드하기 (마무리)

by 졸린이 2021. 9. 24.
반응형

1. [파이썬] 셀레니움 selenium 웹 크롤링 시작

2. [파이썬] selenium 크롤링, 데이터 수집 ID, TAG, href 찾기

3. [깃허브] github에 vs code project 올리기, 업로드, Push 

4. 티스토리 자동 글쓰기 API Authentication Code & Access Token 발급

5. [파이썬] 티스토리 API 이용 자동 글쓰기. 파이썬 request post (이전 포스팅)

6. [파이썬] 뉴스 크롤링 티스토리에 자동 업로드하기 (마무리)  (현재 글)

7. [파이썬] 윈도우 작업 스케줄러에서 파이썬 자동 실행시키기

8. [파이썬] mouse, keyboard 제어 & 티스토리 api 없이 글 쓰기 (414 제한 오류)

 

드디어 마무리가 되었다.

사실 1번글에서 5번글까지가 핵심 내용이고 이제 나머지는 정리하는 느낌이다.

결과물은 아래와 같은 게시물을 올리는 것이다.

제목에 링크를 달고 글씨 크기를 키운 뒤 썸네일 이미지를 띄우는 식이다. 중간에 광고도 집어넣고

사실 미리보기 글도 같이 올리고 링크도 한번 더 띄우고 싶었으나 티스토리API가 글 작성하는데 글자 수 제한이 있는 것 같다.

길게 작성해서 테스트 해보았는데 Request URI TOO LARGE [414] 에러를 띄었다.

그래서 간략하게 축소했다.

 

먼저 파이썬으로 글 작성하기 전에 완성본을 어떻게 할 지 티스토리 글쓰기에서 먼저 작성했다.

글쓰기를 한 후

 

원하던 형식의 글을 작성하고

모드를 HTML로 변경해준다.

여기 HTML로 작성된 소스를 파이썬에서 사용할 것이다.

이제 파이썬 코드를 보자. 크롤링하는 코드와 티스토리에 요청을 보내는 코드로 작성했다. (crawling.py, tistoryReq.py)

 

crawling.py (전체 코드)
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
 
import tistoryReq
 
import chromedriver_autoinstaller
import time
import subprocess
 
subprocess.Popen(r'C:\Program Files\Google\Chrome\Application\chrome.exe --remote-debugging-port=9999 --user-data-dir="C:\chrometmp"')
chrome_ver = chromedriver_autoinstaller.get_chrome_version().split('.')[0]
 
option = Options()
option.add_experimental_option("debuggerAddress""127.0.0.1.:9999")
try:
    driver = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe', options=option)
except:
    chromedriver_autoinstaller.install(True)
    driver = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe', options=option)
driver.implicitly_wait(10)
 
naverNewsUrl = 'https://news.naver.com/'
 
driver.get(naverNewsUrl)
 
tmNews = driver.find_element_by_id('today_main_news')
hdFlickItem = tmNews.find_element_by_class_name('hdline_flick_item')
 
# 썸네일 기사 
hdAtag = hdFlickItem.find_element_by_tag_name('a')
 
# 링크
hdHref = hdAtag.get_attribute('href')
 
# 이미지
hdImgTag = hdAtag.find_element_by_tag_name('img')
hdImgSrc = hdImgTag.get_attribute('src')
 
driver.get(hdHref)
time.sleep(1)
 
# 제목
driver.switch_to.window(driver.window_handles[-1])
thumTitHead = driver.find_element_by_class_name('tts_head')
hdTitle = thumTitHead.text
 
driver.back()
driver.switch_to.window(driver.window_handles[-1])
 
print('썸네일 링크 : ', hdHref) 
print('썸네일 이미지 src : ', hdImgSrc)
print('썸네일 제목 : ', hdTitle)
 
result = []
result.append(hdTitle)
result.append(hdHref)
result.append(hdImgSrc)
 
# li를 감싼 div 검색
tmNews = driver.find_element_by_id('today_main_news')
# div 내에서 li 리스트 검색
tmNewsLis = tmNews.find_elements_by_tag_name('li')
 
# li는 여러개이므로 for문으로 루프
for li in range(len(tmNewsLis)) :
    tmNews = driver.find_element_by_id('today_main_news')
    tmNewsLis = tmNews.find_elements_by_tag_name('li')
 
    aTag = tmNewsLis[li].find_element_by_tag_name('a')
    href = aTag.get_attribute('href')
 
    print('기사 제목 :', aTag.text)
    print('링크 : ', href)
    result.append(aTag.text)
    result.append(href)
 
    driver.get(href)
    time.sleep(1)
    driver.switch_to.window(driver.window_handles[-1])
    main = driver.find_element_by_id('articleBody'# main content
    try:
        imgTag = main.find_element_by_tag_name('img')
        imgSrc = imgTag.get_attribute('src')
    except:
        imgSrc = ''
    print('이미지 :', imgSrc)
    result.append(imgSrc)
 
    
    driver.back()
    time.sleep(1)
    driver.switch_to.window(driver.window_handles[-1])
    
result = [result[i:i+3for i in range(0len(result), 3)]
print(result)
 
tistoryReq.autoWrite(result)
 
driver.quit()
cs

 

tistoryReq.py (전체 코드)
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
import requests
# pip install requests
from datetime import datetime
 
today = datetime.today()
year = today.year
mon = today.month
day = today.day
 
centerAd = """
<!-- 중간 -->
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="내용숨김"
     data-ad-slot="내용숨김"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>
"""
infeed = """
<!-- 인피드 -->
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
     style="display:block"
     data-ad-format="autorelaxed"
     data-ad-client="내용숨김"
     data-ad-slot="내용숨김"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>
"""
 
def autoWrite(data):
 
     tistoryUrl = 'https://www.tistory.com/apis/post/write?'
 
     print(len(data))
     cnt = 1
     html = ""
     for i in data:
          
          # <p data-ke-size="size16"><a href={i[1]}>{i[1]}</a>&nbsp;</p>
          
          html += f"""
               <p data-ke-size="size16">&nbsp;</p>
               <p data-ke-size="size16"><h2><a href={i[1]}>{i[0]}</a></h2></p>
               <p><img src={i[2]}/></p>
               <p data-ke-size="size16">&nbsp;</p>
               """
          if cnt == 3:
               html += centerAd
          if cnt == len(data):
               html += infeed
          cnt += 1
 
     print(html)
 
     parameters = {
          'access_token''내용숨김',
          'blogName''hellodoor',
          'title'str(year) + '년 ' + str(mon) + '월 ' + str(day) + '일 뉴스 데일리 헤드라인',
          'content': html,
          'visibility''3',
          'category''974645',
          'tag''news, naver news, headline news, 뉴스, 네이버뉴스, 헤드라인 뉴스, python, 티스토리 api',
          'acceptComment''1'
     }
 
     response = requests.post(tistoryUrl, params=parameters)
     print(response.reason)
     print(response.json)
cs

 

그렇게 긴 코드는 아니지만 나눠서 한번 봐보자.

- crawling.py

1
2
3
4
5
6
7
8
9
10
11
subprocess.Popen(r'C:\Program Files\Google\Chrome\Application\chrome.exe --remote-debugging-port=9999 --user-data-dir="C:\chrometmp"')
chrome_ver = chromedriver_autoinstaller.get_chrome_version().split('.')[0]
 
option = Options()
option.add_experimental_option("debuggerAddress""127.0.0.1.:9999")
try:
    driver = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe', options=option)
except:
    chromedriver_autoinstaller.install(True)
    driver = webdriver.Chrome(f'./{chrome_ver}/chromedriver.exe', options=option)
driver.implicitly_wait(10)
cs

크롬에서 실레니움을 사용하려면 chromedriver.exe 파일을 다운받아야 하는데 이 exe파일과 현재 사용중인 크롬 버전이 다르면 실행이 안된다.

그래서 크롬 버전을 확인하고 버전이 안맞을시 다운 받는다.

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
naverNewsUrl = 'https://news.naver.com/'
 
driver.get(naverNewsUrl)
 
tmNews = driver.find_element_by_id('today_main_news')
hdFlickItem = tmNews.find_element_by_class_name('hdline_flick_item')
 
# 썸네일 기사 
hdAtag = hdFlickItem.find_element_by_tag_name('a')
 
# 링크
hdHref = hdAtag.get_attribute('href')
 
# 이미지
hdImgTag = hdAtag.find_element_by_tag_name('img')
hdImgSrc = hdImgTag.get_attribute('src')
 
driver.get(hdHref)
time.sleep(1)
 
# 제목
driver.switch_to.window(driver.window_handles[-1])
thumTitHead = driver.find_element_by_class_name('tts_head')
hdTitle = thumTitHead.text
 
driver.back()
driver.switch_to.window(driver.window_handles[-1])
cs

위 내용은 앞에 글에서도 나와 있다.

3행 : 원하는 url로 접속을 한다.

5행 : html에서 원하는 id를 찾는다. (위에서는 div의 id이다)

6행 : 찾은 div 안에서 class name으로 다시 원하는 것을 검색한다.

 

9행 : 다시 찾은 요소 안에서 태그가 a인 것을 찾는다.

12행 : a 태그안에서 href 내용을 찾는다. (링크)

15, 16행 : 마찬가지로 img 태그를 찾고 src 내용을 검색한다.

 

18행 : 찾은 링크를 다시 driver.get() 으로 접속한다.

22행 : driver.switch_to.window(driver.window_handles[-1]

새로운 창이 열리면서 driver가 바라보는 곳을 열린 새 창으로 변경해줘야 한다.

그리고 원하는 것을 다시 검색 후

 

26행에서 driver.back()으로 뒤로가기

주의할 점이 새로운 창을 갖다가 오면 이전에 검색해놨던 요소를 사용할 수 없게 된다.

하위 요소를 검색하고 싶으면 다시 상위요소도 검색해야 한다.

 

위에 for문을 살펴보면

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
# li는 여러개이므로 for문으로 루프
for li in range(len(tmNewsLis)) :
    tmNews = driver.find_element_by_id('today_main_news')
    tmNewsLis = tmNews.find_elements_by_tag_name('li')
 
    aTag = tmNewsLis[li].find_element_by_tag_name('a')
    href = aTag.get_attribute('href')
 
    print('기사 제목 :', aTag.text)
    print('링크 : ', href)
    result.append(aTag.text)
    result.append(href)
 
    driver.get(href)
    time.sleep(1)
    driver.switch_to.window(driver.window_handles[-1])
    main = driver.find_element_by_id('articleBody'# main content
    try:
        imgTag = main.find_element_by_tag_name('img')
        imgSrc = imgTag.get_attribute('src')
    except:
        imgSrc = ''
    print('이미지 :', imgSrc)
    result.append(imgSrc)
 
    
    driver.back()
    time.sleep(1)
    driver.switch_to.window(driver.window_handles[-1])
cs

2행 : 검색한 tmNewsLis의 크기 만큼 for문을 돌린다. (li element의 모음)

원래 for li in tmNewsLis: 

이렇게 많이 작성하는데 위 상황은 새 창을 열고 다시 뒤로오고 값이 사라져 있기 때문에 크기만 잡아놓고

배열처럼 순서대로 접근하기 위해 위 코드처럼 작성했다.

 

3행, 4행 : 위에 계속 설명처럼 새창을 갖다가 오면 값을 잃는데 다시 왔을 때 다시 값을 넣어주기 위해서 다시 검색한다.

 

그리고 나머지 코드들은 다 똑같다. 검색하고 링크찾아 가서 필요한 값 검색해서 가져오고 배열에 append하여 집어넣고

중간에 try: except:는 이미지가 없는 경우도 있어서 오류가 뜰 때가 있었다.

그래서 try로 감싸주고 이미지가 없으면 그냥 빈 값을 넣어준다. (result 배열에 순서대로 들어가야 하기 때문)

1
2
3
4
5
6
7
result = [result[i:i+3for i in range(0len(result), 3)]
print(result)
 
tistoryReq.autoWrite(result)
 
driver.quit()
 
cs

그리고 result는 현재 그냥 1차원 배열이지만 기사 제목, 링크, 썸네일 이렇게 세 종류로 순서대로 들어가 있다.

1행에서 데이터를 사용하게 하기 편하기 위해 [n][3] 크기로 2차원 배열로 바꾼다.

 

그리고 가공한 데이터를 4행에서 tistoryReq.autoWrite(result) 함수를 호출한다.

tistoryReq는 티스토리에 요청할 .py 파일이름이다. 상단에 import해서 내부 함수를 호출할 수 있다.

 

- tistoryReq.py

1
2
3
4
5
6
7
8
import requests
# pip install requests
from datetime import datetime
 
today = datetime.today()
year = today.year
mon = today.month
day = today.day
cs

1행 : 요청을 보내기 위해 import한다.

requests 모듈을 사용하기 위해선 cmd에서 설치해줘야 한다.

$ pip install requests

3행 : 날짜 정보를 가져오기 위해 import 했다.

5행~8행 : 오늘 날짜의 연도, 달, 일을 가져와서 저장한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
centerAd = """
<!-- 중간 -->
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="내용숨김"
     data-ad-slot="내용숨김"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>
"""
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
def autoWrite(data):
 
     tistoryUrl = 'https://www.tistory.com/apis/post/write?'
     print(len(data))
     cnt = 1
     html = ""
     for i in data:
          html += f"""
               <p data-ke-size="size16">&nbsp;</p>
               <p data-ke-size="size16"><h2><a href={i[1]}>{i[0]}</a></h2></p>
               <p><img src={i[2]}/></p>
               <p data-ke-size="size16">&nbsp;</p>
               """
          if cnt == 3:
               html += centerAd
          if cnt == len(data):
               html += infeed
          cnt += 1
 
     print(html)
 
     parameters = {
          'access_token''내용숨김',
          'blogName''hellodoor',
          'title'str(year) + '년 ' + str(mon) + '월 ' + str(day) + '일 뉴스 데일리 헤드라인',
          'content': html,
          'visibility''3',
          'category''974645',
          'tag''news, naver news, headline news, 뉴스, 네이버뉴스, 헤드라인 뉴스, python, 티스토리 api',
          'acceptComment''1'
     }
 
     response = requests.post(tistoryUrl, params=parameters)
cs

1행 : 함수를 선언했다. crawling.py에서 호출한다.

3행 : 요청을 보낼 uri

6행 : 요청보낼 때 content 파라미터에 넣을 html 언어를 선언했다.

7행 : data배열 크기만큼 반벅한다.

8행 : 문자열 앞에 f를 붙이면 {}사이에 변수를 넣을 수 있다.

아까 티스토리에 복사해 놓을 html을 긁어서 복사하고 여기에 붙여 놨다.

제목(링크)

사진

이런식으로 반복이기 때문에 구조는 단순하다. data 배열안에 i를 반복하는데 i도 배열이다. 0,1,2 로 나눠있는데

0번째 제목을 넣었고 1에 링크를 넣었고 2에 이미지 주소를 넣었었다.

 

위에 제목에 <h2><a href>로 감싸줘서 제목에 링크를 달아주었다.

14행 : 그리고 세 번 반복하고 중간광고를 넣어주고

16행 : 마지막에서는 인피드 광고를 넣었다.

22행 : 파라미터에 작성한 값들을 전부 때려 집어 넣고 요청을 보내면 이쁘게 작성이 되어있다.

 

끝!

이제 cmd에서 이 파이썬이 있는 폴더로 가서 

$ python crawling.py 명령어를 입력하면 포스팅이 된다.

 

하지만 수동으로 매번 명령어를 입력하기는 싫다.

여러 방법이 있지만 가장 간단한 작업스케줄러에 올리는 작업을

다음 포스팅에서 ㅎㅎ.. 

반응형

댓글