前言

总算入行上班几个月了,不得不说业务是真的不消停啊。。

本人工作上经常遇到一种场景:为甲方做自动化接口处理工具,登录需要短信验证码,,

嘛算是摸索出了一套selenium代码模板,主要解决如下痛点

  • 会话超时/断开时,又要找甲方问短信等验证码登录
  • 调试途中增减修改功能,算是调试中热更新

分享一下

模板代码

app.py

#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import importlib
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import backend basepath = os.path.abspath(os.path.dirname(__file__))
driver_path = os.path.join(basepath, 'chromedriver.exe')
logger = backend.logger def init_browser(driver_path=None):
options = webdriver.ChromeOptions()
options.add_argument('--no-sandbox')
options.add_argument('--disable-gpu')
prefs = {
'profile.default_content_setting_values': {
'notifications': 2
}}
options.add_experimental_option('prefs', prefs)
options.add_experimental_option('excludeSwitches', ['enable-automation'])
options.add_experimental_option("useAutomationExtension", False)
browser = webdriver.Chrome(options=options, executable_path=driver_path)
browser.maximize_window()
browser.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
return browser def jump_security(wait, mouse):
wait.until(EC.presence_of_element_located((By.ID, 'details-button'))).click()
ele = wait.until(EC.presence_of_element_located((By.ID, 'proceed-link')))
mouse.move_to_element(ele).click().perform() def init_login(driver, wait, mouse):
username_inp = wait.until(EC.presence_of_element_located((By.ID, "username")))
username_inp.send_keys("user")
password_inp = driver.find_element_by_id("password")
password_inp.send_keys("password") class App(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls.error_num = 0
cls.driver = init_browser(driver_path)
cls.wait = WebDriverWait(cls.driver, 20)
cls.mouse = ActionChains(cls.driver)
cls.driver.get('https://www.target.com/login')
# jump_security(cls.wait, cls.mouse)
init_login(cls.driver, cls.wait, cls.mouse)
cls._instance = object.__new__(cls)
return cls._instance # 模式1:client无限循环
def run_unlimited():
while True:
try:
obj = App()
input('等待登录并进入目标页面后,回此处按回车 >>> ')
back = backend.Backend(obj)
results = back.main()
except Exception as e:
pass
finally:
mode = input('供backend修改的阻塞暂停')
importlib.reload(backend) # 模式2:构建本地api服务
from flask import Flask
app = Flask(__name__) @app.route("/", methods=["GET"])
def main():
importlib.reload(backend)
back = backend.Backend(App())
results = back.main() if __name__ == '__main__':
os.system('taskkill /im chromedriver.exe /F') # win专用,清残留进程
os.system('taskkill /im chrome.exe /F')
run_unlimited()
# app.run()

前端有两部分,一是单例的selenium,二是此自动化处理工具的形式:client循环形式 / api服务形式

  1. 单例的 _new_ 里init一些属性,处理登录那部分也可以放后台

  2. 两种形式其实就是看形式是要主动触发还是被动触发,至于具体做什么就放后台


backend.py

#!/usr/bin/python
# -*- coding: utf-8 -*-
import json
import os
import re
from concurrent.futures import ThreadPoolExecutor, as_completed
import requests
import simplejson
from loguru import logger
from retry import retry
from tqdm import tqdm, trange
import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) basepath = os.path.abspath('./')
logger.add(f'{basepath}/logs/{os.path.basename(__file__)[:-3]}.log',
format="{level} | {time:YYYY-MM-DD HH:mm:ss} | {function}:{line} - {message}",
level="INFO", retention='5 days') class Backend(object):
def __init__(self, obj):
self.sess = requests.session()
self.driver = obj.driver
self.sess.headers = {'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-Hans-CN, zh-Hans; q=0.5',
'Cache-Control': 'no-cache',
'Connection': 'Keep-Alive',
'Content-Length': '561',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': 'SESSION=abcdefg',
'Host': 'www.target.com',
'Referer': 'https://www.target.com/path',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko',
'X-Requested-With': 'XMLHttpRequest'
} def get_cookie(self):
self.driver.find_element_by_xpath('//input[@class="e.g:trigger btn"]').click()
cookies = {_["name"]: _["value"] for _ in self.driver.get_cookies()}
return cookies def get_headers(self):
cookies = self.get_cookie()
token = self.driver.execute_script('return window.sessionStorage.getItem("token")')
self.sess.headers.update({
'Authorization': token,
'Cookie': f'SESSION={cookies["SESSION"]}; acw_tc={cookies["acw_tc"]}'
}) @retry((json.decoder.JSONDecodeError, simplejson.errors.JSONDecodeError, KeyError, ValueError), tries=3, delay=1)
def do_api(self):
url = 'https://www.target.com/api/path'
payload = {
'params': '31b1xu0',
}
self.get_headers()
resp = self.sess.post(url, json=payload, verify=False, timeout=10)
if resp.status_code == 200:
self.pre_api_task(resp.json()) # do what you need todo
else:
raise ValueError(f'do_api failed:: {resp.text}') def do_selenium_command(self):
self.driver.execute_script("$('p[class=imgShow]').click()")
self.driver.execute_script("document.getElementsByClassName('supportRadioOptional1 checked')[0].click();")
pagenum = int(re.search(r'共 (\d+) 页', self.driver.page_source).group(1))
for _ in trange(pagenum, ncols=40):
self.pre_page_task() # do what you need todo
self.driver.execute_script(f"PaginationpageTable.gotoPage('next', '{_+2}', '50');") def main(self):
self.do_selenium_command()
self.do_api() if __name__ == '__main__':
requests.get('127.0.0.1:5000')

基于前面说的短信验证码,让甲方登录后selenium一顿操作就把api的headers补完了,可以愉快地请求接口了

需要js取参数的话可以这样写token = self.driver.execute_script('return window.sessionStorage.getItem("token")')

目前遇到的一些注意点:

  1. 渲染的页面带frame,需要switch_to再xpath等处理,可把driver.page_source写进文件判断是否该目标页顺带测定位
  2. 有时driver.find_element_by_*无法定位,试试用js;有些JS/Jquery功能在老版IE上用不了,回用mouse处理(套娃呢喂);连续使用js时要注意响应等待时间
  • basepath处用'./'取巧了一下(与pyinstaller打包有关),可以基于此变量做一些本地文件处理

Last

毕竟最终是为甲方做的,程序要以甲方设备为准 即使它是win7,用pywin32定位句柄出现兼容问题即使业务网站只兼容IE内核,js部分功能无法用头发掉光了啊

毕竟是个人摸索出的,可能有更优解,如大佬路过还请不要吝啬交(p)流(y)一下心得

根据业务摸索出的一个selenium代码模版(python)的更多相关文章

  1. 请写出一段JavaScript代码,要求页面有一个按钮,点击按钮弹出确认框。程序可以判断出用

    请写出一段JavaScript代码,要求页面有一个按钮,点击按钮弹出确认框.程序可以判断出用 户点击的是“确认”还是“取消”. 解答: <HTML> <HEAD> <TI ...

  2. [Selenium]通过Selenium实现在当前浏览器窗口点击一个图标之后,弹出另外一个窗口,关闭这个窗口,再回到原来的窗口进行操作

    public void clickReportIcon(){ String initialWindowHandle = driver.getWindowHandle(); //保存原始的浏览器窗口 p ...

  3. 如何用java写出无副作用的代码

    搞java的同学们可能对无副作用这个概念比较陌生,这是函数式编程中的一个概念,无副作用的意思就是: 一个函数(java里是方法)的多次调用中,只要输入参数的值相同,输出结果的值也必然相同,并且在这个函 ...

  4. fir.im Weekly - 如何写出零 bug 的代码

    神兽护体,代码无bug.经常看到代码注释的各种形状,这是一种程序员情怀.那么,如何能写出零 Bug 的代码呢,来看看@码农翻身 的这篇手册--零Bug的代码是怎么炼成的. 写零 Bug 一定少不了代码 ...

  5. 如何写出没有BUG的代码

    1947年9月9日,美国海军准将 Grace Hopper 在哈佛学院计算机实验室里使用 Mark II 和 Mark III 计算机进行研究工作.她的团队跟踪到 Mark II 上的一个错误,操作人 ...

  6. python 全栈开发,Day50(Javascript简介,第一个JavaScript代码,数据类型,运算符,数据类型转换,流程控制,百度换肤,显示隐藏)

    一.Javascript简介 Web前端有三层: HTML:从语义的角度,描述页面结构 CSS:从审美的角度,描述样式(美化页面) JavaScript:从交互的角度,描述行为(提升用户体验) Jav ...

  7. 如何写出健壮的Java代码

    近来在公司写代码,写出的代码发现BUG很多,为了实现一个功能,代码改了又改,影响了工单的效率,也影响个人绩效,因此从网上找了些关于写健壮代码的文章看了看,再加上自己的一些经验总结. 所谓健壮的代码是指 ...

  8. (转)Python新手写出漂亮的爬虫代码2——从json获取信息

    https://blog.csdn.net/weixin_36604953/article/details/78592943 Python新手写出漂亮的爬虫代码2——从json获取信息好久没有写关于爬 ...

  9. (转)Python新手写出漂亮的爬虫代码1——从html获取信息

    https://blog.csdn.net/weixin_36604953/article/details/78156605 Python新手写出漂亮的爬虫代码1初到大数据学习圈子的同学可能对爬虫都有 ...

随机推荐

  1. 【WHash】更有空间感的感知哈希

    转载请注明出处 背景 在重复图识别领域,对于识别肉眼相同图片,感知哈希效果是很鲁棒的.上一篇文章 [PHash]更懂人眼的感知哈希 介绍的PHash识别效果很好,但是它有一个缺点,只关注低频信息,并没 ...

  2. Docker部署Mysql8.0.20并配置主从复制

    1. Linux安装Mysql8.0.20并配置主从复制(一主一从,双主双从)   Linux安装Mysql8.0.20并配置主从复制(一主一从,双主双从) 2. 前提准备 # 创建主从数据库文件夹 ...

  3. Nginx(三):反向代理,负载均衡

    环境准备   配置反向代理,负载均衡,动静分离需要的必备环境,JDK,2个tomcat开启8080和8081端口. 安装jdk [root@localhost ~]# rpm -qa|grep jav ...

  4. db2常用操作

    1. db2建立远程节点编目及删除 db2 catalog tcpip node nodeName remote remoteIp server remotePort db2 list node di ...

  5. centos 7 yum 安装 python3

    sudo yum install epel-release sudo yum install python34参考:http://stackoverflow.com/questions/8087184 ...

  6. 在IDEA中使用JDBC获取数据库连接时的报错及解决办法

    在IDEA中使用JDBC获取数据库连接时,有时会报错Sat Dec 19 19:32:18 CST 2020 WARN: Establishing SSL connection without ser ...

  7. 嵌入式开发笔记——调试组件SEGGER_HardFaultHandle

    一.前言 在使用Cortex-M内核的MCU进行开发时,有时候会因为对内存错误访问等原因造成程序产生异常从而进入HardFaultHandler错误中断.如果程序结构比较复杂,尤其是运行了RTOS时可 ...

  8. 卷积涨点论文 | Asymmetric Convolution ACNet | ICCV | 2019

    文章原创来自作者的微信公众号:[机器学习炼丹术].交流群氛围超好,我希望可以建议一个:当一个人遇到问题的时候,有这样一个平台可以快速讨论并解答,目前已经1群已经满员啦,2群欢迎你的到来哦.加入群唯一的 ...

  9. 海选与包装,Python中常用的两个高阶函数(讲义)

    一.filter(function, iterable) - 过滤("海选") # 判断落在第一象限的点[(x1, y1), (x2, y2)...] points = [(-1, ...

  10. 分布式文件系统之 FastDFS

    FastDFS 百度百科 FastDFS 是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储.文件同步.文件访问(文件上传.文件下载)等,解决了大容量存储和负载均衡的问题.特别适合 ...