หน้าเว็บ

วันอังคารที่ 19 มกราคม พ.ศ. 2553

ดูดการ์ตูนจากเว็ป onemanga ด้วย Ruby Mechanize

หลังจากได้รู้จักกับ Ruby Mechanize ไปบ้างแล้ว คราวนี้มาดูตัวอย่างการนำไปใช้งานกันบ้าง ซึ่งผมก็มี project เล็กๆเป็น case ตัวอย่างที่อยากนำเสนอครับ น่าจะเป็น project ที่คนรักการ์ตูนต้องอยากเอาไปลองเล่นแน่ๆ

Onemanga เว็ปการ์ตูนออนไลน์อันดับหนึ่งของผม
งานที่เราจะนำ Ruby Mechanize มาใช้ในวันนี้เป็นการไล่เก็บการ์ตูนที่ถูกสแกนแล้วนำมาโพสเอาไว้ให้เพื่อนๆในเน็ตได้อ่านกันแบบออนไลน์ ซึ่งเว็บที่มีคนสแกนการ์ตูนให้อ่านนั้นมีอยู่ไม่น้อเหมือนกันแต่ที่ผมใช้บริการบ่อยๆก็ต้องที่ Onemanga ครับ เพราะการ์ตูนเยอะ อัพเดตค่อนข้างเร็วและคุณภาพของรูปที่สแกนก็อยู่ในขั้นใช้ได้ทีเดียว แถมได้ฝึกภาษาองกฤษด้วยเพราะแปลจากญี่ปุ่นเป็นอังกฤษเรียบร้อยแล้ว

เว็ป Onemanga จะอนุญาตให้เราอ่านการ์ตูนได้ทุกเรื่องครับแต่จะอ่านได้แบบออนไลน์เท่านั้นคือ click เปิดดูรูปไปทีละหน้าๆ ไม่สามารถ download ไฟล์รูปของการ์ตูนเรื่องนั้นทีเดียวได้ นั้นคือถ้าเราอยากเก็บไฟล์รูปของการ์ตูนเรื่องที่เราชอบจากเว็ปนี้เราต้องใช้วิธี save as หน้าการ์ตูนโดยการคลิ๊กขวาเท่านั้น ถ้าเรื่องนึงเอาแบบสั้นๆเลยนะมีซัก 10 ตอน ตอนละ 20 หน้า คงต้องคลิ้กแล้วโหลดเว็บกันทีละหน้าถึง 200 ครั้ง แบบนี้ไม่ดีแน่ แต่อย่าเพิ่งหมดหวังครับ เราจะมาดูกันว่า Ruby Mechanize จะช่วยจัดการกับปัญหานี้ได้อย่างไร

ทำความเข้าใจขั้นตอนปกติด้วยการ save as กันก่อน
ก่อนอื่นเราไปดูวิธีการ save รูปจากการ์ตูนเรื่องที่เราสนใจในเว็ปนี้แบบปกติกันก่อน เมื่อทำความเข้าใจแล้วจึงค่อยเขียนโค้ดให้ทำขั้นตอนเหล่านั้นให้โดยอัตโนมัติอีกที

1) ที่หน้าแรกของเว็ปเราจะเห็นรายชื่อการ์ตูนที่อัพเดตใหม่ในแต่ละวัน แต่เพื่อความง่ายต่อการค้นหาการ์ตูนเรื่องที่ต้องการเราจะใส่ชื่อการ์ตูนลงไปใน input text box ตรงมุมบนขวาแล้วคลิ๊กปุ่ม search ซึ่งผมจะกำหนดให้ชื่อของการ์ตูนที่เรานำไป search นั้นเป็นพารามิเตอร์ตัวหนึ่งที่ต้องใส่เวลาเรารันโค้ด ruby ด้วย
ในตัวอย่างนี้ผมจะลองค้นหาเรื่อง Naruto ละกันครับ



2) หน้าของเว็ปจะโชว์เรื่อง Naruto ถ้าหากว่ามีการ์ตูนเรื่องนี้อยู่จริง



