본문 바로가기
SW프로그래밍 개발/Python

엑셀, 이메일, 크롤링까지 한 번에! 파이썬 자동화 프로젝트

by ICT리더 리치 2025. 8. 13.
반응형

보고서 만들고, 메일 보내고, 데이터 긁어오고… 클릭 대신 파이썬이 야근을 대신합니다.

안녕하세요, ICT리더 리치입니다. 실무에서 가장 시간을 많이 잡아먹는 일이 무엇일까요? 저는 늘 엑셀 정리 → 이메일 발송 → 웹 데이터 수집을 반복하다가 “이걸 한 번에 이어붙이면 하루가 바뀌겠다”는 확신이 들었어요. 오늘 글에서는 파이썬으로 엑셀 처리, 이메일 발송/수신 체크, 웹 크롤링을 하나의 워크플로로 묶는 자동화 프로젝트를 실제 코드와 함께 설명합니다. 초보자도 바로 따라 할 수 있도록 환경 세팅부터 스케줄링까지 전 과정을 담았어요.

파이썬 자동화 인포그래픽: 노트북으로 업무 자동화를 수행하는 여성, 크롤링·스프레드시트·이메일 아이콘, 체크리스트 포함
엑셀·이메일·크롤링을 한 번에 — 자동화 핵심 포인트

1. 프로젝트 개요와 환경 세팅

이번 자동화의 목표는 (1) 엑셀 데이터 정리→리포트 저장, (2) 이메일 일괄 발송·수신 확인, (3) 웹에서 신규 데이터 수집을 스크립트 한 번으로 끝내는 것입니다. 권장 환경은 Python 3.10+, 가상환경(venv), 그리고 주요 라이브러리 pandas, openpyxl, requests, beautifulsoup4, selenium, smtplib, imaplib입니다. 크롬 자동화를 쓸 경우 WebDriver(또는 Chrome for Testing) 경로 설정이 필요합니다. 기업망에서는 프록시/방화벽 정책을 반드시 확인하세요.

Python 3.10+ venv pip + requirements.txt
⚙️ 빠른 설치 (권장 명령어)
# 1) 가상환경 생성 & 활성화
python -m venv .venv
# Windows
. .venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate

# 2) 필수 패키지 설치
python -m pip install --upgrade pip
pip install -r requirements.txt

# 3) 환경변수(.env) 준비 후 테스트 실행
python -m pip show pandas
python src/main.py --dry-run
📁 프로젝트 폴더 구조 (권장)
automation_project/
 ├─ data/                # 원시/중간 데이터 (입출력)
 ├─ reports/             # 최종 엑셀 결과물(.xlsx)
 ├─ logs/                # 실행 로그(.log)
 ├─ src/
 │   ├─ config.py        # 설정/환경변수 로딩
 │   ├─ excel_builder.py # 집계·서식·보고서 생성
 │   ├─ mailer.py        # SMTP 발송·IMAP 확인
 │   ├─ scraper.py       # 웹 요청/파싱 파이프라인
 │   └─ main.py          # 엔트리포인트(크롤→리포트→메일)
 ├─ .env                 # 비밀키·계정 (커밋 금지)
 ├─ requirements.txt     # 의존성 목록
 └─ README.md            # 사용법/유지보수 메모
