Python3、selenium实现自动化会议室预订程序

背景描述:

    定时定点自动化预约指定日期的指定会议室,实现自动化会议室抢占预订功能。

技术栈:

python3 、 pyinstaller(exe打包工具)、 selenium、Html5等

实现思路:

公司OA办公系统使用SSO单点登录,所有相关子系统登录前需要先进行系统登录后, 才能跳转登录到需要的业务系统。

通过selenium+chromdriver实现用户行为模拟,定位相关界面元素后,输入指定人员信息后进行自动化相关操作(如点击登录按钮、点击提交按钮、选中复选框等)。

1、 分析界面DOM结构:

登录界面: 

由于公司会做每日健康统计,当日首次登录后,会在登录环节中通过iframe页面嵌入来进行相关信息统计,需要在登录成功后做一次判断处理,否则无法进行后续的逻辑处理(程序中通过webdriver.Chrome().switch_to.frame() 进行iframe界面切换处理):

分析dom界面过程与登录界面相似,这里不做赘述、详细处理请见后续代码实现。

登录环节完成后,程序跳转会议室选择界面:

 同样分析DOM获取界面的url访问链接,界面中部分dom元素有前后关联性,需要在自动填写界面信息时注意下。

提交申请单,完成申请。

项目结构: 

核心代码结构:

会议预订处理代码(order_meeting.py文件)

import io
import json
import sys

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.support.select import Select
import selenium.webdriver.support.ui as ui
from selenium.webdriver import DesiredCapabilities

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf8')
cap = DesiredCapabilities().CHROME
cap["marionette"] = False
option = webdriver.ChromeOptions()
# option.add_argument('headless')
driver = webdriver.Chrome(executable_path="./chromedriver.exe", options=option)

# sso登录界面,请自行设置
LOGIN_URL = "https://sso.xxx.com/login?service=http%3A%2F%2Fmeeting.xxx.com.cn%2Fmeeting%2F"


def crawl_url(submit_meeting_url,
              username, pwd,
              date, begin_hour, begin_minus, end_hour, end_minus,
              type, title, content,
              meeting_space, meeting_room_option):
    driver.get(LOGIN_URL)
    wait = ui.WebDriverWait(driver, 10)
    wait.until(lambda dr: dr.find_element_by_id('fm1').is_displayed())
    driver.find_element_by_id("username").send_keys(username)
    driver.find_element_by_id("password").send_keys(pwd)
    driver.find_element_by_xpath(".//*[@type='submit']").submit()
    # 处理每日健康报备界面
    try:
        driver.switch_to.frame('layui-layer-iframe1')
        ui.WebDriverWait(driver, 2).until(lambda dr: dr.find_element_by_id('hei').is_displayed())

        health_dom = driver.find_element_by_xpath('//span[@class="el-checkbox__input" and position()=1]')
        health_dom.click()

        continue_login_button = driver.find_element_by_xpath('//div[@class="mt-16 btnCheck"]/button')
        continue_login_button.click()
    except NoSuchElementException as e:
        print('NoSuchElementException: ' + e.msg)
        pass
    except Exception as e:
        print(e)
        pass

    # 提交会议室预约申请
    driver.get(submit_meeting_url)
    ui.WebDriverWait(driver, 30).until(lambda s: s.execute_script("return jQuery.active == 0"))
    wait.until(lambda dr: dr.find_element_by_id('root').is_displayed())  # 登录成功

    js = "var l = document.getElementById('layui-layer2'); l.style.display='None';" \
         "var s = document.getElementById('layui-layer-shade2'); s.style.display='None';"
    driver.execute_script(js)

    ui.WebDriverWait(driver, 10).until(lambda dr: dr.find_element_by_id('meeting-info-form').is_displayed())
    # 会议主题、会议内容、会议标题等内容设定
    driver.find_element_by_name("type").send_keys(type)
    driver.find_element_by_id("title").send_keys(title)
    driver.find_element_by_id("content").send_keys(content)

    # 会议日期设定
    wait.until(lambda dr: dr.find_element_by_id('startDate').is_displayed())
    driver.execute_script("document.getElementById('startDate').value='" + date + "'")

    # 会议时间设定
    meeting_time = driver.find_element_by_xpath('//span[@class="combodate" and position()=1]/select[position()=1]')
    Select(meeting_time).select_by_visible_text(str(begin_hour))
    meeting_time = driver.find_element_by_xpath('//span[@class="combodate" and position()=1]/select[position()=2]')
    Select(meeting_time).select_by_visible_text(str(begin_minus))

    meeting_time = driver.find_element_by_xpath('//span[@class="combodate" and position()=last()]/select[position()=1]')
    Select(meeting_time).select_by_visible_text(str(end_hour))
    meeting_time = driver.find_element_by_xpath('//span[@class="combodate" and position()=last()]/select[position()=2]')
    Select(meeting_time).select_by_visible_text(str(end_minus))

    # 会议地点选定
    driver.find_element_by_xpath('//div[@class="radio-box" and position()=' + str(meeting_space) + ']').click()
    # 会议地点会议室选定
    ui.WebDriverWait(driver, 30).until(lambda s: s.execute_script("return jQuery.active == 0"))
    meeting_room = driver.find_element_by_id("roomId")
    Select(meeting_room).select_by_visible_text(meeting_room_option)

    driver.execute_script("document.getElementById('submit-btn').click()")
    ui.WebDriverWait(driver, 30).until(lambda s: s.execute_script("return jQuery.active == 0"))
    driver.find_element_by_xpath('//a[@class="layui-layer-btn0"]').click()
    ui.WebDriverWait(driver, 30).until(lambda s: s.execute_script("return jQuery.active == 0"))
    driver.quit()


