"""定時に Web から一覧を集め、SQLite に貯め、前回との差（新規・値の変化）だけを CSV とログに出す見本。
対象は練習用に公開されている books.toscrape.com（取得を許された見本サイト）。
使い方: python shuushuu.py [ページ数]   ※タスクスケジューラ／cron から定時に呼ぶ想定
"""
import csv, datetime as dt, logging, re, sqlite3, sys, time, urllib.request
from html.parser import HTMLParser
from pathlib import Path

BASE = "https://books.toscrape.com/catalogue/page-{}.html"
HERE = Path(sys.executable).parent if getattr(sys, "frozen", False) else Path(__file__).parent  # exe の時は exe の横に書く（1ファイルの exe は一時フォルダに展開されるため）
logging.basicConfig(filename=HERE / "run.log", level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s", encoding="utf-8")  # Windows でも UTF-8 で書く

class Ichiran(HTMLParser):
    def __init__(s): super().__init__(); s.rows, s.cur, s.inprice = [], None, False
    def handle_starttag(s, tag, a):
        a = dict(a)
        if tag == "h3": s.cur = {}
        if tag == "a" and s.cur is not None and "title" in a and "title" not in s.cur: s.cur.update(title=a["title"], url=a["href"])
        if tag == "p" and a.get("class") == "price_color": s.inprice = True
    def handle_data(s, d):
        if s.inprice and s.cur is not None:
            s.cur["price"] = float(re.sub(r"[^\d.]", "", d)); s.rows.append(s.cur); s.cur, s.inprice = None, False

def toru(url, kai=3):
    for i in range(kai):
        try:
            with urllib.request.urlopen(urllib.request.Request(url, headers={"User-Agent": "mou-sample/1.0"}), timeout=20) as r:
                return r.read().decode("utf-8")
        except Exception as e:
            logging.warning("取得失敗 %s 回目 %s %s", i + 1, url, e); time.sleep(2 * (i + 1))
    raise RuntimeError(f"{kai}回とも取れない: {url}")

def main(pages):
    db = sqlite3.connect(HERE / "data.sqlite")
    db.execute("create table if not exists item(url text primary key, title text, price real, first_seen text, last_seen text)")
    now, shin, henka, kazu = dt.datetime.now().isoformat(timespec="seconds"), [], [], 0
    for n in range(1, pages + 1):
        p = Ichiran(); p.feed(toru(BASE.format(n)))
        for r in p.rows:
            kazu += 1
            old = db.execute("select price from item where url=?", (r["url"],)).fetchone()
            if old is None:
                shin.append(r); db.execute("insert into item values(?,?,?,?,?)", (r["url"], r["title"], r["price"], now, now))
            else:
                if old[0] != r["price"]: henka.append({**r, "mae": old[0]})
                db.execute("update item set price=?, last_seen=? where url=?", (r["price"], now, r["url"]))
        time.sleep(1)  # 相手のサーバーに負荷をかけない間隔
    db.commit()
    out = HERE / f"sa-{now[:10]}.csv"
    with open(out, "w", newline="", encoding="utf-8-sig") as f:  # Excel でそのまま開ける形
        w = csv.writer(f); w.writerow(["種類", "題名", "前の値", "今の値", "URL"])
        for r in shin: w.writerow(["新規", r["title"], "", r["price"], r["url"]])
        for r in henka: w.writerow(["値の変化", r["title"], r["mae"], r["price"], r["url"]])
    logging.info("見た %d 件 ／ 新規 %d ／ 値の変化 %d → %s", kazu, len(shin), len(henka), out.name)
    print(f"見た {kazu} 件 ／ 新規 {len(shin)} ／ 値の変化 {len(henka)} → {out.name}")

if __name__ == "__main__":
    try: main(int(sys.argv[1]) if len(sys.argv) > 1 else 2)
    except Exception: logging.exception("止まった"); raise
