EST. 2026 — บันทึกงานเซิร์ฟเวอร์จริง SYSADMIN · SECURITY · AI · DIRECTADMIN เล่มที่ 01
THE SYSADMIN ALMANAC
หน้าหลัก / Linux / เกณฑ์ราคากลางคอมพิวเตอร์ ICT ภาครัฐ + จัดสเปก DIY ด้วยสคริปต์
Linux

เกณฑ์ราคากลางคอมพิวเตอร์ ICT ภาครัฐ + จัดสเปก DIY ด้วยสคริปต์

โดย Ton-Or ·22 ก.ค. 2026·อ่าน 9 นาที
เกณฑ์ราคากลางคอมพิวเตอร์ ICT ภาครัฐ + จัดสเปก DIY ด้วยสคริปต์

เอกสารเกณฑ์ราคากลางเขียนสเปกขั้นต่ำไว้ครบ แต่ไม่ได้บอกว่าราคาจริงหน้าร้านเป็นเท่าไร ผมเลยเขียนสคริปต์ดึงราคา Advice มาเทียบทีละชิ้น แล้วพบว่าประกอบเองให้ผ่านทั้งสองเกณฑ์ในเครื่องเดียว ต่างจากงบข้อ 5 แค่หลักสิบบาท

สรุปใน 1 นาที

อาการ/ปัญหา
เกณฑ์ราคากลาง ICT ภาครัฐกำหนดสเปกไว้ แต่ไม่บอกราคาจริงหน้าร้าน
ใครควรอ่าน
คนเตรียมจัดซื้อคอมพิวเตอร์ภาครัฐ ที่อยากรู้ว่าประกอบเองคุ้มไหม
ทางแก้แบบสั้น
อ่านเกณฑ์ ธ.ค. 2568 ทีละข้อ แล้วใช้สคริปต์ Python ดึงราคา Advice มาจัดสเปก DIY เทียบกับราคาเครื่องสำเร็จรูป
ข้ามไปขั้นตอน

พอดีมีน้องในวงการถามมาว่าเกณฑ์ราคากลางคอมพิวเตอร์ภาครัฐรอบล่าสุด (ฉบับเดือนธันวาคม 2568) มันบังคับสเปกไว้แค่ไหน แล้วถ้าประกอบเองจะคุ้มกว่าซื้อเครื่องสำเร็จตามงบหรือเปล่า ผมเลยนั่งอ่านเอกสารเกณฑ์จริงทีละข้อ แล้วเขียนสคริปต์ Python ดึงราคาจากหน้า Advice มาจัดสเปกเทียบให้เห็นเป็นตัวเลข ไม่ใช่เดาเอา งานนี้มันมีกับดักซ่อนอยู่หลายจุด ที่ถ้าอ่านเกณฑ์ผ่านๆ แล้วจะพลาด เลยอยากเล่าให้ฟังทั้งฝั่งเกณฑ์และฝั่งโค้ด

ชื่อไฟล์และ path ในบทความเป็นตัวอย่างแบบ relative ส่วนโครงสร้างสคริปต์และตัวเลขราคาคือของจริงจากรอบ crawl ล่าสุด

เกณฑ์ ICT สองข้อที่ต้องอ่านให้ขาด

ในเล่มมีสองรายการที่คนซื้อเครื่องเดสก์ท็อปต้องดู คือข้อ 4 เครื่องสำหรับงานสำนักงาน กับข้อ 5 เครื่องสำหรับงานประมวลผลแบบที่ 1 สองข้อนี้ต่างกันที่ซีพียูกับการ์ดจอเป็นหลัก ที่เหลือ (RAM DDR5 16GB, SSD 500GB, จอ 23 นิ้ว, แป้นพิมพ์+เมาส์) เหมือนกันเป๊ะ

ข้อ 4 — งานสำนักงาน

งบ 20,000 บาท

CPU ≥4 core / 8 thread, Turbo ≥4.4 GHz, Cache ระดับเดียวกัน ≥12 MB, RAM DDR5 ≥16 GB, SSD ≥500 GB, จอ ≥23 นิ้ว -> ไม่บังคับการ์ดจอ

ข้อ 5 — งานประมวลผล

งบ 24,000 บาท

CPU ≥8 core / 8 thread, Turbo ≥4.6 GHz, Cache ระดับเดียวกัน ≥16 MB, บวกเงื่อนไข GPU หน่วยความจำ ≥2 GB ที่เหลือเท่าข้อ 4

จุดที่คนอ่านผ่านแล้วพลาดคือคำว่า "แกนหลักรวมกันไม่น้อยกว่า 8 แกน" ในข้อ 5 มันหมายถึง physical core จริงๆ ไม่ใช่ thread ทำให้ซีพียูฝั่งประหยัดหลายตัวที่มี 6 core 12 thread ผ่านข้อ 4 สบายแต่ตกข้อ 5 ทันที เรื่องนี้ต้องให้สคริปต์เช็คให้ ไม่ใช่กะด้วยตา

