1. 需求
win10x86环境集成selenium+chrome+chromeDriver打包输出exe
2. 环境初始化
# 相关版本
python=3.7.8
pyinstaller=5.5
selenium=4.5.0
selenium-wire=5.0.0
ps:
selenium-wire可选不用安装-selenium扩展库(用于监听浏览器请求响应方便输出结果=请求头、响应头等)
selenium也可通过日志方式获取到相关请求信息
# 安装
pip install pyinstaller
pip install selenium
pip install selenium-wire
# 加速源(可选) -i https://pypi.tuna.tsinghua.edu.cn/simple
3. 相关代码
import sys
# from seleniumwire import webdriver as wb
from selenium import webdriver as wb
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
def get_login_info():
"""
登录获取后续登录界面所需授权信息
:return:
"""
# 1. 浏览器参数初始化
chrome_driver = r"resources/chromedriver_v106.exe" # 浏览器驱动
chrome_app = r"resources/chrome.exe" # 浏览器
chrome_driver = ExeUtils.get_resources(chrome_driver)
chrome_app = ExeUtils.get_resources(chrome_app)
capabilities = DesiredCapabilities.CHROME # 用于监听selenium请求事件(日志信息=请求+响应)
capabilities["goog:loggingPrefs"] = {"performance": "ALL"}
s = Service(chrome_driver)
chrome_options = wb.ChromeOptions()
chrome_options.add_experimental_option("excludeSwitches", ['enable-automation']) # 反检测
chrome_options.add_argument("--no-sandbox") # 最高权限运行
chrome_options.add_argument("--headless") # 无页面运行
chrome_options.add_argument("--disable-gpu") # 禁用gpu
chrome_options.add_argument("disable-cache") # 禁用缓存
chrome_options.add_argument("--disable-extensions") # 禁用扩展插件
chrome_options.binary_location = chrome_app # 添加浏览器
# chrome_options.add_argument("--proxy-server=http://ip:port") # 设置代理
# chrome_options.add_argument("--start-maxmized") # 窗口最大化
b = wb.Chrome(service=s, options=chrome_options,desired_capabilities=capabilities)
try:
login_url = "xxxx"
b.get(login_url)
# 2. 【登录页面】
# 2.1 账号密码
b.find_element(by=By.XPATH, value='//*[@id="username"]').send_keys(username) # xpath方式定位元素标签后输入内容
b.find_element(by=By.XPATH, value='//*[@id="password"]').send_keys(password)
# 2.2 省份
province_element = b.find_element(by=By.XPATH, value='//*[@id="dk_container_system"]/a')
province_element.click() # 点击事件
time.sleep(1)
selected_province = b.find_element(by=By.XPATH, value='//*[@id="dk_container_system"]/div/ul/li[16]')
selected_province.click()
# print("selectedProvince:", province_element.text)
# 2.3 点击登录
b.find_element(by=By.XPATH, value='//*[@id="passwordBox"]/section[6]/input[4]').click()
# print("loginSuccess")
self.logger.info("SeleniumLoginSuccess")
time.sleep(15)
# 3. 【首页】-302多次跳转后的页面
# 3.1 切换到最后跳转的页面
page_num = b.window_handles # 当前页
b.switch_to.window(page_num[-1]) # 切换到新窗口
b.refresh() # 刷新页面->屏蔽弹出的公告栏
time.sleep(5)
b.get_screenshot_as_file(f"{screen_shot}/index.png")
time.sleep(5)
# 3.2 【我的数据列表】-页面局部刷新跳转到工单数据列表页
b.switch_to.frame("iframe_home") # iframe-xpath定位
b.find_element(by=By.XPATH, value='//*[@id="CLAIM"]/div[1]/div[2]/span/span/button').click()
b.switch_to.default_content() # 跳转到之前页面
time.sleep(5)
b.get_screenshot_as_file(f"{screen_shot}/claimList.png") # 保存截屏图片
time.sleep(5)
# 3.3 获取用户登录授权信息-Cookies&CSRF
# 3.3.1 使用selenium事件监听-日志巡检获取
logs = b.get_log("performance")
for entry in logs:
# "Network.request":请求信息;"Network.response":响应信息;
log = json.loads(entry["message"])["message"]
if "Network.request" not in log["method"]:
continue
if not log.get("xx"):
continue
tmp_headers = log.get("xx").get("headers")
if not tmp_headers:
continue
if "Cookies" not in tmp_headers or "CSRF" not in tmp_headers:
continue
cookie_val = tmp_headers.get("Cookies")
csrf_val = tmp_headers.get("CSRF")
if "SESSIONID" not in cookie_val and "oneapm" not in cookie_val and not csrf_val:
continue
headers = {"Cookies": cookie_val, "CSRF": csrf_val}
break
# 3.3.2 使用selenium-wire获取所需授权信息(wb导入对象也需要更换)
# for request in b.requests:
# if request.response:
# if request.url == "xxxx":
# headers["CSRF"] = request.headers.get("CSRF")
# headers["Cookies"] = request.headers.get("Cookies")
# break
except Exception as e:
self.logger.error(f"getLoginInfoError:{e}", exc_info=True)
self.login_info = headers
self.logger.info(f"getLoginInfo:{login_info}")
b.close()
class ExeUtils:
"""exe生成相关工具类"""
@staticmethod
def get_resources(path):
"""
获取实际的资源访问路径(本地||临时)
根据打包生成的临时目录访问资源
或者直接运行脚本获取本地访问资源
:param path:
:return:
"""
if getattr(sys, 'frozen', False):
base_path = sys._MEIPASS
else:
base_path = os.path.abspath(".")
return os.path.join(base_path, path)
4. 生成exe
# 目录结构
----spider # 项目目录
----spider/resources # 资源目录
----spider/resources/chrome.exe # 浏览器(需要把浏览器chrome.exe同级目录的所有文件均包含而不只是exe)
----spider/resources/chromedriver.exe # 浏览器驱动
----spider/spider.py # 程序入口文件
# 命令
# 切换到虚拟环境(项目运行环境)
cd spider
pyinstaller -Fw --add-data="resources;resources" --clean spider.py
-F:单文件模式;资源整合到exe
-w:无命令行窗口
--add-data: 资源文件
--clean: 清楚缓存
-n: exe程序名
--add-binary: 可执行程序资源(单独打包整合其他exe)
# 也可以通过先生成*.spec文件后生成exe
pyi-makespec -F spider.py
修改*.spec
pyinstaller -F *.spec
# 相关输出
"""
...
121277 INFO: checking EXE
121278 INFO: Building EXE because EXE-00.toc is non existent
121278 INFO: Building EXE from EXE-00.toc
121278 INFO: Copying bootloader EXE to F:\work\workProject\spider\dist\spider.exe.notanexecutable
121298 INFO: Copying icon to EXE
121300 INFO: Copying icons from ['e:\\study\\python\\python_env\\env_spider\\lib\\site-packages\\PyInstaller\\bootloader\\images\\icon-windowe
d.ico']
121301 INFO: Writing RT_GROUP_ICON 0 resource with 104 bytes
121301 INFO: Writing RT_ICON 1 resource with 3752 bytes
121302 INFO: Writing RT_ICON 2 resource with 2216 bytes
121302 INFO: Writing RT_ICON 3 resource with 1384 bytes
121303 INFO: Writing RT_ICON 4 resource with 38188 bytes
121315 INFO: Embedding manifest in EXE
121316 INFO: Updating manifest in F:\work\workProject\spider\dist\spider.exe.notanexecutable
121317 INFO: Updating resource type 24 name 1 language 0
121328 INFO: Appending PKG archive to EXE
121617 INFO: Fixing EXE headers
124514 INFO: Building EXE from EXE-00.toc completed successfully.
"""
5. 其他问题
# selenium弹窗问题
from selenium.webdriver.common.service import xxx # 通过导包进到service.py修改源码
def start(self):
"""
Starts the Service.
:Exceptions:
- WebDriverException : Raised either when it can't start the service
or when it can't connect to the service
"""
try:
cmd = [self.path]
cmd.extend(self.command_line_args())
self.process = subprocess.Popen(cmd, env=self.env,
close_fds=system() != 'Windows',
stdout=self.log_file,
stderr=self.log_file,
stdin=PIPE,
# 打包成exe修改了源码creationflags=134217728
# creationflags=self.creationflags,
creationflags=134217728,
)
except TypeError:
raise
6. 相关文档