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+3] for i in range(0, len(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> </p>
html += f"""
<p data-ke-size="size16"> </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"> </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+3] for i in range(0, len(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"> </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"> </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 명령어를 입력하면 포스팅이 된다.
하지만 수동으로 매번 명령어를 입력하기는 싫다.
여러 방법이 있지만 가장 간단한 작업스케줄러에 올리는 작업을
다음 포스팅에서 ㅎㅎ..
'Project > Auto Upload' 카테고리의 다른 글
[파이썬] mouse, keyboard 제어 & 티스토리 api 없이 글 쓰기 (414 제한 오류) (2) | 2021.10.07 |
---|---|
[파이썬] 윈도우 작업 스케줄러에서 파이썬 자동 실행 (bat 파일) (0) | 2021.09.24 |
[파이썬] 티스토리 API 이용 자동 글쓰기. 파이썬 request post (2) | 2021.09.11 |
티스토리 자동 글쓰기 API Authentication Code & Access Token 발급 (2) | 2021.09.11 |
[깃허브] github에 vs code project 올리기, 업로드, Push (0) | 2021.09.09 |
댓글