খালি পে-লোড: যখন স্ক্র্যাপার নিখুঁত কাঠামো ফেরত দেয়, আর ভেতরে থাকে শূন্যতা
### মূল উত্তর Stage-1 ডেটা পাইপলাইনে কাঠামোগতভাবে বৈধ কিন্তু সম্পূর্ণ খালি আউটপুট ফেরত এসেছে। শিরোনাম, উৎস ও তথ্যবিন্দু — সব ফাঁকা। কারণ ফেচ বা এক্সট্রাকশন স্তরের ব্যর্থতা, লেখাটি সংক্ষিপ্ত হওয়া নয়। ### মূল তথ্য - information_points ফিল্ডে রিটার্ন করা তালিকার দৈর্ঘ্য শূন্য, অর্থাৎ কোনো তথ্যবিন্দু তৈরি হয়নি। - article_title এবং article_source উভয় ক্ষেত্রেই মান N/A, যা ফেচ স্তরের আগাম ব্যর্থতা নির্দেশ করে। - Additional Notes ক্ষেত্রে প্রম্পটের নির্দেশমূলক বাক্য হুবহু প্রতিধ্বনিত হয়েছে, মডেল কার্যকর হয়নি। - article_type ডিফল্ট Unclassified মানে নেমে এসেছে। - একমাত্র অখালি ক্ষেত্র Domain Label — cricket_world, যা ছাড়াই নির্ধারিত হওয়ায় অবিশ্বস্ত। ### উৎস মূল উৎস: Stage-1 অভ্যন্তরীণ ডায়াগনস্টিক আউটপুট, ডেটা পাইপলাইন রিপোর্ট। প্রকাশের নির্দিষ্ট তারিখ উৎসে উল্লেখ নেই। ### সম্পর্কিত প্রশ্নোত্তর প্রশ্ন: খালি পে-লোড কেন এরর নয়? উত্তর: কারণ আউটপুটের স্কিমা বৈধ, তাই ভ্যালিডেটর সেটি পাস করিয়ে দেয় এবং ত্রুটি অদৃশ্য থাকে। প্রশ্ন: এই ব্যর্থতার মূল স্তর কোনটি? উত্তর: ফেচ ও ডম পার্সিং স্তর, কারণ শিরোনামের মতো সহজ ক্ষেত্রও ফাঁকা ফেরত এসেছে। প্রশ্ন: cricket_world লেবেলটি কেন বিপজ্জনক? উত্তর: ছাড়া নির্ধারিত এই রুক্ষ লেবেল পরবর্তী ধাপে পূর্বানুমান হিসেবে গেলে পক্ষপাতদুষ্ট অনুমান তৈরি করে।
রাত ১টা ৪০। ময়মনসিংহের ভাড়া ঘরটা। ফ্যানের একটানা শব্দ, ল্যাপটপের পাখার সঙ্গে তাল মিলিয়ে। টার্মিনালে শেষ লাইনটা এসে থেমেছে — Stage-1 extraction complete। JSON ফাইলটা খুললাম। প্রতিটি কী নিজের জায়গায়: article_title, article_source, article_summary, information_points, entities, time_sensitivity, source_quality। কোনো ফিল্ড বাদ পড়েনি। স্কিমা ভ্যালিডেটর সবুজ বাতি জ্বালিয়েছে। তারপর ভেতরে চোখ রাখলাম।
article_title: N/A। article_source: N/A। information_points — একটা খালি লিস্ট। entities ঘরে লেখা আছে "উপরের ইনফরমেশন পয়েন্ট থেকে শনাক্ত করুন" — অথচ উপরে কোনো পয়েন্ট নেই। source_quality ঘরে লেখা "উৎস ফিল্ড দেখে বিচার করুন" — অথচ উৎস ফিল্ডে শুধু N/A। time_sensitivity: not assessed। article_type: Unclassified।
কাঠামো নিখুঁত। বিষয়বস্তু শূন্য।
আমি নোটবুক খুলি প্রথম হুইসেলের আগে, বন্ধ করি বাজারের পরে। সেই রাতে নোটবুকে যা লিখলাম, সেটা কোনো ম্যাচের স্কোর নয় — একটা ফাঁকা পাতা। আর অদ্ভুত ব্যাপার হলো, ওই ফাঁকা পাতাটাই ছিল পুরো সপ্তাহের সবচেয়ে জরুরি এন্ট্রি। কারণ যেটা এসেছিল, সেটা ভুল তথ্য ছিল না। সেটা কোনো তথ্যই ছিল না — অথচ দেখতে হুবহু তথ্যের মতোই।

