2 คะแนน โดย GN⁺ 2024-08-07 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • ในเดือนธันวาคม 2022 ท่ามกลางเงินเฟ้อสูง ได้สร้างไปป์ไลน์ pricewatcher.gr เพื่อติดตามการเปลี่ยนแปลงราคาของซูเปอร์มาร์เก็ต 3 รายใหญ่ในกรีซทุกวัน
  • รายการสินค้าอยู่หลังการเรนเดอร์ด้วย JavaScript และ infinite scroll จึงใช้ Playwright ควบคุมเบราว์เซอร์เพื่อดึงข้อมูลจาก DOM แทน curl หรือ requests.get()
  • สภาพแวดล้อมสำหรับรันงานเปลี่ยนจากการใช้แล็ปท็อปเก่าซึ่งช้าเกินไป และ AWS EC2 ซึ่งแพงเกินไป มาเป็นการ สร้างเซิร์ฟเวอร์ Hetzner เฉพาะเมื่อจำเป็น แล้วรันสแครปเปอร์ 3 ตัวแบบขนานก่อนปิดเครื่อง
  • สำหรับซูเปอร์มาร์เก็ตที่ Akamai บล็อก IP ที่ไม่ใช่ที่พักอาศัย ใช้ Tailscale exit node เพื่อให้ทราฟฟิกออกผ่าน IP บ้าน และตรวจจับความล้มเหลวด้วยอีเมลแจ้งเตือน การแปลงข้อมูลที่เข้มงวด และฮิวริสติกจำนวนสินค้า
  • ลดเวลารันด้วยเซิร์ฟเวอร์ 8vCPU และการบล็อกคำขอรูปภาพ โดยต้นทุนการรัน 31 ครั้งล่าสุดอยู่ที่เซิร์ฟเวอร์ Hetzner €4.94, IPv4 €0.09 และค่าเก็บข้อมูล Cloudflare R2 €0.00

การสแครปเว็บไซต์ซูเปอร์มาร์เก็ตที่ใช้ JavaScript

  • ในเดือนธันวาคม 2022 ท่ามกลางสถานการณ์เงินเฟ้อสูง ได้สร้าง pricewatcher.gr เพื่อติดตามการเปลี่ยนแปลงราคาของซูเปอร์มาร์เก็ต 3 รายใหญ่ในกรีซ
  • e-shop ของซูเปอร์มาร์เก็ตทั้งสามแห่งใช้ การเรนเดอร์ด้วย JavaScript และบางส่วนโหลดสินค้าเพิ่มเติมเมื่อเลื่อนหน้าจอ คล้าย infinite scroll ของโซเชียลมีเดีย
  • ไม่สามารถดึงข้อมูลสินค้าด้วย curl หรือ requests.get() แบบง่าย ๆ ได้ จึงใช้ Playwright
  • Playwright ควบคุมเบราว์เซอร์แบบโปรแกรมได้ และจัดการงานต่อไปนี้ผ่าน API
    • เปิดแท็บใหม่และไปยัง URL
    • ตรวจสอบ DOM
    • ดูรายละเอียดขององค์ประกอบ
    • ดักจับและตรวจสอบคำขอ
  • Playwright รองรับ Chromium, Safari, Firefox และใช้งานได้กับ Node, Java, .NET, Python
  • สแครปเปอร์ค้นหาองค์ประกอบ load more ของ infinite scroll แล้วเลื่อนต่อไปเรื่อย ๆ จากนั้นตัดสินค้าที่มีป้ายหมดออกจากรายการ li.product-item
  • สุดท้ายจะแยกวิเคราะห์ ชื่อสินค้า·ราคา·รูปภาพ·ลิงก์ ฯลฯ จาก `` ของแต่ละสินค้า และทำขั้นตอนเดียวกันซ้ำกับหมวดหมู่สินค้าถัดไป