if __name__ == '__main__':
    # 读取配置文件处理逻辑
    with open("./config/app.json", encoding="utf-8") as json_file:
        config = json.load(json_file)
        crawl_url(config['meeting_url'],
                  config['username'], config['password'],
                  config['meeting_date'],
                  config['begin_hour'], config['begin_minus'], config['end_hour'], config['end_minus'],
                  config['meeting_type'], config['meeting_title'], config['meeting_content'],
                  config['meeting_space'], config['meeting_room_option'])

添加定时处理程序逻辑 schedule_runner.py,用于定点定时拉起程序处理:

#!/usr/bin/python
# -*- coding: utf-8 -*-
import json
import os
import datetime
import time
import psutil


# 运行exe文件
def run():
    kill()
    # os.chdir(r"C:\Users\Desktop\\")
    os.chdir(r"./")
    path = "会议预订.exe"
    print("运行 会议预订.exe 进程")
    os.system(path)


# 杀掉进程
def kill():
    pids = psutil.pids()
    for pid in pids:
        p = psutil.Process(pid)
        if p.name() == '会议预订.exe':
            print("杀死 会议预订.exe 进程")
            cmd = 'taskkill /F /IM 会议预订.exe'
            os.system(cmd)


def main(begin_date='2020-12-15', begin_hour=23, begin_minus=58, begin_seconds=40, task_num=24, request_internal=15,
         detect_internal=60):
    count = task_num
    while True:
        now = datetime.datetime.now()
        print(now)
        # 2020-12-15 23:58分开始准备频繁执行任务
        if now.date().strftime('%Y-%m-%d') == begin_date \
                and now.hour == begin_hour and now.minute >= begin_minus and now.second >= begin_seconds:
            while count >= 0:
                run()
                count -= 1
                # request_internal 秒后重新发起请求
                time.sleep(request_internal)
        # 每隔 1min 检测一次
        time.sleep(detect_internal)


if __name__ == '__main__':
    with open("./config/schedule.json") as json_file:
        config = json.load(json_file)
        main(config['begin_date'],
             config['begin_hour'],
             config['begin_minus'],
             config['begin_seconds'],
             config['task_num'],
             config['request_internal'],
             config['detect_internal'])

通过pyinstaller打包python程序为exe可执行程序,方便使用。打包的配置文件分别为:

# -*- mode: python -*-

block_cipher = None

a = Analysis(['../order_meetting.py'],
             pathex=['../Py-Job'],
             hiddenimports=[],
             hookspath=None,
             runtime_hooks=None)
pyz = PYZ(a.pure)
exe = EXE(pyz,
          a.scripts,
          a.binaries+[],
          a.zipfiles,
          a.datas,
          name='会议预订',
          debug=True,
          strip=None,
          upx=True,
          console=True)
# -*- mode: python -*-

block_cipher = None

a = Analysis(['../schedule_runner.py'],
             pathex=['../Py-Job'],
             hiddenimports=[],
             hookspath=None,
             runtime_hooks=None)
pyz = PYZ(a.pure)
exe = EXE(pyz,
          a.scripts,
          a.binaries+[],
          a.zipfiles,
          a.datas,
          name='定时检测程序',
          debug=True,
          strip=None,
          upx=True,
          console=True)

打包命令: pyinstaller -F ./XXX.spec

最终效果:

app.json配置文件 

schedule.json配置文件:

改进点: 

1、会议选择以及配置文件可以做一个配置界面进行处理,方便用户使用;

2、核心预订功能可以做并发处理,提高命中抢到会议室的成功率。

 

分享以上,欢迎三连,感谢!

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值