🔐 .env 예시 (버전관리 제외)
TZ=Asia/Seoul
# SMTP
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=you@example.com
SMTP_PASS=app_password
# IMAP
IMAP_HOST=imap.gmail.com
IMAP_USER=you@example.com
IMAP_PASS=app_password
TIP 운영/개발 환경은 .env.prod, .env.dev 등으로 분리하고, .gitignore.env*를 반드시 등록하세요.
📦 requirements.txt (샘플)
pandas>=2.2
openpyxl>=3.1
requests>=2.32
beautifulsoup4>=4.12
lxml>=5.0
selenium>=4.22
python-dotenv>=1.0
loguru>=0.7
🚫 .gitignore (권장)
.venv/
.env*
__pycache__/
logs/*.log
reports/*.xlsx
*.pyc
  • 가상환경 경로가 작업 스케줄러/크론과 동일한지 확인
  • 로그 회전(log rotation) 설정으로 logs/ 용량 관리
  • 민감정보는 코드 하드코딩 금지 → .env/Keychain 사용

환경 세팅 & 폴더 구조 인포그래픽: automation_project 디렉토리 트리와 .env·requirements 카드, 체크포인트
환경세팅&폴더구조 - venv·.env·로그 회전을 한 장에 정리

2. 엑셀 자동화: 데이터 정리·리포트 생성

핵심은 정형화된 입력 → 정제 → 피벗/집계 → 서식 포함 출력입니다. 원본을 그대로 믿지 말고, 결측치·중복·타입을 먼저 정리하세요. 아래는 라이브러리 선택 가이드입니다.

라이브러리 주요 역할 장점/적합 시나리오
pandas 데이터 처리/집계 대량 처리·피벗·그룹화가 많을 때
openpyxl 엑셀 서식/차트/수식 서식 있는 보고서(.xlsx) 출력
import pandas as pd

df = pd.read_excel("raw.xlsx")
df["매출"] = pd.to_numeric(df["매출"], errors="coerce").fillna(0)
df = df.drop_duplicates().fillna({"지역":"미기입"})

pivot = (df.groupby(["지역","상품"])
           .agg(수량=("수량","sum"), 매출=("매출","sum"))
           .reset_index()
           .sort_values(["지역","매출"], ascending=[True,False]))

with pd.ExcelWriter("리포트.xlsx", engine="openpyxl") as writer:
    pivot.to_excel(writer, index=False, sheet_name="요약")

3. 이메일 자동화: 대량 발송·수신 확인

SMTP로 보고서를 일괄 발송하고, IMAP으로 수신함을 점검하여 반송/문의 메일을 분류합니다. 회사 보안정책에 따라 앱 비밀번호 또는 사내 SMTP를 사용하세요.

  • 제목/본문 템플릿에 {이름}, {월} 같은 플레이스홀더 사용
  • 첨부파일(리포트.xlsx) 자동 첨부 및 MIME 타입 지정
import smtplib, ssl, imaplib, email
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email import encoders

SMTP_HOST, SMTP_PORT = "smtp.gmail.com", 587
USER, PASS = "you@example.com", "app_password"  # 보안: 환경변수로 관리 권장

def send_report(to_addr):
    msg = MIMEMultipart()
    msg["From"], msg["To"] = USER, to_addr
    msg["Subject"] = "월간 매출 리포트"
    msg.attach(MIMEText("안녕하세요,\n월간 리포트를 첨부드립니다.", "plain", "utf-8"))

    with open("리포트.xlsx","rb") as f:
        part = MIMEBase("application","octet-stream")
        part.set_payload(f.read())
    encoders.encode_base64(part)
    part.add_header("Content-Disposition", "attachment; filename*=UTF-8''리포트.xlsx")
    msg.attach(part)

    context = ssl.create_default_context()
    with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as s:
        s.starttls(context=context)
        s.login(USER, PASS)
        s.send_message(msg)

# 수신함 확인(예: 제목에 '반송' 포함 메일 카운트)
def count_bounce():
    imap = imaplib.IMAP4_SSL("imap.gmail.com")
    imap.login(USER, PASS)
    imap.select("INBOX")
    status, data = imap.search(None, '(SUBJECT "반송")')
    ids = data[0].split()
    imap.logout()
    return len(ids)

4. 웹 크롤링: 데이터 수집 파이프라인

정적 페이지는 requests + BeautifulSoup, 동적 로딩은 Selenium으로 처리합니다. robots.txt, 서비스 약관, 요청 간 지연(sleep) 등 윤리적 크롤링 원칙을 지키세요. 크롤링 결과는 CSV로 저장하고 앞선 엑셀 처리 단계에 합류시킵니다.

import time, csv, requests
from bs4 import BeautifulSoup

rows = []
for page in range(1, 4):
    url = f"https://example.com/list?page={page}"
    r = requests.get(url, timeout=10)
    soup = BeautifulSoup(r.text, "lxml")
    for item in soup.select(".card"):
        title = item.select_one(".title").get_text(strip=True)
        price = item.select_one(".price").get_text(strip=True)
        rows.append([title, price])
    time.sleep(1)  # 예의 있는 지연

with open("crawl.csv","w",newline="",encoding="utf-8") as f:
    csv.writer(f).writerows([["title","price"], *rows])

파이썬 자동화 5단계 플로우: 크롤링→정제/집계→엑셀 보고서→스케줄링, 하단 체크리스트 포함
수집부터 발송까지 자동화를 위한 5 STEP 워크플로우

5. 워크플로 통합: 하루 10분 루틴

하나의 실행 스크립트에서 크롤링 → 엑셀 정리 → 이메일 발송 순서로 연결합니다. 에러는 로그 파일로 남기고, 실패 시 재시도 횟수를 제한하세요.

단계 작업 산출물
1 크롤링 crawl.csv
2 정제·집계 리포트.xlsx
3 메일 발송/로그 send.log
# main.py (의사코드)
# 1) 웹에서 데이터 수집 -> 2) 집계 리포트 생성 -> 3) 메일 발송
# 예외 발생 시 로그 기록 후 종료 코드로 신호

# main.py에서 크롤링 → 엑셀 생성 → 메일 발송을 순차 실행하고, 실패 시 로그 남기기/재시도 전략을 둡니다.
    
# src/config.py
from dotenv import load_dotenv; load_dotenv()
TARGETS = [
    {"url": "https://example.com/board", "selector": "table.list"},
    # 필요 시 추가
]
MAIL_SUBJECT = "[자동보고] 일일 지표 보고서"
MAIL_TEMPLATE = """

# 일일 자동 보고서

첨부된 엑셀 파일에서 상세 데이터를 확인하세요.

본 메일은 자동 발송되었습니다.


"""
# src/main.py
import logging, os
from src.scraper import fetch_table, polite_sleep
from src.excel_builder import save_report
from src.mailer import send_mail
from src.config import TARGETS, MAIL_SUBJECT, MAIL_TEMPLATE

os.makedirs("logs", exist_ok=True); os.makedirs("reports", exist_ok=True)

logging.basicConfig(
    filename="logs/app.log",
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s"
)
logger = logging.getLogger(__name__)

def run_pipeline():
    try:
        tables = []
        for t in TARGETS:
            df = fetch_table(t["url"], t["selector"])
            tables.append(df)
            polite_sleep()
        report_path = save_report(tables)
        send_mail(MAIL_SUBJECT, MAIL_TEMPLATE, attachments=[report_path])
        logger.info("SUCCESS: %s", report_path)
        return 0
    except Exception as e:
        logger.exception("FAILED: %s", e)
        return 1

if __name__ == "__main__":
    raise SystemExit(run_pipeline())
    

노트북 앞에서 미소 짓는 젊은 여성의 클린 썸네일
밝고 단정한 자동화 프로젝트를 구현하는 20대 여성개발자의 대표 썸네일

6. 배포·스케줄링: Windows/Mac/Linux

가상환경과 의존성 파일(requirements.txt)을 함께 배포하세요. OS별 스케줄링 포인트는 아래와 같습니다.

  • Windows: 작업 스케줄러에서 python main.py 매일 09:00 실행
  • macOS/Linux: 크론 예) 0 9 * * 1-5 /usr/bin/python /path/main.py >> /path/send.log 2>&1
  • 로그/알림: 실패 시 종료코드 감지→슬랙/메일 알림 훅
Windows (작업 스케줄러)
:: schedule.bat
schtasks /Create /TN "Daily_Py_Auto" /TR "C:\Path\to\venv\python.exe C:\repo\run.py" /SC DAILY /ST 08:30 /F
Linux/macOS (cron)
# crontab -e
30 8 * * 1-5 /path/to/venv/bin/python /repo/run.py >> /repo/logs/cron.log 2>&1
추가예시1 - 크롤링 설계: 안정성, 차단 회피, 스케줄링

추가예시1 - 크롤링 설계: 안정성, 차단 회피, 스케줄링

  • 반복/재시도: 지수형 백오프(1s→2s→4s), 실패 로그 저장, 마지막 시도 후 알림 메일
  • 차단 회피: 합법적 범위의 wait_for_selector·랜덤 지연·헤드리스/UA 순환. 서비스 약관과 robots.txt 준수
  • 데이터 무결성: 스키마(컬럼명/타입) 고정, 수집 버전태깅(YYYYMMDD_HHMM), 해시로 중복 방지
  • 스케줄링: 매일 08:30 실행, 실패 시 10분 간격 3회 재시도
# /src/scrape_daily.py
import asyncio, random, time, hashlib, csv
from pathlib import Path
from playwright.async_api import async_playwright
from datetime import datetime

RAW_DIR = Path("data/raw")
LOG_DIR = Path("logs")
RAW_DIR.mkdir(parents=True, exist_ok=True); LOG_DIR.mkdir(exist_ok=True)

def _stamp(prefix="collect"):
    return f"{prefix}_{datetime.now().strftime('%Y%m%d_%H%M')}"

async def fetch_table(page):
    await page.goto("https://example.com/login", timeout=60_000)
    # ✅ 실제 타겟 페이지 규칙/약관 및 로봇 배제 표준 준수 필요
    await page.fill("#id", "YOUR_ID")
    await page.fill("#pw", "YOUR_PW")
    await page.click("button[type=submit]")
    await page.wait_for_url("**/dashboard", timeout=60_000)

    await page.goto("https://example.com/report")
    await page.wait_for_selector("table.report-table")
    rows = await page.locator("table.report-table tbody tr").all()

    data = []
    for r in rows:
        tds = await r.locator("td").all_inner_texts()
        data.append([t.strip() for t in tds])
    return data

