본 포스팅은 프로젝트 기획 및 기록용입니다.
1. 기획
파이썬의 BeautifulSoup4, Selenium을 이용하여 내일 날씨, 내일 운세를 크롤링하고
마크다운 형식으로 텍스트를 만들어주는 프로그램
운세 크롤링할 페이지 : 네이버 운세, BeautifulSoup4 이용.
날씨 크롤링할 페이지 : 네이버 날씨, Selenium 이용하여 지도 캡쳐, 날씨 강우확률 최저/최고 기온 크롤링
유의할 점 : BeautifulSoup4, Selenium 모두 셀렉터 형식으로 데이터를 크롤링하기 때문에
클래스 혹은 아이디가 변경될 경우 프로그램 먹통될 가능성이 있다.
현재 네이버 날씨에서 확인 가능한 날씨는 5가지가 있다.
흐리고 비, 흐리고 한때 비, 흐림, 구름많음, 맑음
추후 눈이 내리거나 안개가 낀 날에는 다른 상태가 있을거라 예상한다.
1-1. 글의 구성 (MarkDown)
# 날씨 소식
### YYYY년 MM월 DD일 X요일
의 날씨 소식입니다.
내일 오전의 날씨는 **(흐리고 비 | 흐리고 한때 비 | 흐림 | 구름많음 | 맑음)** 입니다.
그리고 내일 오후의 날씨는 **(흐리고 비 | 흐리고 한때 비 | 흐림 | 구름많음 | 맑음)** 입니다.