เมื่อผมคลิ๊กตาม link ชื่อ Naruto แล้วหน้าของเว็ปโชว์จำนวนตอนทั้งหมดของการ์ตูนเรื่องนี้
ถ้าผมลองคลิ๊กตอนใดตอนหนึ่งดูผมก็จะเข้าสู่หน้าของการยืนยันก่อนการการ์ตูนในตอนนั้นๆเสมอ ซึ่งคุณผู้อ่านจะสังเกตุได้ว่า url ของเราจะอยู่ในรูปแบบ "http://www.onemanga.com/ชื่อเรื่อง/ตอนที่/" ซึ่งในที่นี้จะเป็น ดังนั้นผมจึงขอกำหนดให้ "ตอนที่" เป็นพารามิเตอร์อีกตัวหนึ่งของโค้ดด้วย



3) เมื่อคลิ๊กเลือกตอนที่ต้องการอ่านแล้วเราจะเข้าสู่หน้ายืนยันเพื่อเริ่มอ่าน ก็ให้เราคลิ๊กไปที่ "Beginning ชื่อเรื่อง ตอนที่"

4) หน้าเว็บจะโชว์รูป scan หน้าแรกของตอนนั้นๆ ซึ่งสามารถ save รูปสแกนได้โดยการคลิ๊กขวาแล้วเลือก save as เท่านั้น ถ้าจะอ่านหน้าถัดไปเราก็คลื๊กปุ่ม next หรือคลิ๊กไปที่รูปได้เลย ในหน้านี้จะมี drop down box ที่ชื่อว่า "Page" อยู่ด้านบนของรูป scan ด้วยซึ่งมันจะแสดงจำนวนหน้าทั้งหมดภายในตอนนั้นๆ เราจะใช้จำนวนหน้าตัวนี้



ได้เวลาของโค้ด Ruby แล้ว
เราจะทำ 4 ขั้นตอนข้างต้นแบบอัตโนมัติโดยใช้โค้ด ruby และความสามารถของไลบรารี Mechanize จากที่เคยพูดไปใน blog ก่อนหน้านี้เข้าช่วย ซึ่งโค้ดของผมมีหน้าตาแบบนี้ครับ

scrub_onemanga.rb
require 'mechanize'
 
ROOT_ADDRESS = "http://www.onemanga.com" 
 
def series_exist?(series, address)
  # Search for existing series
  page = @agent.get(address)
  search_form = page.forms.first
  search_form.series_name = series
  search_result = @agent.submit(search_form)
  regex_series = Regexp.new(series) 
  
  search_result.links.each do |link|
    if link.text =~ regex_series
      return true
    end
  end
  return false  
end
 
if ARGV.nil?
  puts "please enter Series name and Chapter you need to download."
  exit
end
 
series, chapter = ARGV[0], ARGV[1]
 
begin
  @agent = WWW::Mechanize.new
  puts "Searching for #{series} chapter #{chapter}"
  unless series_exist?(series, ROOT_ADDRESS) then
    puts "#{series} is not found in this site. Try again."
    exit
  end
 
  puts "Found #{series}!"
  puts "Go to first managa page of this chapter"
  address = ROOT_ADDRESS + '//' + series + '//' + chapter.to_s + '//'
   
  page = @agent.get(address)
  # Now we are at the summaries page before start reading the chapter.
  # Then, Click at link 'Begin reading /Series/ /Chapter/'
  manga_page = @agent.click( page.links.select { |link| link.text =~ /Begin reading/ }.first )
 
  # Calculate number of page in this chapter by verify 'page' drop-down box
  # Walkthrough HTML node using method 'search' which result the Nokogiri's object
  p_count = 0
  manga_page.search('select#id_page_select.page-select option').each do |ch|
    if ch.text =~ /\d{2}/ then p_count += 1 end
  end
  puts "Chapter##{chapter} has #{p_count} pages"
  puts "Starting scrub each page ..."
 
  1.upto(p_count) do |num|
    manga_page.search('img.manga-page').each do |img|
      puts img['src'] + " is saved" 
      filename = chapter.to_s + "_" + num.to_s + ".jpg"
      @agent.get(img['src']).save_as(filename)
    end
     
    next_page_address = ROOT_ADDRESS + manga_page.search('img.manga-page').first.parent['href']
    manga_page = @agent.get(next_page_address)
  end
 
  puts "DONE !!!\n"
 