প্রথমে বুঝতে হবে Stage-1 আসলে কী
যে কোনো ক্রিকেট ডেটা ডেস্কে, যে কোনো বেটিং মডেলে, পাইপলাইন কাজ করে অন্তত দুই স্তরে। প্রথম স্তর ডেটা তোলে — শিরোনাম, উৎস, তথ্যবিন্দু, সত্তা, সময়সংবেদনশীলতা। দ্বিতীয় স্তর সেই কাঁচামাল নিয়ে বিশ্লেষণ করে, মডেল চালায়, মার্কেটের সঙ্গে মেলায়। Stage-1 হলো সেই প্রথম কারিগর, যে সকালে দোকান খুলে মাল তোলে। Stage-2 হলো সেই কারিগর, যে সেই মাল দিয়ে রান্না করে।
সমস্যাটা হচ্ছে, Stage-1 মাঝে মাঝে দোকান খোলে, ঝাড়ু দেয়, সাইনবোর্ড টাঙায়, আলো জ্বালায় — আর মাল তোলে না। কিন্তু সাইনবোর্ডটা ঠিকঠাক টাঙানো থাকে। তাই বাইরে থেকে দেখে বোঝার উপায় নেই যে দোকানে কিছু নেই।

দুই হাজার সতেরো সালে এই শহরেই, ঠিক এই ঘর থেকেই, চার মাস পাইথন শিখেছিলাম। প্রথম স্ক্র্যাপারটা যখন লিখলাম, ওটা ২০১৭-১৮ প্রিমিয়ার লিগের প্রতিটি শট, প্রতিটি স্প্রেডশিট কলাম, প্রতিটি পাসিং প্রবণতা টেনে আনত। প্রথম প্রকাশিত লেখাটা ছিল হাডার্সফিল্ড টাউনের ওপর চার হাজার শব্দের ব্রেকডাউন। মূল বক্তব্য ছিল একটা সংখ্যা: দলটা মাইনাস ১৭ দশমিক ৩ এক্সজি ডিফারেন্সিয়াল নিয়ে টিকে ছিল, কারণ গোলকিপার ইউনাস লসল প্রত্যাশার চেয়ে ৪ দশমিক ১ গোল বেশি বাঁচিয়েছিলেন। লেখাটা তিন হাজার বার শেয়ার হয়েছিল, আর আমার প্রথম পেইড চুক্তি হয়েছিল।
ওই সময় থেকে আমি একটা নিয়ম মেনে চলি। কাঁচা CSV ফাইল তিনটা আলাদা হার্ড ড্রাইভে ব্যাকআপ রাখতাম, আর প্রতিটা লেখা খুলতাম একটা ডেটা অ্যাপেন্ডিক্স দিয়ে। সম্পাদকরা দৈর্ঘ্য নিয়ে অভিযোগ করতেন। কিন্তু ওই অ্যাপেন্ডিক্সটাই আমার সিগনেচার হয়ে গেল — পাঠক আমার কথা বিশ্বাস করতেন, কারণ তারা যাচাই করতে পারতেন।
আঠারো সালের রাশিয়া বিশ্বকাপে ক্রোয়েশিয়ার দৌড় নিয়ে যখন সবার মুখে "আত্মা" আর "মনোবল", তখন আমি শুধু সংখ্যা মিলিয়েছিলাম। ডেনমার্ক, রাশিয়া আর ইংল্যান্ডের বিরুদ্ধে টানা তিনটা এক্সট্রা টাইম ম্যাচ। নকআউটে মোট তিনশো পঁচাত্তর মিনিট ফুটবল। চারটা নকআউট ম্যাচে এক্সজি মাত্র ৫ দশমিক ৮। ফাইনালের দুই দিন আগে প্রকাশ করলাম একটা মডেল — ফ্রান্সের প্রত্যাশিত জয় ২ দশমিক ১ বনাম ১ দশমিক ০। ফ্রান্স চার-দুই জিতল। ক্রোয়েশিয়া অলৌকিক কিছু না; ওটা ছিল অতিরিক্ত সময় আর ক্লান্ত পায়ের হিসাবের খাতা। একটা ইউরোপীয় বেটিং সিন্ডিকেট মেইল করে আমার ম্যাচ-পূর্ববর্তী ফাইল চাইল; আমি একটা CSV আর এক লাইন লেখা পাঠালাম।
এই কারণেই এখন আমি কোনো দাবি ছাড়া কিছু প্রকাশ করি না, আর প্রতিটা মডেল আউটপুট টাইমস্ট্যাম্প দিয়ে আর্কাইভ করি। কিন্তু ওই রাতে প্রথমবার ঘটল উল্টোটা। আর্কাইভ করার মতো কিছুই ছিল না। কারণ Stage-1 ফেরত দিয়েছিল একটা শেল — ভেতরে কিছু নেই।
চারটা সংকেত, যেগুলো একসঙ্গে পড়তে হয়
প্রথম সংকেত: খালি লিস্ট। information_points একটা শূন্য অ্যারে ফেরত দিয়েছে। এটাই সবচেয়ে পরিষ্কার প্রমাণ। যেকোনো সত্যিকারের ক্রিকেট লেখা, এমনকি তিন বাক্যের একটা ছোট ইমপ্যাক্ট আপডেটও, অন্তত এক-দুইটা তথ্যবিন্দু তৈরি করবেই — কে বলেছে, কত রানে, কোন ওভারে, কার কোট। শূন্য লিস্ট মানে সাধারণত এটা নয় যে লেখাটা পাতলা; শূন্য লিস্ট মানে এক্সট্রাকশন লজিকের হাতে ইনপুটটাই পৌঁছায়নি। এই দুটো জিনিসের মধ্যে পার্থক্য করা জরুরি, কারণ "লেখা ছোট" ধরলে আপনি এক্সট্রাক্টর ঠিক করতে বসে যাবেন — অথচ সমস্যা তার অনেক আগে, ফেচ লেয়ারে।
দ্বিতীয় সংকেত: শিরোনাম আর উৎস, দুটোই N/A। এটা কাকতালীয় নয়। শিরোনাম হলো পৃথিবীর সবচেয়ে সহজে পাওয়া ফিল্ড। একটা HTML পেজের ট্যাগ, বা পেজের মেটাডেটা — যেকোনো আধা-ভাঙা স্ক্র্যাপারও ওটা তুলতে পারে। যখন শিরোনামও নেই, তখন বুঝতে হবে ডম পার্সিং বা ফেচিং তার আগেই ভেঙেছে। সম্ভাব্য কারণগুলো চেনা: অ্যান্টি-বট ব্লক, জাভাস্ক্রিপ্ট রেন্ডার হয়নি, পেজটা ৪০৪ বা পেওয়াল ফেরত দিয়েছে, কিংবা ভুল করে লিস্টিং পেজ বা এরর পেজ ধরা পড়েছে। বাস্তব ক্রিকেট কভারেজে এটা খুব সাধারণ — ম্যাচ সেন্টারের লাইভ পেজটা ধরা পড়ে, কিন্তু আসল রিপোর্ট পেজটা ধরা পড়ে না।
তৃতীয় সংকেত, আর এটাই সবচেয়ে বড় অস্বস্তির জায়গা: Additional Notes ফিল্ডে টেমপ্লেট ভাষা। ওখানে লেখা ছিল "উপরের তথ্যবিন্দু থেকে শনাক্ত করুন" ধরনের নির্দেশমূলক বাক্য। মানে প্রম্পটটা যেমন লেখা ছিল, তেমনই ফেরত এসেছে। মডেল কাজ করেনি — শুধু নির্দেশটা প্রতিধ্বনিত হয়েছে। এটা ফাঁকা ফলাফলের চেয়েও খারাপ, কারণ দেখতে মনে হয় কিছু একটা প্রসেস হয়েছে।
চতুর্থ সংকেত: Unclassified ফলব্যাক। article_type যখন ডিফল্ট মানে নেমে আসে, তখন সেটা তথ্যের অভাব নয় — সেটা লজিকের ব্যর্থতার স্বাক্ষর।
এই চারটা আলাদা আলাদা দেখলে দুর্বল লাগে। একসঙ্গে দেখলে ছবিটা পরিষ্কার। Stage-1 এমন একটা আউটপুট তৈরি করেছে যার গঠন সঠিক, কিন্তু যার ভেতরে কোনো বস্তু নেই। আর নিচের যেকোনো ধাপ যদি শুধু "গঠন অখণ্ড" দেখে পাস করিয়ে দেয়, ওই ফাঁকা শেলটা পুরো রিপোর্ট পর্যন্ত পৌঁছে যাবে।
আমার অভিজ্ঞতায় এটাই ডেটা পাইপলাইনের সবচেয়ে বিপজ্জনক ব্যর্থতা। একটা এরর মেসেজ সৎ। একটা ক্র্যাশ সৎ। কিন্তু একটা খালি শেল অসৎ — কারণ সে সিস্টেমকে বোঝায় সব ঠিক আছে।
যে ভুলটা সবাই করে: ডোমেইন লেবেলকে প্রায়রি বানানো
এখানে একটা জিনিস আছে যেটা সাধারণত কেউ খেয়াল করে না। পুরো আউটপুটে একমাত্র অখালি ক্ষেত্র ছিল Domain Label: cricket_world।
এখন ভাবুন। কোনো Themen নির্ধারণকারী স্তর হয়তো URL বা পেজ মেটাডেটা দেখে এই লেবেলটা বসিয়েছে। কোনো ছাড়াই। মানে লেবেলটার নির্ভরযোগ্যতা নিজেই প্রশ্নবিদ্ধ। হতে পারে স্ক্র্যাপার একটা ক্রিকেট সাইটের কোনো অ-ক্রিকেট পেজ ধরেছে। হতে পারে ধরেছে স্পোর্টস সেকশনের জেনেরিক লিস্টিং পেজ।
এখন সবচেয়ে বড় ফাঁদটা আসে এখানেই। যদি পরের বার পাইপলাইনে এই লেবেলটাকে পূর্বানুমান হিসেবে খাওয়ানো হয় — "যেহেতু cricket_world, তাই ক্রিকেটের তথ্য খোঁজো" — তাহলে মডেল ক্রিকেটের ফাঁকা জায়গা নিজের কল্পনা দিয়ে ভরিয়ে ফেলবে। শূন্য ইনপুট, পক্ষপাতদুষ্ট আউটপুট। এটা নিছক একটা অদক্ষতা নয়; এটা ভুল তথ্য উৎপাদনের কারখানা।
আমার নিয়ম হলো এই: একটা রুক্ষ ডোমেইন লেবেল কোনো লেবেল না থাকার চেয়েও বিপজ্জনক, যদি সেটা প্রায়রি হিসেবে প্রবেশ করে। লেবেল ইনপুটের ফল হওয়া উচিত, ইনপুটের জায়গা নেওয়া উচিত নয়।
এই অশ্বাসটা আমার মডেলের ভার্সনিং অভ্যাস থেকেই এসেছে। দ্বিতীয় সহস্রাব্দের বিশের দশকের শুরুতে যখন বুনডেসলিগা দর্শকশূন্য স্টেডিয়ামে ফিরল, সেই সপ্তাহে নয়টা ম্যাচের মাত্র দুইটায় ঘরের দল জিতেছিল। আমি অনুমান করিনি; তিন সপ্তাহ ধরে ইউরোপের শীর্ষ পাঁচ লিগের বিশ্রাম-পূর্ব ও বিশ্রাম-পরবর্তী ডেটা টেনেছি। ঘরের দলের জয়ের হার ৪৫ দশমিক ২ শতাংশ থেকে নেমে এসেছিল ৩৩ দশমিক ৮ শতাংশে। পেনাল্টি কমেছিল ২২ শতাংশ। প্রতিপক্ষের এক্সজি বেড়েছিল। তারপর কোরিওবাদের সহগ বসিয়ে মডেল সংস্করণ ২ দশমিক ০-এ পৌঁছেছি, আর প্রতিটা সহগ পরিবর্তন পাবলিক চেঞ্জলগে লিখে রেখেছি। পাঠক দেখতে পেতেন আমি ঠিক কী বদলেছি, কেন বদলেছি।
এই স্বচ্ছতা একটা জিনিস শেখায়। কাঁচামাল ভালো না হলে সহগ ঠিক করার কোনো মানে নেই। Stage-1 ফাঁকা ফেরত দিলে Stage-2-তে বসে প্যারামিটার টিউন করা মানে জ্বরের কারণ না খুঁজে থার্মোমিটার আঁচড়ানো।
সহজ সমাধানটা ভুল সমাধান
এখন সবাই বলবে — খালি লিস্ট পেলেই এরর ছোড়ো। থ্রেশহোল্ড বসাও। দুইশো অক্ষরের কম হলে ব্যর্থ ঘোষণা করো। এই কর্তব্যপরায়ণতার প্রশংসা করি, কিন্তু এতে মূল সমস্যার শিকড় ছোঁয়া হয় না।
আমার পাল্টা যুক্তি হলো এই: ফাঁকা ফলাফলকে এরর বানানো মানে এখনো দুইটা আলাদা প্রশ্নকে এক করে ফেলা। প্রশ্ন এক — পেজটা কি আমরা পেয়েছি? প্রশ্ন দুই — পেজটা কি আমরা বুঝেছি? থ্রেশহোল্ড দিয়ে আপনি দুইটার যেকোনোটা মেরে দিতে পারেন, কিন্তু কোনটা মেরেছেন সেটা জানবেন না।
এখানেই সবচেয়ে প্রচলিত ভুলটা লুকিয়ে আছে। একটা খালি পে-লোড দেখলে স্বাভাবিক প্রতিক্রিয়া হলো এক্সট্রাক্টর ঠিক করতে বসে যাওয়া — রেগুলার এক্সপ্রেশন বদলানো, প্রম্পট পরিমার্জন, মডেল বদলানো। অথচ যদি শিরোনামও ফাঁকা থাকে, তাহলে আপনার এক্সট্রাক্টর প্রায় নিশ্চিতভাবেই নির্দোষ। আপনি হয়তো খুব যত্নে একটা ঘর সাজাচ্ছেন, যে ঘরের দরজা ভাড়াটে কেউ তালা দিয়ে গেছে।
বাস্তব ক্রিকেট ডেস্কে এর একটা স্পষ্ট উদাহরণ আছে। কেউ যদি একটা সিরিজের স্কোরকার্ড পেজ থেকে ডেটা টানেন, আর পেজটা আসলে একটা abandon হওয়া ম্যাচের স্টাব পেজ হয় — তথ্য তো আসবে, কিন্তু ভুল তথ্য। আর যিনি ডেটার অভাবে মডেল না চালিয়ে চুপ করে বসে আছেন, তার শেষ মুহূর্তে আসা নতুন লাইনআপ ইনফরমেশনটা না থাকা সত্ত্বেও ম্যাচ আপডেটটা সঠিকভাবে নামানো কোনো অসম্ভব ঘটনা নয়।
আর একটা কথা। ব্যর্থতাগুলো প্রায়ই দলবেঁধে আসে। কোনো একটা দিনে ফেচ ফেল করলে সেটা একবার হয়। কিন্তু অ্যান্টি-বট বা রেন্ডার ব্লক হলে সেটা টানা কয়েকদিন হয়। এই ধরনের পুনরাবৃত্ত ব্যর্থতার একটা সময়-ধারা থাকে। যদি নোটবুকে তারিখসহ লিখে রাখেন কোন দিন কোন স্তর ফেল করল, দশ দিনের মধ্যেই ধাঁচটা দেখা যায়। মাঝেমধ্যে একটা খালি লেখা আর টানা একটা প্যাটার্ন — এই দুইটা একই রোগের লক্ষণ নয়।
একটা ফাঁকা পে-লোডও এক ধরনের স্বীকারোক্তি
সেই রাতের এন্ট্রি নোটবুকে শেষ হলো একটা লাইনে: Stage-1 — গঠন বৈধ, বিষয়বস্তু শূন্য, দাবি বাতিল। পরদিন সকালে ফেচ লেয়ারে গিয়ে দেখলাম, সন্দেহটা ঠিকই ছিল।
স্কিমা ভ্যালিডেটর সবর্দা সত্য বলে না। সে শুধু জানে ফিল্ডগুলো আছে কি নেই। সে জানে না ফিল্ডের ভেতরে জীবন আছে কি নেই। এই পার্থক্যটা কে ধরবে? যে মানুষটা প্রথম হুইসেলের আগে নোটবুক খোলে, আর বাজার বন্ধ হওয়ার পরে সেটা বন্ধ করে।
আমি সেই মানুষ।

যে কোনো পাইপলাইনে এখন আমার প্রথম কাজ শিরোনামটা চোখে পড়া আর ভেতরের প্রথম লাইনটা পড়া — সংখ্যাগুলো নয়, বাক্যটা। যদি বাক্যটা টেমপ্লেটের মতো শোনায়, বাকিটা অর্থহীন। একটা ক্লোজিং লাইন বাজারের স্বীকারোক্তি, যখন কেউ দেখছে না। একটা ফাঁকা পে-লোডও তেমনি — শুধু এবার সে স্বীকার করছে যে সে কিছুই জানে না।
পরের বার যখন কোনো স্ক্র্যাপার ফলাফল ফেরত দেবে, আর প্রতিটি ফিল্ড জায়গামতো থাকবে, তখন একটা প্রশ্ন করুন। কাঠামোটা সুন্দর, ঠিক আছে। কিন্তু ভেতরের ঘরটা কি কেউ আসলে বাস করছে?
— Root: The Scraper
