鱼C论坛

 找回密码
 立即注册
查看: 2350|回复: 2

[技术交流] Python学习笔记--每日健康打卡及出校报备

[复制链接]
发表于 2021-6-18 18:44:17 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 skyrimz 于 2021-6-18 19:17 编辑

本文首发于我的csdn博客,转发自己的文章应该没问题吧

前言

距离上次练习已经过去了大半年,现在终于有时间接着进行一些其他方面的练习了。上次的弹幕练习属于为爱发电,这回更注重实用性,在练习的同时顺便解决一下生活中的实际问题,一举两得~

这次主要有两个内容,分别是学校的每日健康打卡以及外出报备的自动化处理,通过seleniumrequest库两种不同的方法实现,省去了每次都要手动填表的麻烦。如果有时间,还会涉及到UI以及exe打包相关内容。

话不多说,开始!


一、通过selenium实现自动化外出报备


1.1 思路

报备出校流程为:登录-填表-提交-得到结果,其中表格部分内容为自动生成,其余通过selenium配合xpath即可完成。

虽然也可以用requests实现,但相对要麻烦很多(登录就要麻烦不少,headers的选择,密码也经过MD5处理过。。。


1.2 selenium相关


要使用selenium,必须配置好系统环境变量以及对应浏览器的webdriver,且版本要相匹配。

例如要使用Chrome浏览器则需要对应的driver。

需要注意的是,如果电脑上有多个Chrome核心的浏览器,在使用以下代码时可能会出现调用失败的情况:


  1. driver = webdriver.Chrome()
复制代码


此时指定webdriver所在路径即可解决:

  1. driver = webdriver.Chrome(r'X:\xxxxxxx\chromedriver.exe')
复制代码


正常运行时,浏览器页面会一直出现,便于观察代码执行过程,这在测试阶段是十分必要的。当程序测试完毕后,就不再需要监测过程,得到结果便可。此时可以使浏览器在后台静默运行,不再出现浏览器窗口,代码如下:

  1. option = webdriver.ChromeOptions()
  2. option.add_argument('headless')  # 设置静默执行
  3. option.add_argument("window-size=1920x1080")  # 最大化,便于截图
  4. driver = webdriver.Chrome(r'X:\xxxxxxx\chromedriver.exe', options=option)
复制代码


当使用静默模式运行时,最大化窗口不能使用
  1. driver.maximize_window()
复制代码
,而要用
  1. option.add_argument("window-size=Width x Height")
复制代码
,如1920x1080。


至此,driver相关设置完成。

1.3 xpath的获取

通过
  1. find_element_by_xpath()
复制代码
可以很方便的实现元素的定位,只需找出对应元素的xpath即可进行操作。通过浏览器查看网页源码即可获取目标元素的xpath:



                               
登录/注册后可看大图


得到xpath如下:


  1.     uid_xpath = './/input[@name="username"]'
  2.     pwd_xpath = './/input[@name="password"]'
  3.     button1_xpath = './/span[@class="password_arrows"]'
  4.     button2_xpath = './/a[@id="preview_start_button"]'
  5.     phonenum_xpath = './/input[@name="fieldSQlxdh"]'
  6.     reason_xpath = './/textarea[@name="fieldSQqjsy"]'
  7.     schedule_xpath = './/textarea[@name="fieldSQxcap"]'
  8.     button3_xpath = './/label[@for="V1_CTRL69"]'
  9.     timestart_xpath = './/input[@name="fieldQJqjkssj"]'
  10.     timeend_xpath = './/input[@name="fieldQJqjjssj"]'
  11.     dayend_xpath = './/input[@name="fieldQJqjrqTo"]'
  12.     button4_xpath = './/a[@class="command_button_content"]'
  13.     button5_xpath = './/button[@class="dialog_button default fr"]'
复制代码


以上为需要填入数据或选择类型以及提交按钮的xpath。

1.4 通过xpath操作相关元素

通过
  1. find_element_by_xpath()
复制代码
定位元素后,即可进行相关操作。本次主要涉及点击清空内容以及填写数据这三种操作,分别对应:



  1.     driver.find_element_by_xpath().click()
  2.     driver.find_element_by_xpath().clear()
  3.     driver.find_element_by_xpath().send_keys()
复制代码


需要进行填写的数据有:账号,密码,手机号,出校原因,出校安排,开始时间,结束时间以及结束日期,离校类型为点选。


                               
登录/注册后可看大图



                               
登录/注册后可看大图


需要注意的是,若操作速度过快可能会因为元素未加载完成无法定位而导致失败,因此需要进行延时处理。

最简单的办法是通过
  1. time.sleep()
复制代码
进行等待,每次执行语句前后强制等待,确保相应元素有足够的时间完成加载:



  1.     time.sleep(1)
  2.     driver.find_element_by_xpath(uid_xpath).clear()  #清空已存在内容
  3.     driver.find_element_by_xpath(uid_xpath).send_keys(username)  #传入用户名

  4.     time.sleep(1)
  5.     driver.find_element_by_xpath(pwd_xpath).clear()
  6.     driver.find_element_by_xpath(pwd_xpath).send_keys(pwd)  #传入密码

  7.     time.sleep(1)
  8.     driver.find_element_by_xpath(button1_xpath).click()  #登录

  9.     time.sleep(5)
  10.     driver.find_element_by_xpath(button2_xpath).click()  #开始办理

  11.     time.sleep(2)
  12.     driver.find_element_by_xpath(phonenum_xpath).clear()   #手机号
  13.     driver.find_element_by_xpath(phonenum_xpath).send_keys(phonenum)

  14.     time.sleep(1)
  15.     driver.find_element_by_xpath(reason_xpath).clear()  #原因
  16.     driver.find_element_by_xpath(reason_xpath).send_keys(reason)

  17.     time.sleep(1)
  18.     driver.find_element_by_xpath(schedule_xpath).clear()  #行程
  19.     driver.find_element_by_xpath(schedule_xpath).send_keys(schedule)

  20.     time.sleep(1)
  21.     driver.find_element_by_xpath(button3_xpath).click()  #离校类型

  22.     time.sleep(1)
  23.     driver.find_element_by_xpath(timestart_xpath).clear()  #起始时间
  24.     driver.find_element_by_xpath(timestart_xpath).send_keys(timestart)

  25.     time.sleep(1)
  26.     driver.find_element_by_xpath(timeend_xpath).clear()  #结束时间
  27.     driver.find_element_by_xpath(timeend_xpath).send_keys(timeend)

  28.     time.sleep(1)
  29.     driver.find_element_by_xpath(dayend_xpath).clear()  #结束日期
  30.     driver.find_element_by_xpath(dayend_xpath).send_keys(dayend)

  31.     time.sleep(1)
  32.     driver.find_element_by_xpath(button4_xpath).click()  #提交

  33.     time.sleep(1)
  34.     driver.find_element_by_xpath(button5_xpath).click()  #最终确定

  35.     time.sleep(1)
  36.     driver.find_element_by_xpath(button6_xpath).click()
复制代码


可以发现,这种方式缺点很明显

1. 重复代码过多,不够简洁。
2. 若设置较长等待时间会延长程序运行时间而降低效率,若等待时间过短则无法达到目的。

selenium的显式等待可以准确判断等待时间,既能达到确定元素的目的,也能缩短延时时间,提高效率,但其用法较为复杂,还需要导入相关的库:



  1. from selenium.webdriver.common.by import By
  2. from selenium.webdriver.support.ui import WebDriverWait
  3. from selenium.webdriver.support import expected_conditions as EC
复制代码


使用显式等待方式的同时,为精简代码,可以把不同元素及其对应的xpath存为dict,待填写数据存为list:

  1. xpath_dict = {
  2.         'uid_xpath': './/input[@name="username"]',  # 账号
  3.         'pwd_xpath': './/input[@name="password"]',  # 密码
  4.         'button1_xpath': './/span[@class="password_arrows"]',  # 登录按钮
  5.         'button2_xpath': './/a[@id="preview_start_button"]',  # 开始办理
  6.         'phone_num_xpath': './/input[@name="fieldSQlxdh"]',  # 电话号码
  7.         'reason_xpath': './/textarea[@name="fieldSQqjsy"]',  # 出校事由
  8.         'schedule_xpath': './/textarea[@name="fieldSQxcap"]',  # 出校日程
  9.         'button3_xpath': './/label[@for="V1_CTRL69"]',  # 离校类型
  10.         'time_start_xpath': './/input[@name="fieldQJqjkssj"]',  # 开始时间
  11.         'time_end_xpath': './/input[@name="fieldQJqjjssj"]',  # 结束时间
  12.         'day_end_xpath': './/input[@name="fieldQJqjrqTo"]',  # 结束日期
  13.         'button4_xpath': './/a[@class="command_button_content"]',  # 提交
  14.         'button5_xpath': './/button[@class="dialog_button default fr"]'  # 最终确定
  15.     }
  16.    
  17.         data_list = [
  18.         username,
  19.         pwd,
  20.         phone_num,
  21.         reason,
  22.         schedule,
  23.         time_start,
  24.         time_end,
  25.         day_end
  26.         ]
复制代码


此处利用for循环即可自动判断操作类型为点击或填写数据,并按顺序自动完成。


  1.     count = 0
  2.     for i in xpath_dict:
  3.         if 'button' in i:  # 判断操作类型
  4.             try:
  5.                 WebDriverWait(driver, 10).until(
  6.                     EC.presence_of_element_located((By.XPATH, xpath_dict[i]))
  7.                 )
  8.             finally:
  9.                 driver.find_element_by_xpath(xpath_dict[i]).click()

  10.         else:
  11.             try:
  12.                 WebDriverWait(driver, 10).until(
  13.                     EC.presence_of_element_located((By.XPATH, xpath_dict[i]))
  14.                 )
  15.             finally:
  16.                 driver.find_element_by_xpath(xpath_dict[i]).clear()
  17.                 driver.find_element_by_xpath(xpath_dict[i]).send_keys(data_list[count])
  18.                 count += 1
复制代码


填报完成后,可根据需要通过截图等方式留存并发送报备结果,或继续填加自动运行等功能。


二、通过requests库实现自动化健康打卡

2.1 思路

这个项目利用requests库来进行处理,之前的练习主要使用了get(),本次则主要涉及post(),具体流程与上文相同,区别在于不使用selenium模拟操作,直接进行数据的获取与提交。

还有一个不使用selenium的原因是,打卡需要获取定位,然而pc端总是无法定位,只能换用requests的方法

系统使用的是腾讯的api接口,通过抓包发现确实可以获取正确定位,但是系统却无法读取,原因未知。



                               
登录/注册后可看大图



                               
登录/注册后可看大图


通过多次抓包试验,最终确定整个打卡过程共存在三次post行为,分别为登录,请求日期以及提交,对应三个不同的url。其中登录和提交带有数据,请求日期为单纯的post。


2.2 requests.post()的使用

和get的用法差不多,以登陆系统为例,本次需要注意的有请求的headers以及data:

1. headers必须包含Content-Type。涉及到的系统的数据交换都是以json格式进行的,必须声明Content-Type,否则会返回错误的response。
2. data所传输的数据必须为json格式。一般post的数据有form data和request payload两种形式,前者一般可以直接使用,而后者需要进行格式的转换。此处需要用到json.dumps()将python对象转换为json格式。(顺便一提,这系统登录时提交的密码是明文的,都不处理下吗。。。


2.3 登录

登录时提交账号密码等数据即可:

  1. headers = {
  2.         'User-Agent': 'xxx',
  3.         'Content-Type': 'application/json'
  4.         }

  5.         data_origin = {user_account,
  6.                    user_password}

  7.         login_url = 'xxxxxxxx'

  8.         login_res = requests.post(login_url, headers=headers, data=json.dumps(data_origin))
  9.         login_res_json = login_res.json()
复制代码



                               
登录/注册后可看大图


通过返回的response可以判断登录是否成功,200为成功,其他则为不同情况的错误:


                               
登录/注册后可看大图


若不传入cookie,则会返回:{'code': 405, 'msg': '凭据已经过期,请重新认证', 'datas': ''}。这会导致请求日期和提交无法进行,因此需要在登陆后获取cookie并在接下来的post中传入。(尝试过单独使用session()而不传入cookie,无效,原因未知。


  1.     cookies = login_res.cookies  # 获取登陆后cookie
  2.     cookie = requests.utils.dict_from_cookiejar(cookies)  # 转换为可传入参数格式
复制代码


2.4 获取日期及打卡状态


                               
登录/注册后可看大图



                               
登录/注册后可看大图


可以看出,相关数据都储存在datas的hunch_list中,其中date1为打卡当天日期,state为打卡状态,1为已打卡,0为未打卡。


                               
登录/注册后可看大图


对日期进行请求:



  1.     date_res = requests.post(date_url, headers=headers, cookies=cookie)
  2.     date_res_json = date_res.json()
复制代码


将date1提取出来,提交时要用到:

  1. today = date_res_json['datas']['hunch_list'][0]['date1']
复制代码


接下来根据state的值判断是否继续打卡:


  1.     statue = 0  # 是否继续执行,0为继续
  2.     if date_res_json['datas']['hunch_list'][0]['state'] == 1:
  3.         statue += 1
  4.         print('今日已打卡!')
  5.     else:
  6.         print('今日未打卡,将自动打卡。。。')
复制代码


2.5 提交数据


                               
登录/注册后可看大图


提交时,数据主要包括两部分:date和punch_form。其中date的值就是之前提取过的date1,punch_form为需要进行填报的内容,包括所在地,联系方式,体温等。最后将这两部分数据打包提交即可:


                               
登录/注册后可看大图



  1.     if statue == 0:
  2.         res_submit = requests.post(submit_url, headers=headers, data=json.dumps(data_punch), cookies=cookie)
  3.         res_submit_json = res_submit.json()

  4.         if res_submit_json['code'] == 200:
  5.             print('打卡成功!')
  6.         else:
  7.             print('打卡失败!')
  8.             print('错误!:%s' % (res_submit_json['msg']))
复制代码


完工~















小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2021-6-18 19:09:38 | 显示全部楼层
欢迎交流
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-6-19 22:41:08 | 显示全部楼层
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-22 22:38

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表