เลือกสภาพแวดล้อมสำหรับรันทุกวัน

  • บน M1 MacBook Pro การประมวลผลซูเปอร์มาร์เก็ตหนึ่งแห่งทั้งหมดใช้เวลา 50 นาทีถึง 2 ชั่วโมง 30 นาที และแม้รันสแครปเปอร์ 3 ตัวแบบขนานก็แทบไม่ต่างกันอย่างเห็นได้ชัด
  • แล็ปท็อปเพียงพอสำหรับการพัฒนาและทดสอบ แต่จำเป็นต้องมีสภาพแวดล้อมถาวรสำหรับรันทุกวัน
  • รันด้วยแล็ปท็อปเก่าเพียงเครื่องเดียว

    • ความพยายามแรกคือแล็ปท็อปเก่ารุ่นปี 2013
    • สเปกคือโปรเซสเซอร์สาย M แบบดูอัลคอร์ 2.20GHz และ RAM 4GB ก่อนเพิ่ม RAM เป็น 12GB ในภายหลัง
    • แต่แม้ซูเปอร์มาร์เก็ตที่ “เร็ว” ก็ใช้เวลาประมวลผล มากกว่า 2 ชั่วโมง จึงไม่ถึงประสิทธิภาพที่คาดไว้
  • รันบนคลาวด์

    • พิจารณาอินสแตนซ์ EC2 บน AWS ระดับ 4 คอร์·RAM 8GB แต่มีต้นทุนสูงเกินไปสำหรับไซด์โปรเจกต์
    • ณ เวลาที่เขียน c5a.xlarge ใน eu-north-1 มีราคา $0.1640 ต่อชั่วโมง หรือประมาณ $118.08 ต่อเดือน / $1,416.96 ต่อปี
    • เซิร์ฟเวอร์ระดับเทียบเท่าของ Hetzner คือ cpx31 ราคา $17.22(€15.72) ต่อเดือน หรือ $206.64 ต่อปี ถูกกว่า AWS ประมาณ 7 เท่า
    • จึงเลือก Hetzner เป็นสภาพแวดล้อมสำหรับรันงานสุดท้าย

ไปป์ไลน์รายวันที่สร้างด้วย Concourse

  • แล็ปท็อปเก่าไม่ได้ทำการสแครปเองโดยตรง แต่ทำหน้าที่เป็น เซิร์ฟเวอร์ CI ที่มอบหมายงานให้เซิร์ฟเวอร์ Hetzner
  • ใช้ Concourse เป็นเครื่องมือ CI
    • Concourse แนะนำตัวเองว่าเป็น “a continuous thing-doer”
    • มุ่งสู่บิลด์ที่ทำซ้ำได้ผ่านโมเดลไปป์ไลน์แบบ declarative และการจัดการเวอร์ชันของอินพุต
  • ไปป์ไลน์รันทุกคืนตามลำดับต่อไปนี้
    • สร้างเซิร์ฟเวอร์สำหรับสแครป
    • รันงานสแครปซูเปอร์มาร์เก็ต 3 แห่งแบบขนาน
    • หลังงานทั้งหมดเสร็จสิ้น ปิดเซิร์ฟเวอร์เพื่อลดต้นทุน
    • ส่งเอาต์พุตดิบของแต่ละสแครปเปอร์ไปยังงานแปลงข้อมูล
    • โหลดข้อมูลที่แปลงแล้วเข้าสู่ pricewatcher.gr
    • หากขั้นตอนใดล้มเหลว ส่งอีเมลแจ้งเตือน

การเลี่ยงข้อจำกัด IP และ Tailscale exit node

  • ซูเปอร์มาร์เก็ตที่ใช้ทดสอบทำงานปกติ แต่ซูเปอร์มาร์เก็ตอีกแห่งอยู่หลัง Akamai และเปิดกฎไฟร์วอลล์ที่บล็อกคำขอจาก IP ที่ไม่ใช่ที่พักอาศัย
  • โครงสร้างที่ต้องการกลับด้านกับ VPN ทั่วไป คือทำให้ดูเหมือนว่าคำขอออกจาก IP บ้านจริง
  • ใช้ Tailscale เชื่อมอุปกรณ์หลายเครื่องให้เหมือนอยู่ในเครือข่ายเดียวกัน
  • เมื่อกำหนดให้อุปกรณ์หนึ่งเป็น exit node ใน Tailscale จะตั้งค่าให้คำขอจากอุปกรณ์อื่นออกผ่านโหนดนั้นได้
  • แล็ปท็อปเก่าจึงทำหน้าที่เป็น exit node สำหรับทราฟฟิกสแครปด้วย
  • การที่ ISP ที่ใช้อยู่ใช้ CGNAT ทำให้ IP สาธารณะไม่ได้ผูกกับบุคคลใดบุคคลหนึ่งเท่านั้น แต่ถูกแชร์กับลูกค้ารายอื่นของ ISP จึงยังเป็นตัวแปรอยู่

