Recently, have had some scheduling tasks that need to execute on the website. This will cause high effort for this manually routine job.

For reducing the effort, I build an automation mechanism to replace the routine job, here will record the process for anyone who needs refer.

This article will use an example as a case to demonstrate how to build this automation process, and the following are this example of system processing:

Requirement

We join a website activity for getting the daily coin, and these tasks need manually access a website landing page, and log in to the OpenID with a Facebook login. After login, need to confirm the tasks button, if the button status is “Get coins”, will need to click and get coins, and if the status is “Check Info”, it means the daily tasks are already done.

Accuracy and precision are need execute the process at 04:00 am and should notify the final process to me.

Technical

For automation processing, here are some tech stacks I choose:

  • Python: design the full system process
  • Selenium: a tool for automation interaction with the website
  • Chromium: to be a web driver

Environment settings

First, need to install Python:

sudo yum install python3
sudo yum install python3-pip

Install Chromium and relative package:

sudo yum install epel-release
sudo yum install chromium

Install Selenium library

pip3 install selenium

Download Chromium WebDriver:

Selenium needs a compatible version with Chromium and WebDriver。Can download WebDriver and de-compress Download page: https://sites.google.com/chromium.org/driver/downloads?authuser=0

Make sure the version of WebDriver you download matches the version of Chromium you have installed.

For example:

wget https://chromedriver.storage.googleapis.com/114.0.5735.90/chromedriver_linux64.zip

uzip chromedriver_linux64.zip

Now, Selenium and Chromium have been successfully installed on CentOS and the path to WebDriver has been set. We can use the Selenium Python library to control the Chromium browser.

Non-graphical Linux environment

Because my Linux host is not a real graphical environment, it may be necessary to set a virtual display (Virtual Display) to simulate a graphical interface. We can use Xvfb (X virtual frame buffer) or other similar tools to create a virtual display. This way, the Chromium browser will be able to run even without an actual monitor.

Install Xvfb with the following command:

sudo yum install xorg-x11-server-Xvfb

Setting environment variable:

For Xvfb to run correctly, some environment variables need to be set. Execute the following command in the terminal:

export DISPLAY=:99

Please note that:99 is the display number of the virtual display, you can adjust this value according to your needs.

Execute Xvfb:

Xvfb :99 -screen 0 1280x1024x24 &

This will start Xvfb on virtual display:99 with a resolution set to 1280x1024 and a color depth of 24 bits.

Next, the next step is to install Google Chrome

sudo dnf install google-chrome-stable

If Error: GPG check FAILED error occurs, then bypass GPG (this will bypass GPG verification, but be aware that this reduces security.)

sudo dnf install google-chrome-stable --nogpgcheck

Execute the Python program:

Next, We can execute the Python program and use Selenium to control the Chromium browser. Before executing a Python program, make sure the DISPLAY environment variable is set to the correct virtual display.

DISPLAY=:99 python main.py

Example

Here is a Python example using the Selenium module to achieve the goal:

# -*- coding: utf-8 -*-
import pytest
import time
import json
import logging
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.common.exceptions import NoSuchElementException, TimeoutException

class TestGetgift():
  def setup_method(self, method):
    webdriver_path = '/home/chromedriver'
    self.driver = webdriver.Chrome(executable_path=webdriver_path)
    self.vars = {}

  def teardown_method(self, method):
    self.driver.quit()

  def test_getgift(self):
    self.driver.get("https://example.com")
    self.driver.set_window_size(1024, 768)

    wait = WebDriverWait(self.driver, 10)
    element = wait.until(expected_conditions.visibility_of_element_located((By.CSS_SELECTOR, 'img[alt="facebook"]')))
    self.driver.find_element(By.CSS_SELECTOR, 'img[alt="facebook"]').click()

    wait = WebDriverWait(self.driver, 10)
    element = wait.until(expected_conditions.visibility_of_element_located((By.ID, 'email')))
    self.driver.find_element(By.ID, "email").send_keys("YOUR EMAIL")
    self.driver.find_element(By.ID, "pass").send_keys("YOUR PASSWORD")
    self.driver.find_element(By.ID, "loginbutton").click()

    time.sleep(2)
    self.driver.get("https://example.com")

    wait = WebDriverWait(self.driver, 10)
    element = wait.until(expected_conditions.visibility_of_element_located((By.CSS_SELECTOR, '.mainPage_mainPage__container .swiper-slide-active')))

    elem = self.driver.find_element_by_css_selector('.mainPage_mainPage__container .swiper-slide-active')
    html = elem.get_attribute('outerHTML')
    print(html)

    try:
      wait = WebDriverWait(self.driver, 10)
      element = wait.until(expected_conditions.visibility_of_element_located((By.CSS_SELECTOR, '.mainPage_mainPage__container .swiper-slide-active .button_button')))
      self.driver.find_element(By.CSS_SELECTOR, ".mainPage_mainPage__container .swiper-slide-active .button_button").click()
      time.sleep(3)
      //send notification
    except TimeoutException:
      logging.info("already get coin")
      //send notification
    logging.info("done")
if __name__ == "__main__":
    test = TestGetgift()
    test.setup_method(None)
    test.test_getgift()
    test.teardown_method(None)

Finally, establish a schedule

0 4 * * * DISPLAY=:99 /usr/bin/python3 ~/main.py >> ~/cron.log 2>&1

Error Handling

If install the package has the error message

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)

It means system default is ASCII, and can solve this problem with defined the

export LANG=en_US.utf8
ex