{"id":238,"date":"2026-05-15T06:00:13","date_gmt":"2026-05-14T22:00:13","guid":{"rendered":"https:\/\/junai.ai\/blog\/python-automation-22\/"},"modified":"2026-05-17T07:31:29","modified_gmt":"2026-05-16T23:31:29","slug":"python-automation-22","status":"publish","type":"post","link":"https:\/\/junai.ai\/blog\/python-automation-22\/","title":{"rendered":"\ud30c\uc774\uc36c \uc790\ub3d9\ud654 \uc2a4\ud06c\ub9bd\ud2b8 \u2014 cron\u00b7shebang (22\ud3b8)"},"content":{"rendered":"\n<!-- WordPress REST API \ubc1c\ud589\uc6a9 HTML (\uc790\ub3d9 \uc0dd\uc131) -->\n<!-- WP-FEATURED-MEDIA-ID: 601 -->\n<div style=\"max-width:800px;margin:0 auto;\">\n<style>\n\/* js-textbook \u2014 PostgreSQL \uad50\uc7ac \ub3d9\uc77c \ud314\ub808\ud2b8 *\/\n:root {\n  --color-primary: #336791;\n  --color-accent: #60a5fa;\n  --color-bg: #fafafa;\n  --color-bg-card: #ffffff;\n  --color-text: #1e293b;\n  --color-text-muted: #64748b;\n  --hero-start: #0f172a;\n  --hero-end: #336791;\n  --font-body: -apple-system, BlinkMacSystemFont, 'Apple SD Gothic Neo', 'Noto Sans KR', sans-serif;\n  --size-body: 17px;\n  --line-height: 1.75;\n}\n* { box-sizing: border-box; }\n.container { max-width: 760px; margin: 0 auto; padding: 0 22px 80px; }\n.hero { background: linear-gradient(135deg, var(--hero-start) 0%, var(--hero-end) 100%); color: #fff; padding: 72px 22px 56px; text-align: center; }\n.hero .badge { display: inline-block; background: rgba(96,165,250,0.18); color: var(--color-accent); padding: 6px 14px; border-radius: 999px; font-size: 13px; font-weight: 600; letter-spacing: 0.5px; margin-bottom: 18px; }\n.hero h1 { margin: 0 0 18px; font-size: 36px; line-height: 1.3; letter-spacing: -0.3px; }\n.hero p.sub { margin: 0 auto; max-width: 580px; font-size: 17px; color: #bfdbfe; }\n.hero img { width: 100%; max-width: 720px; height: auto; margin: 36px auto 0; display: block; border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,0.3); }\n.meta { display: flex; gap: 14px; justify-content: center; margin-top: 20px; font-size: 13px; color: #93c5fd; flex-wrap: wrap; }\n.meta span::before { content: \"\u00b7\"; margin-right: 14px; color: #1e3a8a; }\n.meta span:first-child::before { content: \"\"; margin: 0; }\narticle { background: var(--color-bg-card); margin-top: -36px; padding: 44px 28px; border-radius: 14px; box-shadow: 0 2px 18px rgba(0,0,0,0.06); }\narticle p { margin: 0 0 18px; }\nh2 { font-size: 28px; line-height: 1.35; letter-spacing: -0.3px; margin: 48px 0 18px; padding-bottom: 10px; border-bottom: 2px solid var(--color-primary); }\nh2:first-of-type { margin-top: 8px; }\nh3 { font-size: 21px; line-height: 1.4; margin: 30px 0 12px; color: var(--color-primary); }\ncode { background: #f1f5f9; color: #0f172a; padding: 2px 6px; border-radius: 4px; font-family: 'SFMono-Regular', Menlo, Consolas, monospace; font-size: 0.92em; }\npre { background: #0f172a; color: #e2e8f0; padding: 18px 20px; border-radius: 10px; overflow-x: auto; font-size: 14.5px; line-height: 1.65; margin: 18px 0; }\npre code { background: transparent; color: inherit; padding: 0; }\nul, ol { margin: 0 0 18px; padding-left: 24px; }\nli { margin-bottom: 8px; }\nstrong { color: #0f172a; }\n.databox { background: #eff6ff; border-left: 4px solid var(--color-primary); padding: 18px 20px; border-radius: 6px; margin: 22px 0; }\n.databox p { margin: 0 0 8px; }\n.databox p:last-child { margin: 0; }\n.databox strong { color: var(--color-primary); }\n.warnbox { background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); border-left: 4px solid #d97706; padding: 18px 20px; border-radius: 6px; margin: 22px 0; }\n.warnbox strong { color: #92400e; }\n.tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 22px 0; }\ntable { width: 100%; border-collapse: collapse; font-size: 15px; background: var(--color-bg-card); }\nth, td { padding: 11px 12px; text-align: left; border-bottom: 1px solid #e2e8f0; vertical-align: top; }\nth { background: #f1f5f9; font-weight: 700; color: #0f172a; }\ntd:first-child, th:first-child { font-weight: 700; }\n.cta { background: linear-gradient(135deg, #336791 0%, #60a5fa 100%); color: #fff; padding: 30px 24px; border-radius: 12px; margin-top: 44px; text-align: center; }\n.cta h3 { color: #fff; margin: 0 0 10px; }\n.cta p { margin: 0; color: #dbeafe; }\n.series-nav { background: #eff6ff; padding: 18px 22px; border-radius: 10px; margin-top: 24px; font-size: 14.5px; color: var(--color-text-muted); }\n.series-nav strong { color: var(--color-primary); }\n@media (max-width: 480px) {\n  .hero { padding: 52px 18px 44px; }\n  .hero h1 { font-size: 26px; }\n  .hero p.sub { font-size: 15px; }\n  article { padding: 28px 18px; border-radius: 10px; }\n  h2 { font-size: 22px; }\n  h3 { font-size: 18px; }\npre { font-size: 13px; padding: 14px 16px; }\n}\n@media (max-width: 560px) {\n  .tablewrap table, .tablewrap thead, .tablewrap tbody, .tablewrap tr, .tablewrap th, .tablewrap td { display: block; width: auto; }\n  .tablewrap thead { display: none; }\n  .tablewrap tr { margin: 0 0 14px; border: 1px solid #e2e8f0; border-radius: 10px; overflow: hidden; }\n  .tablewrap td { border: none; border-bottom: 1px solid #f1f5f9; padding: 9px 14px; }\n  .tablewrap td:first-child { background: #eff6ff; font-weight: 800; font-size: 15.5px; }\n  .tablewrap td:last-child { border-bottom: none; }\n  .tablewrap td[data-label]::before { content: attr(data-label) \" \u2014 \"; font-weight: 700; color: var(--color-primary); }\n}\n<\/style>\n<header class=\"hero\">\n  <span class=\"badge\">\ud30c\uc774\uc36c \uad50\uc7ac \u00b7 22\ud3b8 \/ 27\ud3b8<\/span>\n  <h1>\ud30c\uc774\uc36c \uc790\ub3d9\ud654 \uc2a4\ud06c\ub9bd\ud2b8<\/h1>\n  <p class=\"sub\">\uc7a0\ub4e0 \uc0c8\ubcbd\uc5d0\ub3c4 \ub3cc\uc544\uac00\ub294 \ucf54\ub4dc. 1~21\ud3b8\uc774 \ud55c \ud30c\uc77c\ub85c \ubaa8\uc778\ub2e4.<\/p>\n  <div class=\"meta\"><span>\uc2e4\uc804<\/span><span>\uc77d\ub294 \uc2dc\uac04 7\ubd84<\/span><span>2026-05-13<\/span><\/div>\n  <img decoding=\"async\" src=\"https:\/\/junai.ai\/blog\/wp-content\/uploads\/2026\/05\/hero-220.jpg\" alt=\"\uc77c\uc77c \ub9ac\ud3ec\ud2b8\ub97c cron \uc73c\ub85c 9\uc2dc\uc5d0 \uc2e4\ud589\ud558\ub294 \ucf54\ub4dc\uc640 cron \uc124\uc815 \ud654\uba74\">\n<\/header>\n\n<div class=\"container\">\n<article>\n\n<p>21\ud3b8\uae4c\uc9c0\uc758 \ub3c4\uad6c\ub4e4\uc740 \ub2e4 \ud55c \uac00\uc9c0 \ubaa9\ud45c\ub97c \ud5a5\ud588\uc2b5\ub2c8\ub2e4 \u2014 <strong>\uc7a1\uc77c\uc744 \ucef4\ud4e8\ud130\uc5d0\uac8c \ub5a0\ub118\uae30\uae30<\/strong>. \ub9e4\uc77c \uac19\uc740 \ud3f4\ub354\uc758 \ud30c\uc77c\uc744 \uc815\ub9ac, \ud658\uc728 \ubc1b\uc544 \uc5d1\uc140\uc5d0 \ucd94\uac00, \ub85c\uadf8 \ubcf4\uace0\uc11c \ub9cc\ub4e4\uc5b4 \uba54\uc77c, \uc2e0\uaddc \uac00\uc785\uc790 \uc2ac\ub799 \uc54c\ub9bc. \uc774\ub7f0 \uc791\uc5c5\uc774 \ud55c \ubc88 \uc9e0 \uc2a4\ud06c\ub9bd\ud2b8 + cron \ud55c \uc904\ub85c \uc0ac\ub78c \uc190\uc744 \ub5a0\ub0a9\ub2c8\ub2e4. 22\ud3b8\uc740 1~21\ud3b8 \ub3c4\uad6c\ub97c \ud55c \ud30c\uc77c\uc5d0 \ubb36\ub294 \uc2e4\uc804.<\/p>\n\n<p>22\ud3b8\uc744 \ub9c8\uce58\uba74 \u2460 \uc798 \uc4f4 \uc2a4\ud06c\ub9bd\ud2b8 5\uc694\uc18c \u2461 <code>argparse<\/code> \ub85c CLI \uc778\uc790 \u2462 <code>logging<\/code> \uc73c\ub85c \ub85c\uadf8 \u2463 cron\u00b7systemd timer \ub85c \uc2a4\ucf00\uc904 \u2464 \uc54c\ub9bc(\uba54\uc77c\u00b7\uc2ac\ub799) \uae30\ubcf8 \u2014 5\uac00\uc9c0\ub85c \uc644\uc131\ub41c \uc790\ub3d9\ud654\ub97c \ub9cc\ub4ed\ub2c8\ub2e4.<\/p>\n\n<h2>\uc798 \uc4f4 \uc790\ub3d9\ud654 \uc2a4\ud06c\ub9bd\ud2b8\uc758 5\uc694\uc18c<\/h2>\n\n<pre><code>#!\/usr\/bin\/env python3\n\"\"\"\uc77c\uc77c \ub9e4\ucd9c \ub9ac\ud3ec\ud2b8 \u2014 sales.csv \u2192 summary.csv (\uc5b4\uc81c \ub370\uc774\ud130 \uae30\uc900).\"\"\"\nimport argparse, logging, sys\nfrom datetime import date, timedelta\nfrom pathlib import Path\nimport pandas as pd\n\ndef main() -&gt; int:\n    ap = argparse.ArgumentParser()\n    ap.add_argument(\"--input\", default=\"sales.csv\")\n    ap.add_argument(\"--out\", default=None)\n    args = ap.parse_args()\n\n    logging.basicConfig(\n        level=logging.INFO,\n        format=\"%(asctime)s [%(levelname)s] %(message)s\",\n    )\n    log = logging.getLogger(__name__)\n\n    yesterday = date.today() - timedelta(days=1)\n    out = Path(args.out or f\"summary-{yesterday}.csv\")\n\n    try:\n        df = pd.read_csv(args.input)\n        df = df[df[\"date\"] == str(yesterday)]\n        summary = df.groupby(\"item\")[\"price\"].agg([\"sum\", \"count\"])\n        summary.to_csv(out)\n        log.info(\"\uc800\uc7a5: %s (%d items)\", out, len(summary))\n        return 0\n    except FileNotFoundError as e:\n        log.error(\"\uc785\ub825 \ud30c\uc77c \uc5c6\uc74c: %s\", e)\n        return 1\n    except Exception as e:\n        log.exception(\"\uc608\uae30\uce58 \ubabb\ud55c \uc624\ub958\")\n        return 2\n\nif __name__ == \"__main__\":\n    sys.exit(main())<\/code><\/pre>\n\n<p>5\uc694\uc18c\uac00 \ub2e4 \ub4e4\uc5b4\uac14\uc2b5\ub2c8\ub2e4.<\/p>\n\n<ol>\n  <li><strong>shebang<\/strong> <code>#!\/usr\/bin\/env python3<\/code> \u2014 \uc9c1\uc811 \uc2e4\ud589 \uac00\ub2a5 (<code>chmod +x<\/code> \ud6c4 <code>.\/script.py<\/code>)<\/li>\n  <li><strong>docstring<\/strong> \u2014 \uc774 \uc2a4\ud06c\ub9bd\ud2b8\uac00 \ubb50 \ud558\ub294\uc9c0 \ud55c \uc904<\/li>\n  <li><strong>argparse<\/strong> \u2014 <code>--input\u00b7--out<\/code> \uac19\uc740 \uc635\uc158 + <code>--help<\/code> \uc790\ub3d9 \uc0dd\uc131<\/li>\n  <li><strong>logging<\/strong> \u2014 print \ub300\uc2e0 \uc2dc\uac01\u00b7\ub808\ubca8 \ud3ec\ud568, \ub098\uc911\uc5d0 \ud30c\uc77c \ucd9c\ub825 \ubcc0\uacbd \uac00\ub2a5<\/li>\n  <li><strong>main() + sys.exit<\/strong> \u2014 \uc131\uacf5 0, \uc2e4\ud328 1\u00b72 \uc885\ub8cc \ucf54\ub4dc. cron \uc774 \uc774\uac78 \ubcf4\uace0 \uc2e4\ud328 \uc54c\ub9bc<\/li>\n<\/ol>\n\n<h2>argparse \u2014 CLI \uc778\uc790 \ucc98\ub9ac<\/h2>\n\n<pre><code>import argparse\n\nap = argparse.ArgumentParser(description=\"\ub9e4\ucd9c \ub9ac\ud3ec\ud2b8 \uc0dd\uc131\")\nap.add_argument(\"--date\", type=str, help=\"YYYY-MM-DD\")\nap.add_argument(\"--top\", type=int, default=10, help=\"\uc0c1\uc704 N\uac1c\")\nap.add_argument(\"--verbose\", \"-v\", action=\"store_true\")\nap.add_argument(\"file\", nargs=\"?\", default=\"sales.csv\")  # \uc704\uce58 \uc778\uc790\n\nargs = ap.parse_args()\n# python report.py sales.csv --date 2026-05-13 --top 5 -v\n# args.date == \"2026-05-13\", args.top == 5, args.verbose == True<\/code><\/pre>\n\n<p>\uc635\uc158 \ucd94\uac00 \ud55c \uc904\uc5d0 <code>--help<\/code> \uba54\uc2dc\uc9c0\ub3c4 \uc790\ub3d9 \uc0dd\uc131\ub429\ub2c8\ub2e4. \uc785\ub825 \ub3c4\uad6c\uac00 \uc0ac\ub78c\uc774 \uc4f8 \uc218 \uc788\ub294 \uc9c4\uc9dc CLI \uac00 \ub3fc\uc694.<\/p>\n\n<h2>logging \u2014 print \ubcf4\ub2e4 \ud56d\uc0c1 \uc88b\ub2e4<\/h2>\n\n<pre><code>import logging\n\nlogging.basicConfig(\n    filename=\"report.log\",                # \ud30c\uc77c\uc5d0 \ub204\uc801\n    level=logging.INFO,\n    format=\"%(asctime)s [%(levelname)s] %(name)s: %(message)s\",\n)\nlog = logging.getLogger(__name__)\n\nlog.debug(\"\ub514\ubc84\uadf8 \uc815\ubcf4\")     # \uc548 \ubcf4\uc784 (level &gt; DEBUG)\nlog.info(\"\uc815\uc0c1 \uba54\uc2dc\uc9c0\")\nlog.warning(\"\uc8fc\uc758\")\nlog.error(\"\uc5d0\ub7ec \ubc1c\uc0dd\")\nlog.exception(\"\uc608\uc678 + traceback \uac19\uc774\")<\/code><\/pre>\n\n<div class=\"databox\">\n  <p><strong>\ud83d\udccc print \uc640 logging \uc758 \ucc28\uc774<\/strong><\/p>\n  <p>print \ub294 <em>&#8220;\uc9c0\uae08 \ud654\uba74\uc5d0 \ucc0d\uae30&#8221;<\/em>, logging \uc740 <em>&#8220;\uc218\uc900\u00b7\uc2dc\uac01\u00b7\uc704\uce58\ub97c \ubd99\uc5ec \uc5b4\ub518\uac00\uc5d0 \uae30\ub85d&#8221;<\/em>. \uc790\ub3d9\ud654\ub294 \uc0ac\ub78c\uc774 \uc548 \ubcf4\ub294 \uc0c8\ubcbd\uc5d0 \ub3cc\uc544\uc11c, \ub098\uc911\uc5d0 \ubb34\uc2a8 \uc77c\uc774 \uc788\uc5c8\ub294\uc9c0 \ucd94\uc801\uc774 \ud544\uc218. logging \ud55c \uc904\ub85c <strong>\uc624\ub298 \uc0c8\ubcbd 3\uc2dc\uc5d0 \ubb34\uc5c7\uc774 \uc2e4\ud328\ud588\ub294\uc9c0<\/strong> \uc54c \uc218 \uc788\uc5b4\uc694.<\/p>\n<\/div>\n\n<h2>cron \u2014 \uc2dc\uac04 \ub9de\ucdb0 \uc2e4\ud589<\/h2>\n\n<pre><code># \uc178\uc5d0\uc11c\n$ crontab -e\n\n# \ud3b8\uc9d1\uae30\uc5d0 \ud55c \uc904 \ucd94\uac00 \u2014 \ub9e4\uc77c \uc624\uc804 9\uc2dc\n0 9 * * * \/home\/user\/myapp\/.venv\/bin\/python \/home\/user\/myapp\/daily_report.py &gt;&gt; \/home\/user\/myapp\/cron.log 2&gt;&amp;1\n\n# 5\ubd84\ub9c8\ub2e4\n*\/5 * * * * \/path\/to\/python \/path\/to\/script.py\n\n# \ub9e4\uc8fc \uc6d4\uc694\uc77c 8\uc2dc\n0 8 * * 1 \/path\/to\/python \/path\/to\/weekly.py<\/code><\/pre>\n\n<p>\ud06c\ub860 \ub2e4\uc12f \uce78: <code>\ubd84 \uc2dc \uc77c \uc6d4 \uc694\uc77c<\/code>. <code>*<\/code> \ub294 &#8220;\ubaa8\ub450&#8221;. <a href=\"https:\/\/crontab.guru\/\" target=\"_blank\" rel=\"noopener\">crontab.guru<\/a> \uac00 \uc790\uc5f0\uc5b4\ub85c \ud480\uc5b4 \ubcf4\uc5ec\uc918\uc11c \uc785\ubb38\uc790\uc5d0\uac8c \uc548\uc804\ud569\ub2c8\ub2e4.<\/p>\n\n<div class=\"warnbox\">\n  <strong>\u26a0\ufe0f cron \ud568\uc815 3\uac00\uc9c0.<\/strong> \u2460 <strong>\uc808\ub300 \uacbd\ub85c \uc0ac\uc6a9<\/strong> \u2014 cron \ud658\uacbd\uc5d4 PATH \uac00 \uac70\uc758 \ube44\uc5b4\uc788\uc74c. <code>python<\/code> \uc774 \uc544\ub2c8\ub77c <code>\/home\/u\/.venv\/bin\/python<\/code>. \u2461 <strong>\ud45c\uc900 \ucd9c\ub825\uc740 \uc0ac\ub77c\uc9c4\ub2e4<\/strong> \u2014 <code>&gt;&gt; logfile 2&gt;&amp;1<\/code> \ub85c \ub9ac\ub2e4\uc774\ub809\ud2b8. \u2462 <strong>\uac00\uc0c1\ud658\uacbd \ud65c\uc131\ud654 \uc548 \ub428<\/strong> \u2014 venv \uc758 <code>python<\/code> \uc744 \uc9c1\uc811 \ud638\ucd9c\ud558\uba74 \uc790\ub3d9 \ud65c\uc131\ud654 \ud6a8\uacfc.\n<\/div>\n\n<h3>systemd timer \u2014 cron \uc758 \ud604\ub300\uc2dd \ub300\uccb4<\/h3>\n\n<pre><code># \/etc\/systemd\/system\/daily-report.service\n[Unit]\nDescription=Daily sales report\n\n[Service]\nType=oneshot\nExecStart=\/home\/user\/myapp\/.venv\/bin\/python \/home\/user\/myapp\/daily_report.py\nUser=user\nWorkingDirectory=\/home\/user\/myapp\n\n# \/etc\/systemd\/system\/daily-report.timer\n[Unit]\nDescription=Run daily report at 09:00\n\n[Timer]\nOnCalendar=*-*-* 09:00:00\nPersistent=true\n\n[Install]\nWantedBy=timers.target\n\n$ sudo systemctl enable --now daily-report.timer<\/code><\/pre>\n\n<p>cron \ubcf4\ub2e4 \ub85c\uadf8\u00b7\uc2e4\ud328 \uc54c\ub9bc\uc774 \uae54\ub054\ud569\ub2c8\ub2e4. ubuntu-linux 19~20\ud3b8(systemd\u00b7cron)\uc744 \uadf8\ub300\ub85c \uc751\uc6a9.<\/p>\n\n<h2>\uc54c\ub9bc \u2014 \uba54\uc77c\u00b7\uc2ac\ub799 + \ub9c8\ubb34\ub9ac<\/h2>\n\n<pre><code># \uc2ac\ub799 Incoming Webhook \u2014 \ud658\uacbd \ubcc0\uc218\uc5d0 \uc800\uc7a5\nimport os, requests\n\ndef slack(text: str):\n    url = os.environ[\"SLACK_WEBHOOK_URL\"]\n    requests.post(url, json={\"text\": text}, timeout=10)\n\n# \uba54\uc77c (smtplib) \u2014 \ud68c\uc0ac \uba54\uc77c\uc740 \ubcf4\ud1b5 SMTP \uc815\ubcf4\uac00 \ud68c\uc0ac\uc5d0\uc11c \uc90c\nimport smtplib\nfrom email.message import EmailMessage\n\ndef send_mail(to: str, subject: str, body: str):\n    msg = EmailMessage()\n    msg[\"Subject\"] = subject\n    msg[\"From\"] = \"auto@junai.ai\"\n    msg[\"To\"] = to\n    msg.set_content(body)\n    with smtplib.SMTP(\"smtp.junai.ai\", 587) as s:\n        s.starttls()\n        s.login(os.environ[\"SMTP_USER\"], os.environ[\"SMTP_PASS\"])\n        s.send_message(msg)<\/code><\/pre>\n\n<p>\uc790\ub3d9\ud654 + \uc54c\ub9bc = \uc790\ub3d9\uc73c\ub85c \uc77c\uc774 \ub05d\ub098\uace0 \uc0ac\ub78c\uc5d0\uac8c\ub294 \uacb0\uacfc\ub9cc. \ud68c\uc0ac\uc5d0\uc11c \ubcf8\uc778\uc774 \ub9e4\uc77c 5\ubd84\uc529 \ud558\ub358 \uc77c\uc744 \ud55c \ubc88 \uc2a4\ud06c\ub9bd\ud2b8\ub85c \ub9cc\ub4e4\uba74 1\ub144\uc5d0 \uc57d 20\uc2dc\uac04\uc774 \ub3cc\uc544\uc635\ub2c8\ub2e4.<\/p>\n\n<p>\ub2e4\uc74c \ubbf8\uc158: \u2460 \ub2e4\uc6b4\ub85c\ub4dc \ud3f4\ub354\uc758 .png \ub97c \ub0a0\uc9dc\ubcc4 \ud558\uc704 \ud3f4\ub354\ub85c \uc815\ub9ac\ud558\ub294 \uc2a4\ud06c\ub9bd\ud2b8 \u2461 \ud658\uc728 API \ud638\ucd9c\ud574 CSV \uc5d0 \ub204\uc801 \u2462 \ubcf8\uc778\uc774 \ub9e4\uc77c \ubc18\ubcf5\ud558\ub294 \uc791\uc5c5 \ud558\ub098\ub97c \uace8\ub77c \uc2a4\ud06c\ub9bd\ud2b8\ud654.<\/p>\n\n<div class=\"cta\">\n  <h3>\ub2e4\uc74c \ud3b8 \ubbf8\ub9ac\ubcf4\uae30<\/h3>\n  <p>23\ud3b8 \u2014 &#8220;\ud30c\uc774\uc36c \uc6f9 \ud06c\ub864\ub9c1&#8221;: requests + BeautifulSoup\u00b7Playwright \uc785\ubb38. \uc815\uc801 \ud398\uc774\uc9c0\ubd80\ud130 JS \ub3d9\uc801 \ud398\uc774\uc9c0\uae4c\uc9c0.<\/p>\n<\/div>\n\n<div class=\"series-nav\">\n  <strong>\ud83d\udcda 27\ud3b8 \ud30c\uc774\uc36c \uad50\uc7ac \uc2dc\ub9ac\uc988 \u2014 22\/27\ud3b8<\/strong><br>\n  \u2190 21\ud3b8 &#8220;pandas \uae30\ucd08&#8221; \u00b7 \ub2e4\uc74c: 23\ud3b8 &#8220;\uc6f9 \ud06c\ub864\ub9c1&#8221;\n<\/div>\n\n<\/article>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>argparse\u00b7logging\u00b7cron\u00b7systemd\u00b7\uba54\uc77c\/\uc2ac\ub799 \uc54c\ub9bc. 27\ud3b8 \uad50\uc7ac 22\ud3b8.<\/p>\n","protected":false},"author":1,"featured_media":601,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16],"tags":[],"class_list":["post-238","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/posts\/238","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/comments?post=238"}],"version-history":[{"count":1,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/posts\/238\/revisions"}],"predecessor-version":[{"id":628,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/posts\/238\/revisions\/628"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/media\/601"}],"wp:attachment":[{"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/media?parent=238"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/categories?post=238"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/junai.ai\/blog\/wp-json\/wp\/v2\/tags?post=238"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}