
[Opinion] 우리는 왜 아침마다 오하아사를 열어 보게 될까
약 4년째, 게임과 서브컬처, 인터넷 방송이라는 취미를 공유하는 친목 커뮤니티에서 활동하고 있다. 오랫동안 인터넷 망령 생활을 해 온 수십 명의 사람들이 모여 있는 곳이다.
이곳에서 아침을 시작하는 루틴 중 하나는, 꽤 오랫동안 아침별점(오하아사, @Hi_Ohaasa)를 확인하는 것이다. 모르는 사람들에게 간단하게 설명하자면, 일본 아사히에서 만든 별자리 기반 운세 코너다.
국내에서는 X 계정을 통해 매일 별자리별 운세를 확인할 수 있다.
특이한 점이라면, 매일 별자리 별로 순위를 매겨서 보여준다는 것이다.

디스코드 봇 중 마냥으로 하루의 운세를 받아볼 수 있지만, 해당 X 계정처럼 순위를 보여주지는 않는다.
우리끼리는 이게 하루를 도파민을 느끼며 시작하는 콘텐츠인데, 매일 알림이 오도록 봇을 만들면 좋겠다는 생각을 하게 됐다.
기본적으로 해당 X 계정도 아침별점 홈페이지의 내용을 번역하여 게시된다.
우리에게 필요한 건 순위밖에 없으므로, 금방 만들 수 있을 것 같다는 생각이 들었다.
우선, 정보를 가져올 사이트의 구조를 파악하기로 한다.

개발자 도구를 이용해 html 구조를 살펴보니, oa_horoscope_list 아래로 클래스가 순서대로 나타나는 것을 확인할 수 있었다.
크롤링을 통해 해당 리스트의 순서를 가져오면 될 것 같다.
세팅 충돌을 최소화하기 위해, Playwright를 사용하기로 한다.
pip3 install playwright beautifulsoup4
python3 -m playwright install chromium
그리고 클래스 명으로 사용되는 별자리들의 영어 학명을 한글로 치환하기 위한 맵핑 테이블을 우선 설정해두자.
SIGN_MAP = {
"aries": "양자리", "taurus": "황소자리", "gemini": "쌍둥이자리",
"cancer": "게자리", "leo": "사자자리", "virgo": "처녀자리",
"libra": "천칭자리", "scorpio": "전갈자리", "sagittarius": "사수자리",
"capricorn": "염소자리", "aquarius": "물병자리", "pisces": "물고기자리"
}
크롤링을 통해 사이트의 html 코드를 가져오는 코드를 다음과 같이 작성한다.
item 개수가 12이면 성공이다.
from playwright.sync_api import sync_playwright
from bs4 import BeautifulSoup
import requests
import os
def run_local_crawling():
url = "https://www.asahi.co.jp/ohaasa/week/horoscope/"
# Playwright를 사용해 가상 브라우저 실행
with sync_playwright() as p:
browser = p.chromium.launch(headless=True) # 화면에 브라우저 창을 띄우지 않음
page = browser.new_page()
# 사이트 접속
page.goto(url)
# 핵심: 별자리 리스트(li)가 화면에 그려질 때까지 최대 10초 대기
page.wait_for_selector('ul.oa_horoscope_list li', timeout=10000)
# 자바스크립트 렌더링이 끝난 최종 HTML 코드를 가져옴
html = page.content()
browser.close()
# 가져온 HTML을 BeautifulSoup으로 분석
soup = BeautifulSoup(html, 'html.parser')
horoscope_ul = soup.select_one('ul.oa_horoscope_list')
if not horoscope_ul:
print("❌ 데이터를 찾지 못했습니다.")
return
items = horoscope_ul.select('li')
print(f"✅ 성공! 총 {len(items)}개의 데이터를 찾았습니다.\n")
다음은 포맷에 맞춰 별자리를 순위별로 출력하는 부분이다.
# enumerate를 사용하여 1부터 차례대로 번호(index)를 매깁니다.
for index, item in enumerate(items, start=1):
classes = item.get('class', [])
# 1. 별자리 영문명 및 한글명 추출
english_sign = next((c for c in classes if c in SIGN_MAP), "unknown")
korean_sign = SIGN_MAP.get(english_sign, english_sign)
# 2. 순위 추출 (리스트의 순서 = 순위)
rank_val = index
rank_text = f"{rank_val}위"
# 3. 이모지 설정
if rank_val == 1:
emoji = "🥇"
elif rank_val == 2:
emoji = "🥈"
elif rank_val == 3:
emoji = "🥉"
else:
emoji = "🔹"
print(f"{emoji} {rank_text}: {korean_sign} ({english_sign})")
print("-" * 30)
성공적으로 코드를 작성했다면, 로컬에서 다음과 같은 출력을 얻을 수 있다.
✅ 성공! 총 12개의 데이터를 찾았습니다.
------------------------------
🥇 1위: 양자리
🥈 2위: 사자자리
🥉 3위: 사수자리
🔹 4위: 쌍둥이자리
🔹 5위: 천칭자리
🔹 6위: 물병자리
🔹 7위: 황소자리
🔹 8위: 처녀자리
🔹 9위: 염소자리
🔹 10위: 게자리
🔹 11위: 전갈자리
🔹 12위: 물고기자리
------------------------------
위 메세지를 디스코드로 보내는 함수 역시 작성해야 한다.
추후 깃허브 액션의 환경변수로 DISCORD_WEBHOOK를 설정해 실제 디스코드 채널의 웹후크 URL을 관리할 것이다.
기왕이면 예쁘게 payload 값에 사용자명과 프로필 사진까지 넣어보자.
def send_discord(message):
webhook_url = os.environ.get('DISCORD_WEBHOOK')
if not webhook_url:
print("Webhook URL이 설정되지 않았습니다. 결과만 출력합니다.")
print(message)
return
# 디스코드 봇 프로필 설정
payload = {
"username": "아침별점 요정",
"avatar_url": "",
"content": message
}
requests.post(webhook_url, json=payload)
깃허브 액션을 이용한 자동화 설정을 위해 .yml을 이용하도록 하자.
name: Ohaasa Daily Bot
on:
schedule:
# 한국 시간 기준 매일 오전 6시 실행 (UTC 기준 21시)
- cron: '00 21 * * *'
workflow_dispatch: # 수동으로 실행해볼 수 있는 버튼
jobs:
run-bot:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
pip install playwright beautifulsoup4 requests
- name: Install Playwright Browsers
run: |
playwright install chromium
- name: Execute Script
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
run: python main.py
리포지토리 구조는 다음과 같이 작성하면 될 것 같다.
[Repository]
├── .github
│ └── workflows
│ └── daily_bot.yml # GitHub Actions 설정 파일
└── main.py # 파이썬 크롤링 코드
레포지토리 설정에서 Secrets and variables > Actions > Repository secrets에서 새로운 항목을 추가한다.

디스코드 채팅 채널 > 설정을 통해 웹후크 URL을 받은 다음

DISCORD_WEBHOOK라는 이름으로, 내용은 웹후크 URL을 담아 Repository secret을 생성하면 된다.

레포지토리의 Actions 항목에서 액션을 선택, workflow를 작동시키면 수동으로 결과물을 확인할 수 있다.


GitHub Actions 특성상 첫 스케줄 할당에 시간이 꽤 소요된다고 한다. 그래서 트래픽을 피하기 위해 약간 애매한 시간으로 맞춰놓고, 지연을 최소화했다.

스케줄을 설정하고 하루가 지난 뒤, 정상적으로 설정한 시간대에 작동한 것을 확인했다.