#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
crawl_liannan_hjbh_qtxx.py - 连南瑶族自治县-生态环境 其他信息
URL: http://www.liannan.gov.cn/zwgk/zdly/hjbh/qtxx/
CMS: 静态生成 (content/post_N.html), index_2.html 分页
列表: <li class="first"><span class="date">YYYY-MM-DD</span> <a href=绝对URL title=标题>标题</a></li>
详情: /zwgk/zdly/hjbh/qtxx/content/post_数字.html
标题: meta ArticleTitle
日期: meta PubDate (或正文 时间：)
正文: div#mainContent (含表格-随机抽查公示)
"""
import sys, os, re, time
from datetime import datetime, timezone, timedelta

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from crawler_lib import push_to_searchdb
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

_MAX_PG = 5
for i, a in enumerate(sys.argv):
    if a == '--pages' and i + 1 < len(sys.argv):
        _MAX_PG = int(sys.argv[i + 1])
        break

SITE_NAME = "连南县-环保其他信息"
BASE = "http://www.liannan.gov.cn"
LIST_PATH = "/zwgk/zdly/hjbh/qtxx"
CUTOFF = (datetime.now(timezone.utc) - timedelta(days=365 * 3)).strftime("%Y-%m-%d")
HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0 Safari/537.36"}

LIST_RE = re.compile(r'<li[^>]*>\s*<span class="date">(\d{4}-\d{2}-\d{2})</span>\s*<a[^>]+href="([^"]+)"[^>]*title="([^"]+)"', re.S)


def fetch(url, timeout=25):
    for _ in range(3):
        try:
            r = requests.get(url, headers=HEADERS, timeout=timeout, verify=False)
            r.encoding = "utf-8"
            return r.text
        except Exception:
            time.sleep(2)
    return None


def parse_list(html):
    items = []
    for m in LIST_RE.finditer(html):
        date, href, title = m.group(1).strip(), m.group(2).strip(), m.group(3).strip()
        if not href.startswith("http"):
            href = urljoin(BASE, href)
        if title and date:
            items.append((title, href, date))
    return items


def table_abs(tbl, base_url):
    tb = BeautifulSoup(str(tbl), "html.parser")
    for a in tb.find_all("a", href=True):
        h = a["href"]
        if h and not h.startswith(("http", "javascript", "#")):
            a["href"] = urljoin(base_url, h)
    for im in tb.find_all("img", src=True):
        s = im["src"]
        if s and not s.startswith(("http", "data:")):
            im["src"] = urljoin(base_url, s)
    return str(tb)


def clean_span(text):
    text = re.sub(r'(?<=[\u4e00-\u9fff])[ \t\u3000]+(?=[\u4e00-\u9fff])', '', text)
    text = re.sub(r'(?<=[\u4e00-\u9fff])[ \t\u3000]+(?=\d)', '', text)
    text = re.sub(r'(?<=\d)[ \t\u3000]+(?=[\u4e00-\u9fff])', '', text)
    text = re.sub(r'\s+([，。、；：？！)】」》])', r'\1', text)
    text = re.sub(r'([（【「《])\s+', r'\1', text)
    return re.sub(r'\s+', ' ', text).strip()


def extract_main(main_div, page_url):
    """正文容器内提取: 优先 .conTxt #fontzoom (真实正文), 回退 mainContent
    只遍历块级子元素: 段落入 <p>, table 保留 HTML, 附件独立成段"""
    parts = []
    # 优先定位真实正文容器
    body = main_div
    zoom = main_div.find("div", id="fontzoom") or main_div.find(class_=lambda c: c and isinstance(c, str) and "conTxt" in c)
    if zoom:
        body = zoom
    # 递归遍历块级元素
    def _walk(node):
        for child in node.children:
            name = getattr(child, 'name', None)
            if not name:
                continue
            if name in ('script', 'style'):
                continue
            if name == 'table':
                if child.find_parent('table'):
                    continue
                parts.append(table_abs(child, page_url))
                continue
            if name in ('p', 'div', 'section', 'ul', 'ol', 'h2', 'h3', 'h4'):
                if child.find_parent('table'):
                    continue
                txt_all = child.get_text('', strip=True)
                # 跳过导航/页眉元信息块
                if child.name == 'div' and len(txt_all) < 300 and not child.find('table'):
                    if re.search(r'(时间[:：]|来源[:：]|访问量|字体|分享到|无障碍|网站地图)', txt_all):
                        continue
                if child.name == 'div' and child.find(['p', 'table', 'ul', 'ol'], recursive=False):
                    _walk(child)
                    continue
                if len(txt_all) < 3:
                    continue
                txt = clean_span(txt_all)
                if txt and len(txt) > 2 and not re.match(r'^(\s*【|\[\s*关闭)', txt):
                    parts.append(f'<p>{txt}</p>')
    _walk(body)
    # 去重保序
    uniq = []
    for p in parts:
        if p not in uniq:
            uniq.append(p)
    return '\n'.join(uniq)


def parse_detail(url):
    html = fetch(url)
    if not html:
        return None, None, None
    soup = BeautifulSoup(html, "html.parser")
    title = ""
    mt = soup.find("meta", attrs={"name": re.compile("ArticleTitle", re.I)})
    if mt and mt.get("content"):
        title = mt["content"].strip()
    if not title:
        t = soup.find("title")
        if t:
            title = re.sub(r'[-_].*$', '', t.get_text(strip=True)).strip()
    date = ""
    pd = soup.find("meta", attrs={"name": re.compile("PubDate", re.I)})
    if pd and pd.get("content"):
        m = re.search(r'(\d{4}-\d{2}-\d{2})', pd["content"])
        if m:
            date = m.group(1)
    content = ""
    main = soup.find("div", id="mainContent") or soup.find("div", id="wrap")
    if main:
        content = extract_main(main, url)
    return title, content, date


def main():
    print(f"=== {SITE_NAME} ===")
    all_items = []
    for page in range(_MAX_PG):
        if page == 0:
            url = BASE + LIST_PATH + "/"
        else:
            url = BASE + LIST_PATH + f"/index_{page + 1}.html"
        html = fetch(url)
        if not html:
            print(f"  p{page+1} FAIL"); continue
        items = parse_list(html)
        if not items:
            print(f"  p{page+1} empty, stop"); break
        all_items.extend(items)
        print(f"  p{page+1}: {len(items)}")
        time.sleep(0.5)
    seen, filtered = set(), []
    for title, url, date in all_items:
        if url in seen:
            continue
        seen.add(url)
        if date and date < CUTOFF:
            continue
        filtered.append((title, url, date))
    print(f"  去重/3yr后: {len(filtered)}")
    results = []
    for idx, (title, url, date) in enumerate(filtered):
        dt, content, dd = parse_detail(url)
        ft = dt or title
        fd = date or dd
        if not content or len(content.strip()) < 10:
            print(f"  [{idx+1}] ⚠空 {title[:30]}")
            continue
        summary = re.sub(r'<[^>]+>', '', content)[:200].strip() or ft[:200]
        results.append({"site_name": SITE_NAME, "title": ft, "url": url,
                        "content": content, "summary": summary, "pub_date": fd})
        print(f"  [{idx+1}] ✅ {ft[:35]} ({len(content)}B)")
        time.sleep(0.3)
    if results:
        push_to_searchdb(results, "liannan_hjbh_qtxx")
        print(f"入库 {len(results)}")
    print("完成")


if __name__ == "__main__":
    main()
