- दिसंबर 2022 में ऊंची महंगाई के बीच ग्रीस की 3 सबसे बड़ी सुपरमार्केट चेन में कीमतों के बदलाव को रोज़ ट्रैक करने के लिए pricewatcher.gr पाइपलाइन बनाई गई
- प्रोडक्ट सूची JavaScript rendering और infinite scroll के पीछे थी, इसलिए
curlयाrequests.get()की जगह Playwright से browser को नियंत्रित कर DOM से डेटा निकाला गया - execution environment के लिए सिर्फ पुराना laptop धीमा था और AWS EC2 महंगा, इसलिए ज़रूरत पड़ने पर ही Hetzner server बनाकर 3 scrapers को parallel चलाने और फिर server बंद करने का तरीका अपनाया गया
- जिस सुपरमार्केट में Akamai non-residential IPs को block करता था, वहाँ Tailscale exit node के जरिए घर के IP से traffic भेजा गया, और email alerts, strict transforms, तथा product-count heuristics से failures पकड़े गए
- 8vCPU server और image requests block करने से runtime घटा, और हाल की 31 runs की लागत लगभग Hetzner servers के लिए €4.94, IPv4 के लिए €0.09, और Cloudflare R2 storage के लिए €0.00 रही
JavaScript-आधारित सुपरमार्केट साइट्स की scraping
- दिसंबर 2022 की ऊंची महंगाई के दौरान ग्रीस की 3 सबसे बड़ी सुपरमार्केट चेन में कीमतों के बदलाव को ट्रैक करने के लिए pricewatcher.gr बनाया गया
- तीनों सुपरमार्केट e-shop JavaScript rendering का उपयोग करते थे, और कुछ हिस्सों में social media की infinite scroll की तरह scroll करने पर अतिरिक्त products load होते थे
- साधारण
curlयाrequests.get()से product data नहीं मिल पा रहा था, इसलिए Playwright का उपयोग किया गया - Playwright browser को programmatically control करता है और API के जरिए ये काम करता है
- नया tab खोलना और URL पर जाना
- DOM inspect करना
- element details पढ़ना
- requests intercept और inspect करना
- Playwright Chromium, Safari, Firefox को support करता है और Node, Java, .NET, Python में इस्तेमाल किया जा सकता है
- scraper infinite scroll के
load moreelement को ढूंढ़कर लगातार scroll करता था, फिरli.product-itemसूची में out-of-stock मार्क वाले products को हटाता था - अंत में हर product `` से product name, price, image, link आदि parse किए जाते थे, और यही काम अगली product category पर दोहराया जाता था
रोज़ चलने वाले execution environment का चयन
- M1 MacBook Pro पर एक सुपरमार्केट को पूरा process करने में 50 मिनट से 2 घंटे 30 मिनट लगते थे, और 3 scrapers को parallel चलाने पर भी कोई खास फर्क नहीं दिखा
- development और testing के लिए laptop पर्याप्त था, लेकिन रोज़ चलने वाले एक permanent environment की ज़रूरत थी
-
सिर्फ पुराने laptop पर चलाना
- पहली कोशिश 2013 के एक पुराने laptop के साथ की गई
- इसमें 2.20GHz dual-core M-series processor और 4GB RAM थी, जिसे बाद में 12GB तक बढ़ाया गया
- लेकिन “तेज़” सुपरमार्केट को भी process करने में 2 घंटे से ज़्यादा लगते थे, इसलिए performance उम्मीद से कम रही
-
cloud पर चलाना
- AWS पर 4-core, 8GB RAM वाले EC2 instance पर विचार किया गया, लेकिन side project के लिए इसकी लागत ज़्यादा थी
- लिखे जाने के समय
eu-north-1मेंc5a.xlargeकी कीमत $0.1640 प्रति घंटा थी, यानी लगभग $118.08 प्रति महीना या $1,416.96 प्रति वर्ष - Hetzner का समान server
cpx31$17.22(€15.72) प्रति महीना या $206.64 प्रति वर्ष था, जो AWS से लगभग 7 गुना सस्ता था - अंत में execution environment के लिए Hetzner चुना गया
Concourse से बना daily pipeline
- पुराना laptop सीधे scraping नहीं करता था, बल्कि Hetzner server पर काम सौंपने वाले CI server की भूमिका निभाता था
- CI tool के रूप में Concourse का उपयोग किया गया
- Concourse खुद को “a continuous thing-doer” कहता है
- यह declarative pipeline model और input versioning के जरिए reproducible builds पर ज़ोर देता है
- pipeline हर रात इस क्रम में चलती थी
- scraping server बनाना
- 3 सुपरमार्केट scraping jobs को parallel चलाना
- सभी jobs पूरी होने पर लागत बचाने के लिए server बंद करना
- हर scraper के raw output को transform job तक भेजना
- transformed data को pricewatcher.gr में load करना
- किसी भी चरण में failure होने पर email alert भेजना
IP restrictions को bypass करना और Tailscale exit node
- testing में इस्तेमाल किया गया एक सुपरमार्केट ठीक चल रहा था, लेकिन दूसरे सुपरमार्केट के पीछे Akamai था और उसमें non-residential IP requests को block करने वाला firewall rule enabled था
- ज़रूरी setup सामान्य VPN के उलट था: requests ऐसे दिखें जैसे वे घर के असली IP से जा रही हों
- Tailscale से कई devices को ऐसे जोड़ा गया जैसे वे एक ही network में हों
- Tailscale में किसी एक device को exit node बनाया जाए, तो दूसरे devices की requests उसी node के जरिए बाहर भेजी जा सकती हैं
- पुराना laptop scraping traffic के लिए exit node की भूमिका भी निभाता था
- इस्तेमाल किया जा रहा ISP CGNAT उपयोग करता था, इसलिए public IP किसी एक व्यक्ति के लिए स्थिर न होकर ISP के दूसरे ग्राहकों के साथ shared भी हो सकती थी
failures के प्रकार और उन्हें detect करने का तरीका
- यह setup डेढ़ साल तक चला और कुल मिलाकर भरोसेमंद तरीके से काम करता रहा
- scraping projects लक्ष्य वेबसाइट के developers द्वारा किए गए बदलावों से प्रभावित होने से बच नहीं सकते
- failures को मोटे तौर पर दो हिस्सों में बांटा जा सकता है
-
breaking changes
- वे बदलाव जिनसे scraper तुरंत fail हो जाता है
- उदाहरण:
- survey जुड़ जाने से एक अतिरिक्त button दबाना पड़ना
- layout पूरी तरह बदल जाना, जिससे scraper को बड़े स्तर पर refactor करना पड़े
-
non-breaking changes
- ये ज्यादा मुश्किल होते हैं: scraper चलता रहता है, लेकिन data की interpretation गलत हो जाती है
- उदाहरण के लिए अगर price formatting बदल जाए और decimal part को `` में अलग कर दिया जाए, तो €1.99 वाले potato chips को €199 की तरह parse किया जा सकता है
- ऐसे बदलाव पकड़ने के लिए transform stage को input को यथासंभव सख्ती से validate करने के लिए बनाया गया
- रोज़ execution होने से समस्या देखने का समय मिल जाता है, लेकिन छुट्टियों के दौरान कुछ टूट जाए तो चिंता बनी रहती है
runtime और reliability optimization
- पूरी architecture लगभग शुरू से वैसी ही रही, लेकिन reliability बढ़ाने और manual काम घटाने के लिए कई हिस्सों में बदलाव किए गए
- किए गए improvements में ये शामिल थे
- failure पर email alerts
- किसी खास सुपरमार्केट में product count बहुत ज़्यादा या बहुत कम होने पर alert भेजने वाले heuristics
- timeouts
- शुरुआत से फिर न चलाने वाले retries
- सबसे बड़ा bottleneck scraping runtime था
- समय जितना बढ़ता, लागत उतनी बढ़ती
- failure के बाद शुरुआत से retry करना पड़े तो असुविधा भी बढ़ती
-
बड़ा server इस्तेमाल करना
- server को 4vCPU, 16GB RAM से बदलकर 8vCPU, 16GB RAM किया गया
- runtime लगभग 20% घट गया, और performance MBP के करीब पहुंच गई
- scraping server केवल लगभग 2 घंटे के लिए इस्तेमाल होता था, इसलिए कीमत का अंतर नगण्य था
-
कम fetch करना
- Playwright के
page.routeका उपयोग करके image requests को block किया गया .png,.jpgrequests को abort कर दिया जाता था ताकि products load करते समय images fetch न हों- इस तरीके से scraping तेज़ हुई, bandwidth कम लगी, और target website की लागत भी थोड़ी कम हो सकती थी
- Playwright के
31 runs के आधार पर लागत
- हाल के Hetzner bill के अनुसार लागत इस प्रकार थी
- बनाए गए 31 servers: €4.94
- उन servers को दिए गए 31 IPv4 addresses: €0.09
- scraping data को Cloudflare R2 में store किया जाता है
- Cloudflare R2 के 10GB free tier से अभी तक ऊपर नहीं गए, इसलिए storage cost €0.00 है
- पूरी pipeline Playwright, Hetzner, Concourse, Tailscale, और Cloudflare R2 को जोड़कर सुपरमार्केट की कीमतों में रोज़ होने वाले बदलावों को ट्रैक करती है
1 टिप्पणियां
Hacker News की राय
इस साल की शुरुआत से New Zealand के लिए कुछ ऐसा ही कर रहा हूँ। Playwright/Typescript से डेटा scrape करके Parquet files के रूप में cloud storage में सेव कर रहा हूँ, और अभी तक उसे स्क्रीन पर दिखाया नहीं है
काम का बड़ा हिस्सा Akamai, Cloudflare जैसी reverse proxy services को bypass करने में जाता है। जब पहली बार बनाया था तो लगा था कोई और यह नहीं कर रहा, लेकिन अब पता है कि NZ में कम से कम 3 startups यही काम कर रहे हैं। लगता है inflation ने यहाँ काफी innovation को trigger किया है
pattern वैसा ही है जैसा उम्मीद थी। supermarkets कीमतों को जितना हो सके उतना जटिल बनाने वाली अपनी सामान्य तरकीबें इस्तेमाल करते हैं, और ‘sawtooth’ price changes से समय की कमी वाले लोगों और पैसे की कमी वाले लोगों में फर्क करते हैं। कई बार brand-loyal customers और price-sensitive customers को भी अलग किया जाता है; अगर 3 popular chocolate brands हैं, तो हर हफ्ते उनमें से सिर्फ एक को सही कीमत पर बेचने जैसा
“brand-loyal customers और price-sensitive customers को अलग किया जाता है। अगर 3 popular chocolate brands हैं, तो हर हफ्ते उनमें से सिर्फ एक सही कीमत पर बिकता है” वाला हिस्सा खास तौर पर दिलचस्प है
AU में ऐसे बहुत सारे scrapers बने और गायब हुए, लेकिन आम तौर पर बड़े supermarkets उन्हें block कर देते हैं। usefulness और “ऐसा कुछ है क्यों नहीं?” का एक cycle दोहराता रहता है, जबकि असल में यह पहले भी कई बार मौजूद रहा है
crawler और उसे support करने वाला infrastructure बनाने में 20 घंटे भी नहीं लगे
ऊपर से, सभी stores की कीमतें रखने वाला marketplace सिर्फ एक है, इसलिए यह और उपयोगी है
अच्छा लेख है। मैं जो contact lens price comparison site https://lenspricer.com/ चलाता हूँ, उसमें भी ऐसी ही problems आईं, और यह करीब 30 देशों में operate कर रही है। websites का HTML बदलना सच में सिरदर्द है, इस बात से सहमत हूँ
शुरुआत में सबसे बड़ी बाधाओं में से एक 100 से ज्यादा websites पर same product को match करना था। product names unique लग सकते हैं, लेकिन हर कोई उन्हें अपने तरीके से थोड़ा बदलकर लिखता है। ज्यादातर regex से handle हो जाता है, लेकिन काफी सारे manually map करने पड़े, और कुछ में AI इस्तेमाल किया, लेकिन सब कुछ खुद verify किया
scraper और infrastructure बनाना तुलनात्मक रूप से आसान है। मुश्किल यह है कि सभी scrapers को maintain किया जाए, और जब कोई product site से गायब हो जाए तो यह पता लगाया जाए कि वह scraper error है, blocking है, site change है, या crawl के समय site maintenance में थी
मजेदार project है, लेकिन कभी-कभी कठिन होता है और कई problems fix करने में बहुत चिढ़ होती है
prices अलग-अलग होते हैं, और coverage भी आम तौर पर mail-in rebate के रूप में करीब 30% तक process होती है
stores की संख्या भी बहुत कम लगती है, तो शायद सभी links sponsored links हो सकते हैं। idealo.de पर मैंने कम prices भी पाए
Costco इसके लिए काफी मशहूर है; store में बिकने वाले लगभग सारे electronics और कई दूसरे products custom SKU होते हैं। अक्सर product configuration भी थोड़ा अलग होता है
मैंने अपने शहर में काफी attention पाने वाली एक similar website बनाई थी। apps और website data तक scrape करता हूँ, और Linode के RAM 2GB वाले single server पर 5 IPv4 और 1000 free IPv6 लगाकर इस्तेमाल करता हूँ
सभी products को अधिकतम 40 मिनट के interval पर collect किया जाता है, average करीब 25 मिनट है। curl-impersonate इस्तेमाल करता हूँ और जहाँ संभव हो JSON scrape करता हूँ। market का 90% prices Ajax calls से देता है, और बाकी 10% HTML को regex से आसानी से parse किया जाता है
https://www.economizafloripa.com.br पर देख सकते हैं
https://www.economizafloripa.com.br/?q=parceria-comercial
उस page को देखकर project “essential goods बेचने वाली companies से control वापस लेने वाले लोगों के लिए useful tool” से “एक और money-making attempt” जैसा दिखने लगता है। बेशक यह आपकी आज़ादी है, लेकिन homepage पढ़ते समय मैंने ज्यादा ethical motivation की उम्मीद की थी
अच्छा लेख है
मेरी राय में scraping और parsing को अलग-अलग process में बांटना सबसे अच्छा रहता है। अगर original JSON या HTML सेव करके रखें, तो आप कभी भी वापस जाकर parser को सुधारकर फिर से लागू कर सकते हैं
अपने master’s project के हिस्से के रूप में मैंने Netherlands के लिए ऐसा ही एक system और website बनाई थी: https://www.superprijsvergelijker.nl/
मेरे project में scraping का ज़्यादातर हिस्सा JSON API पर simple HTTP calls भेजने के तरीके से होता है। कुछ websites valid session cookie पाने और bot prevention व CAPTCHA को bypass करने के लिए Playwright instance का इस्तेमाल करती हैं। बाकी crawler/scraper, parser और API मैंने Haskell में बनाए हैं और AWS ECS पर चलाता हूँ। website NextJS है
जिस मुख्य चुनौती को मैं लगातार हल करने की कोशिश कर रहा हूँ, वह है अलग-अलग supermarkets के products को link करना ताकि एक ही screen पर prices सूचीबद्ध किए जा सकें। उदाहरण यहाँ है: https://www.superprijsvergelijker.nl/supermarkt-aanbieding/6...
अगर product के लिए एक भी सही barcode number दिया गया हो, तो यह अधिकतर अच्छी तरह काम करता है
Australia के दो बड़े supermarkets के मामले में, मुझे लगता है कि दोनों तरफ सिर्फ price analysis AI algorithms चलाने भर से भी anti-competitive pricing वाली duopoly structure बन सकती है। Algorithms अंततः profit maximize करने के लिए सहयोग करने की दिशा में जा सकते हैं
सार्वजनिक रूप से मिले prices से यह कानूनी रूप से किया जा सकता है, और अगर supply cost या product-wise profit data share किया जाए तो गैरकानूनी रूप से भी। परिणाम शायद मिलता-जुलता होगा
दो trained AI multi-dimensional, शायद hyper-dimensional regression analysis के जरिए किसी अजीब तरीके से profit maximization करेंगे, और consumers को उन कंपनियों के maximize किए गए profits का बोझ उठाना पड़ेगा जो ऊपर से competitors दिखती हैं। अगर price data इस तरह पाया जा सकता है, तो duopoly पर केंद्रित दो machine learning implementations चलाने के लिए बहुत ज़्यादा चीज़ों की ज़रूरत नहीं होगी
तर्क यह है कि अगर सारे prices publicly available हों, तो consumers को उल्टा अधिक कीमत चुकानी पड़ती है। मतलब supermarkets अपनी prices उस point पर align कर लेते हैं जहाँ सबको maximum profit मिलता है
यहाँ के supermarkets कई सालों से “price hunters” रखते आए हैं, यानी वे लोग जो competitor stores में जाकर हर चीज़ की price note करते हैं
Norway में अक्सर दिखता है कि supermarket A किसी हफ्ते किसी खास item पर discount देता है, तो अगले या उसके अगले हफ्ते supermarket B भी customers खींचने के लिए वैसा ही discount देता है
ऐसे बदलाव पकड़ने के लिए automatic checks जोड़े जा सकते हैं। उदाहरण के लिए, अगर sanity check fail हो जाए तो price/product changes को sync न किया जाए
मानदंड हो सकते हैं कि कोई भी price 100% से ज़्यादा न बदले, और active products की संख्या 20% से ज़्यादा न बदले
इसके बजाय मैंने ऐसी heuristics का इस्तेमाल scraping success verify करने के लिए किया। जैसे यह जांचना कि आज scrape किए गए products की संख्या पिछले 7 दिनों के average के लगभग 10% range में है या नहीं
मुश्किल scraping खुद नहीं, बल्कि लगातार अधिक sophisticated होती जा रही blocking mechanisms bypass करना है
residential proxies को लगातार rotate करना पड़ता है, high-rated proxies इस्तेमाल करने पड़ते हैं, और यह ध्यान रखना पड़ता है कि data scraping patterns उजागर न हों। कुछ supermarkets network tab में network requests दिखाते ही नहीं, इसलिए सिर्फ API response fetch करने का तरीका काम नहीं करता
फिर भी mobile app पर man-in-the-middle attack करके network requests और data देखने की कोशिश करें, तो अगर ठीक से disguise न किया जाए तो block हो जाता है
मैंने कोशिश की थी, लेकिन cost और ongoing development work की वजह से लगा कि इसकी value नहीं है। असल में कुछ supermarket price comparison services बस low-wage workers से scrape करवाती हैं
यह side project है, इसलिए अगर time demand बहुत frequent हो गई तो मैं बस इसे बंद कर दूँगा और code व data सब public कर दूँगा
लेकिन मुझे जिज्ञासा है कि network requests का network tab में न दिखना कैसे संभव है
मेरे लिए सबसे मुश्किल हिस्सा supermarkets के बीच products को link और compare करना है
products की price transparency हो तो अच्छा होगा। stores और regions के हिसाब से इस process को track करना बहुत आसान हो सकता है
उदाहरण के लिए milk alternative oat milk की price को pincode और grocery store के हिसाब से compare किया जा सकता है। साथ ही “shrinkflation” भी track की जा सकती है, जहाँ price वही रहती है लेकिन quantity कम हो जाती है
उस लिहाज़ से लगता है कि prices तो track हो रही हैं, लेकिन मुझे wonder है कि क्या gram या ounce के हिसाब से cost भी देखते हैं। manufacturer या store price वही रखकर consumer को कम quantity दे सकता है, और मैं जानना चाहता हूँ कि tool इसे पकड़ पाएगा या नहीं
फिर भी यह आसानी से जोड़ा जा सकने वाला feature है, और अगर shrinkflation हुई हो तो उसका timing दिखाया जा सकता है
Swedish बाज़ार में हम यह काम 8 साल से ज़्यादा समय से कर रहे हैं। https://www.matspar.se/ नाम की एक वेबसाइट है, जहाँ ग्राहक प्रमुख ऑनलाइन स्टोर्स के सभी प्रोडक्ट्स देख सकते हैं, कीमतों की तुलना कर सकते हैं और फिर मनचाहे प्रोडक्ट्स कार्ट में डाल सकते हैं
आखिर में डिलीवरी फीस समेत कार्ट की कुल राशि की तुलना की जाती है, और ग्राहक अपने पसंदीदा स्टोर में कार्ट export करके ऑर्डर कर सकते हैं
मैं co-founder और मौजूदा CTO हूँ, इसलिए इस दौरान scraping और maintenance काफी किया है। हम रोज़ 3 करोड़ से ज़्यादा कीमतें scrape कर रहे हैं
सार्वजनिक डेटा के अनुसार कंपनी का revenue लगभग 4 लाख डॉलर है और कर्मचारी 6 हैं: https://www.allabolag.se/5590076351/matspar-i-sverige-ab
जब मैं किसी नए इलाके में शिफ्ट हुआ था तो कीमतें track करता था, लेकिन अब लगता है कि हमेशा कम कीमत रखने वाले मार्केट या 2 बड़े स्टोर्स से shopping करना कहीं ज़्यादा आसान है
Europe में शायद Aldi/Lidl होंगे
अमेरिका में Costco/Trader Joe's हो सकते हैं
ऑनलाइन के लिए CamelCamelCamel/Amazon है। यह food के लिए नहीं, बल्कि health/beauty/कुछ electronics के लिए है
अगर manufacturer से direct खरीदा जा सके, तो कभी-कभी वह बेहतर भी होता है। उदाहरण के लिए, मैंने अपने पसंदीदा खास soap brand को wholesale site से bulk में खरीदा, तो वह retail price के आधे से भी कम पड़ा। shampoo भी gallon में direct खरीदना किसी भी retail store से काफी सस्ता था
Trader Joe's की quality ज़्यादा अच्छी है, लेकिन आम तौर पर ज़्यादा महंगा है