এই টিউটোরিয়ালে আপনি শিখবেন কিভাবে মেকানাইজ (Mechanize) ব্যবহার করে লিংক ক্লিক করতে হয়, ফর্ম পূরণ করতে হয় এবং ফাইল আপলোড করতে হয়। আপনি মেকানাইজ পেজ অবজেক্ট স্লাইস করার পদ্ধতিও শিখবেন এবং গুগল সার্চ অটোমেট করে ফলাফল সংরক্ষণ করার উপায়ও জানবেন।
বিষয়সূচি
- সিঙ্গেল পেজ বনাম পেজিনেশন
- মেকানাইজ (Mechanize)
- এজেন্ট (Agent)
- পেজ (Page)
- নোকোগিরি (Nokogiri) মেথডসমূহ
- লিংক (Links)
- ক্লিক (Click)
- ফর্ম (Forms)
সিঙ্গেল পেজ বনাম পেজিনেশন
আমরা ইতিমধ্যে নোকোগিরি ব্যবহার করে একটি একক পেজের স্ক্রিন স্ক্র্যাপ করার পদ্ধতি শিখেছি। এটি বহু পেজ থেকে কন্টেন্ট এক্সট্র্যাকশনের জন্য একটি ভালো ভিত্তি তৈরি করেছে।
আসলে আমরা যে সমস্যাটি সমাধান করতে চাই সেটি ১৪০টিরও বেশি エピソード-র কন্টেন্ট পেতে জড়িত—যা একটি একক ওয়েব পেজে যুক্ত করা যেকোনো যুক্তিসঙ্গতভাবে সম্ভব নয়। আমাদের পেজিনেশনের সাথে কাজ করতে হবে এবং কন্টেন্টটি র্যাবিট হোল (গভীরতর স্তর) পর্যন্ত অনুসরণ করার উপায় খুঁজে বের করতে হবে।
ইহানেই নোকোগিরি থামে এবং আরেকটি দরকারী জেম মেকানাইজ (Mechanize) কাজে লাগে।
মেকানাইজ (Mechanize)
মেকানাইজ একটি আরো শক্তিশালী টুল যাতে অনেক সুবিধা রয়েছে। এটি মূলত ওয়েবসাইটের সাথে ইন্টারঅ্যাকশন অটোমেট করতে সক্ষম করে যেখান থেকে আপনাকে কন্টেন্ট বের করতে হয়। এই দিক থেকে এটি ক্যাপিবারা (Capybara) টেস্টিং-এর কিছু কার্যকারিতার কথা স্মরণ করায়।
ভুলবেন না, একটি একক পেজে নোকোগিরি নিয়ে খেলা নিজে যথেষ্ট ভালো, কিন্তু আরও জটিল ডেটা এক্সট্র্যাকশন কাজের জন্য আমাদের কিছু অধিক হর্সপাওয়ার দরকার। আমরা প্রয়োজনততন পেজ ক্রল করতে পারি এবং তাদের এলিমেন্টের সাথে ইন্টারঅ্যাক্ট করতে পারি—মানুষের আচরণ অনুকরণ এবং অটোমেট করে। খুব শক্তিশালী বিষয়!
এই জেম আপনাকে লিংক ফলো করতে, ফর্ম ফিল্ড পূরণ করতে এবং সাবমিট করতে সক্ষম করে—কুকি (cookies) হ্যান্ডলিংও এতে সম্ভব। এর মানে আপনি ইউজার লগইন অনুকরণ করে প্রাইভেট সেশনে ঢুকে শুধু আপনার অ্যাক্সেসযোগ্য সাইট থেকে কন্টেন্ট পেতে পারেন।
আপনি আপনার শংসাপত্র দিয়ে লগইন ফর্ম পূরণ করুন এবং মেকানাইজকে কীভাবে ফলো করবেন তা বলে দিন। যেহেতু আপনি লিংক ক্লিক করতে এবং ফর্ম সাবমিট করতে পারেন, এই টুল দিয়ে করা যায় না এমন কিছুই কম। এর নোকোগিরির সাথে ঘনিষ্ঠ সম্পর্ক রয়েছে এবং এটিও নোকোগিরির উপর নির্ভর করে। আーロন প্যাটারসন (Aaron Patterson) আবার এই চমৎকার জেমের লেখকদের একজন।
মেকানাইজ এজেন্ট ইনস্ট্যানশিয়েট করা
কিছু মেকানাইজ করার আগে, আমাদের একটি মেকানাইজ এজেন্ট ইনস্ট্যানশিয়েট করতে হবে।
some_scraper.rb
require 'mechanize' agent = Mechanize.new
এই agentটি একটি পেজ ফেচ (fetch) করতে ব্যবহৃত হবে, ঠিক যেভাবে আমরা নোকোগিরির সাথে করতাম।
some_scraper.rb
require 'mechanize' agent = Mechanize.new podcast_url = "https://betweenscreens.fm/" page = agent.get(podcast_url)
এখানে মেকানাইজ এজেন্টটি পডকাস্ট পেজটি এবং এর কুকিগুলো পেয়েছে।
পেজ কন্টেন্ট এক্সট্র্যাক্ট করা
এখন আমাদের কাছে এক্সট্র্যাকশনের জন্য প্রস্তুত একটি পেজ রয়েছে। এক্সট্র্যাকশন করার আগে, আমি পরামর্শ দিই inspect মেথড ব্যবহার করে আন্ডার দ্য হুড (under the hood) এক নজর দিন।
some_scraper.rb
require 'mechanize' agent = Mechanize.new podcast_url = "https://betweenscreens.fm/" page = agent.get(podcast_url) puts page.inspect
আউটপুট কাফি বিস্তৃত। নিজেই দেখুন Mechanize::Page অবজেক্টটি কীসহ গঠিত। এখানে আপনি সেই পেজের সমস্ত অ্যাট্রিবিউট দেখতে পাবেন।
আমার মতে, এটি আপনার প্রয়োজনীয় ডেটা স্লাইস করে বের করার জন্য খুব হ্যান্ডি একটি অবজেক্ট।
আউটপুট
#<Mechanize::Page {url #https://betweenscreens.fm/>} {meta_refresh} {title "Between | Screens "} {iframes #<Mechanize::Page::Frame nil "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/290328784&color=ff0000&auto...> #<Mechanize::Page::Frame nil "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/290126141&color=ff0000&auto...> #<Mechanize::Page::Frame nil "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/289018386&color=ff0000&auto...> #<Mechanize::Page::Frame nil "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/287425105&color=ff0000&auto...> #<Mechanize::Page::Frame nil "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/287105342&color=ff0000&auto...> #<Mechanize::Page::Frame nil "https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/221003494&color=ff0000&auto...> #<Mechanize::Page::Frame nil "">https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/218101809&color=ff0000&auto...} {frames} {links #<Mechanize::Page::Link "Logo cube" "/"> #https://github.com/vis-kid/betweenscreens"> #<Mechanize::Page::Link "about" "pages/about/"> #<Mechanize::Page::Link "design" "design/"> #<Mechanize::Page::Link "code" "code/"> #<Mechanize::Page::Link "Randy J. Hunt" "episodes/144/"> #<Mechanize::Page::Link "Jason Long" "episodes/143/"> #<Mechanize::Page::Link "David Heinemeier Hansson" "episodes/142/"> #<Mechanize::Page::Link "Zach Holman" "episodes/141/"> #<Mechanize::Page::Link "Joel Glovier" "episodes/140/"> #<Mechanize::Page::Link "João Ferreira" "episodes/139/"> #<Mechanize::Page::Link "Corwin Harrell" "episodes/138/"> #<Mechanize::Page::Link "Older Stuff »" "page/2/"> #<Mechanize::Page::Link "Exercise" "/tags/exercise/"> #<Mechanize::Page::Link "Company benefits" "/tags/company-benefits/"> #<Mechanize::Page::Link "Tmux" "/tags/tmux/"> #<Mechanize::Page::Link "FileTask" "/tags/filetask/"> #<Mechanize::Page::Link "Decision making" "/tags/decision-making/"> #<Mechanize::Page::Link "Favorite feature" "/tags/favorite-feature/"> #<Mechanize::Page::Link "Working out" "/tags/working-out/"> #<Mechanize::Page::Link "Scott Savarie" "/tags/scott-savarie/"> #<Mechanize::Page::Link "Titles" "/tags/titles/"> #<Mechanize::Page::Link "Erik Spiekermann" "/tags/erik-spiekermann/"> #<Mechanize::Page::Link "Newbie mistakes" "/tags/newbie-mistakes/"> #<Mechanize::Page::Link "Playbook" "/tags/playbook/"> #<Mechanize::Page::Link "Delegation" "/tags/delegation/"> #<Mechanize::Page::Link "Heat maps" "/tags/heat-maps/"> #<Mechanize::Page::Link "Europe" "/tags/europe/"> #<Mechanize::Page::Link "Sizing type" "/tags/sizing-type/"> #<Mechanize::Page::Link "Focus" "/tags/focus/"> #<Mechanize::Page::Link "Virtual assistants" "/tags/virtual-assistants/"> #<Mechanize::Page::Link "Writing" "/tags/writing/"> #<Mechanize::Page::Link "Hacking" "/tags/hacking/"> #<Mechanize::Page::Link "Joel Glovier" "/tags/joel-glovier/"> #<Mechanize::Page::Link "Corwin Harrell" "/tags/corwin-harrell/"> #<Mechanize::Page::Link "Mario C. Delgado" "/tags/mario-c-delgado/"> #<Mechanize::Page::Link "Tom Dale" "/tags/tom-dale/"> #<Mechanize::Page::Link "Obie Fernandez" "/tags/obie-fernandez/"> #<Mechanize::Page::Link "Chad Pytel" "/tags/chad-pytel/"> #<Mechanize::Page::Link "Zach Holman" "/tags/zach-holman/"> #<Mechanize::Page::Link "Max Luster" "/tags/max-luster/"> #<Mechanize::Page::Link "Kyle Fiedler" "/tags/kyle-fiedler/"> #<Mechanize::Page::Link "Roberto Machado" "/tags/roberto-machado/">} {forms}>
আপনি যদি HTML পেজটিই দেখতে চান, তবে body বা content মেথড ব্যবহার করতে পারেন।
some_scraper.rb
... print page.body ...
আউটপুট
<!doctype html> <html> <head> <meta charset="utf-8" /> <meta http-equiv='X-UA-Compatible' content='IE=edge;chrome=1' /> <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"> <meta name="viewport" content="initial-scale=1"> <title>Between | Screens </title> <link rel="alternate" type="application/atom+xml" title="Atom Feed" href="/feed.xml" /> <link href="stylesheets/all-11b45acc.css" rel="stylesheet" /> <script src="javascripts/all-4c20da82.js"></script> </head> <body> <header> <div id="logo"> <a href="/"><img src="images/Between_Screens_Logo_Cube_Up-539d6997.svg" alt="Logo cube" /></a> </div> <nav class="navigation"> <ul class="nav-list"> fork">https://github.com/vis-kid/betweenscreens">fork! <li><a href="pages/about/">about</a></li> <li><a href="design/">design</a></li> <li><a href="code/">code</a></li> </ul> </nav> </header> <div id="main" role="main"> <div class='posts'> <ul> <li> <article class="index-article"> <span class='post-date'>Oct 27 | 2016</span><h2 class='post-title'><a href="episodes/144/">Randy J. Hunt</a></h2> <h3 class='topic-list'>Organizing teams | Diversity | Desires | Pizza rule | Effective over clever | Novel solutions | Straightforwardness | Research | Coffeeshop test | Small changes | Reducing errors | Granular diffs</h3> <div class='soundcloud-player-small'> <iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/290328784&color=ff0000&...> </div> </article> </li> <li> <article class="index-article"> <span class='post-date'>Oct 25 | 2016</span><h2 class='post-title'><a href="episodes/143/">Jason Long</a></h2> <h3 class='topic-list'>Open source | Empathy | Lower barriers | Learning tool | Design contributions | Git website | Branding | GitHub | Neovim | Tmux | Design love | Knowing audiences | Showing work | Dribbble | Progressions | Ideas</h3> <div class='soundcloud-player-small'> <iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/290126141&color=ff0000&...> </div> </article> </li> <li> <article class="index-article"> <span class='post-date'>Oct 18 | 2016</span><h2 class='post-title'><a href="episodes/142/">David Heinemeier Hansson</a></h2> <h3 class='topic-list'>Rails community | Tone | Technical disagreements | Community policing | Ungratefulness | No assholes allowed | Basecamp | Open source persona | Aspirations | Guarding motivations | Dealing with audiences | Pressure | Honesty | Diverse opinions | Small talk</h3> <div class='soundcloud-player-small'> <iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/289018386&color=ff0000&...> </div> </article> </li> <li> <article class="index-article"> <span class='post-date'>Oct 12 | 2016</span><h2 class='post-title'><a href="episodes/141/">Zach Holman</a></h2> <h3 class='topic-list'>Getting Fired | Taboo | Transparency | Different Perspectives | Timing | Growth Stages | Employment & Dating | Managers | At-will Employment | Tech Industry | Europe | Low hanging Fruits | Performance Improvement Plans | Meeting Goals | Surprise Firings | Firing Fast | Mistakes | Company Culture | Communication</h3> <div class='soundcloud-player-small'> <iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/287425105&color=ff0000&...> </div> </article> </li> <li> <article class="index-article"> <span class='post-date'>Oct 10 | 2016</span><h2 class='post-title'><a href="episodes/140/">Joel Glovier</a></h2> <h3 class='topic-list'>Digital Product Design | Product Design @ GitHub | Loving Design | Order & Chaos | Drawing | Web Design | HospitalRun | Diversity | Startup Culture | Improving Lives | CURE International | Ember | Offline First | Hospital Information System | Designers & Open Source</h3> <div class='soundcloud-player-small'> <iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/287105342&color=ff0000&...> </div> </article> </li> <li> <article class="index-article"> <span class='post-date'>Aug 26 | 2015</span><h2 class='post-title'><a href="episodes/139/">João Ferreira</a></h2> <h3 class='topic-list'>Masters @ Work | Subvisual | Deadlines | Design personality | Design problems | Team | Pushing envelopes | Delightful experiences | Perfecting details | Company values</h3> <div class='soundcloud-player-small'> <iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/221003494&color=ff0000&...> </div> </article> </li> <li> <article class="index-article"> <span class='post-date'>Aug 06 | 2015</span><h2 class='post-title'><a href="episodes/138/">Corwin Harrell</a></h2> <h3 class='topic-list'>Q&A | 01 | University | Graphic design | Design setup | Sublime | Atom | thoughtbot | Working location | Collaboration & pairing | Vim advocates | Daily routine | Standups | Clients | Coffee walks | Investment Fridays |</h3> <div class='soundcloud-player-small'> <iframe width="100%" height="166" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/218101809&color=ff0000&...> </div> </article> </li> </ul> </div> <section> <div class='pagination-link'><a href="page/2/">Older Stuff »</a></div> </section> </div> <footer> <div class='footer-tags'> <h3>Random Tags</h3> <ul class='random-tag-list'> <li><a href="/tags/exercise/">Exercise</a></li> <li><a href="/tags/company-benefits/">Company benefits</a></li> <li><a href="/tags/tmux/">Tmux</a></li> <li><a href="/tags/filetask/">FileTask</a></li> <li><a href="/tags/decision-making/">Decision making</a></li> <li><a href="/tags/favorite-feature/">Favorite feature</a></li> <li><a href="/tags/working-out/">Working out</a></li> <li><a href="/tags/scott-savarie/">Scott Savarie</a></li> <li><a href="/tags/titles/">Titles</a></li> <li><a href="/tags/erik-spiekermann/">Erik Spiekermann</a></li> <li><a href="/tags/newbie-mistakes/">Newbie mistakes</a></li> <li><a href="/tags/playbook/">Playbook</a></li> <li><a href="/tags/delegation/">Delegation</a></li> <li><a href="/tags/heat-maps/">Heat maps</a></li> <li><a href="/tags/europe/">Europe</a></li> <li><a href="/tags/sizing-type/">Sizing type</a></li> <li><a href="/tags/focus/">Focus</a></li> <li><a href="/tags/virtual-assistants/">Virtual assistants</a></li> <li><a href="/tags/writing/">Writing</a></li> <li><a href="/tags/hacking/">Hacking</a></li> </ul> </div> <div class='recent-posts'> <h3>Random Interviewees</h3> <ul> <li><a href="/tags/joel-glovier/">Joel Glovier</a></li> <li><a href="/tags/corwin-harrell/">Corwin Harrell</a></li> <li><a href="/tags/mario-c-delgado/">Mario C. Delgado</a></li> <li><a href="/tags/tom-dale/">Tom Dale</a></li> <li><a href="/tags/obie-fernandez/">Obie Fernandez</a></li> <li><a href="/tags/chad-pytel/">Chad Pytel</a></li> <li><a href="/tags/zach-holman/">Zach Holman</a></li> <li><a href="/tags/max-luster/">Max Luster</a></li> <li><a href="/tags/kyle-fiedler/">Kyle Fiedler</a></li> <li><a href="/tags/roberto-machado/">Roberto Machado</a></li> </ul> </div> </footer> </body> </html>
যেহেতু এই পডকাস্টের পেজে সামান্য সংখ্যক এলিমেন্ট রয়েছে, এখানে github.com থেকে রিটার্ন হওয়া Mechanize::Page দেখানো হচ্ছে যাতে আরও ভেরাইटी দেখা যায়। আমার মতে এটি ফিল পাওয়ার জন্য গুরুত্বপূর্ণ।
আউটপুট github.com
#<Mechanize::Page {url #https://github.com/>} {meta_refresh} {title "How people build software · GitHub"} {iframes} {frames} {links #<Mechanize::Page::Link "Skip to content" "#start-of-content"> #https://github.com/"> #<Mechanize::Page::Link "\n Personal\n" "/personal"> #<Mechanize::Page::Link "\n Open source\n" "/open-source"> #<Mechanize::Page::Link "\n Business\n" "/business"> #<Mechanize::Page::Link "\n Explore\n" "/explore"> #<Mechanize::Page::Link "Sign up" "/join?source=header-home"> #<Mechanize::Page::Link "Sign in" "/login"> #<Mechanize::Page::Link "Pricing" "/pricing"> #<Mechanize::Page::Link "Blog" "/blog"> #https://help.github.com"> #https://github.com/search"> #https://help.github.com/terms"> #https://help.github.com/privacy"> #<Mechanize::Page::Link "Sign up for GitHub" "/join?source=button-home"> #<Mechanize::Page::Link "\n \n \n \n \n A whole new Universe\n \n Learn about the exciting features and announcements revealed at this year's GitHub Universe conference.\n \n \n " "/universe-2016"> #<Mechanize::Page::Link "Individuals " "/personal"> #<Mechanize::Page::Link "Communities " "/open-source"> #<Mechanize::Page::Link "Businesses " "/business"> #<Mechanize::Page::Link "NASA" "//github.com/nasa"> #<Mechanize::Page::Link "Sign up for GitHub" "/join?source=button-home"> #https://github.com/contact"> #https://developer.github.com"> #https://training.github.com"> #https://shop.github.com"> #https://github.com/blog"> #https://github.com/about"> #https://github.com"> #https://github.com/site/terms"> #https://github.com/site/privacy"> #https://github.com/security"> #https://status.github.com/"> #https://help.github.com"> #<Mechanize::Page::Link "Reload" ""> #<Mechanize::Page::Link "Reload" "">} {forms #<Mechanize::Form {name nil} {method "GET"} {action "/search"} {fields [hidden:0x3feb90f8297c type: hidden name: utf8 value: ✓] [text:0x3feb90f827d8 type: text name: q value: ]} {radiobuttons} {checkboxes} {file_uploads} {buttons}> #<Mechanize::Form {name nil} {method "POST"} {action "/join"} {fields [hidden:0x3feb90f7be38 type: hidden name: utf8 value: ✓] [hidden:0x3feb90f7bbb8 type: hidden name: authenticity_token value: vjRATKj7smXreq6Lt02r+MzW+ewWoi+fRzQXPedFAlOZgwzxQ0dZnChirhDfd7vyWZZZBO+ZFydLNedjIEDsrQ==] [text:0x3feb90f7b9d8 type: text name: user[login] value: ] [text:0x3feb90f7b7f8 type: text name: user[email] value: ] [field:0x3feb90f7b654 type: password name: user[password] value: ] [hidden:0x3feb90f7b474 type: hidden name: source value: form-home]} {radiobuttons} {checkboxes} {file_uploads} {buttons [button:0x3feb90f7a038 type: submit name: value: ]}>}>
পডকাস্টে ফিরে, আপনি এনকোডিং, HTTP রেসপন্স কোড, URI, বা রেসপন্স হেডারও দেখতে পারেন।
some_scraper.rb
require 'mechanize' agent = Mechanize.new podcast_url = "https://betweenscreens.fm/" page = agent.get(podcast_url) puts 'Encodings' puts page.encodings puts 'Repsonse Headers' puts page.response puts 'HTTP response code' puts page.code puts 'URI' puts page.uri
আউটপুট
Encodings EUC-JP utf-8 utf-8 Repsonse Headers {"server"=>"GitHub.com", "date"=>"Sat, 29 Oct 2016 17:56:00 GMT", "content-type"=>"text/html; charset=utf-8", "transfer-encoding"=>"chunked", "last-modified"=>"Fri, 28 Oct 2016 01:48:56 GMT", "access-control-allow-origin"=>"*", "expires"=>"Sat, 29 Oct 2016 18:06:00 GMT", "cache-control"=>"max-age=600", "content-encoding"=>"gzip", "x-github-request-id"=>"501C936D:C723:1631523C:5814E2B0"} HTTP response code 200 URI https://betweenscreens.fm/
আপনি যদি আরও গভীরে যেতে চান তবে আরও অনেক কিছু রয়েছে। আমি এখানেই থামছি।
নোকোগিরি (Nokogiri) মেথডসমূহ
atsearch
মেকানাইজ পেজ থেকে ডেটা স্ক্র্যাপ করতে নোকোগিরি ব্যবহার করে। আপনি প্রথম আর্টিকেলে নোকোগিরি সম্পর্কে যা শিখেছেন তা মেকানাইজ পেজেও প্রয়োগ করতে পারেন। অর্থাৎ, সাধারণত পেজ নেভিগেট করার জন্য মেকানাইজ এবং স্ক্র্যাপিংয়ের জন্য নোকোগিরি মেথড ব্যবহার করা হয়।
উদাহরণস্বরূপ, আপনি যদি একটি সিঙ্গেল অবজেক্ট সার্চ করতে চান তবে at ব্যবহার করতে পারেন, যখন search একটি নির্দিষ্ট পেজে সিলেক্টরের সাথে মিল খাওয়ানো সকল অবজেক্ট রিটার্ন করে। অন্য কথায়, এই মেথড দুটি নোকোগিরি ডকুমেন্ট অবজেক্ট এবং মেকানাইজ পেজ অবজেক্ট—দুটি misma কাজ করে।
some_scraper.rb
require 'mechanize'
agent = Mechanize.new
podcast_url = "https://betweenscreens.fm/"
page = agent.get(podcast_url)
first_title = page.at('h2.post-title')
all_titles = page.search('h2.post-title')
all_titles.each do |title|
puts title
end
puts " * "*33
puts first_title
আউটপুট
<h2 class="post-title"><a href="episodes/144/">Randy J. Hunt</a></h2> <h2 class="post-title"><a href="episodes/143/">Jason Long</a></h2> <h2 class="post-title"><a href="episodes/142/">David Heinemeier Hansson</a></h2> <h2 class="post-title"><a href="episodes/141/">Zach Holman</a></h2> <h2 class="post-title"><a href="episodes/140/">Joel Glovier</a></h2> <h2 class="post-title"><a href="episodes/139/">João Ferreira</a></h2> <h2 class="post-title"><a href="episodes/138/">Corwin Harrell</a></h2> * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * <h2 class="post-title"><a href="episodes/144/">Randy J. Hunt</a></h2>
লিংক (Links)
linkslink_withlinks_with
আমরা পুরো সাইটকে আমাদের ইচ্ছামত নেভিগেট করতে পারি। মেকানাইজের সবচেয়ে গুরুত্বপূর্ণ অংশ হলো লিংক নিয়ে খেলার ক্ষমতা। অন্যথায় আপনি শুধু নোকোগিরির সাথে থাকতে পারতেন। চলুন দেখি একটি পেজ তার লিংকগুলো চাইলে কী রিটার্ন করে।
some_scraper.rb
require 'mechanize'
agent = Mechanize.new
podcast_url = "https://betweenscreens.fm/"
page = agent.get(podcast_url)
puts "#{page.links}"
আউটপুট
[#<Mechanize::Page::Link "Logo cube" "/"> , #https://github.com/vis-kid/betweenscreens"> , #<Mechanize::Page::Link "about" "pages/about/"> , #<Mechanize::Page::Link "design" "design/"> , #<Mechanize::Page::Link "code" "code/"> , #<Mechanize::Page::Link "Randy J. Hunt" "episodes/144/"> , #<Mechanize::Page::Link "Jason Long" "episodes/143/"> , #<Mechanize::Page::Link "David Heinemeier Hansson" "episodes/142/"> #<Mechanize::Page::Link "Zach Holman" "episodes/141/"> , #<Mechanize::Page::Link "Joel Glovier" "episodes/140/"> , #<Mechanize::Page::Link "João Ferreira" "episodes/139/"> , #<Mechanize::Page::Link "Corwin Harrell" "episodes/138/"> , #<Mechanize::Page::Link "Older Stuff »" "page/2/"> , #<Mechanize::Page::Link "Exercise" "/tags/exercise/"> , #<Mechanize::Page::Link "Company benefits" "/tags/company-benefits/"> , #<Mechanize::Page::Link "Tmux" "/tags/tmux/"> , #<Mechanize::Page::Link "FileTask" "/tags/filetask/"> , #<Mechanize::Page::Link "Decision making" "/tags/decision-making/"> , #<Mechanize::Page::Link "Favorite feature" "/tags/favorite-feature/"> , #<Mechanize::Page::Link "Working out" "/tags/working-out/"> , #<Mechanize::Page::Link "Scott Savarie" "/tags/scott-savarie/"> , #<Mechanize::Page::Link "Titles" "/tags/titles/"> #<Mechanize::Page::Link "Erik Spiekermann" "/tags/erik-spiekermann/"> , #<Mechanize::Page::Link "Newbie mistakes" "/tags/newbie-mistakes/"> , #<Mechanize::Page::Link "Playbook" "/tags/playbook/"> , #<Mechanize::Page::Link "Delegation" "/tags/delegation/"> , #<Mechanize::Page::Link "Heat maps" "/tags/heat-maps/"> , #<Mechanize::Page::Link "Europe" "/tags/europe/"> , #<Mechanize::Page::Link "Sizing type" "/tags/sizing-type/"> , #<Mechanize::Page::Link "Focus" "/tags/focus/"> , #<Mechanize::Page::Link "Virtual assistants" "/tags/virtual-assistants/"> , #<Mechanize::Page::Link "Writing" "/tags/writing/"> , #<Mechanize::Page::Link "Hacking" "/tags/hacking/"> , #<Mechanize::Page::Link "Joel Glovier" "/tags/joel-glovier/"> , #<Mechanize::Page::Link "Corwin Harrell" "/tags/corwin-harrell/"> , #<Mechanize::Page::Link "Mario C. Delgado" "/tags/mario-c-delgado/"> , #<Mechanize::Page::Link "Tom Dale" "/tags/tom-dale/"> , #<Mechanize::Page::Link "Obie Fernandez" "/tags/obie-fernandez/"> , #<Mechanize::Page::Link "Chad Pytel" "/tags/chad-pytel/"> , #<Mechanize::Page::Link "Zach Holman" "/tags/zach-holman/"> , #<Mechanize::Page::Link "Max Luster" "/tags/max-luster/"> , #<Mechanize::Page::Link "Kyle Fiedler" "/tags/kyle-fiedler/"> , #<Mechanize::Page::Link "Roberto Machado" "/tags/roberto-machado/"> ]
হ olid মলি, এটা ব্রেক ডাউন করি। যেহেতু আমরা মেকানাইজকে অন্য কোথাও देखने বলিনি, তাই শুধু সেই প্রথম পেজ থেকে লিংক এর একটি অ্যারে পেয়েছি। মেকানাইজ ঐ পেজটি নিচের দিকে যেতে যেতে স্ক্যান করে এবং টপ টু বটম লিংক লিস্ট রিটার্ন করে। আউটপুটে দেখা লিংকগুলোর লোকেশন বোঝাতে একটি ছবি গ্রিন পয়েন্টার দিয়ে তৈরি করা হয়েছিল।
বিশ্রান্ত, এটি ইতিমধ্যে আমার পডকাস্টের রিডিজাইনের এন্ড রেজাল্ট দেখাচ্ছে। আমার মতে ডেমো용으로는 এই ভার্সনটি কিছুটা ভালো। আপনি ফাইনাল রেজাল্ট কেমন দেখে এবং কেন আমার পুরনো সিন্যাট্রা সাইট স্ক্র্যাপ করতে হয়েছিল তার একটি ঝলক পাচ্ছেন।
স্ক্রিনশট


সদা ভাবেই, আমরা সেখান থেকে শুধু টেক্সট এক্সট্র্যাক্ট করতে পারি।
some_scraper.rb
require 'mechanize' agent = Mechanize.new podcast_url = "https://betweenscreens.fm/" page = agent.get(podcast_url) page.links.each do |link| puts link.text end
আউটপুট
Logo cube fork! about design code Randy J. Hunt Jason Long David Heinemeier Hansson Zach Holman Joel Glovier João Ferreira Corwin Harrell Older Stuff » Exercise Company benefits Tmux FileTask Decision making Favorite feature Working out Scott Savarie Titles Erik Spiekermann Newbie mistakes Playbook Delegation Heat maps Europe Sizing type Focus Virtual assistants Writing Hacking Joel Glovier Corwin Harrell Mario C. Delgado Tom Dale Obie Fernandez Chad Pytel Zach Holman Max Luster Kyle Fiedler Roberto Machado
এত সব লিংক একসাথে পাওয়া খুব উপকারী হতে পারে অথবা খুব বিরক্তিকরও। خوشির কথা, আমাদের पास কিছু টুল আছে যা আমাদের প্রয়োজনীয় জিনিসটি ফাইন-টিউন করতে সাহায্য করে।
some_scraper.rb
require 'mechanize'
agent = Mechanize.new
podcast_url = "https://betweenscreens.fm/"
page = agent.get(podcast_url)
focus_link = agent.page.links.find { |link| link.text == 'Focus' }
puts focus_link
আউটপুট
Focus
বুম! এখন আমরা কোথাও পৌঁছাচ্ছি! আমরা এভাবে নির্দিষ্ট লিংক টার্গেট করতে পারি। আমরা নির্দিষ্ট ক্রাইটেরিয়া—যেমন এর টেক্সট—মিলান করা লিংক টার্গেট করতে পারি আরও সুন্দর API যেমন links_with বা link_with দিয়ে। আরো, যদি একাধিক Focus লিংক থাকে, তবে ব্র্যাকেট [] ব্যবহার করে পেজের নির্দিষ্ট নম্বরের ওটাতে জুম ইন করতে পারি।
some_scraper.rb
require 'mechanize' agent = Mechanize.new podcast_url = "https://betweenscreens.fm/" page = agent.get(podcast_url) focus_link = agent.page.links_with(:text => 'Focus')[2] puts focus_link
যদি আপনি লিংক টেক্সট না পেয়ে লিংকটাই (href) খুঁজতে চান, তবে শুধু নির্দিষ্ট href স্পেসিফাই করলেই চলে। মেকানাইজ আপনার পথে বাধা দেওয়া নয়। text-এর বদলে মেথডগুলোকে href দিন।
some_scraper.rb
page = agent.page.link_with(href: '/episodes/95/') page = agent.page.links_with(href: '/episodes/95/')
আপনি যদি কাঙ্খিত টেক্সট সহ প্রথম লিংকটাই খুঁজতে চান, তবে এই সিন্ট্যাক্সও ব্যবহার করতে পারেন। খুব সুবিধাজনক এবং কিছুটা অধিক রিডেবল।
some_scraper.rb
focus_links = agent.page.link_with(:text => 'Focus')
আর সেই ফেলাকে ফলো করে Focus লিংকের পেছনে কী আছে তা দেখার কথা? চলুন এটাকে click করি!
ক্লিক (Click)
some_scraper.rb
require 'mechanize'
agent = Mechanize.new
podcast_url = "https://betweenscreens.fm/"
page = agent.get(podcast_url)
focus_links = agent.page.links.find { |link| link.text == 'Focus' }.click.links
puts focus_links
এটি আমাদের আগের মতো লিংকের একটি লম্বা লিস্ট দেবে। দেখুন .click.links কনবাইন করা কত সহজ ছিল। মেকানাইজ আপনার জন্য লিংকটি ক্লিক করে এবং নতুন ডেস্টিনেশনে পেজ ফলো করে। যেহেতু আমরা লিংকের লিস্টও চেয়েছিলাম, সেটা সেই নতুন পেজে মেকানাইজ যা পাবে সেটা দেবে।
মান লें আমার একজন ইন্টারভিউই-এর দুটি টেক্সট লিংক আছে—একটি ট্যাগের तरफ এবং অন্যটি একটি সাম্প্রতিক এপিসোডের দিকে—এবং আমি উভয় পেজ থেকে লিংক পেতে চাই।
some_scraper.rb
require 'mechanize' agent = Mechanize.new podcast_url = "https://betweenscreens.fm/" page = agent.get(podcast_url) links = agent.page.links_with(text: "Some interviewee") links.each do |link| puts link.click.links end
এটি আপনাকে উভয় পেজের জন্য লিংক লিস্ট দেবে। আপনি ইন্টারভিউই-এর প্রতিটি লিংক এর ওভার ইটারেট করুন, এবং মেকানাইজ ক্লিক করা লিংক ফলো করে নতুন পেজে যা লিংক পাবে সেটা আপনার জন্য সংগ্রহ করবে। নিচে কিছু উদাহরণ দেওয়া আছে যাতে আপনি কম্বিনেশনগুলো তুলনা করে শুরু করতে পারেন।
some_scraper.rb
agent.page.links.find { |l| l.text == 'Focus' }
agent.page.links.find { |l| l.text == 'Focus' }.click
agent.page.link_with(text: 'Focus')
agent.page.links_with(text: 'Focus')[0]
agent.page.links_with(text: 'Focus')[1].click
agent.page.links_with(text: 'Focus')[2].click.links
agent.page.link_with(href: '/some-href')
agent.page.link_with(href: '/some-href').click
agent.page.links_with(href: '/some-href')
agent.page.links_with(href: '/some-href').click
ফর্ম (Forms)
submitfield_withcheckbox_withradiobuttons_withfile_uploads
চলুন ফর্মের দিকে তাকাই!
some_scraper.rb
require 'mechanize' agent = Mechanize.new google_url = "https://google.com/" page = agent.get(google_url) forms = page.forms puts forms.inspect
আউটপুট
[#<Mechanize::Form # Attention!! {name "f"} # Attention!! {method "GET"} {action "/search"} {fields [hidden:0x3fea91d2eb08 type: hidden name: ie value: ISO-8859-1] [hidden:0x3fea91d2e964 type: hidden name: hl value: es] [hidden:0x3fea91d2e7e8 type: hidden name: source value: hp] [hidden:0x3fea91d2e5f4 type: hidden name: biw value: ] [hidden:0x3fea91d2e428 type: hidden name: bih value: ] # Attention!! [text:0x3fea91d2e248 type: name: q value: ] # Attention!! [hidden:0x3fea91d2bcb4 type: hidden name: gbv value: 1]} {radiobuttons} {checkboxes} {file_uploads} {buttons [submit:0x3fea91d2e0f4 type: submit name: btnG value: Buscar con Google] [submit:0x3fea91d2be80 type: submit name: btnI value: Voy a tener suerte]}> ]
যেহেতু আমরা forms মেথড ব্যবহার করেছি, আমরা একটি অ্যারে পাব—যদি কেবল একটি ফর্ম রিটার্ন হোক তবুও। এখন যেহেতু আমরা জানি ফর্মটির নাম "f", আমরা সিঙ্গুলার ভার্সন form ব্যবহার করে সেটিকে টার্গেট করতে পারি।
... {name "f"} ...
some_scraper.rb
require 'mechanize'
agent = Mechanize.new
google_url = "https://google.com/"
page = agent.get(google_url)
search_form = page.form('f')
puts search_form.inspect
form('f') ব্যবহার করে আমরা যে নির্দিষ্ট ফর্মটির সাথে কাজ করতে চাই সেটিকে আলাদা করলাম। ফলস্বরূপ, আমরা আর অ্যারে পাব না।
আউটপুট
#<Mechanize::Form # Attention!! {name "f"} # Attention!! {method "GET"} {action "/search"} {fields [hidden:0x3ffe9ce85ba4 type: hidden name: ie value: ISO-8859-1] [hidden:0x3ffe9ce859d8 type: hidden name: hl value: es] [hidden:0x3ffe9ce857bc type: hidden name: source value: hp] [hidden:0x3ffe9ce85618 type: hidden name: biw value: ] [hidden:0x3ffe9ce853e8 type: hidden name: bih value: ] # Attention!! [text:0x3ffe9ce851cc type: name: q value: ] # Attention!! [hidden:0x3ffe9ce84bdc type: hidden name: gbv value: 1]} {radiobuttons} {checkboxes} {file_uploads} {buttons [submit:0x3ffe9ce85078 type: submit name: btnG value: Buscar con Google] [submit:0x3ffe9ce84e48 type: submit name: btnI value: Voy a tener suerte]}>
আমরা টেক্সট ইনপুট ফিল্ডের নাম (q)ও চিনতে পারি।
... [text:0x3ffe9ce851cc type: name: q value: ] ...
আমরা সেই নামে এটি টার্গেট করে এর ভ্যালু সেট করতে পারি রুবি অ্যাট্রিবিউটের মতো। আউটপুট উদাহরণে দেখুন এটি ডিফল্টভাবে খালি।
some_scraper.rb
require 'mechanize'
agent = Mechanize.new
google_url = "https://google.com/"
page = agent.get(google_url)
search_form = page.form('f')
search_form.q = 'New Google Search'
puts search_form.inspect
আউটপুট
#<Mechanize::Form {name "f"} {method "GET"} {action "/search"} {fields [hidden:0x3fcb85b6a784 type: hidden name: ie value: ISO-8859-1] [hidden:0x3fcb85b6a57c type: hidden name: hl value: es] [hidden:0x3fcb85b6a3b0 type: hidden name: source value: hp] [hidden:0x3fcb85b6a16c type: hidden name: biw value: ] [hidden:0x3fcb85b67f20 type: hidden name: bih value: ] # Attention!! [text:0x3fcb85b67d18 type: name: q value: New Google Search] # Attention!! [hidden:0x3fcb85b67728 type: hidden name: gbv value: 1]} {radiobuttons} {checkboxes} {file_uploads} {buttons [submit:0x3fcb85b67b9c type: submit name: btnG value: Buscar con Google] [submit:0x3fcb85b67994 type: submit name: btnI value: Voy a tener suerte]}>
উপরে দেখুন টেক্সট ফিল্ডের ভ্যালু New Google Search এ পরিবর্তন হয়েছে। এখন আমাদের শুধু ফর্মটি submit করে গুগল যা রিটার্ন করে সেই পেজ থেকে রেজাল্ট সংগ্রহ করতে হবে। এর চেয়ে সহজ কিছু নেই। এবার অন্য কিছু সার্চ করি!
some_scraper.rb
require 'mechanize'
agent = Mechanize.new
google_url = "https://google.com/"
page = agent.get(google_url)
search_form = page.form('f')
search_form.q = 'GitHub TouchFart'
page = agent.submit(search_form)
pp page.search('h3.r').map(&:text)
এখানে আমি CSS সিলেক্টর h3.r ব্যবহার করে সার্চ রেজাল্ট হেডার চিনতে পেরেছি, এর text ম্যাপ করেছি এবং প্রিটি প্রিন্ট করেছি। কঠিন ছিল না, তাই না? এটি একটি সহজ উদাহরণ নিশ্চয়, কিন্তু এই টুল দিয়ে আপনার হাতে কত অसीম সম্ভাবনা আছে তা চিন্তা করুন!
আউটপুট
["GitHub - hungtruong/TouchFart: A fart app for the new Macbook ...", "TouchFart/TouchFart at master · hungtruong/TouchFart · GitHub", "Commits · hungtruong/TouchFart · GitHub", "Projects · hungtruong/TouchFart · GitHub", "Pull Requests · hungtruong/TouchFart · GitHub", "Issues · hungtruong/TouchFart · GitHub", "TouchFart/license.txt at master · hungtruong/TouchFart · GitHub", "Add autoplay attribute to <audio> tag and touchfart (er ... - GitHub", "Find file - File Finder · GitHub", "Fart app for the new Macbook Pro's Touch... #3860 on topic touchfart ..."]
মেকানাইজে আপনার জন্য আপনাকে ঘামানোর মতো বিভিন্ন ইনপুট ফিল্ড আছে। আপনি ফাইলও আপলোড করতে পারেন!
field_withcheckbox_withradiobuttons_withfile_uploads
আপনি রেডিও বাটন এবং চেকবক্স তাদের নাম দিয়ে চিনতে পারেন এবং check দিয়ে চেক করতে পারেন।
some_scraper.rb
form.radiobuttons_with(:name => 'gender')[3].check form.checkbox_with(:name => 'coder').check
অপশন ট্যাগ ইউজারকে ড্রপ-ডাউন লিস্ট থেকে একটি আইটেম সিলেক্ট করতে দেয়। আবার আমরা নাম দিয়ে টার্গেট করি এবং আমাদের পছন্দের অপশন নম্বর সিলেক্ট করি।
some_scraper.rb
form.field_with(:name => 'countries').options[22].select
ফাইল আপলোডও ফর্মে টেক্সট ইনপুটের মতো কাজ করে রুবি অ্যাট্রিবিউটের মতো সেট করে। আপনি আপলোড ফিল্ড চিনে নিন এবং তারপর আপনি যে ফাইল পাথ (ফাইল নাম) ট্রান্সফার করতে চান সেটা স্পেসিফাই করুন। শুনতে যত জটিল লাগে, আসলে তত নয়। দেখি!
some_scraper.rb
form.file_uploads.first.file_name = "some-path/some-image.jpg"
শেষ ভাবনা
দেখুন, কোনো ম্যাজিক নেই! এখন আপনি নিজেরamente মজা করার জন্য পুরোপুরি সক্ষম। নোকোগিরি এবং মেকানাইজ সম্পর্কে আরো কিছু শিখতে থাকবেই, কিন্তু অপ্রয়োজনীয় দিকগুলোর পেছনে বেশি وقت ব্যয় না করে নিজে খেলুন এবং ডকুমেন্টেশন দেখুন যখন বিগিনার আর্টিকেলের সুযোগের বাইরে কোনো সমস্যায় পড়েন।
আশা করি আপনি বুঝতে পেরেছেন এই জেমটি কত সুন্দরভাবে সহজ এবং কত শক্তি দেয়। আমরা সবাই জানি জনপ্রিয় সংস্কৃতির মাধ্যমে, এটাও দায়িত্ব নিয়ে আসে। কানूনী ফ্রেমওয়ার্কের ภายর এবং যখন আপনার কাছে API অ্যাক্সেস নেই তখনই এটি ব্যবহার করুন। সম্ভবত আপনার এই টুলগুলো নিয়মিত ব্যবহার হবena, কিন্তু আপনার কোনো রিয়েল স্ক্র্যাপিং প্রয়োজন হলে এটি কত উপকারী হয় সেটা বলার নেই!
वादা অনুযায়ী, পরবর্তী আর্টিকেলে আমরা একটি রিয়েল-ওয়ার্ল্ড উদাহরণ দেখব যেখানে আমি আমার পডকাস্ট সাইট থেকে ডেটা স্ক্র্যাপ করব। আমি পুরনো সিন্যাট্রা সাইট থেকে এক্সট্র্যাক্ট করে আমার নতুন মিডলম্যান (Middleman) সাইটে ট্রান্সফার করব যেটি প্রতিটি এপিসোডের জন্য .markdown ফাইল ব্যবহার করে। আমরা তারিখ, এপিসোড নম্বর, ইন্টারভিউই-এর নাম, হেডার, সাবহেডার ইত্যাদি এক্সট্র্যাক্ট করব। সেখানে দেখা হবে!