iGPU ผ่านข้อ GPU ได้

ข้อ 5 บังคับ GPU หน่วยความจำ ≥2 GB แต่ตัวเกณฑ์เปิดช่องให้ iGPU ที่ "ใช้หน่วยความจำหลักในการแสดงภาพ ≥2 GB" ผ่านได้ -> ถ้าเลือกซีพียูที่มี iGPU ในตัว ก็ ตัดการ์ดจอแยกออกได้ทั้งก้อน ประหยัดไปหลายพันบาท

ปัญหาคือเกณฑ์ไม่ได้บอกราคาจริง

เอกสารบอกแค่สเปกขั้นต่ำกับเพดานราคา แต่ไม่ได้บอกว่าของที่ผ่านสเปกจริงๆ ตอนนี้ราคาหน้าร้านเท่าไร มีของหรือเปล่า ผมเลยไปดึงราคาจาก Advice มาเป็นฐานอ้างอิง เพราะมีหน้าเว็บให้ดูสต็อกรายสาขาและราคาสมาชิกครบ

ปัญหาแรกที่เจอคือหน้า product ของ Advice เป็น Nuxt SSR ข้อมูลราคาไม่ได้อยู่ใน HTML ตรงๆ แต่ฝังอยู่ใน __NUXT_DATA__ เป็น "index graph" คือทุกค่าอ้างถึงกันด้วยเลข index ไม่ใช่ค่าจริง ต้องเขียนตัว resolve ไล่ตามลูกโซ่เอง

import json, re
from pathlib import Path

# หน้า Advice เป็น Nuxt SSR — ข้อมูลจริงฝังใน __NUXT_DATA__ เป็น index graph
NUXT_RE = re.compile(r'__NUXT_DATA__"[^>]*>(\[.*?\])</script>', re.S)

def load_graph(html_path):
    text = Path(html_path).read_text(encoding="utf-8", errors="ignore")
    m = NUXT_RE.search(text)
    return json.loads(m.group(1)) if m else None

# ค่าทุกตัวใน graph อ้างกันด้วย index ไม่ใช่ค่าตรงๆ ต้องไล่ resolve เอง
def resolve(graph, idx, depth=0, seen=frozenset()):
    if depth > 25 or idx in seen or not isinstance(idx, int) or not (0 <= idx < len(graph)):
        return idx
    seen = seen | {idx}
    val = graph[idx]
    if isinstance(val, dict):
        return {k: resolve(graph, v, depth + 1, seen) for k, v in val.items()}
    if isinstance(val, list):
        # unwrap reactive wrapper ของ Nuxt ก่อนไล่ต่อ
        if len(val) == 2 and val[0] in ("ShallowReactive", "Reactive", "Ref"):
            return resolve(graph, val[1], depth + 1, seen)
        return [resolve(graph, v, depth + 1, seen) for v in val]
    return val

พอ resolve graph กลับมาเป็น dict ปกติแล้ว ตัวข้อมูลสินค้าจะอยู่ใต้ seed ชื่อ pdp-hero-seed: เจาะเข้าไปที่ product_get แล้วดึงราคากับสเปกออกมาได้ จุดที่สำคัญคือราคาสมาชิก (SALEPRICE6 ใน item_master) มักถูกกว่าราคาขายหน้าเว็บเยอะ ต้องเก็บทั้งคู่

def extract(graph):
    tree = resolve(graph, 0)
    pdp  = next(v for k, v in tree["data"].items()
                if str(k).startswith("pdp-hero-seed:"))
    prod = find_first(pdp, lambda x: isinstance(x, dict) and "product_get" in x)
    pg   = prod["product_get"]
    im   = pg.get("item_master") or {}
    specs = [strip_html(s["spec_value"]) for s in (pg.get("spec_dtls") or [])
             if s.get("spec_value")]
    return {
        "sku":          pg.get("code"),
        "name":         pg.get("Product") or pg.get("header"),
        "group":        pg.get("Grp"),                        # CPU / MB / RAM / ...
        "price_sale":   to_int(prod.get("price_sale")),       # ราคาขายหน้าเว็บ
        "price_member": to_int(im.get("SALEPRICE6")),         # ราคาสมาชิก (ถูกกว่าเยอะ)
        "spec_full":    " | ".join(specs),                    # สเปกดิบ คั่นด้วย |
        "stock_type":   (pg.get("stock") or {}).get("type"),  # instock / outstock
    }

find_first, strip_html, to_int เป็น helper เล็กๆ ในไฟล์เดียวกัน — ไล่ dict/list หา key ที่ต้องการ, ถอด tag HTML, แปลงราคาที่มี comma ให้เป็น int แล้วรันทีเดียวจบทั้งโฟลเดอร์ cache

