2020-12-28 11:44:59 +08:00

363 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding:utf-8 -*-
"""
@Author : g1879
@Contact : g1879@qq.com
@File : driver_page.py
"""
from os import popen
from pathlib import Path
from pprint import pprint
from re import search as RE_SEARCH
from typing import Union
from selenium import webdriver
from DrissionPage.config import OptionsManager, DriverOptions
from DrissionPage.drission import Drission
from DrissionPage.session_page import SessionPage
from .common import unzip
def show_settings(ini_path: str = None) -> None:
"""打印ini文件内容"""
om = OptionsManager(ini_path)
print('paths:')
pprint(om.get_option('paths'))
print('\nchrome options:')
pprint(om.get_option('chrome_options'))
print('\nsession options:')
pprint(om.get_option('session_options'))
def set_paths(driver_path: str = None,
chrome_path: str = None,
debugger_address: str = None,
tmp_path: str = None,
download_path: str = None,
user_data_path: str = None,
cache_path: str = None,
ini_path: str = None,
check_version: bool = True) -> None:
"""快捷的路径设置函数 \n
:param driver_path: chromedriver.exe路径
:param chrome_path: chrome.exe路径
:param debugger_address: 调试浏览器地址127.0.0.1:9222
:param download_path: 下载文件路径
:param tmp_path: 临时文件夹路径
:param user_data_path: 用户数据路径
:param cache_path: 缓存路径
:param ini_path: 要修改的ini文件路径
:param check_version: 是否检查chromedriver和chrome是否匹配
:return: None
"""
om = OptionsManager(ini_path)
def format_path(path: str) -> str:
return '' if not path else str(path).replace('/', '\\')
if driver_path is not None:
om.set_item('paths', 'chromedriver_path', format_path(driver_path))
if chrome_path is not None:
om.set_item('chrome_options', 'binary_location', format_path(chrome_path))
if debugger_address is not None:
om.set_item('chrome_options', 'debugger_address', format_path(debugger_address))
if tmp_path is not None:
om.set_item('paths', 'tmp_path', format_path(tmp_path))
if download_path is not None:
experimental_options = om.get_value('chrome_options', 'experimental_options')
experimental_options['prefs']['download.default_directory'] = format_path(download_path)
om.set_item('chrome_options', 'experimental_options', experimental_options)
om.save()
if user_data_path is not None:
set_argument('--user-data-dir', format_path(user_data_path), ini_path)
if cache_path is not None:
set_argument('--disk-cache-dir', format_path(cache_path), ini_path)
if check_version:
check_driver_version(format_path(driver_path), format_path(chrome_path))
def set_argument(arg: str, value: Union[bool, str], ini_path: str = None) -> None:
"""设置浏览器配置argument属性 \n
:param arg: 属性名
:param value: 属性值有值的属性传入值没有的传入bool
:param ini_path: 要修改的ini文件路径
:return: None
"""
do = DriverOptions(ini_path=ini_path)
do.remove_argument(arg)
if value:
arg_str = arg if isinstance(value, bool) else f'{arg}={value}'
do.add_argument(arg_str)
do.save()
def set_headless(on_off: bool = True, ini_path: str = None) -> None:
"""设置是否隐藏浏览器界面 \n
:param on_off: 开或关
:param ini_path: 要修改的ini文件路径
:return: None
"""
on_off = True if on_off else False
set_argument('--headless', on_off, ini_path)
def set_no_imgs(on_off: bool = True, ini_path: str = None) -> None:
"""设置是否禁止加载图片 \n
:param on_off: 开或关
:param ini_path: 要修改的ini文件路径
:return: None
"""
on_off = True if on_off else False
set_argument('--blink-settings=imagesEnabled=false', on_off, ini_path)
def set_no_js(on_off: bool = True, ini_path: str = None) -> None:
"""设置是否禁用js \n
:param on_off: 开或关
:param ini_path: 要修改的ini文件路径
:return: None
"""
on_off = True if on_off else False
set_argument('--disable-javascript', on_off, ini_path)
def set_mute(on_off: bool = True, ini_path: str = None) -> None:
"""设置是否静音 \n
:param on_off: 开或关
:param ini_path: 要修改的ini文件路径
:return: None
"""
on_off = True if on_off else False
set_argument('--mute-audio', on_off, ini_path)
def set_user_agent(user_agent: str, ini_path: str = None) -> None:
"""设置user agent \n
:param user_agent: user agent文本
:param ini_path: 要修改的ini文件路径
:return: None
"""
set_argument('user-agent', user_agent, ini_path)
def set_proxy(proxy: str, ini_path: str = None) -> None:
"""设置代理 \n
:param proxy: 代理网址和端口
:param ini_path: 要修改的ini文件路径
:return: None
"""
set_argument('--proxy-server', proxy, ini_path)
def check_driver_version(driver_path: str = None, chrome_path: str = None) -> bool:
"""检查传入的chrome和chromedriver是否匹配 \n
:param driver_path: chromedriver.exe路径
:param chrome_path: chrome.exe路径
:return: 是否匹配
"""
print('正在检测可用性...')
om = OptionsManager()
driver_path = driver_path or om.get_value('paths', 'chromedriver_path') or 'chromedriver'
chrome_path = str(chrome_path or om.get_value('chrome_options', 'binary_location'))
do = DriverOptions(read_file=False)
do.add_argument('--headless')
if chrome_path:
do.binary_location = chrome_path
try:
driver = webdriver.Chrome(driver_path, options=do)
driver.quit()
print('版本匹配,可正常使用。')
return True
except Exception as e:
print(f'出现异常:\n{e}\n可执行easy_set.get_match_driver()自动下载匹配的版本。\n'
f'或自行从以下网址下载https://chromedriver.chromium.org/downloads')
return False
# -------------------------自动识别chrome版本号并下载对应driver------------------------
def get_match_driver(ini_path: Union[str, None] = 'default',
save_path: str = None,
chrome_path: str = None,
show_msg: bool = True,
check_version: bool = True) -> Union[str, None]:
"""自动识别chrome版本并下载匹配的driver \n
:param ini_path: 要读取和修改的ini文件路径
:param save_path: chromedriver保存路径
:param chrome_path: 指定chrome.exe位置
:param show_msg: 是否打印信息
:param check_version: 是否检查版本匹配
:return: None
"""
save_path = save_path or str(Path(__file__).parent)
chrome_path = chrome_path or _get_chrome_path(ini_path, show_msg)
chrome_path = Path(chrome_path).absolute() if chrome_path else None
if show_msg:
print('chrome.exe路径', chrome_path, '\n')
ver = _get_chrome_version(chrome_path)
if show_msg:
print('version', ver, '\n')
zip_path = _download_driver(ver, save_path, show_msg=show_msg)
if not zip_path and show_msg:
print('没有找到对应版本的driver。')
try:
driver_path = unzip(zip_path, save_path)[0]
except TypeError:
driver_path = None
if show_msg:
print('\n解压路径', driver_path, '\n')
if driver_path:
Path(zip_path).unlink()
if ini_path:
set_paths(driver_path=driver_path, chrome_path=str(chrome_path), ini_path=ini_path, check_version=False)
if check_version:
if not check_driver_version(driver_path, chrome_path) and show_msg:
print('获取失败,请手动配置。')
else:
if show_msg:
print('获取失败,请手动配置。')
return driver_path
def _get_chrome_path(ini_path: str = None, show_msg: bool = True) -> Union[str, None]:
"""从ini文件或系统变量中获取chrome.exe的路径 \n
:param ini_path: ini文件路径
:return: chrome.exe路径
"""
# -----------从ini文件中获取--------------
if ini_path:
try:
path = OptionsManager(ini_path).chrome_options['binary_location']
except KeyError:
path = None
else:
path = None
if path and Path(path).is_file():
print('ini文件中', end='')
return str(path)
# -----------从注册表中获取--------------
import winreg
try:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe',
reserved=0, access=winreg.KEY_READ)
k = winreg.EnumValue(key, 0)
winreg.CloseKey(key)
if show_msg:
print('注册表中', end='')
return k[1]
except FileNotFoundError:
pass
# -----------从系统路径中获取--------------
paths = popen('set path').read().lower()
r = RE_SEARCH(r'[^;]*chrome[^;]*', paths)
if r:
path = Path(r.group(0)) if 'chrome.exe' in r.group(0) else Path(r.group(0)) / 'chrome.exe'
if path.exists():
if show_msg:
print('系统中', end='')
return str(path)
paths = paths.split(';')
for path in paths:
path = Path(path) / 'chrome.exe'
try:
if path.exists():
if show_msg:
print('系统变量中', end='')
return str(path)
except OSError:
pass
def _get_chrome_version(path: str) -> Union[str, None]:
"""根据文件路径获取版本号 \n
:param path: chrome.exe文件路径
:return: 版本号
"""
if not path:
return
path = str(path).replace('\\', '\\\\')
try:
return (popen(f'wmic datafile where "name=\'{path}\'" get version').read()
.lower().split('\n')[2].replace(' ', ''))
except:
return None
def _download_driver(version: str, save_path: str = None, show_msg: bool = True) -> Union[str, None]:
"""根据传入的版本号到镜像网站查找,下载最相近的 \n
:param version: 本地版本号
:return: 保存地址
"""
if not version:
return
page = SessionPage(Drission().session)
page.get('http://npm.taobao.org/mirrors/chromedriver')
remote_ver = None
loc_main = version.split('.')[0]
try:
loc_num = int(version.replace('.', ''))
except ValueError:
return None
for i in page.eles('xpath://pre/a'):
remote_main = i.text.split('.')[0]
try:
remote_num = int(i.text.replace('.', '').replace('/', ''))
except ValueError:
continue
if remote_main == loc_main and remote_num >= loc_num:
remote_ver = i.text
break
if remote_ver:
url = f'https://cdn.npm.taobao.org/dist/chromedriver/{remote_ver}chromedriver_win32.zip'
save_path = save_path or Path(__file__).parent
result = page.download(url, save_path, file_exists='overwrite', show_msg=show_msg)
if result[0]:
return result[1]
return None