rescue SocketError => e
  puts e.to_s
end

จะเห็นว่าโค้ดนี้ต้องการพารามิเตอร์ 2 ตัวด้วยกันซึ่งก็คือ "ชื่อเรื่อง" และ "ตอนที่" ซึ่งค่าทั้งสองจะถูกเก็บอยู่ในตัวแปรที่ชื่อ series และ chapter ตามลำดับ

จากโค้ดข้างตันคุณผู้อ่านจะได้เห็นตัวอย่างของการใช้เมธอดต่างๆ ของอ็อบเจกต์จากคลาส WWW::Mechanize ซึ่งตัวหลักๆที่ใช้ในโค้ดนี้ได้แก่

1) เมธอด get ซึ่งจะรับค่าเป็น url และจะคืนค่าออกมาเป็นอ็อบเจกต์ Page ซึ่งเก็บ content ของเว็บเพจตาม url ที่เราใส่ลงไปทั้งหมด อ็อบเจกต์ Page จึงเปรียบเสมือนหน้าเว็ปจริงๆ ที่เราสามารถที่จะดำเนินการใดๆกับมันต่อไปก็ได้เช่น แสดง link ทั้งหมดที่อยู่ในหน้านั้นด้วยเมธอด links หรือกรอกแบบฟอร์มและ submit เป็นต้น

2) เมธอด click ซึ่งใช้เปลี่ยนหน้าเว็บเพจไปยัง link ที่ต้องการ โดยพารามิเตอร์ของเมธอดนี้จะต้องเป็นอ็อบเจกต์ link นะครับไม่ใช่ url(ลองดูโค้ดบรรทัดที่ 43 อ็อบเจกต์ link นั้นได้มาจากการเรียกใช้เมธอด links ของอ็อบเจกต์ Page อีกที)

3) เมธอด search โดยเมธอดนี้จะทำการค้นหาโหนดที่เราต้องการในหน้าเว็บเพจนั้นๆ ซึ่งโหนดที่เป็นพารามิเตอร์ของเมธอดนี้นั้นเราสามารถใส่มาในรูปของ html node, css node หรือ xpath node ก็ได้ จากโค้ดจะพบค่าของโหนดที่ผมใส่เป็นพารามิเตอร์ให้กับเมธอด search ในบรรทัดที่ 48 นั้นคือ 'select#id_page_select.page-select option' ซึ่งมันคือค่าโหนดของ dropdown box ที่ชื่อ Page นั่นเอง ผมนำค่าโหนดนี้มาจากโปรแกรม firebug ครับ โดยการเข้าไปที่โหมด firebug แล้วใช้เม้าท์จิ้มไปที่ component ที่เราต้องการรู้โหนด ในที่นี้คือ dropdown box จากนั้น ค่าของโหนดจะถูกแสดงขึ้นที่หน้าต่างของโปรแกรม firebug ลองเล่นดูนะครับ... ค่าที่คืนค่าออกมาจากเมธอด search จะเป็นอ็อบเจกต์ของคลาส Nokogiri::XML::Node ครับ ซึ่งสามารถนำไปใช้ประโยชน์ต่อได้อีกมากมายเช่นหาค่าที่เก็บอยู่ใน node นั้นหรือค่าที่อยู่ใน node แม่ ของมัน ฯลฯ อันนี้จะค่อนข้างเวียนหัวนิดนึงต้องค่อยๆทำความเข้าใจไปนะครับ ผมก็ยังงงๆ อยู่เลย

ผลที่ได้หลังจากทดลองรันโค้ด scrub_onemanga.rb จะปรากฏดังรูปด้านล่างนี้ แล้วเราก็จะได้รูป scan ของตอนนั้นๆของการ์ตูนเรื่องที่เราค้นหาครับ



เป็นอย่างไรกันบ้างครับ
หวังว่าคุณผู้อ่านจะได้ไอเดียในการนำ Ruby Mechanize ไปใช้งานหรือต่อยอดกับโปรเจกของคุณได้บ้าง
ใครมีประสบการณ์หรือไอเดียเจ๋งที่อยากจะแชร์กันก็ลอง post เข้ามาพูดคุยกันดูนะครับ

 

2 ความคิดเห็น: