0

Web Scraping trong Ruby với Watir

Watir là một thư viện mã nguồn mở sử dụng cho việc automating test. Watir tương tác với trình duyệt giống như cách con người làm: nhấp vào liên kết, điền vào các biểu mẫu và xác nhận văn bản. Nhưng không chỉ vậy, tôi sẽ xây dựng một web scraper mô phỏng việc vào một trang để đăng nhập, comment, tải dữ liệu và nhiều thứ khác.

Chức năng chính của Watir là nó sử dụng Selenium web-driver, có nghĩa là chúng ta có thể tạo một trang web động với JavaScript. Một gem tương tự là Mechanize - phù hợp cho các trang tĩnh và không sử dụng nhiều JavaScript hoặc AJAX.

Modes

Có thể chạy Watir trong trình duyệt bình thường (Chrome/Firefox) hoặc headless.

Chế độ headless cho phép phân tích một trang không có giao diện - trong hầu hết các hệ thống UNIX, Watir yêu cầu cài đặt sẵn Xvfb (trên Ubuntu). Watir sử dụng PhantomJS để mô phỏng trình duyệt web và chạy một trang trong trình mô phỏng. Nếu muốn phân tích trang bằng Chrome, cần cài đặt chrome-driver. Ngoài ra, Watir còn cho phép chạy một trang như iPhone, iPad hoặc các thiết bị di động khác.

Tôi sẽ xây dựng một gem đơn giản, cho phép đăng ký, đăng nhập, mời bạn bè hoặc like trang trên Facebook.

Source code

Nào cùng bắt đầu.

def initialize(email, password)
  @email = email
  @password = password
end

Đơn giản là chỉ định email và mật khẩu.

def browser
  @_browser ||= Watir::Browser.new(:chrome)
end

Chỉ định trình duyệt chạy là Chrome. Nếu bỏ qua phantomjs ở đây, nó sẽ chạy chế độ headless.

def login
  return true if @logged_in

  browser.goto('https://www.facebook.com/')
  form = browser.form(id: 'login_form')

  return false unless form.exist?

  form.text_field(name: 'email').set(email)
  form.text_field(name: 'pass').set(password)
  form.input(value: 'Log In').click

  sleep(2)
  @logged_in = main_page?
end

Phương thức đăng nhập vào Facebook với các thông tin cần thiết.

  • Sử dụng goto để redirect từ trang hiện tại sang trang chủ Facebook.
  • form - tìm kiếm form đăng nhập của Facebook với id: login_form.

Chú ý ở đây, nếu tìm một phần tử không tồn tại thì script trên sẽ đợi (mặc định 30s) sau đó sẽ trả về false. Nên tốt nhất là bạn kiểm tra xem phần tử đó có thực sự có trên trang web đích không.

  • text_field tìm field input và dùng phương thức set truyền params vào field.
  • dễ thấy phương thức click dùng để nhấp chuột vào một phần tử.

Tại sao phải chạy phương thức sleep trong 2s? - là để chờ cho tất cả assetjavascript được tải xong.

def main_page?
  browser.element(id: 'userNavigationLabel').exist?
end

Phương thức main_page? kiểm tra nếu thanh điều hướng tồn tại nghĩa là đã đăng nhập thành công.

def registration_params_valid?(params)
  return false unless params.keys.uniq.sort == REGISTRATION_INPUTS.uniq.sort
  return false if params.values.map(&:blank?).include?(true)
  return false if EMAIL_REGEX.match(params[:email]).nil?

  true
end

registration_params_valid? kiểm tra tất cả các trường đăng ký đều đã được điền và hợp lệ.

def create_account(**args)
  raise unless registration_params_valid?(args)

  browser.goto('https://www.facebook.com/')
  form = browser.form(id: 'reg')
  form.text_field(name: 'firstname').set(args[:first_name])
  form.text_field(name: 'lastname').set(args[:last_name])
  form.text_field(name: 'reg_email__').set(email)
  form.text_field(name: 'reg_email_confirmation__').set(email)
  form.text_field(name: 'reg_passwd__').set(password)
  form.select_list(name: 'birthday_day').select(args[:day])
  form.select_list(name: 'birthday_month').select(args[:month])
  form.select_list(name: 'birthday_year').select(args[:year])
  form.radio(name: 'sex', value: sex(args[:sex])).set
  form.button(name: 'websubmit').click
end

Phương thức create_account dùng để đăng ký trên Facebook. Check registration_params_valid? trước khi đến main page Facebook và tiến hành đăng ký.

def sex(value)
  value.downcase.strip == 'male' ? '2' : '1'
end

Phương thức này nhận vào tham số và trả ra giá trị hợp lệ.

def search(query)
  login unless logged_in

  form = browser.form(action: '/search/web/direct_search.php')
  form.inputs.last.to_subtype.clear
  sleep(0.5)
  form.inputs.last.to_subtype.set(query)
  form.button(type: 'submit').click
end

Phương thức này tìm đến field input tìm kiếm của trang sau khi kiểm tra đã được đăng nhập. Nếu không, thực hiện đăng nhập trước. Đôi khi watir đã click quá nhanh và không phải các phần tử đều đã được nhập, sử dụng sleep để chờ.

def perform(query, options = {})
  login unless logged_in

  search(query)
  browser.link(href: "/search/#{options[:name]}/?q=#{query}&ref=top_filter").click
  button = browser.button(class_name: options[:class_name])
  button.click if button.exist?
end

Phương thức này dùng để mời một người bạn, like page, vv... Cần truyền một truy vấn, class của nút cần click và tên tab. Nhớ rằng, nút đầu tiên tìm thấy sẽ được click.

def like_page(name)
  perform(name, name: 'pages', class_name: 'PageLikeButton')
end
def invite_friend(name)
  perform(name, name: 'people', class_name: 'FriendRequestAdd')
end

Để sử dụng phương thức perform, chỉ cần truyền truy vấn và click vào nút bên phải rồi chuyển sang đúng tab.

Testing

Các phương thức cơ bản đã đầy đủ. Bạn có thể download tại đây.

Chỉ cần clone, chạy bundle install và thưởng thức:

$ bundle console
$ scraper = NopioScraper::Facebook.new(‘your_email’',’your password’)
$ scraper.like_page('nopio')

Tất nhiên, ví dụ này thực sự đơn giản. Chẳng may trình duyệt bật popup hay cảnh báo thì bạn phải sửa lại code của tôi 😃. Mỗi trình duyệt hoạt động một cách khác nhau và thật khó để dự đoán được mong muốn của bạn.

Như đã thấy, web scrapingsimulating là không giới hạn. Bạn có thể viết một mã mà có thể làm hầu như tất cả mọi thứ.

Tìm hiểu thêm tại đây:

Happy web scraping!

Nguồn: https://www.nopio.com/blog/web-scraping-with-watir/


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í