async def run_scraper():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        context = await browser.new_context(user_agent="Mozilla/5.0 auto-report")
        page = await context.new_page()
        data = await fetch_table(page)
        await browser.close()
        return data

def save_csv(rows):
    stamp = _stamp("raw")
    fp = RAW_DIR / f"{stamp}.csv"
    with fp.open("w", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        w.writerow(["날짜", "카테고리", "수량", "금액"])
        w.writerows(rows)
    return fp

def scrape_with_retry(max_retry=3):
    for i in range(max_retry):
        try:
            rows = asyncio.run(run_scraper())
            fp = save_csv(rows)
            return fp
        except Exception as e:
            (LOG_DIR / "scrape_error.log").write_text(f"{datetime.now()} :: {e}\n", encoding="utf-8")
            if i == max_retry - 1:
                raise
            time.sleep(2 ** i + random.random())  # 1→2→4 sec

7. 자주 묻는 질문 (FAQ)

Q 크롤링이 법적으로 문제될 수 있나요?

robots.txt, 서비스 약관, 개인정보 처리 방침을 확인하고 요청 빈도 조절·적절한 헤더 설정·로그인 필요 영역 접근 금지 등 기본 원칙을 지키면 위험을 크게 줄일 수 있습니다.

Q 회사 메일 서버에서 SMTP 인증이 막혀 있어요.

사내 SMTP 릴레이 주소·포트·인증 방식을 IT팀에 확인하세요. 외부 메일 사용 시 앱 비밀번호 또는 IP 허용 목록 등록이 필요할 수 있습니다.

Q 대용량 엑셀 처리 성능이 떨어집니다.

CSV로 중간 저장하고, 필요한 컬럼만 로딩(usecols)·명시적 dtype 설정·청크 처리(chunksize)로 메모리를 절약하세요.

Q Selenium 없이도 동적 페이지를 수집할 수 있나요?

네. 브라우저 개발자도구의 네트워크 탭에서 호출되는 JSON API가 있으면 requests로 직접 호출하는 편이 더 가볍고 안정적입니다.

Q 실패 시 재시도는 어떻게 설계하나요?

지수 백오프(예: 1초→2초→4초), 최대 시도 횟수 제한, 실패 로그를 적용하고 임계 실패 시 관리자 알림을 발송하세요.

8. 마무리 요약

✅ 엑셀·이메일·크롤링을 하나로 묶으면 ‘업무의 흐름’이 자동화됩니다

정제→집계→보고서→발송의 풀라인을 스크립트로 연결하면 반복 업무가 재현 가능하고 예측 가능한 시스템으로 변합니다. 작은 자동화부터 시작해 크론/작업 스케줄러로 일상에 심어보세요. 야근이 줄어드는 순간, 자동화의 진가를 체감하게 됩니다.

 

반응형