# แกะทุกไฟล์ HTML ใน cache ออกมาเป็น JSON ก้อนเดียว
python tools/extract_advice.py data/advice_cache/*.html > data/advice_products.json

กรองเฉพาะของที่มีจริง แล้วให้สคริปต์เช็คสเปกเอง

ทีนี้ของสำคัญคือ ราคาถูกแต่ไม่มีของ ก็ไม่มีความหมาย ผมเลยกรองเฉพาะ stock_type = instock (ออนไลน์) ก่อน แล้วค่อยเอา spec_full ของซีพียูมา parse เทียบเกณฑ์ ข้อ 4 กับ ข้อ 5 ทีละตัว สเปกดิบของ CPU มันเรียงเป็น brand | model | socket | core | thread | base | boost | L1 | L2 | L3 | iGPU

import json

rows = json.load(open("data/advice_products.json", encoding="utf-8"))

def passes(cpu, core_min, thread_min, ghz_min, l3_min):
    # spec_full: brand | model | socket | core | thread | base | boost | L1 | L2 | L3 | iGPU
    f = [x.strip() for x in cpu["spec_full"].split("|")]
    socket        = f[2]
    core, thread  = int(f[3]), int(f[4])
    boost         = float(f[6].replace("GHz", "").strip())
    l3_mb         = int(f[9].replace("MB", "").strip())
    ddr5          = socket != "AM4"        # AM4 = DDR4 -> ตกเกณฑ์ "DDR5 หรือดีกว่า"
    return (core >= core_min and thread >= thread_min
            and boost >= ghz_min and l3_mb >= l3_min and ddr5)

cpus = [r for r in rows if r["group"] == "CPU" and r["stock_type"] == "instock"]
for c in cpus:
    ok4 = passes(c, 4, 8, 4.4, 12)   # ข้อ 4 งานสำนักงาน
    ok5 = passes(c, 8, 8, 4.6, 16)   # ข้อ 5 งานประมวลผล
    print(f'{c["name"]:<34} ข้อ4={ok4}  ข้อ5={ok5}  {c["price_sale"]}฿')

พอรันจริงกับข้อมูลรอบล่าสุด มันโผล่ปัญหาที่อ่านเกณฑ์เฉยๆ ไม่มีทางเห็น สามข้อ

กับดัก DDR5

เกณฑ์เขียนว่า RAM ต้องเป็น DDR5 หรือดีกว่า -> ซีพียู AMD AM4 อย่าง R5 5600 / 5600G แม้จำนวนคอร์ผ่านสบาย แต่แพลตฟอร์มเป็น DDR4 ตกเกณฑ์ทันที สคริปต์เลยต้องเช็ค socket ด้วย ไม่ใช่ดูแค่ core กับความเร็ว

เรื่องที่สอง ซีพียู 8 core ขึ้นไปที่ผ่านข้อ 5 ณ วันที่ crawl หมดสต็อกออนไลน์เกือบหมด (i5-14400/14500, i7-13700, R7 5700X ขึ้น outstock ทั้งแถบ ต้องสั่งเข้าสาขา) เรื่องที่สาม i3-14100 ตัวมี iGPU ก็ outstock เหลือแต่รุ่น F ที่ไม่มีการ์ดจอในตัว แปลว่าถ้าจะจัดข้อ 4 ด้วยตัวนี้ต้องบวกการ์ดจอแยกเข้าไปอีก พวกนี้ถ้าไม่ได้เก็บสถานะสต็อกมาด้วยตั้งแต่ต้น จะจัดสเปกบนกระดาษสวยแต่ซื้อจริงไม่ได้

เทียบเกณฑ์กับของจริง แล้วออกมาเป็นตาราง

ขั้นสุดท้ายคือประกอบเป็นชุด แล้วคิดราคารวมให้ครบ ราคาที่ควรเอาไปเทียบกับงบราชการไม่ใช่ราคาสินค้าเปล่าๆ ต้องบวก VAT 7% กับค่าบริการหลังการขายของผู้ประกอบการเข้าไปด้วย ผมเขียนตัวคิดเลขให้ reproduce ตัวเลขในตารางได้ตรงเป๊ะ

# ราคาชิ้นส่วน Build ประหยัดสุด (ราคา sale ที่ดึงมาได้จาก Advice)
parts = {
    "CPU  Core Ultra 5 225 (10C/10T 4.9GHz 20MB +iGPU)": 5410,
    "MB   H810M DDR5 LGA1851":                            2290,
    "RAM  16GB DDR5-6000":                                6520,
    "SSD  512GB NVMe PCIe":                               2845,
    "PSU  600W":                                           770,
    "Case mATX":                                           820,
    "Monitor 23.8\" IPS":                                 2420,
    "KB+M":                                                359,
}
subtotal = sum(parts.values())          # 21,434
after_vat = round(subtotal * 1.07)      # + VAT 7%
grand     = round(after_vat * 1.05)     # + ค่าบริการหลังการขาย 5%
print(subtotal, after_vat, grand)       # 21434 22934 24081
การคิดราคารวมราคาสินค้า21,434→×1.07VAT 7%→×1.05ค่าบริการหลังการขาย→รวมทั้งสิ้น24,081

ตัวออกรายงานสุดท้ายผมให้ matplotlib วาดเป็นตาราง PNG เทียบทุกชุดในหน้าเดียว (ต้องมีฟอนต์ไทยในเครื่องด้วย ไม่งั้นตัวหนังสือเป็นสี่เหลี่ยม) ทั้ง pipeline เดินเป็นขั้นแบบนี้

  1. โหลดหน้า product ของ Advice เก็บเป็น HTML ไว้ใน cache (กัน rate-limit และ crawl ซ้ำได้ไม่ต้องยิงเว็บใหม่)
  2. รัน extract_advice.py แกะ __NUXT_DATA__ ออกมาเป็น JSON ที่มีราคา sale/member + สเปก + สถานะสต็อก
  3. กรองเฉพาะ stock_type = instock แล้ว parse spec_full เทียบเกณฑ์ ข้อ 4 / ข้อ 5
  4. ประกอบเป็นชุด แล้วคิดราคารวม VAT + ค่าบริการหลังการขาย
  5. รัน render_build_table.py ออกเป็นตารางภาพเทียบทุกชุดในหน้าเดียว
# ออกเป็นตารางภาพเทียบทุกชุด -> docs/build_comparison.png
python tools/render_build_table.py

สรุปตัวเลขที่ได้

ผลที่ออกมาน่าสนใจกว่าที่คิด ชุดประหยัดสุดที่เลือกซีพียู Core Ultra 5 225 (10C/10T 4.9GHz 20MB มี iGPU) ราคาสินค้า 21,434 บาท พอบวกภาษีและค่าบริการเป็นราคารวมทั้งสิ้น 24,081 บาท เครื่องนี้ผ่านทั้งข้อ 4 และข้อ 5 ในก้อนเดียว

20,000งบ ข้อ 4 office
24,000งบ ข้อ 5 compute
24,081DIY รวมภาษี+บริการ
+81ส่วนต่างจาก ข้อ 5
ซื้อสำเร็จรูปตามเกณฑ์

ข้อ 5 งบ 24,000 บาท

ได้เครื่องที่ผ่านเกณฑ์ "พอดีเส้น" -> 8C/8T 4.6GHz 16MB ตามที่เขียนในเอกสาร ไม่มีส่วนเผื่อ ผู้ขายเลือกของถูกสุดที่ยังผ่านให้

DIY ตามสคริปต์

Core Ultra 5 225 รวม 24,081 บาท

ราคารวมต่างกันแค่ +81 บาท แต่ได้ 10C/10T 4.9GHz 20MB -> เกินทั้งข้อ 4 และข้อ 5 ในเครื่องเดียว ถ้ากดราคาสมาชิกยิ่งถูกลงอีก

ส่วนตัวผมสรุปเลยว่า ถ้าต้องการเครื่องที่ "ผ่านเกณฑ์ ICT" จริงๆ การจัดเองด้วยของที่มีสต็อกตอนนี้ทำได้ในราคาระดับเดียวกับซื้อสำเร็จ ต่างกันหลักสิบบาทแต่ได้ของเกินสเปกไปเยอะ ประเด็นสำคัญไม่ใช่ว่าจะประหยัดกี่บาท แต่คือการมีสคริปต์ที่ดึงราคาจริงมาเทียบเกณฑ์ให้อัตโนมัติ ทำให้เห็นกับดัก DDR5, ปัญหาสต็อก, และช่อง iGPU ที่อ่านเอกสารเปล่าๆ ไม่มีทางเห็น รอบหน้าราคาขยับเมื่อไร รัน crawl ใหม่แล้วสั่งสคริปต์เดิมทับ ตารางก็อัปเดตเองทั้งใบ

ใครจะเอาโครงนี้ไปปรับใช้กับเกณฑ์ปีอื่นหรือร้านอื่น เปลี่ยน regex ที่แกะ SSR กับ index ของ spec_full ให้ตรงเว็บนั้นก็พอ ที่เหลือ logic เทียบเกณฑ์ใช้ซ้ำได้เลย ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ

เคยเจองานแบบนี้ไหม? ติดตรงไหน?

มาแลกวิธีกันต่อในบอร์ด LinuxThai

→ คุยต่อในบอร์ด

ความคิดเห็น 0

ยังไม่มีความคิดเห็น — เป็นคนแรกเลย