ประเภทความล้มเหลวและวิธีตรวจจับ

  • การตั้งค่านี้ใช้งานมา 1 ปีครึ่ง และโดยรวมทำงานได้อย่างน่าเชื่อถือ
  • โปรเจกต์สแครปย่อมได้รับผลกระทบจากการเปลี่ยนแปลงของผู้พัฒนาเว็บไซต์เป้าหมาย
  • ความล้มเหลวแบ่งได้กว้าง ๆ เป็นสองประเภท
  • การเปลี่ยนแปลงที่ทำให้พัง

    • คือกรณีที่การเปลี่ยนแปลงของเว็บไซต์ทำให้สแครปเปอร์ล้มเหลวทันที
    • ตัวอย่างเช่น
      • มีการเพิ่มแบบสำรวจ จึงต้องกดปุ่มเพิ่มอีกครั้ง
      • เลย์เอาต์เปลี่ยนไปทั้งหมด จนต้องรีแฟกเตอร์สแครปเปอร์ครั้งใหญ่
  • การเปลี่ยนแปลงที่ไม่ทำให้พัง

    • กรณีที่สแครปเปอร์ยังรันได้ตามปกติ แต่การตีความข้อมูลผิดเพี้ยนไปนั้นจัดการยากกว่า
    • เช่น หากรูปแบบการแสดงราคาถูกเปลี่ยนให้แยกส่วนทศนิยมด้วย `` มันฝรั่งทอดราคา €1.99 อาจถูกแยกวิเคราะห์เป็น €199
    • เพื่อจับการเปลี่ยนแปลงแบบนี้ จึงตั้งค่าขั้นตอนแปลงข้อมูลให้ตรวจสอบอินพุตอย่างเข้มงวดที่สุดเท่าที่ทำได้
    • เนื่องจากรันทุกวัน จึงยังมีเวลาตรวจสอบปัญหา แต่ถ้าพังระหว่างพักร้อนก็อาจเป็นปัจจัยที่น่ากังวล

การปรับเวลาในการรันและเสถียรภาพให้เหมาะสม

  • สถาปัตยกรรมโดยรวมแทบคงเดิมตั้งแต่แรก แต่มีการปรับหลายส่วนเพื่อเพิ่มความน่าเชื่อถือและลดงานที่ต้องทำด้วยมือ
  • การปรับปรุงที่นำมาใช้ประกอบด้วย
    • อีเมลแจ้งเตือนเมื่อเกิดความล้มเหลว
    • ฮิวริสติกที่ส่งการแจ้งเตือนเมื่อจำนวนสินค้าของซูเปอร์มาร์เก็ตบางแห่งมากหรือน้อยเกินไป
    • timeout
    • retry ที่ไม่เริ่มใหม่ตั้งแต่ต้น
  • คอขวดที่ใหญ่ที่สุดคือ เวลาในการรันสแครป
    • ยิ่งใช้เวลานาน ต้นทุนก็ยิ่งเพิ่ม
    • เมื่อเกิดความล้มเหลวแล้วต้องลองใหม่ตั้งแต่ต้น ความไม่สะดวกก็ยิ่งมากขึ้น
  • ใช้เซิร์ฟเวอร์ที่ใหญ่ขึ้น

    • เปลี่ยนเซิร์ฟเวอร์จาก 4vCPU·16GB RAM เป็น 8vCPU·16GB RAM
    • เวลารันลดลงประมาณ 20% และได้ประสิทธิภาพใกล้เคียงกับที่ได้จาก MBP
    • เนื่องจากใช้เซิร์ฟเวอร์สแครปเพียงประมาณ 2 ชั่วโมง ส่วนต่างราคาจึงถือว่าเล็กน้อยมาก
  • ดึงข้อมูลให้น้อยลง

    • ใช้ page.route ของ Playwright เพื่อบล็อกคำขอรูปภาพ
    • จัดการคำขอ .png, .jpg ด้วยการ abort เพื่อไม่ดึงรูปภาพระหว่างโหลดสินค้า
    • วิธีนี้ทำให้การสแครปเร็วขึ้น และยังช่วยลดแบนด์วิดท์กับต้นทุนของเว็บไซต์เป้าหมายได้เล็กน้อย

ต้นทุนสำหรับการรัน 31 ครั้ง

  • ค่าใช้จ่ายตามใบแจ้งหนี้ Hetzner ล่าสุดมีดังนี้
    • เซิร์ฟเวอร์ที่สร้าง 31 เครื่อง: €4.94
    • ที่อยู่ IPv4 31 รายการที่เซิร์ฟเวอร์เหล่านั้นได้รับ: €0.09
  • ข้อมูลสแครปถูกเก็บไว้ใน Cloudflare R2
  • เนื่องจากยังไม่เกิน free tier 10GB ของ Cloudflare R2 ค่าเก็บข้อมูลจึงเป็น €0.00
  • ไปป์ไลน์ทั้งหมดทำงานโดยผสาน Playwright, Hetzner, Concourse, Tailscale และ Cloudflare R2 เพื่อติดตามการเปลี่ยนแปลงราคาซูเปอร์มาร์เก็ตทุกวัน

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

 
GN⁺ 2024-08-07
ความคิดเห็นจาก Hacker News
  • ตั้งแต่ต้นปีนี้ก็ทำอะไรคล้าย ๆ กันสำหรับ New Zealand อยู่ ใช้ Playwright/Typescript ดึงข้อมูลแล้วเก็บเป็นไฟล์ Parquet ลงบน cloud storage แต่ยังไม่ได้เอามาแสดงบนหน้าจอ
    งานส่วนใหญ่หมดไปกับการหลบเลี่ยง บริการ reverse proxy อย่าง Akamai และ Cloudflare ตอนเริ่มทำคิดว่าไม่มีใครทำ แต่ตอนนี้รู้แล้วว่าใน NZ มีสตาร์ตอัปอย่างน้อย 3 แห่งที่ทำเรื่องเดียวกัน ดูเหมือนว่าเงินเฟ้อจะกระตุ้นนวัตกรรมที่นี่พอสมควร
    รูปแบบก็เป็นไปตามคาด ซูเปอร์มาร์เก็ตใช้วิธีเดิม ๆ ในการทำให้ราคาซับซ้อนที่สุดเท่าที่จะทำได้ และใช้การขึ้นลงราคาแบบ ‘ฟันเลื่อย’ เพื่อแยกคนที่ไม่มีเวลาออกจากคนที่ไม่มีเงิน พวกเขายังมักแบ่งลูกค้าที่ภักดีต่อแบรนด์ออกจากลูกค้าที่อ่อนไหวต่อราคา เช่น ถ้ามีแบรนด์ช็อกโกแลตยอดนิยม 3 แบรนด์ ในแต่ละสัปดาห์ก็จะมีแค่หนึ่งแบรนด์ที่ขายในราคาสมเหตุสมผล

    • อยากรู้ว่าซูเปอร์มาร์เก็ตเอาการอัปเดตราคาไปใช้ในทางที่ผิดกับ การแบ่งกลุ่มลูกค้า อย่างไรบ้าง และก็อยากรู้ด้วยว่าคนที่ไม่มีเวลาและคนที่ขาดเงินจริง ๆ มักตอบสนองกันอย่างไร
      ตรงที่ว่า “แบ่งลูกค้าที่ภักดีต่อแบรนด์ออกจากลูกค้าที่อ่อนไหวต่อราคา ถ้ามีแบรนด์ช็อกโกแลตยอดนิยม 3 แบรนด์ ในแต่ละสัปดาห์ก็จะมีแค่หนึ่งแบรนด์ที่ขายในราคาสมเหตุสมผล” นี่น่าสนใจเป็นพิเศษ
    • ใน Australia ความ ถูกกฎหมาย ของวิธีนี้ยังค่อนข้างกำกวม คิดว่า NZ ก็น่าจะคล้ายกัน
      ใน AU มี scraper แบบนี้เกิดขึ้นแล้วก็หายไปเยอะ แต่โดยปกติซูเปอร์มาร์เก็ตรายใหญ่จะบล็อกมัน วนซ้ำระหว่างความรู้สึกว่าใช้งานได้จริงกับ “ทำไมไม่มีสิ่งนี้นะ?” ทั้งที่จริง ๆ มันเคยมีมาแล้วหลายครั้ง
    • ผมทำ https://bbdeals.in/ สำหรับ India ใช้หลัก ๆ ตอนซื้อผลไม้ ช่วยประหยัดค่าใช้จ่ายได้ประมาณ 20% ซึ่งช่วงนี้ก็ถือว่าไม่เลว
      ใช้เวลาไม่ถึง 20 ชั่วโมงในการทำ crawler และ infrastructure ที่รองรับมัน
    • ในฐานะชาว Kiwi อยากรู้ว่าคุณจะเปิดเผยโปรเจกต์นี้หรือโปรเจกต์ของตัวเองได้ไหม ค่อนข้างสนใจทีเดียว
    • คนที่สั่งส่งของชำออนไลน์จะได้ประโยชน์จาก การเปรียบเทียบราคา เพราะสามารถสั่งจากหลายร้านพร้อมกันได้
      แถมยังมี marketplace แค่เจ้าเดียวที่มีราคาจากหลายร้านทั้งหมดอยู่ด้วย เลยยิ่งมีประโยชน์
  • บทความดีมาก ผมเจอปัญหาคล้ายกันในเว็บไซต์เปรียบเทียบราคาคอนแทคเลนส์ที่ทำอยู่ https://lenspricer.com/ และตอนนี้เปิดให้บริการในราว 30 ประเทศ เข้าใจดีเลยว่าการที่เว็บไซต์เปลี่ยน HTML เป็นเรื่องน่าปวดหัวแค่ไหน
    อุปสรรคใหญ่ช่วงแรกอย่างหนึ่งคือการ จับคู่สินค้าเดียวกัน ข้ามเว็บไซต์มากกว่า 100 แห่ง แม้จะคิดว่าชื่อสินค้าน่าจะเป็นเอกลักษณ์ แต่ทุกเจ้าก็ชอบเขียนดัดแปลงในแบบของตัวเอง ส่วนใหญ่จัดการได้ด้วย regex แต่ก็มีจำนวนไม่น้อยที่ต้องแมปด้วยมือ และบางส่วนใช้ AI แต่ก็ตรวจสอบเองทั้งหมด
    การสร้าง scraper และ infrastructure นั้นค่อนข้างง่ายกว่า สิ่งที่ยากคือการดูแล scraper ทั้งหมด และเวลาสินค้าหายไปจากเว็บไซต์ ต้องแยกให้ออกว่าเป็นความผิดพลาดของ scraper ถูกบล็อก เว็บไซต์เปลี่ยน หรือแค่เว็บไซต์ปิดปรับปรุงตอนที่ crawl อยู่
    เป็นโปรเจกต์ที่สนุก แต่บางครั้งก็ยาก และมีปัญหาจุกจิกที่น่ารำคาญเวลาแก้

    • คุณกำลังทำสิ่งที่จำเป็นมาก ทุกปีเวลาซื้อของพื้นฐานอย่าง คอนแทคเลนส์ ต้องปวดหัวเพราะบริษัทประกัน
      ราคาก็หลากหลายมาก ส่วนความคุ้มครองก็มักเป็นแนวคืนเงินทางไปรษณีย์ราว 30%
    • อยากรู้ว่าคุณใส่คอนแทคเลนส์ตอนทำงานได้ไหม ผมรู้สึกว่าตาเมื่อยเวลาจ้องจอนาน ๆ ไม่รู้ว่าคุณมีวิธีแก้ไหม
    • ในหน้า Germany มีข้อความใต้ราคาว่า “บางลิงก์อาจเป็นลิงก์ผู้สนับสนุน” แต่ไม่ได้ระบุว่าลิงก์ไหนบ้าง แบบนี้ ถูกกฎหมาย หรือเปล่าก็ไม่แน่ใจ
      จำนวนร้านก็ดูน้อยมากด้วย บางทีทุกลิงก์อาจเป็นลิงก์ผู้สนับสนุนทั้งหมดก็ได้ ใน idealo.de ผมยังเจอราคาที่ต่ำกว่านี้
    • อย่างน้อยในสหรัฐฯ ผู้ค้าปลีกรายใหญ่พยายามทำให้การเปรียบเทียบราคายากขึ้น โดยให้ซัพพลายเออร์ทำ SKU ที่ต่างออกไปเล็กน้อย
      Costco ขึ้นชื่อเรื่องนี้มาก สินค้าอิเล็กทรอนิกส์เกือบทั้งหมดที่ขายในร้านและสินค้าอื่นอีกมากมี SKU แบบสั่งทำพิเศษ และบ่อยครั้งตัวสินค้าก็มีสเปกต่างกันนิดหน่อยด้วย
    • กรณีแบบนี้น่าจะเป็น use case ที่ LLM ช่วยได้ดีจริง ๆ ใช่ไหม
  • ผมสร้างเว็บไซต์คล้ายกันในเมืองของตัวเองและได้รับความสนใจพอสมควร ดึงข้อมูลทั้งจากแอปและเว็บไซต์ แล้วรันบนเซิร์ฟเวอร์เดี่ยว RAM 2GB ของ Linode พร้อม IPv4 5 ตัวและ IPv6 ฟรี 1000 ตัว
    สินค้าทั้งหมดถูกเก็บข้อมูลทุก ๆ ไม่เกิน 40 นาที โดยเฉลี่ยราว 25 นาที ใช้ curl-impersonate และพยายามดึง JSON ให้ได้มากที่สุด 90% ของตลาดส่งราคาผ่าน Ajax call และอีก 10% ที่เหลือก็ parse HTML ได้ง่ายด้วย regex
    ดูได้ที่ https://www.economizafloripa.com.br

    • หลังจากดึงข้อมูลจากแอปและเว็บไซต์แล้ว คุณก็พยายามขายข้อมูลนั้นกลับให้กับผู้ประกอบการ แถมยังเสนอให้นำข้อมูลไปใช้ฝึก AI ด้วย ทำให้ดูเหมือนกับว่าทีมงานจัดการทุกอย่างด้วยมือเอง
      https://www.economizafloripa.com.br/?q=parceria-comercial
      พอเห็นหน้านั้นแล้ว โปรเจกต์นี้ก็ดูเปลี่ยนจาก “เครื่องมือที่มีประโยชน์สำหรับคนที่อยากทวงคืนการควบคุมจากบริษัทที่ขายสินค้าจำเป็นต่อการดำรงชีวิต” ไปเป็น “อีกหนึ่งความพยายามหาเงิน” แน่นอนว่านั่นเป็นสิทธิของคุณ แต่ตอนอ่านหน้าแรกผมคาดหวังแรงจูงใจที่มีจริยธรรมกว่านี้
    • อยากรู้ว่าใน workflow แบบนี้ การ หมุนเวียน IPv6 ทำงานอย่างไร
  • เป็นบทความที่ดีมาก
    ผมคิดว่าทางที่ดีที่สุดคือแยกกระบวนการ scraping กับ parsing ออกจากกันเป็นคนละโปรเซส ถ้าเก็บ JSON หรือ HTML ต้นฉบับไว้ ก็ย้อนกลับมาแก้ parser แล้วรันใหม่ได้ทุกเมื่อ
    ผมเคยทำระบบและเว็บไซต์คล้าย ๆ กันสำหรับเนเธอร์แลนด์เป็นส่วนหนึ่งของโปรเจ็กต์ปริญญาโท: https://www.superprijsvergelijker.nl/
    งาน scraping ส่วนใหญ่ในโปรเจ็กต์ของผมใช้การส่ง HTTP request แบบตรง ๆ ไปยัง JSON API บางเว็บไซต์ใช้ Playwright instance เพื่อเอา session cookie ที่ยังใช้ได้ และหลบระบบกันบอทกับ CAPTCHA ส่วน crawler/scraper, parser และ API ที่เหลือทำด้วย Haskell แล้วรันบน AWS ECS เว็บไซต์ใช้ NextJS
    โจทย์หลักที่ผมยังพยายามแก้อยู่คือการจับคู่สินค้าจากซูเปอร์มาร์เก็ตต่าง ๆ เพื่อแสดงราคาไว้ในหน้าจอเดียว ตัวอย่างอยู่ที่นี่: https://www.superprijsvergelijker.nl/supermarkt-aanbieding/6...
    ถ้ามีการให้หมายเลขบาร์โค้ดที่ถูกต้องมาอย่างน้อยหนึ่งรายการ ส่วนใหญ่ก็ทำงานได้ดี

    • ใช่ ผมก็ทำแบบนั้นเหมือนกันเป๊ะ ๆ และได้ประโยชน์จากมันหลายครั้งจนแทบไม่กล้ายอมรับเลย การเก็บ JSON/HTML ต้นฉบับมีประโยชน์มากจริง ๆ
    • เจ๋งมาก กำลังหาของแบบนี้อยู่พอดี
  • ผมคิดว่าซูเปอร์มาร์เก็ตรายใหญ่สองเจ้าของ Australia แค่ใช้ อัลกอริทึม AI วิเคราะห์ราคา ทั้งสองฝั่ง ก็สามารถสร้างโครงสร้างแบบ duopoly ที่ตั้งราคาเชิงต่อต้านการแข่งขันได้แล้ว อัลกอริทึมสุดท้ายมีแนวโน้มจะไปในทางร่วมมือกันเพื่อเพิ่มกำไรสูงสุด
    ทำแบบถูกกฎหมายก็ได้ด้วยแค่ข้อมูลราคาที่หาได้สาธารณะ และถ้าแชร์ต้นทุนซัพพลายหรือข้อมูลกำไรต่อสินค้า ก็ทำแบบผิดกฎหมายได้ด้วย ผลลัพธ์ก็น่าจะคล้ายกัน
    AI ที่ฝึกมาแล้วสองตัวจะหาวิธีเพิ่มกำไรสูงสุดด้วย regression analysis หลายมิติ หรืออาจจะไฮเปอร์หลายมิติ ในรูปแบบประหลาด ๆ และสุดท้ายผู้บริโภคก็จะเป็นคนแบกรับกำไรสูงสุดของผู้เล่นที่ดูเหมือนจะแข่งขันกันอยู่ ถ้าเข้าถึงข้อมูลราคาแบบนี้ได้ การรันแมชชีนเลิร์นนิงสองชุดที่โฟกัสกับตลาดแบบ duopoly ก็แทบไม่ต้องใช้อะไรมาก

    • “หน่วยงานกำกับการแข่งขัน” ของ Norway(https://konkurransetilsynet.no/norwegian-competition-authori...) วิจารณ์ข้อมูลราคาอาหารที่เปิดเผยและโปร่งใสอยู่บ่อย ๆ ก็เพราะเหตุผลนี้เอง
      ตรรกะของพวกเขาคือ ถ้าราคาทั้งหมดเปิดเผยต่อสาธารณะ ผู้บริโภคกลับจะต้องจ่ายแพงขึ้น เพราะซูเปอร์มาร์เก็ตจะปรับราคาเข้าหาจุดที่ทุกฝ่ายได้กำไรสูงสุดร่วมกัน
      ซูเปอร์มาร์เก็ตที่นี่จ้าง “นักล่าราคา” มาหลายปีแล้ว คนพวกนี้จะไปยังร้านคู่แข่งแล้วจดราคาสินค้าทุกชิ้น
      ใน Norway คุณจะเห็นได้บ่อยว่าถ้าซูเปอร์มาร์เก็ต A ลดราคาสินค้าบางอย่างในสัปดาห์หนึ่ง ซูเปอร์มาร์เก็ต B ก็จะออกโปรโมชันคล้ายกันในสัปดาห์ถัดไปหรืออีกสัปดาห์เพื่อดึงลูกค้า
    • คำที่คุณนึกหาน่าจะเป็น การฮั้วราคา เพียงแต่ไม่ใช่การฮั้วกันโดยคน แต่เป็นการฮั้วกันผ่านซอฟต์แวร์
  • น่าจะเพิ่มการตรวจสอบอัตโนมัติเพื่อจับการเปลี่ยนแปลงแบบนี้ได้ เช่น ถ้า การตรวจสอบความสมเหตุสมผล ไม่ผ่าน ก็ไม่ต้องซิงก์การเปลี่ยนแปลงของราคา/สินค้า
    อาจตั้งเกณฑ์ไว้ได้ว่าราคาแต่ละรายการห้ามเปลี่ยนเกิน 100% และจำนวนสินค้าที่ active อยู่ห้ามเปลี่ยนเกิน 20%

    • ในงานเขียนโปรแกรม การตรวจสอบความสมเหตุสมผล เป็นสิ่งที่ถูกประเมินค่าต่ำเกินไป ต้นทุนด้านประสิทธิภาพก็ต่ำ และช่วยจับบั๊กได้ตั้งแต่เนิ่น ๆ ก่อนที่มันจะทำให้สถานะข้อมูลปนเปื้อน
    • ผมก็เคยคิดเรื่องนี้เหมือนกัน แต่เคยเห็นราคาสินค้ากระโดดเกิน 100% มาแล้ว
      ผมเลยใช้ heuristic แบบนี้เพื่อตรวจว่าวันนี้ scraping สำเร็จหรือไม่แทน เช่นเช็กว่าจำนวนสินค้าที่ดึงมาได้วันนี้อยู่ในช่วงประมาณ 10% ของค่าเฉลี่ย 7 วันล่าสุดหรือเปล่า
  • สิ่งที่ยากไม่ใช่ตัว scraping เอง แต่คือการ หลบระบบป้องกันการบล็อก ที่ซับซ้อนขึ้นเรื่อย ๆ
    ต้องหมุน residential proxy ตลอด ต้องใช้ตัวที่คะแนนดี และต้องไม่เผยแพทเทิร์นการ scraping ข้อมูลออกมา บางซูเปอร์มาร์เก็ตไม่แสดง network request ใน network tab ทำให้ใช้วิธีดึงเฉพาะ API response ไม่ได้
    ถึงจะพยายามทำ man-in-the-middle กับ mobile app เพื่อดู network request และข้อมูล ก็ยังโดนบล็อกถ้าปลอมตัวไม่เนียนพอ
    ผมเคยลองแล้ว แต่สรุปว่าไม่คุ้มเพราะต้นทุนกับงานพัฒนาที่ต้องทำต่อเนื่อง จริง ๆ แล้วบริการเปรียบเทียบราคาซูเปอร์มาร์เก็ตบางเจ้าก็ใช้แรงงานค่าแรงต่ำมานั่งดึงข้อมูลกันตรง ๆ

    • ถ้ามีกฎหมายบังคับให้เปิดข้อมูลราคาเป็น API สาธารณะ ได้ก็คงดี จะได้ไม่ต้องมาปวดหัวกับระบบบล็อกพวกนี้
    • โชคดีที่ยังไม่ถึงขั้นนั้น
      นี่เป็นแค่ side project ถ้ามันเริ่มกินเวลาบ่อยเกินไป ผมก็คงหยุดแล้วเปิดซอร์สโค้ดกับข้อมูลทั้งหมดแทน
      แต่ก็สงสัยว่า network request ที่ไม่โผล่ใน network tab นี่ทำได้ยังไง
      สำหรับผม ส่วนที่ยากที่สุดคือการจับคู่และเปรียบเทียบสินค้าระหว่างซูเปอร์มาร์เก็ตต่าง ๆ
    • ใช้ OCR แล้วถ่ายเฉพาะรูปสินค้าทีละรายการไม่ได้เหรอ อาจไม่ใช่วิธีที่ดีที่สุด แต่ขึ้นอยู่กับวิธีทำ มันอาจติดตามได้ยากหรือแทบเป็นไปไม่ได้
    • ทำแบบ crowdsourcing ด้วย ส่วนขยายเบราว์เซอร์ ก็ได้
  • ถ้ามี ความโปร่งใสด้านราคา ของสินค้า ก็น่าจะดีมาก จะได้ติดตามเรื่องแบบนี้ได้ง่ายขึ้นมากทั้งตามร้านและตามพื้นที่
    เช่นเปรียบเทียบราคา oat milk ซึ่งเป็นนมทางเลือก ตามรหัสไปรษณีย์และตามร้านขายของชำได้ หรือจะติดตาม “shrinkflation” ที่ราคาเท่าเดิมแต่ปริมาณลดลงก็ได้
    ในแง่นั้นก็ดูเหมือนกำลังติดตามราคาอยู่แล้ว เลยสงสัยว่ามีการเช็กต้นทุนต่อกรัมหรือต่อออนซ์ด้วยไหม เพราะผู้ผลิตหรือร้านค้าอาจคงราคาเดิมแต่ให้ปริมาณน้อยลง และผมสงสัยว่าเครื่องมือนี้จะจับเรื่องนั้นได้หรือเปล่า

    • มีการติดตาม ราคาต่อหน่วย อย่าง kg, L ด้วย ตอนแรกผมลังเลว่าจะโชว์และทำกราฟจากตัวเลขนี้ หรือจะแสดงราคาที่คนจ่ายจริงตอนคิดเงิน สุดท้ายเลือกอย่างหลังเพื่อให้ตรงกับราคาที่คน “คุ้นเคย” มากกว่า
      แต่ก็เป็นฟีเจอร์ที่เพิ่มได้ไม่ยาก และถ้ามี shrinkflation เกิดขึ้น ก็สามารถแสดงช่วงเวลาที่มันเกิดได้
    • ผมรำคาญมากเป็นการส่วนตัวเวลาร้านขายของชำไม่ติด ราคาต่อหน่วย ไว้บนป้าย แทบจินตนาการไม่ออกเลยว่าจะมีเหตุผลที่ไม่เป็นการเอาเปรียบลูกค้า
    • ชวนให้ลองจินตนาการว่าถ้ามีกฎหมายบังคับให้เปิดเผยราคาต้นทุนของสินค้า จะดีแค่ไหน อยากเห็นเลยว่าชาวนาได้ X ผู้ผลิตได้ Y แล้วร้านขายของชำบวกเพิ่ม Z
  • ในตลาดสวีเดน มีการทำเรื่องนี้มานานกว่า 8 ปีแล้ว มีเว็บไซต์ชื่อ https://www.matspar.se/ ที่ลูกค้าสามารถดูสินค้าทั้งหมดจากร้านค้าออนไลน์หลัก ๆ เปรียบเทียบราคา แล้วใส่สินค้าที่ต้องการลงในตะกร้าได้
    ตอนท้ายยังสามารถเปรียบเทียบยอดรวมของตะกร้าที่รวมค่าจัดส่งแล้ว และส่งออกตะกร้าไปยังร้านที่ต้องการเพื่อสั่งซื้อได้
    เขาเป็นผู้ร่วมก่อตั้งและปัจจุบันเป็น CTO จึงทำงานด้านการสแครปและการบำรุงรักษามาอย่างมากตลอดช่วงที่ผ่านมา ตอนนี้สแครปราคา มากกว่า 30 ล้านรายการต่อวัน

    • ในมุมธุรกิจ ฉันสงสัยเกี่ยวกับ โมเดลธุรกิจ ว่าพวกเขาสร้างรายได้อย่างไร และมีเป้าหมายระยะยาวคืออะไร
      จากข้อมูลสาธารณะ บริษัทมีรายได้ราว 400,000 ดอลลาร์ และมีพนักงาน 6 คน: https://www.allabolag.se/5590076351/matspar-i-sverige-ab
    • ฉันสงสัยว่ามี บทความเชิงเทคนิค เกี่ยวกับแนวทางการสแครปของพวกเขาหรือไม่ อยากอ่านเพิ่มเติมว่ามีความยากอะไรบ้างและแก้ไขอย่างไร
  • ตอนที่ย้ายไปอยู่พื้นที่ใหม่ ฉันเคยติดตามราคาอยู่ แต่ตอนนี้รู้สึกว่าการไปซื้อจากตลาดหรือร้านใหญ่ 2 แห่งที่คงราคาต่ำไว้เสมอนั้นง่ายกว่ามาก
    ถ้าเป็น Europe ก็น่าจะเป็น Aldi/Lidl
    ถ้าเป็นสหรัฐฯ ก็อาจเป็น Costco/Trader Joe's
    ออนไลน์ก็มี CamelCamelCamel/Amazon สำหรับสินค้าไม่ใช่อาหาร แต่เป็นหมวดสุขภาพ/ความงาม/อิเล็กทรอนิกส์บางส่วน
    ถ้าซื้อจากผู้ผลิตโดยตรงได้ บางครั้งก็ดีกว่าเสียอีก เช่น ฉันเคยซื้อสบู่ยี่ห้อหนึ่งที่ชอบแบบยกล็อตจากเว็บขายส่ง แล้วได้ราคาต่ำกว่าราคาขายปลีกไม่ถึงครึ่ง ส่วนแชมพูก็ซื้อโดยตรงแบบแกลลอนถูกกว่าร้านค้าปลีกไหน ๆ มาก

    • จากประสบการณ์ของฉัน ในสหรัฐฯ คือ Costco/Walmart/Aldi
      Trader Joe's คุณภาพสูงกว่า แต่โดยทั่วไปก็แพงกว่า
    • ในสหรัฐฯ ก็มี ALDI เหมือนกัน แต่จะมีเป็นบางภูมิภาค Trader Joe’s เป็นเจ้าของโดยตระกูลเดียวกับ ALDI และจนกระทั่งราว 10 ปีก่อน ก็ยังไม่ค่อยเห็นทั้งสองร้านอยู่ในพื้นที่เดียวกัน