# 내일 운세
## 띠별 운세
| 띠 | 내일 운세 |
|------------|-------------|
|쥐띠 | 운세 |
|소띠 | 운세 |
|호랑이띠 | 운세 |
|토끼띠 | 운세 |
|용띠 | 운세 |
|뱀띠 | 운세 |
|말띠 | 운세 |
|양띠 | 운세 |
|원숭이띠 | 운세 |
|닭띠 | 운세 |
|개띠 | 운세 |
|돼지띠 | 운세 |
## 별자리 운세
| 별자리 | 날짜 | 내일 운세 |
|----------|--------|--------------|
| 물병자리 | 1월 20일~2월 18일 | 운세 |
| 물고기자리 | 2월 19일~3월 20일 | 운세 |
| 양자리 | 3월 21일~4월 19일 | 운세 |
| 황소자리 | 4월 20일~5월 20일 | 운세 |
| 쌍둥이자리 | 5월 21일~6월 21일 | 운세 |
| 게자리 | 6월 22일~7월 22일 | 운세 |
| 사자자리 | 7월 23일~8월 22일 | 운세 |
| 처녀자리 | 8월 23일~9월 22일 | 운세 |
| 천칭자리 | 9월 23일~10월 22일 | 운세 |
| 전갈자리 | 10월 23일~11월 22일 | 운세 |
| 사수자리 | 11월 23일~12월 21일 | 운세 |
| 염소자리 | 12월 22일~1월 19일 | 운세 |
1-1-1. 코드
from selenium import webdriver
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
import requests, cv2, os
from time import sleep
from tqdm import tqdm
from datetime import datetime, timedelta
tomorrow = datetime.today() + timedelta(days=1)
tomorrowStr = tomorrow.strftime('%Y_%m_%d')
tomorrowTitle = tomorrow.strftime('%Y년 %m월 %d일')
filename = f'{tomorrowStr}의 모든 것.md'
def get날씨():
week = ['월요일',
'화요일',
'수요일',
'목요일',
'금요일',
'토요일',
'일요일']
title = '{} {}'.format(tomorrowTitle, week[tomorrow.weekday()])
url = 'https://weather.naver.com/'
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
driver = webdriver.Chrome(options=chrome_options)
driver.get(url)
driver.set_window_size(1920, 1080)
sleep(0.2)
tomorrowBtn = driver.find_element(By.XPATH, '//*[@id="nation"]/ul/li[2]/button')
driver.execute_script("arguments[0].click();", tomorrowBtn)
map = driver.find_element(By.XPATH, '//*[@id="mapPanel"]')
map.screenshot(f'./{tomorrowStr}_map1.png')
afternoonBtn = driver.find_element(By.XPATH, '//*[@id="mapPanel"]/div[1]/button[3]')
driver.execute_script("arguments[0].click();", afternoonBtn)
map = driver.find_element(By.XPATH, '//*[@id="mapPanel"]')
map.screenshot(f'./{tomorrowStr}_map2.png')
오전날씨 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[2]/span[1]/span').text
오전강수확률 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[2]/span[1]/strong/span[2]').text
오후날씨 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[2]/span[2]/span').text
오후강수확률 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[2]/span[2]/strong/span[2]').text
최저기온 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[3]/strong/span[1]').text
최고기온 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[3]/strong/span[3]').text
imagePath = '{}.png'.format(tomorrowStr)
image1Path = f'{tomorrowStr}_map1.png'
image2Path = f'{tomorrowStr}_map2.png'
image1 = cv2.imread(image1Path, cv2.IMREAD_COLOR)
image2 = cv2.imread(image2Path, cv2.IMREAD_COLOR)
if image1 is None or image2 is None:
print('Image load failed')
exit(0)
merge_image = cv2.hconcat([image1, image2])
cv2.imwrite(imagePath, merge_image)
os.remove(image1Path)
os.remove(image2Path)
형식 = '''
# 날씨 소식
### {}
의 날씨 소식입니다.
<br>
내일 오전의 날씨는 **{}** 입니다.
그리고 내일 오후의 날씨는 **{}** 입니다.
<br>
오전 강수 확률은 **{}** 이고, 오후 강수 확률은 **{}** 입니다.
최저 기온 **{}C** 이고 최고 기온은 **{}C** 입니다.
<br>

---
'''.format(
title,
오전날씨, 오후날씨,
오전강수확률.replace("강수확률\n", ""),
오후강수확률.replace("강수확률\n", ""),
최저기온.replace("최저기온\n", ""),
최고기온.replace("최고기온\n", ""),
imagePath
)
with open(filename, 'w', encoding='utf-8') as file:
file.write(형식)
def get운세(운세, 날짜=None):
url = 'https://search.naver.com/search.naver?where=nexearch&sm=tab_etc&qvt=0&query={} 운세'.format(운세)
with requests.get(url) as response:
if response.status_code != 200:
print(response.status_code)
exit(0)
html = response.text
soup = BeautifulSoup(html, 'html.parser')
data = soup.select('#yearFortune > div.infors > div:nth-child(4) > div.detail._togglePanelSelectLink > p')
if len(data) == 0:
print("Data is Empty")
print(data)
exit(0)
with open(filename, 'a', encoding='utf8') as file:
if 날짜 == None:
file.write('|{}|{}|\n'.format(운세, data[0].text))
else:
file.write('|{}|{}|{}|\n'.format(운세, 날짜, data[0].text.replace(날짜, "")))
sleep(1.5)
def get운세s():
띠s = ['쥐띠', '소띠', '호랑이띠', '토끼띠', '용띠', '뱀띠', '말띠', '양띠', '원숭이띠', '닭띠', '개띠', '돼지띠']
별자리s = {
'물병자리': '01월 20일 ~ 02월 18일',
'물고기자리': '02월 19일 ~ 03월 20일',
'양자리': '03월 21일 ~ 04월 19일',
'황소자리': '04월 20일 ~ 05월 20일',
'쌍둥이자리': '05월 21일 ~ 06월 21일',
'게자리': '06월 22일 ~ 07월 22일',
'사자자리': '07월 23일 ~ 08월 22일',
'처녀자리': '08월 23일 ~ 09월 23일',
'천칭자리': '09월 24일 ~ 10월 22일',
'전갈자리': '10월 23일 ~ 11월 22일',
'사수자리': '11월 23일 ~ 12월 24일',
'염소자리': '12월 25일 ~ 01월 19일'
}
with open(filename, 'a', encoding='utf8') as file:
file.write('# 내일 운세\n\n')
file.write('## 띠별 운세 \n\n')
file.write('| 띠 | 내일 운세 |\n')
file.write('|----------|-------------|\n')
for 띠 in tqdm(띠s, desc=" 띠 운세 크롤링...", mininterval=0.1):
get운세(띠)
sleep(3)
with open(filename, 'a', encoding='utf8') as file:
file.write('\n<br>\n\n## 별자리 운세 \n\n')
file.write('| 별자리 | 날짜 | 내일 운세 |\n')
file.write('|----------|-------|-------------|\n')
for 별자리, 날짜 in tqdm(별자리s.items(), desc="별자리 운세 크롤링...", mininterval=0.1):
get운세(별자리, 날짜)
if __name__ == "__main__":
get날씨()
get운세s()
이 코드에서 중요한 것은 운세 크롤링할 때 무조건 sleep을 넣어주어야 한다는 것이다.
그렇지 않으면 공격으로 인식해 웹 사이트에서 접근 금지로 막아버린다.
1-1-2. 결과
[내일의 모든 것] 내일 날씨, 내일 운세 09월 27일
날씨 소식2024년 09월 27일 금요일의 날씨 소식입니다. 내일 오전의 날씨는 구름많음 입니다.그리고 내일 오후의 날씨는 맑음 입니다. 오전 강수 확률은 20% 이고, 오후 강수 확률은 10% 입니다.최
csexy-1365.tistory.com
1-1-3. 분석
표의 크기, 색상 등으로 가독성이 떨어져 보인다.
마크다운 형식 보다는 HTML 형식으로 만들어서 복사 붙여넣기 하는 것이 좋아보인다.
또한 이미지를 업로드하는 방식이 다소 불편하다.
1-2. HTML 형식으로 변경
<h2>내일의 모든 것</h2>
<h3>날씨 소식</h3>
<br />
<h4>YYYY년 MM월 DD일 X요일</h4>
<p>의 날씨 소식입니다.<p>
<br /><br />
<p>내일 오전의 날씨는 <strong>(흐리고 비 | 흐리고 한때 비 | 흐림 | 구름많음 | 맑음)</strong>입니다.</p>
<p>그리고 내일 오후의 날씨는 <strong>(흐리고 비 | 흐리고 한때 비 | 흐림 | 구름많음 | 맑음)</strong>입니다.</p>
<br />
<img src="YYYY_dd_mm.png" alt="weather_image" />
<br />
<hr />
<br />
<h3>내일 운세</h3>
<h4>띠별 운세</h4>
<table>
<thead>
<tr style="border-bottom: 1px solid e2e2e2; margin: 4px;">
<td style="width: 15%; background: #094044; color: white; padding: 4px; text-align: center;">띠</td>
<td style="width: 75%; background: #094044; color: white; padding: 4px; text-align: center;">내일 운세</td>
</tr>
</thead>
<tbody>
<tr style="border-bottom: 1px solid e2e2e2; margin: 4px;">
<td style="padding: 4px; text-align: center">쥐띠</td>
<td>{}</td>
</tr>
<!-- ... -->
</tbody>
</table>
<br />
<h4>별자리 운세</h4>
<table>
<thead>
<tr style="border-bottom: 1px solid e2e2e2; margin: 4px;">
<td style="width: 15%; background: #094044; color: white; padding: 4px; text-align: center;">별자리</td>
<td style="width: 20%; background: #094044; color: white; padding: 4px; text-align: center;">날짜</td>
<td style="width: 65%; background: #094044; color: white; padding: 4px; text-align: center;">내일 운세</td>
</tr>
</thead>
<tbody>
<tr style="border-bottom: 1px solid e2e2e2; margin: 4px;">
<td style="padding: 4px; text-align: center">물병자리</td>
<td style="padding: 4px; text-align: center">01월 20일 ~ 02월 18일</td>
<td>{}</td>
</tr>
<!-- ... -->
</tbody>
</table>
1-2-1. 코드
from selenium import webdriver
from selenium.webdriver.common.by import By
from bs4 import BeautifulSoup
import requests, cv2, os
from time import sleep
from tqdm import tqdm
from datetime import datetime, timedelta
tomorrow = datetime.today() + timedelta(days=1)
tomorrowStr = tomorrow.strftime('%Y_%m_%d')
tomorrowTitle = tomorrow.strftime('%Y년 %m월 %d일')
filename = f'{tomorrowStr}의 모든 것.html'
def writeStyle():
with open(filename, "w", encoding='utf-8') as file:
file.write('''
<style>
table {
border: 1px #a39485 solid;
font-size: .9em;
box-shadow: 0 2px 5px rgba(0,0,0,.25);
width: 100%;
border-collapse: collapse;
border-radius: 5px;
overflow: hidden;
}
th {
text-align: left;
}
thead {
font-weight: bold;
}
td, th {
padding: 1em .5em;
vertical-align: middle;
}
td {
border-bottom: 1px solid rgba(255,255,255,.4);
}
@media all and (max-width: 768px) {
table, thead, tbody, th, td, tr {
display: block;
}
th {
text-align: right;
}
table {
position: relative;
padding-bottom: 0;
border: none;
box-shadow: 0 0 10px rgba(0,0,0,.2);
}
thead {
float: left;
white-space: nowrap;
}
tbody {
overflow-x: auto;
overflow-y: hidden;
position: relative;
white-space: nowrap;
}
tr {
display: inline-block;
vertical-align: top;
}
th {
border-bottom: 1px solid #a39485;
}
td {
border-bottom: 1px solid #e5e5e5;
}
}
</style>
''')
def get날씨():
week = ['월요일',
'화요일',
'수요일',
'목요일',
'금요일',
'토요일',
'일요일']
title = '{} {}'.format(tomorrowTitle, week[tomorrow.weekday()])
url = 'https://weather.naver.com/'
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
driver = webdriver.Chrome(options=chrome_options)
driver.get(url)
driver.set_window_size(1920, 1080)
sleep(0.2)
tomorrowBtn = driver.find_element(By.XPATH, '//*[@id="nation"]/ul/li[2]/button')
driver.execute_script("arguments[0].click();", tomorrowBtn)
map = driver.find_element(By.XPATH, '//*[@id="mapPanel"]')
map.screenshot(f'./{tomorrowStr}_map1.png')
afternoonBtn = driver.find_element(By.XPATH, '//*[@id="mapPanel"]/div[1]/button[3]')
driver.execute_script("arguments[0].click();", afternoonBtn)
map = driver.find_element(By.XPATH, '//*[@id="mapPanel"]')
map.screenshot(f'./{tomorrowStr}_map2.png')
오전날씨 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[2]/span[1]/span').text
오전강수확률 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[2]/span[1]/strong/span[2]').text
오후날씨 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[2]/span[2]/span').text
오후강수확률 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[2]/span[2]/strong/span[2]').text
최저기온 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[3]/strong/span[1]').text
최고기온 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[3]/strong/span[3]').text
imagePath = '{}.png'.format(tomorrowStr)
image1Path = f'{tomorrowStr}_map1.png'
image2Path = f'{tomorrowStr}_map2.png'
image1 = cv2.imread(image1Path, cv2.IMREAD_COLOR)
image2 = cv2.imread(image2Path, cv2.IMREAD_COLOR)
if image1 is None or image2 is None:
print('Image load failed')
exit(0)
merge_image = cv2.hconcat([image1, image2])
cv2.imwrite(imagePath, merge_image)
os.remove(image1Path)
os.remove(image2Path)
형식 = '''
<h2>내일의 모든 것</h2>
<h3>날씨 소식</h3>
<br />
<h4>{}</h4>
<p>의 날씨 소식입니다.<p>
<br /><br />
<p>내일 오전의 날씨는 <strong>{}</strong>입니다.</p>
<p>그리고 내일 오후의 날씨는 <strong>{}</strong>입니다.</p>
<br />
<p>오전 강수 확률은 <strong>{}</strong> 이고, 오후 강수 확률은 <strong>{}</strong> 입니다.</p>
<p>최저 기온 <strong>{}C</strong> 이고 최고 기온은 <strong>{}C</strong> 입니다.</p>
<br />
<img src="{}" alt="weather_image" />
<br />
<hr />
<br />
'''.format(
title,
오전날씨, 오후날씨,
오전강수확률.replace("강수확률\n", ""),
오후강수확률.replace("강수확률\n", ""),
최저기온.replace("최저기온\n", ""),
최고기온.replace("최고기온\n", ""),
imagePath
)
with open(filename, 'a', encoding='utf-8') as file:
file.write(형식)
def get운세(운세, 날짜=None):
url = 'https://search.naver.com/search.naver?where=nexearch&sm=tab_etc&qvt=0&query={} 운세'.format(운세)
with requests.get(url) as response:
if response.status_code != 200:
print(response.status_code)
exit(0)
html = response.text
soup = BeautifulSoup(html, 'html.parser')
data = soup.select('#yearFortune > div.infors > div:nth-child(4) > div.detail._togglePanelSelectLink > p')
if len(data) == 0:
print("Data is Empty")
print(data)
exit(0)
with open(filename, 'a', encoding='utf8') as file:
if 날짜 == None:
형식 = '''
<tr style="border-bottom: 1px solid e2e2e2; margin: 4px;">
<td style="padding: 4px; text-align: center">{}</td>
<td>{}</td>
</tr>
'''.format(운세, data[0].text)
else:
형식 = '''
<tr style="border-bottom: 1px solid e2e2e2; margin: 4px;">
<td style="padding: 4px; text-align: center">{}</td>
<td style="padding: 4px; text-align: center">{}</td>
<td>{}</td>
</tr>
'''.format(운세, 날짜, data[0].text.replace(날짜, ""))
file.write(형식)
sleep(1.5)
def get운세s():
띠s = ['쥐띠', '소띠', '호랑이띠', '토끼띠', '용띠', '뱀띠', '말띠', '양띠', '원숭이띠', '닭띠', '개띠', '돼지띠']
별자리s = {
'물병자리': '01월 20일 ~ 02월 18일',
'물고기자리': '02월 19일 ~ 03월 20일',
'양자리': '03월 21일 ~ 04월 19일',
'황소자리': '04월 20일 ~ 05월 20일',
'쌍둥이자리': '05월 21일 ~ 06월 21일',
'게자리': '06월 22일 ~ 07월 22일',
'사자자리': '07월 23일 ~ 08월 22일',
'처녀자리': '08월 23일 ~ 09월 23일',
'천칭자리': '09월 24일 ~ 10월 22일',
'전갈자리': '10월 23일 ~ 11월 22일',
'사수자리': '11월 23일 ~ 12월 24일',
'염소자리': '12월 25일 ~ 01월 19일'
}
with open(filename, 'a', encoding='utf8') as file:
file.write('''
<h3>내일 운세</h3>
<h4>띠별 운세</h4>
<table>
<thead>
<tr style="border-bottom: 1px solid e2e2e2; margin: 4px;">
<td style="width: 15%; background: #094044; color: white; padding: 4px; text-align: center;">띠</td>
<td style="width: 75%; background: #094044; color: white; padding: 4px; text-align: center;">내일 운세</td>
</tr>
</thead>
<tbody>
''')
for 띠 in tqdm(띠s, desc=" 띠 운세 크롤링...", mininterval=0.1):
get운세(띠)
with open(filename, 'a', encoding='utf8') as file:
file.write('''
</tbody>
</table>
<br />
<h4>별자리 운세</h4>
<table>
<thead>
<tr style="border-bottom: 1px solid e2e2e2; margin: 4px;">
<td style="width: 15%; background: #094044; color: white; padding: 4px; text-align: center;">별자리</td>
<td style="width: 24%; background: #094044; color: white; padding: 4px; text-align: center;">날짜</td>
<td style="width: 61%; background: #094044; color: white; padding: 4px; text-align: center;">내일 운세</td>
</tr>
</thead>
<tbody>
''')
sleep(3)
for 별자리, 날짜 in tqdm(별자리s.items(), desc="별자리 운세 크롤링...", mininterval=0.1):
get운세(별자리, 날짜)
with open(filename, 'a', encoding='utf-8') as file:
file.write('''
</tbody>
</table>
''')
if __name__ == "__main__":
writeStyle()
get날씨()
get운세s()
1-2-2. 결과
[내일의 모든 것] 내일 날씨, 내일 운세 09월 27일 (2)
(HTML 코드로 제작) 내일의 모든 것날씨 소식 2024년 09월 27일 금요일의 날씨 소식입니다.내일 오전의 날씨는 구름많음입니다.그리고 내일 오후의 날씨는 맑음입니다. 오전 강수 확률은 20% 이고,
csexy-1365.tistory.com
1-2-3. 분석
확실히 HTML과 CSS를 이용하니 테이블이 깔끔하게 표기되고 열의 크기도 자동화가 되니 편하다.
틀은 이렇게 잡도록 하겠다.
2. 코딩
코드 자체는 이미 위에 다 작성되었지만, 핵심 코드들을 소개하고자 한다.
2-1. Selenium을 통한 날씨 크롤링
url = 'https://weather.naver.com/'
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--headless")
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
driver = webdriver.Chrome(options=chrome_options)
driver.get(url)
driver.set_window_size(1920, 1080)
sleep(0.2)
tomorrowBtn = driver.find_element(By.XPATH, '//*[@id="nation"]/ul/li[2]/button')
driver.execute_script("arguments[0].click();", tomorrowBtn)
map = driver.find_element(By.XPATH, '//*[@id="mapPanel"]')
map.screenshot(f'./{tomorrowStr}_map1.png')
afternoonBtn = driver.find_element(By.XPATH, '//*[@id="mapPanel"]/div[1]/button[3]')
driver.execute_script("arguments[0].click();", afternoonBtn)
map = driver.find_element(By.XPATH, '//*[@id="mapPanel"]')
map.screenshot(f'./{tomorrowStr}_map2.png')
오전날씨 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[2]/span[1]/span').text
오전강수확률 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[2]/span[1]/strong/span[2]').text
오후날씨 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[2]/span[2]/span').text
오후강수확률 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[2]/span[2]/strong/span[2]').text
최저기온 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[3]/strong/span[1]').text
최고기온 = driver.find_element(By.XPATH, '//*[@id="weekly"]/ul/li[2]/div/div[3]/strong/span[3]').text
크롬 드라이버를 사용하여 Selenium으로 이미지를 캡쳐하고, 날씨, 강수확률, 최저/최고 기온을 크롤링한다.
Selenium은 동적 크롤링이 가능하여, 내일 날짜를 클릭할 수 있다.
(driver.execute_script("arguments[0].click()", tomorrowBtn))
클릭해서 mapPanel에 해당하는 이미지를 캡쳐했다. (map.screenshot())
2-2. OpenCV를 통한 이미지 병합
image1Path = f'{tomorrowStr}_map1.png'
image2Path = f'{tomorrowStr}_map2.png'
image1 = cv2.imread(image1Path, cv2.IMREAD_COLOR)
image2 = cv2.imread(image2Path, cv2.IMREAD_COLOR)
if image1 is None or image2 is None:
print('Image load failed')
exit(0)
merge_image = cv2.hconcat([image1, image2])
cv2.imwrite(imagePath, merge_image)
코드 자체는 간단하다.
hconcat 메서드를 사용해서 두 이미지를 Horizontal하게 Merge한다.
2-3. Beautiful Soup을 이용한 운세 크롤링
url = 'https://search.naver.com/search.naver?where=nexearch&sm=tab_etc&qvt=0&query={} 운세'.format(운세)
with requests.get(url) as response:
if response.status_code != 200:
print(response.status_code)
exit(0)
html = response.text
soup = BeautifulSoup(html, 'html.parser')
data = soup.select('#yearFortune > div.infors > div:nth-child(4) > div.detail._togglePanelSelectLink > p')
if len(data) == 0:
print("Data is Empty")
print(data)
exit(0)
with open(filename, 'a', encoding='utf8') as file:
if 날짜 == None:
# ...생략
else:
# ...생략
file.write(형식)
sleep(1.5)
네이버 운세의 URL은 뒤에 query 값에 해당 운세를 넣으면 보여준다.
그리고 이 또한 짧은 시간 내에 여러 번 접속할 경우 공격으로 인식하여 잠시동안 접속하지 못하기 때문에 sleep 메서드를 넣어주었다. 만약 이걸 넣지 않고 다수의 트래픽을 유발할 경우 극단적으로 디도스 공격으로 인식되어 큰 낭패를 볼 수도 있으니 꼭 넣기를 바란다.
3. 유지 보수 및 자동화
이는 추후 업그레이드할 예정이다.
계획은 다음과 같다.
3-1. 개인 서버 구축
개인 서버를 통해 매일 오전마다 해당 html을 생성하는 파이썬 코드를 작성한다.
혹은 웹 사이트에 접속하여 Create 버튼을 누르면 해당 HTML 코드를 출력하는 서버를 만든다.
3-2. 이미지 저장
현재 이미지는 같이 복사가 되지 않는다.
그렇기 때문에 개인 서버에 static 형식으로 파싱이 가능하게 하고, 해당 URL 혹은 이미지를 가져와 사용한다.
3-3. 프로그램 확장
현재는 날씨, 운세를 정리해서 출력한다.
이후에는 그 이외에도 매일 체크하면 좋은 정보들을 추려 제공한다.
3-4. 코드 리팩토링
현재는 코드들이 조금 더럽게 느껴진다.
이를 리팩토링하여 최적화를 하고자 한다.
깃허브 링크
GitHub - poinguinie/Everything-in-Tomorrow: 내일의 모든 것, 소스 코드. 내일 운세, 날씨 등 정보 자동화 프
내일의 모든 것, 소스 코드. 내일 운세, 날씨 등 정보 자동화 프로그램. Contribute to poinguinie/Everything-in-Tomorrow development by creating an account on GitHub.
github.com
'제작 > 기타 프로그램' 카테고리의 다른 글
[카카오 채팅방 매니저][Node.js] #1 프로젝트 기획 (0) | 2022.08.09 |
---|---|
[C#] 행렬 계산기 (6) - 전체 코드 (0) | 2020.10.21 |
[C#] 행렬 계산기 (5) - 곱셈 계산 (0) | 2020.10.21 |
[C#] 행렬 계산기 (4) - 이차원 배열로 더 간단하게 표현 (0) | 2020.10.20 |
[C#] 행렬 계산기 (3) - 대리 연산자를 활용하여 함축화 (0) | 2020.10.15 |