λ§μΆ€ν μ·¨μ κ³΅κ³ μΆμ² λ° μ΄λ©μΌ λ°μ‘ μλν μμ€ν
Google μ€λ¬Έμ§λ₯Ό ν΅ν΄ μμ§ν ꡬμ§μ λ°μ΄ν°λ₯Ό λΆμνμ¬, κ° μ§μμμ μ νΈλμ λ§λ μ±μ© κ³΅κ³ λ₯Ό λ§€μΉνκ³ κ°μΈνλ HTML μ΄λ©μΌμ μλμΌλ‘ λ°μ‘νλ μμ€ν μ λλ€.
- κ³΅κ³ λ§€μΉ: 4λ¨κ³ νν°λ§ μμ€ν μΌλ‘ ꡬμ§μ μ νΈλμ μ΅μ νλ κ³΅κ³ μΆμ²
- κ°μΈνλ μ΄λ©μΌ: νμ¬ λ‘κ³ , D-day μ 보, μ±μ© νν νκ·Έκ° ν¬ν¨λ λ§μΆ€ν HTML μ΄λ©μΌ
- Google Sheets μ°λ: μ€λ¬Έ μλ΅ λ°μ΄ν° μλ μμ§ λ° λ°μ‘ κ²°κ³Ό κΈ°λ‘
- Gmail API ν΅ν©: OAuth 2.0 κΈ°λ° μμ ν μ΄λ©μΌ λ°μ‘
- μ νΈ κΈ°μ μ°μ μμ: μ§μμκ° μ νν λκΈ°μ κ·Έλ£Ή κ³΅κ³ μ λ³ν νμ
job-finder/test/
βββ main.py # λ©μΈ μ€ν νμΌ
βββ requirements.txt # ν¨ν€μ§ μμ‘΄μ±
βββ .env # νκ²½ λ³μ μ€μ (보μ)
βββ .env_sample # νκ²½ λ³μ ν
νλ¦Ώ
βββ credentials.json # Google OAuth μΈμ¦ μ 보
β
βββ data/ # μ±μ© κ³΅κ³ λ°μ΄ν° (JSON)
β βββ μΏ ν‘_5923444_250917_crawling.json
β βββ μΏ ν‘_6753367_250917_crawling.json
β βββ ...
β
βββ template/ # μ΄λ©μΌ ν
νλ¦Ώ
β βββ test_email.html # HTML μ΄λ©μΌ κΈ°λ³Έ ν
νλ¦Ώ
β
βββ services/ # ν΅μ¬ λΉμ¦λμ€ λ‘μ§
βββ matching/
β βββ job_matcher.py # κ³΅κ³ λ§€μΉ μκ³ λ¦¬μ¦
βββ email/
β βββ email_service.py # μ΄λ©μΌ λ°μ‘ μΈν°νμ΄μ€
β βββ gmail_service.py # Gmail API ν΅ν©
βββ sheets/
β βββ sheets_auth.py # Google Sheets OAuth μΈμ¦
β βββ sheets_service.py # Sheets λ°μ΄ν° μ½κΈ°/μ°κΈ°
βββ template/
βββ template_generator.py # κ°μΈν μ΄λ©μΌ μμ±
- Python 3.7 μ΄μ
- Google Cloud Platform κ³μ
- Gmail API λ° Google Sheets API νμ±νλ νλ‘μ νΈ
- Google Cloud Consoleμμ μ νλ‘μ νΈ μμ±
- Gmail APIμ Google Sheets API νμ±ν
- OAuth 2.0 ν΄λΌμ΄μΈνΈ ID μμ± (λ°μ€ν¬ν± μ± μ ν)
- μΈμ¦ μ 보λ₯Ό
credentials.jsonμΌλ‘ λ€μ΄λ‘λνμ¬ νλ‘μ νΈ λ£¨νΈμ μ μ₯
# 1. μ μ₯μ ν΄λ‘
git clone https://github.com/your-username/job-finder.git
cd job-finder/test
# 2. κ°μνκ²½ μμ± (κΆμ₯)
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# 3. ν¨ν€μ§ μ€μΉ
pip install -r requirements.txt
# 4. νκ²½ λ³μ μ€μ
cp .env_sample .envSPREADSHEET_ID=your-google-sheet-id-here
WORKSHEET_NAME=μ€λ¬Έμ§ μλ΅ μνΈ1Google Sheet ID μ°Ύλ λ°©λ²:
- Sheets URL:
https://docs.google.com/spreadsheets/d/[SPREADSHEET_ID]/edit - μ€κ°μ κΈ΄ λ¬Έμμ΄μ΄ SPREADSHEET_IDμ λλ€.
python main.py첫 μ€ν μ:
- λΈλΌμ°μ κ° μλμΌλ‘ μ΄λ¦¬λ©° Google κ³μ λ‘κ·ΈμΈ μμ²
- Gmail λ° Google Sheets κΆν μΉμΈ
gmail_token.pickleκ³Όsheets_token.pickleμλ μμ±- μ΄ν μ€νλΆν°λ ν ν° μ¬μ¬μ©μΌλ‘ λ³λ μΈμ¦ λΆνμ
1. Google Sheetsμμ μ€λ¬Έ μλ΅ λ°μ΄ν° λ‘λ
β
2. μ μ
κ³΅κ³ λ₯Ό μ°Ύλ μ§μμ νν°λ§
β
3. κ° μ§μμμ λν΄:
ββ data/ ν΄λμ μ±μ© κ³΅κ³ μ λ§€μΉ
ββ κ°μΈνλ HTML μ΄λ©μΌ μμ±
ββ Gmail APIλ‘ μ΄λ©μΌ λ°μ‘
ββ λ°μ‘ κ²°κ³Ό κΈ°λ‘
β
4. Google Sheetsμ λ°μ‘ μν μ
λ°μ΄νΈ
β
5. μ΅μ’
ν΅κ³ μΆλ ₯ (μ±κ³΅/μ€ν¨ 건μ)
JobMatcher ν΄λμ€κ° λ΄λΉ:
- κ²½λ ₯ νν°λ§: "μ μ " 쑰건μ λ§λ κ³΅κ³ λ§ μ ν
- κ³ μ© νν νν°λ§: ν¬λ§ κ³ μ© νν(μ κ·μ§, μΈν΄ λ±)μ λ§μΆ° νν°λ§
- μ§λ¬΄ λ§€μΉ: 1μμ β 2μμ β 3μμ ν¬λ§ μ§λ¬΄μ κ³΅κ³ λΉκ΅
- νλ ₯ 쑰건 νν°λ§: μ§μμ νλ ₯ 쑰건 μΆ©μ‘± μ¬λΆ νμΈ
μ λ ¬ μ°μ μμ:
- NULL νλ κ°μ (μμ±λ λμ κ³΅κ³ μ°μ )
- μ§λ¬΄ μ°μ μμ (1μμ λ§€μΉ > 2μμ > 3μμ)
- νλ ₯ 쑰건 μ νλ
- κ³ μ© νν λ§€μΉ μ μ
- νμ¬λͺ (λμ μ)
μ§μ μ§λ¬΄:
- λ°μ΄ν° λΆμκ°
- λ°μ΄ν° μ¬μ΄μΈν°μ€νΈ
- λ°μ΄ν° μμ§λμ΄
- AI μμ§λμ΄
- AI κΈ°νμ
- AI μ°κ΅¬μ
EmailTemplateGeneratorκ° λ΄λΉ:
- ν νλ¦Ώ κΈ°λ° μμ±: test_email.html κΈ°λ³Έ ꡬ쑰 μ¬μ©
- λμ 컨ν μΈ μ½μ : λ§€μΉλ κ³΅κ³ μΉ΄λλ₯Ό HTMLλ‘ λ³ννμ¬ μ½μ
- νμ¬ λ‘κ³ νμ: 20κ° μ΄μ νκ΅ μ£Όμ κΈ°μ λ‘κ³ URL λ§€ν
- D-day κ³μ°: λ§κ°μΌκΉμ§ λ¨μ μΌμ μλ κ³μ°
- κ³ μ© νν νκ·Έ: μμλ³ λ±μ§ μμ±
- μ μ : νλμ
- κ²½λ ₯: 볡μμμ
- μ κ·μ§: λ Ήμ
- μΈν΄: λ Έλμ
- κ³μ½μ§/μ νν: 보λΌμ
- μ νΈ κΈ°μ νμ: μ§μμκ° μ νν λκΈ°μ κ³΅κ³ μ β λ³ν μΆκ°
μ€λ¬Έ μλ΅ μνΈμ νμν 컬λΌ:
| 컬λΌλͺ | μ€λͺ | μμ |
|---|---|---|
μ±ν¨ |
μ§μμ μ΄λ¦ | νκΈΈλ |
μ΄λ©μΌ μ£Όμ |
μμ μ΄λ©μΌ | example@gmail.com |
μ°Ύκ³ κ³μ κ³΅κ³ μ κ²½λ ₯ 쑰건μ μ νν΄μ£ΌμΈμ. |
κ²½λ ₯ 쑰건 | μ μ |
ν¬λ§ μ§λ¬΄ 1μμ (νμμλ΅) |
1μμ μ§λ¬΄ | λ°μ΄ν° λΆμκ° |
ν¬λ§ μ§λ¬΄ 2μμ |
2μμ μ§λ¬΄ | λ°μ΄ν° μ¬μ΄μΈν°μ€νΈ |
ν¬λ§ μ§λ¬΄ 3μμ |
3μμ μ§λ¬΄ | AI μμ§λμ΄ |
ν¬λ§ κ³ μ© νν (볡μμ ν) |
κ³ μ© νν | μ κ·μ§, μΈν΄ |
μ°Ύκ³ κ³μ κ³΅κ³ μ νλ ₯ 쑰건μ μ νν΄μ£ΌμΈμ. |
νλ ₯ 쑰건 | νμ¬ 4λ μ |
| λκΈ°μ κ·Έλ£Ή μ ν 컬λΌλ€ | μ νΈ κΈ°μ κ΅° | μΌμ±, SK, LG... |
μ 체 μν¬νλ‘μ°λ₯Ό μ‘°μ¨νλ λ©μΈ μ€ν νμΌμ λλ€.
- νκ²½ λ³μ κ²μ¦
- λ°μ΄ν° λ‘λ λ° νν°λ§
- μ΄λ©μΌ λ°μ‘ λ° κ²°κ³Ό κΈ°λ‘
- ν΅κ³ μΆλ ₯
κ³΅κ³ λ§€μΉ λ‘μ§μ λλ€.
match_jobs_for_user(): μ¬μ©μλΉ μμ 10κ° κ³΅κ³ μΆμ²- 4λ¨κ³ νν°λ§ μμ€ν
- ν€μλ κΈ°λ° μ§λ¬΄λͺ λ§€μΉ
- μ νΈ κΈ°μ μ°μ μμ μ²λ¦¬
κ°μΈνλ HTML μ΄λ©μΌμ μμ±ν©λλ€.
- ν νλ¦Ώ μΉν (μ΄λ¦, κ³΅κ³ λ¦¬μ€νΈ)
- νμ¬ λ‘κ³ μ½μ
- D-day κ³μ°
- λ°μν λͺ¨λ°μΌ λμμΈ
Gmail API μ μμ€ μμ μ μ²λ¦¬ν©λλ€.
- OAuth 2.0 μΈμ¦ νλ¦
- ν ν° μΊμ± λ° κ°±μ
- MIME λ©μμ§ μμ±
- μ΄λ©μΌ λ°μ‘
Google Sheets λ°μ΄ν° μμ μ λ΄λΉν©λλ€.
load_recipients_from_sheet(): μ€λ¬Έ μλ΅ λ°μ΄ν° λ‘λwrite_status_to_sheet(): λ°μ‘ μν λ° νμμ€ν¬ν κΈ°λ‘
data/ ν΄λμ JSON νμΌ κ΅¬μ‘°:
{
"group_name": "μΏ ν‘",
"company_name": "μΏ ν‘",
"job_title": "[μΏ ν‘λ‘μ§μ€ν±μ€μλΉμ€] λ‘μΌμ€μΉ A/S μ΄μ λ΄λΉμ",
"position_name": "λ‘μΌμ€μΉ A/S μ΄μ λ΄λΉμ",
"processed_position_name": "λ°μ΄ν° λΆμκ°",
"min_experience_level": "μ μ
",
"employment_type": "μ κ·μ§",
"min_education_level": "νμ¬ 4λ
μ ",
"application_start_date": "2025-09-12",
"application_deadline_date": "2025-10-15",
"team_introduction": "ν μκ°...",
"job_duties": "λ΄λΉ μ
무...",
"qualifications": "μ격 μ건...",
"preferred_qualifications": "μ°λ μ¬ν...",
"work_location": "μμΈ",
"recruitment_process": "μλ₯μ ν - μ νλ©΄μ - λλ©΄λ©΄μ ",
"application_link": "https://..."
}- 10κ° μ΄λ©μΌ λ°μ‘λ§λ€ 2μ΄ λκΈ°
- Gmail API ν λΉλ μ΄κ³Ό λ°©μ§
- κ° μ΄λ©μΌ λ°μ‘ μ€ν¨ μ κ³μ μ§ν
- μ€λ₯ λ©μμ§ Google Sheetsμ κΈ°λ‘ (μ΅λ 50μ)
- μ΅μ’ μ±κ³΅/μ€ν¨ ν΅κ³ μΆλ ₯
- λ€μκ³Ό κ°μ΄ νμ₯ κ°λ₯ν©λλ€:
- λ€λ₯Έ κΈ°μ
κ³΅κ³ μΆκ°:
data/ν΄λμ λμΌ νμμ JSON νμΌ μΆκ° - μλ‘μ΄ μ§λ¬΄ μΆκ°: job_matcher.py:37-42μ
JOB_ROLE_KEYWORDSλμ λ리 μμ - νμ¬ λ‘κ³ μΆκ°: template_generator.py:158-181μ
_load_company_logos()λ©μλμ URL μΆκ° - μ΄λ©μΌ λμμΈ λ³κ²½: test_email.html ν νλ¦Ώ μμ
FileNotFoundError: credentials.json not found
β Google Cloud Consoleμμ OAuth μΈμ¦ μ 보λ₯Ό λ€μ΄λ‘λνμ¬ νλ‘μ νΈ λ£¨νΈμ μ μ₯νμΈμ.
HttpError 429: Quota exceeded
β main.pyμμ λκΈ° μκ° λ리기 (time.sleep(2) β time.sleep(5))