gomyck-tools 1.2.12__py3-none-any.whl → 1.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gomyck_tools-1.3.1.dist-info/METADATA +66 -0
- gomyck_tools-1.3.1.dist-info/RECORD +4 -0
- {gomyck_tools-1.2.12.dist-info → gomyck_tools-1.3.1.dist-info}/WHEEL +1 -1
- gomyck_tools-1.3.1.dist-info/top_level.txt +1 -0
- ctools/__init__.py +0 -0
- ctools/aes_tools.py +0 -35
- ctools/api_result.py +0 -55
- ctools/application.py +0 -386
- ctools/b64.py +0 -7
- ctools/bashPath.py +0 -13
- ctools/bottle_web_base.py +0 -169
- ctools/bottle_webserver.py +0 -143
- ctools/bottle_websocket.py +0 -75
- ctools/browser_element_tools.py +0 -314
- ctools/call.py +0 -71
- ctools/cftp.py +0 -74
- ctools/cjson.py +0 -54
- ctools/ckafka.py +0 -159
- ctools/compile_tools.py +0 -18
- ctools/console.py +0 -55
- ctools/coord_trans.py +0 -127
- ctools/credis.py +0 -111
- ctools/cron_lite.py +0 -252
- ctools/ctoken.py +0 -34
- ctools/cword.py +0 -30
- ctools/czip.py +0 -19
- ctools/database.py +0 -185
- ctools/date_utils.py +0 -43
- ctools/dict_wrapper.py +0 -20
- ctools/douglas_rarefy.py +0 -136
- ctools/download_tools.py +0 -57
- ctools/enums.py +0 -4
- ctools/ex.py +0 -31
- ctools/excelOpt.py +0 -36
- ctools/html_soup.py +0 -35
- ctools/http_utils.py +0 -24
- ctools/images_tools.py +0 -27
- ctools/imgDialog.py +0 -44
- ctools/metrics.py +0 -131
- ctools/mqtt_utils.py +0 -289
- ctools/obj.py +0 -20
- ctools/pacth.py +0 -74
- ctools/plan_area_tools.py +0 -97
- ctools/process_pool.py +0 -36
- ctools/pty_tools.py +0 -72
- ctools/resource_bundle_tools.py +0 -121
- ctools/rsa.py +0 -70
- ctools/screenshot_tools.py +0 -127
- ctools/sign.py +0 -20
- ctools/sm_tools.py +0 -49
- ctools/snow_id.py +0 -76
- ctools/str_diff.py +0 -20
- ctools/string_tools.py +0 -85
- ctools/sys_info.py +0 -157
- ctools/sys_log.py +0 -89
- ctools/thread_pool.py +0 -35
- ctools/upload_tools.py +0 -40
- ctools/win_canvas.py +0 -83
- ctools/win_control.py +0 -106
- ctools/word_fill.py +0 -562
- ctools/word_fill_entity.py +0 -46
- ctools/work_path.py +0 -69
- gomyck_tools-1.2.12.dist-info/METADATA +0 -57
- gomyck_tools-1.2.12.dist-info/RECORD +0 -62
- gomyck_tools-1.2.12.dist-info/top_level.txt +0 -1
ctools/bottle_web_base.py
DELETED
@@ -1,169 +0,0 @@
|
|
1
|
-
import inspect
|
2
|
-
import threading
|
3
|
-
from functools import wraps
|
4
|
-
|
5
|
-
import bottle
|
6
|
-
from bottle import response, Bottle, request
|
7
|
-
|
8
|
-
from ctools import ctoken
|
9
|
-
from ctools.dict_wrapper import DictWrapper
|
10
|
-
from ctools.api_result import R
|
11
|
-
from ctools.sys_log import flog as log
|
12
|
-
|
13
|
-
bottle.BaseRequest.MEMFILE_MAX = 1024 * 1024 * 50
|
14
|
-
func_has_params = {}
|
15
|
-
|
16
|
-
class GlobalState:
|
17
|
-
lock = threading.Lock()
|
18
|
-
withOutLoginURI = [
|
19
|
-
'/',
|
20
|
-
'/index',
|
21
|
-
'/login'
|
22
|
-
]
|
23
|
-
allowRemoteCallURI = [
|
24
|
-
|
25
|
-
]
|
26
|
-
token = {}
|
27
|
-
interceptors = []
|
28
|
-
|
29
|
-
def init_app(context_path=None):
|
30
|
-
app = Bottle()
|
31
|
-
app.context_path = context_path
|
32
|
-
|
33
|
-
@app.hook('before_request')
|
34
|
-
def before_request():
|
35
|
-
for interceptor in GlobalState.interceptors:
|
36
|
-
res: R = interceptor['func']()
|
37
|
-
if res.code != 200: bottle.abort(res.code, res.message)
|
38
|
-
|
39
|
-
@app.error(401)
|
40
|
-
def unauthorized(error):
|
41
|
-
response.status = 401
|
42
|
-
log.error("系统未授权: {} {} {}".format(error.body, request.method, request.fullpath))
|
43
|
-
return R.error(resp=R.Code.cus_code(401, "系统未授权! {}".format(error.body)))
|
44
|
-
|
45
|
-
@app.error(403)
|
46
|
-
def unauthorized(error):
|
47
|
-
response.status = 403
|
48
|
-
log.error("访问受限: {} {} {}".format(error.body, request.method, request.fullpath))
|
49
|
-
return R.error(resp=R.Code.cus_code(403, "访问受限: {}".format(error.body)))
|
50
|
-
|
51
|
-
@app.error(404)
|
52
|
-
def not_found(error):
|
53
|
-
response.status = 404
|
54
|
-
log.error("404 not found : {} {} {}".format(error.body, request.method, request.fullpath))
|
55
|
-
return R.error(resp=R.Code.cus_code(404, "资源未找到: {}".format(error.body)))
|
56
|
-
|
57
|
-
@app.error(405)
|
58
|
-
def method_not_allow(error):
|
59
|
-
response.status = 405
|
60
|
-
log.error("请求方法错误: {} {} {}".format(error.status_line, request.method, request.fullpath))
|
61
|
-
return R.error(resp=R.Code.cus_code(405, '请求方法错误: {}'.format(error.status_line)))
|
62
|
-
|
63
|
-
@app.error(500)
|
64
|
-
def internal_error(error):
|
65
|
-
response.status = 500
|
66
|
-
log.error("系统发生错误: {} {} {}".format(error.body, request.method, request.fullpath))
|
67
|
-
return R.error(msg='系统发生错误: {}'.format(error.exception))
|
68
|
-
|
69
|
-
@app.hook('after_request')
|
70
|
-
def after_request():
|
71
|
-
enable_cors()
|
72
|
-
|
73
|
-
app.install(params_resolve)
|
74
|
-
return app
|
75
|
-
|
76
|
-
def enable_cors():
|
77
|
-
response.headers['Access-Control-Allow-Origin'] = '*'
|
78
|
-
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
|
79
|
-
request_headers = request.headers.get('Access-Control-Request-Headers')
|
80
|
-
response.headers['Access-Control-Allow-Headers'] = request_headers if request_headers else ''
|
81
|
-
response.headers['Access-Control-Expose-Headers'] = '*'
|
82
|
-
|
83
|
-
# annotation
|
84
|
-
def before_intercept(order=0):
|
85
|
-
def decorator(func):
|
86
|
-
log.info("add before interceptor: {}".format(func.__name__))
|
87
|
-
GlobalState.interceptors.append({'order': order, 'func': func})
|
88
|
-
GlobalState.interceptors = sorted(GlobalState.interceptors, key=lambda x: x['order'])
|
89
|
-
return decorator
|
90
|
-
|
91
|
-
# annotation
|
92
|
-
def rule(key):
|
93
|
-
def return_func(func):
|
94
|
-
@wraps(func)
|
95
|
-
def decorated(*args, **kwargs):
|
96
|
-
# if GlobalState.licenseInfo is not None and key not in GlobalState.licenseInfo.access_module:
|
97
|
-
# log.error("系统未授权! {} {}".format(request.fullpath, '当前请求的模块未授权!请联系管理员!'))
|
98
|
-
# return R.error(resp=R.Code.cus_code(9999, "系统未授权! {}".format('当前请求的模块未授权!请联系管理员!')))
|
99
|
-
return func(*args, **kwargs)
|
100
|
-
return decorated
|
101
|
-
return return_func
|
102
|
-
|
103
|
-
# annotation or plugins, has auto install, don't need to call
|
104
|
-
def params_resolve(func):
|
105
|
-
@wraps(func)
|
106
|
-
def decorated(*args, **kwargs):
|
107
|
-
if func_has_params.get(request.fullpath) is not None and not func_has_params.get(request.fullpath):
|
108
|
-
return func(*args, **kwargs)
|
109
|
-
if func_has_params.get(request.fullpath) is None:
|
110
|
-
sig = inspect.signature(func)
|
111
|
-
params = sig.parameters
|
112
|
-
if not params.get('params'):
|
113
|
-
func_has_params[request.fullpath] = False
|
114
|
-
return func(*args, **kwargs)
|
115
|
-
else:
|
116
|
-
func_has_params[request.fullpath] = True
|
117
|
-
if request.method == 'GET':
|
118
|
-
queryStr = request.query.decode('utf-8')
|
119
|
-
page_info = PageInfo(
|
120
|
-
page_size=10 if request.headers.get('page_size') is None else int(request.headers.get('page_size')),
|
121
|
-
page_index=1 if request.headers.get('page_index') is None else int(request.headers.get('page_index'))
|
122
|
-
)
|
123
|
-
queryStr.page_info = page_info
|
124
|
-
return func(params=queryStr, *args, **kwargs)
|
125
|
-
elif request.method == 'POST':
|
126
|
-
query_params = request.query.decode('utf-8')
|
127
|
-
content_type = request.get_header('content-type')
|
128
|
-
if content_type == 'application/json':
|
129
|
-
params = request.json or {}
|
130
|
-
dict_wrapper = DictWrapper(params)
|
131
|
-
dict_wrapper.update(query_params.dict)
|
132
|
-
return func(params=dict_wrapper, *args, **kwargs)
|
133
|
-
elif 'multipart/form-data' in content_type:
|
134
|
-
form_data = request.forms.decode()
|
135
|
-
form_files = request.files.decode()
|
136
|
-
dict_wrapper = DictWrapper(form_data)
|
137
|
-
dict_wrapper.update(query_params.dict)
|
138
|
-
dict_wrapper.files = form_files
|
139
|
-
return func(params=dict_wrapper, *args, **kwargs)
|
140
|
-
elif 'application/x-www-form-urlencoded' in content_type:
|
141
|
-
params = request.forms.decode()
|
142
|
-
dict_wrapper = DictWrapper(params.dict)
|
143
|
-
dict_wrapper.update(query_params.dict)
|
144
|
-
return func(params=dict_wrapper, *args, **kwargs)
|
145
|
-
elif 'text/plain' in content_type:
|
146
|
-
params = request.body.read().decode('utf-8')
|
147
|
-
dict_wrapper = DictWrapper({'body': params})
|
148
|
-
dict_wrapper.update(query_params.dict)
|
149
|
-
return func(params=dict_wrapper, *args, **kwargs)
|
150
|
-
else:
|
151
|
-
return func(*args, **kwargs)
|
152
|
-
return decorated
|
153
|
-
|
154
|
-
class PageInfo:
|
155
|
-
def __init__(self, page_size, page_index):
|
156
|
-
self.page_size = page_size
|
157
|
-
self.page_index = page_index
|
158
|
-
|
159
|
-
# 通用的鉴权方法
|
160
|
-
def common_auth_verify(aes_key):
|
161
|
-
if request.path.startswith('/static') or request.path in GlobalState.withOutLoginURI:
|
162
|
-
return R.ok(to_json_str=False)
|
163
|
-
auth_token = request.get_header('Authorization')
|
164
|
-
if auth_token is None:
|
165
|
-
auth_token = request.get_cookie('Authorization')
|
166
|
-
payload = ctoken.get_payload(auth_token, aes_key)
|
167
|
-
if payload:
|
168
|
-
return R.ok(to_json_str=False)
|
169
|
-
return R.error(resp=R.Code.cus_code(401, "未授权"), to_json_str=False)
|
ctools/bottle_webserver.py
DELETED
@@ -1,143 +0,0 @@
|
|
1
|
-
import sys
|
2
|
-
from socketserver import ThreadingMixIn
|
3
|
-
from wsgiref.simple_server import WSGIServer, WSGIRequestHandler, make_server
|
4
|
-
|
5
|
-
from bottle import ServerAdapter, Bottle, template, static_file, abort, redirect, response
|
6
|
-
|
7
|
-
from ctools import sys_info
|
8
|
-
|
9
|
-
"""
|
10
|
-
module_names = list(globals().keys())
|
11
|
-
def get_modules():
|
12
|
-
mods = []
|
13
|
-
for modname in module_names:
|
14
|
-
if modname == 'base' or modname == 'online' or modname.startswith('__') or modname == 'importlib': continue
|
15
|
-
module = globals()[modname]
|
16
|
-
mods.append(module)
|
17
|
-
return mods
|
18
|
-
|
19
|
-
def get_ws_modules():
|
20
|
-
from . import websocket
|
21
|
-
return [websocket]
|
22
|
-
"""
|
23
|
-
|
24
|
-
"""
|
25
|
-
from ctools import bottle_web_base, token, bottle_webserver
|
26
|
-
from ctools.api_result import R
|
27
|
-
|
28
|
-
secret_key = "xxx"
|
29
|
-
app = bottle_web_base.init_app('子模块写 context_path, 主模块就不用写任何东西')
|
30
|
-
|
31
|
-
# 通用的鉴权方法
|
32
|
-
@bottle_web_base.before_intercept(0)
|
33
|
-
def token_check():
|
34
|
-
return bottle_web_base.common_auth_verify(secret_key)
|
35
|
-
|
36
|
-
@app.post('/login')
|
37
|
-
def login(params):
|
38
|
-
return R.ok(token.gen_token({'username': 'xxx'}, secret_key, 3600))
|
39
|
-
|
40
|
-
@app.get('/demo')
|
41
|
-
@bottle_web_base.rule('DOC:DOWNLOAD')
|
42
|
-
def demo(params):
|
43
|
-
print(params)
|
44
|
-
|
45
|
-
main_app = bottle_webserver.init_bottle() # 这里可以传 APP 当做主模块, 但是 context_path 就不好使了, 上下文必须是 /
|
46
|
-
main_app.mount(app.context_path, app)
|
47
|
-
main_app.set_index(r'轨迹点位压缩.html')
|
48
|
-
main_app.run()
|
49
|
-
"""
|
50
|
-
|
51
|
-
_default_port = 8888
|
52
|
-
|
53
|
-
class CBottle:
|
54
|
-
|
55
|
-
def __init__(self, bottle: Bottle, port=_default_port, quiet=False):
|
56
|
-
self.port = port
|
57
|
-
self.quiet = quiet
|
58
|
-
self.bottle = bottle
|
59
|
-
self.index_root = './'
|
60
|
-
self.index_filename = 'index.html'
|
61
|
-
self.is_tpl = False
|
62
|
-
self.tmp_args = {}
|
63
|
-
self.redirect_url = None
|
64
|
-
self.static_root = './static'
|
65
|
-
self.download_root = './download'
|
66
|
-
|
67
|
-
@self.bottle.route(['/', '/index'])
|
68
|
-
def index():
|
69
|
-
try:
|
70
|
-
if self.redirect_url: return redirect(self.redirect_url)
|
71
|
-
if self.is_tpl: return template(f"{self.index_root}/{self.index_filename}", self.tmp_args)
|
72
|
-
return static_file(filename=self.index_filename, root=self.index_root)
|
73
|
-
except FileNotFoundError:
|
74
|
-
abort(404, "File not found...")
|
75
|
-
|
76
|
-
@self.bottle.route('/static/<filepath:path>')
|
77
|
-
def static(filepath):
|
78
|
-
try:
|
79
|
-
return static_file(filepath, root=self.static_root)
|
80
|
-
except FileNotFoundError:
|
81
|
-
abort(404, "File not found...")
|
82
|
-
|
83
|
-
@self.bottle.route('/download/<filepath:path>')
|
84
|
-
def download(filepath):
|
85
|
-
return static_file(filepath, root=self.download_root, download=True)
|
86
|
-
|
87
|
-
@self.bottle.route('/favicon.ico')
|
88
|
-
def favicon():
|
89
|
-
response.content_type = 'image/svg+xml'
|
90
|
-
svg_icon = '''<?xml version="1.0" encoding="UTF-8"?>
|
91
|
-
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
92
|
-
<circle cx="16" cy="16" r="14" fill="#007bff"/>
|
93
|
-
<path d="M16 8a8 8 0 0 0-8 8h2a6 6 0 0 1 12 0h2a8 8 0 0 0-8-8z" fill="white"/>
|
94
|
-
<circle cx="16" cy="20" r="2" fill="white"/>
|
95
|
-
</svg>
|
96
|
-
'''
|
97
|
-
return svg_icon
|
98
|
-
|
99
|
-
def run(self):
|
100
|
-
http_server = WSGIRefServer(port=self.port)
|
101
|
-
print('Click the link below to open the service homepage %s' % '\n \t\t http://localhost:%s \n \t\t http://%s:%s' % (self.port, sys_info.get_local_ipv4(), self.port), file=sys.stderr)
|
102
|
-
self.bottle.run(server=http_server, quiet=self.quiet)
|
103
|
-
|
104
|
-
def set_index(self, filename='index.html', root='./', is_tpl=False, redirect_url=None, **kwargs):
|
105
|
-
self.index_root = root
|
106
|
-
self.index_filename = filename
|
107
|
-
self.is_tpl = is_tpl
|
108
|
-
self.redirect_url = redirect_url
|
109
|
-
self.tmp_args = kwargs
|
110
|
-
|
111
|
-
def set_static(self, root='./static'):
|
112
|
-
self.static_root = root
|
113
|
-
|
114
|
-
def set_download(self, root='./download'):
|
115
|
-
self.download_root = root
|
116
|
-
|
117
|
-
def mount(self, context_path, app, **kwargs):
|
118
|
-
self.bottle.mount(context_path, app, **kwargs)
|
119
|
-
|
120
|
-
def init_bottle(app:Bottle=None, port=_default_port, quiet=False) -> CBottle:
|
121
|
-
bottle = app or Bottle()
|
122
|
-
return CBottle(bottle, port, quiet)
|
123
|
-
|
124
|
-
class ThreadedWSGIServer(ThreadingMixIn, WSGIServer):
|
125
|
-
daemon_threads = True
|
126
|
-
|
127
|
-
class CustomWSGIHandler(WSGIRequestHandler):
|
128
|
-
def log_request(*args, **kw): pass
|
129
|
-
|
130
|
-
class WSGIRefServer(ServerAdapter):
|
131
|
-
|
132
|
-
def __init__(self, host='0.0.0.0', port=_default_port):
|
133
|
-
super().__init__(host, port)
|
134
|
-
self.server = None
|
135
|
-
|
136
|
-
def run(self, handler):
|
137
|
-
req_handler = WSGIRequestHandler
|
138
|
-
if self.quiet: req_handler = CustomWSGIHandler
|
139
|
-
self.server = make_server(self.host, self.port, handler, server_class=ThreadedWSGIServer, handler_class=req_handler)
|
140
|
-
self.server.serve_forever()
|
141
|
-
|
142
|
-
def stop(self):
|
143
|
-
self.server.shutdown()
|
ctools/bottle_websocket.py
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
from bottle import ServerAdapter, Bottle
|
2
|
-
from geventwebsocket.handler import WebSocketHandler
|
3
|
-
|
4
|
-
from ctools import sys_log
|
5
|
-
|
6
|
-
"""
|
7
|
-
module_names = list(globals().keys())
|
8
|
-
def get_modules():
|
9
|
-
mods = []
|
10
|
-
for modname in module_names:
|
11
|
-
if modname == 'base' or modname == 'online' or modname.startswith('__') or modname == 'importlib': continue
|
12
|
-
module = globals()[modname]
|
13
|
-
mods.append(module)
|
14
|
-
return mods
|
15
|
-
|
16
|
-
def get_ws_modules():
|
17
|
-
from . import websocket
|
18
|
-
return [websocket]
|
19
|
-
"""
|
20
|
-
|
21
|
-
"""
|
22
|
-
ws_app = bottle_web_base.init_app('/websocket_demo')
|
23
|
-
|
24
|
-
@ws_app.route('/script_debug', apply=[websocket])
|
25
|
-
@bottle_web_base.rule('DOC:DOWNLOAD')
|
26
|
-
def script_debug(ws: WebSocket):
|
27
|
-
print(123)
|
28
|
-
|
29
|
-
socket_app = bottle_websocket.init_bottle()
|
30
|
-
socket_app.mount(app.context_path, ws_app)
|
31
|
-
socket_app.run()
|
32
|
-
"""
|
33
|
-
|
34
|
-
_default_port = 8887
|
35
|
-
|
36
|
-
class CBottle:
|
37
|
-
|
38
|
-
def __init__(self, bottle: Bottle, port=_default_port, quiet=False):
|
39
|
-
self.port = port
|
40
|
-
self.quiet = quiet
|
41
|
-
self.bottle = bottle
|
42
|
-
|
43
|
-
def run(self):
|
44
|
-
socket_server = WebSocketServer(port=self.port)
|
45
|
-
self.bottle.run(server=socket_server, quiet=self.quiet)
|
46
|
-
|
47
|
-
def mount(self, context_path, app):
|
48
|
-
self.bottle.mount(context_path, app)
|
49
|
-
|
50
|
-
def init_bottle(port=_default_port, quiet=False) -> CBottle:
|
51
|
-
bottle = Bottle()
|
52
|
-
return CBottle(bottle, port, quiet)
|
53
|
-
|
54
|
-
class CustomWebSocketHandler(WebSocketHandler):
|
55
|
-
def log_request(self):
|
56
|
-
if '101' not in str(self.status):
|
57
|
-
log_msg = self.format_request()
|
58
|
-
for nk in sys_log.neglect_keywords:
|
59
|
-
if nk in log_msg:
|
60
|
-
return
|
61
|
-
self.logger.info(log_msg)
|
62
|
-
|
63
|
-
class WebSocketServer(ServerAdapter):
|
64
|
-
|
65
|
-
def __init__(self, host='0.0.0.0', port=_default_port):
|
66
|
-
super().__init__(host, port)
|
67
|
-
self.server = None
|
68
|
-
|
69
|
-
def run(self, handler):
|
70
|
-
from gevent import pywsgi
|
71
|
-
self.server = pywsgi.WSGIServer((self.host, self.port), handler, handler_class=CustomWebSocketHandler)
|
72
|
-
self.server.serve_forever()
|
73
|
-
|
74
|
-
def stop(self):
|
75
|
-
self.server.stop()
|
ctools/browser_element_tools.py
DELETED
@@ -1,314 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import time
|
3
|
-
|
4
|
-
from business.common.constant import Scheduler
|
5
|
-
from lxml import etree
|
6
|
-
from lxml import html
|
7
|
-
from pynput import keyboard
|
8
|
-
from selenium import webdriver
|
9
|
-
from selenium.common import NoSuchElementException, WebDriverException, NoSuchWindowException
|
10
|
-
from selenium.webdriver.chrome.options import Options
|
11
|
-
from selenium.webdriver.chrome.service import Service
|
12
|
-
from selenium.webdriver.common.by import By
|
13
|
-
from urllib3.exceptions import MaxRetryError
|
14
|
-
|
15
|
-
from ctools import application, string_tools
|
16
|
-
|
17
|
-
keyboard_listener = None
|
18
|
-
ctrl_pressed = None
|
19
|
-
g_driver = None
|
20
|
-
g_callback = None
|
21
|
-
g_result = []
|
22
|
-
g_quite_flag = False
|
23
|
-
picture_path = ""
|
24
|
-
|
25
|
-
def get_hovered_element_html(url, explore, callback):
|
26
|
-
global g_driver, g_callback, g_result
|
27
|
-
g_callback = callback
|
28
|
-
g_driver = explore.init()
|
29
|
-
driver = g_driver
|
30
|
-
driver.maximize_window()
|
31
|
-
driver.get(url)
|
32
|
-
start_keyboard_listener()
|
33
|
-
handle_arr = [driver.current_window_handle]
|
34
|
-
close_page_flag = False
|
35
|
-
while g_driver and not g_quite_flag:
|
36
|
-
try:
|
37
|
-
try:
|
38
|
-
if len(driver.window_handles) == 0:
|
39
|
-
break_func()
|
40
|
-
break
|
41
|
-
except WebDriverException:
|
42
|
-
break_func()
|
43
|
-
break
|
44
|
-
except MaxRetryError:
|
45
|
-
break_func()
|
46
|
-
break
|
47
|
-
try:
|
48
|
-
driver.find_element(value="ck-overlay")
|
49
|
-
except NoSuchElementException as e:
|
50
|
-
driver.execute_script(explore.listen_script)
|
51
|
-
except Exception:
|
52
|
-
pass
|
53
|
-
for handle in driver.window_handles:
|
54
|
-
if handle in handle_arr: continue
|
55
|
-
if not close_page_flag: driver.execute_script(overlay_script)
|
56
|
-
handle_arr.append(handle)
|
57
|
-
driver.switch_to.window(handle)
|
58
|
-
try:
|
59
|
-
driver.execute_script(explore.listen_script)
|
60
|
-
except Exception:
|
61
|
-
continue
|
62
|
-
close_page_flag = False
|
63
|
-
time.sleep(0.5)
|
64
|
-
cursor_html = driver.execute_script(explore.return_script)
|
65
|
-
if cursor_html:
|
66
|
-
g_result.clear()
|
67
|
-
cursor_dom = html.fragment_fromstring(cursor_html)
|
68
|
-
cursor_dom.set("ck-flag", "ck")
|
69
|
-
enhance_cursor_html = etree.tostring(cursor_dom, method="html", encoding="unicode")
|
70
|
-
page_html = driver.page_source.replace(cursor_html, enhance_cursor_html)
|
71
|
-
page_dom_tree = etree.ElementTree(etree.HTML(page_html))
|
72
|
-
match_dom = page_dom_tree.xpath('//*[@ck-flag="ck"]')
|
73
|
-
for ck_element in match_dom:
|
74
|
-
ck_xpath = page_dom_tree.getpath(ck_element)
|
75
|
-
#print('XPATH IS {}'.format(ck_xpath))
|
76
|
-
try:
|
77
|
-
ele = driver.find_element(By.XPATH, ck_xpath)
|
78
|
-
#print('XPATH_HTML: {}'.format(ele.get_attribute('outerHTML')))
|
79
|
-
except Exception:
|
80
|
-
pass
|
81
|
-
g_result.append(ck_xpath)
|
82
|
-
except NoSuchWindowException as e:
|
83
|
-
close_page_flag = True
|
84
|
-
handle_arr = []
|
85
|
-
try:
|
86
|
-
driver.switch_to.window(driver.window_handles[-1])
|
87
|
-
driver.execute_script(hide_shade)
|
88
|
-
except Exception:
|
89
|
-
print('切换HANDLE失败:', e)
|
90
|
-
break_func()
|
91
|
-
break
|
92
|
-
except Exception as e:
|
93
|
-
print('全局错误:', e)
|
94
|
-
break_func()
|
95
|
-
break
|
96
|
-
try:
|
97
|
-
g_driver.quit()
|
98
|
-
except Exception as e:
|
99
|
-
pass
|
100
|
-
|
101
|
-
def break_func():
|
102
|
-
keyboard_listener.stop()
|
103
|
-
g_callback(None)
|
104
|
-
|
105
|
-
def on_press(key):
|
106
|
-
global keyboard_listener, ctrl_pressed, g_driver, g_quite_flag, picture_path
|
107
|
-
if key == keyboard.Key.ctrl_l and not ctrl_pressed:
|
108
|
-
ctrl_pressed = True
|
109
|
-
elif key == keyboard.Key.esc:
|
110
|
-
g_result.clear()
|
111
|
-
keyboard_listener.stop()
|
112
|
-
g_quite_flag = True
|
113
|
-
g_callback(None)
|
114
|
-
elif hasattr(key, 'vk') and key.vk == 192 and ctrl_pressed:
|
115
|
-
# print("g_result: %s" % g_result)
|
116
|
-
if g_result:
|
117
|
-
try:
|
118
|
-
select_element = g_driver.find_element(By.XPATH, g_result[0])
|
119
|
-
picture_path = "%s.png" % os.path.join(application.Server.screenshotPath, "element-"+string_tools.get_uuid())
|
120
|
-
select_element.screenshot(picture_path)
|
121
|
-
except Exception:
|
122
|
-
pass
|
123
|
-
|
124
|
-
keyboard_listener.stop()
|
125
|
-
g_quite_flag = True
|
126
|
-
g_callback(g_result)
|
127
|
-
|
128
|
-
def on_release(key):
|
129
|
-
global ctrl_pressed
|
130
|
-
if key == keyboard.Key.ctrl_l:
|
131
|
-
ctrl_pressed = False
|
132
|
-
|
133
|
-
def start_keyboard_listener():
|
134
|
-
global keyboard_listener
|
135
|
-
keyboard_listener = keyboard.Listener(on_press=on_press, on_release=on_release)
|
136
|
-
keyboard_listener.start()
|
137
|
-
|
138
|
-
|
139
|
-
overlay_script = """
|
140
|
-
// 创建遮罩层
|
141
|
-
var shade = document.createElement('div');
|
142
|
-
shade.id = 'ck-shade-parent';
|
143
|
-
shade.style.position = 'fixed';
|
144
|
-
shade.style.top = '0';
|
145
|
-
shade.style.left = '0';
|
146
|
-
shade.style.width = '100%';
|
147
|
-
shade.style.height = '100%';
|
148
|
-
shade.style.backgroundColor = '#000'; // 使用纯色背景
|
149
|
-
shade.style.filter = 'alpha(opacity=60)'; // 设置透明度,IE8-IE9
|
150
|
-
shade.style.opacity = '0.8'; // 设置透明度,现代浏览器
|
151
|
-
shade.style.zIndex = '9999';
|
152
|
-
document.body.appendChild(shade);
|
153
|
-
|
154
|
-
// 创建覆盖内容
|
155
|
-
var overlayContent = document.createElement('div');
|
156
|
-
overlayContent.id = 'ck-shade-oc';
|
157
|
-
overlayContent.className = 'overlay-content';
|
158
|
-
overlayContent.style.position = 'absolute';
|
159
|
-
overlayContent.style.top = '50%';
|
160
|
-
overlayContent.style.left = '50%';
|
161
|
-
overlayContent.style.transform = 'translate(-50%, -50%)';
|
162
|
-
overlayContent.style.backgroundColor = 'white';
|
163
|
-
overlayContent.style.padding = '20px';
|
164
|
-
overlayContent.style.borderRadius = '8px';
|
165
|
-
overlayContent.style.textAlign = 'center';
|
166
|
-
shade.appendChild(overlayContent);
|
167
|
-
|
168
|
-
// 创建消息内容
|
169
|
-
var message = document.createElement('p');
|
170
|
-
message.id = 'ck-shade-msg';
|
171
|
-
message.innerText = '当前页面未激活,请关闭激活状态的录制页面';
|
172
|
-
message.style.color = '#000'; // 设置文本颜色为黑色
|
173
|
-
message.style.fontSize = '16px'; // 设置文本大小
|
174
|
-
overlayContent.appendChild(message);
|
175
|
-
|
176
|
-
"""
|
177
|
-
|
178
|
-
hide_shade = """
|
179
|
-
var shade = document.getElementById('ck-shade-parent');
|
180
|
-
if (shade) {
|
181
|
-
shade.parentNode.removeChild(shade);
|
182
|
-
}
|
183
|
-
"""
|
184
|
-
|
185
|
-
class IE:
|
186
|
-
|
187
|
-
@staticmethod
|
188
|
-
def init():
|
189
|
-
ie_options = webdriver.IeOptions()
|
190
|
-
ie_options.ignore_protected_mode_settings = True
|
191
|
-
ie_options.ignore_zoom_level = True
|
192
|
-
ie_options.require_window_focus = True
|
193
|
-
return webdriver.Ie(options=ie_options)
|
194
|
-
|
195
|
-
listen_script = """
|
196
|
-
var overlay = document.createElement('div');
|
197
|
-
overlay.id = 'ck-overlay';
|
198
|
-
overlay.style.position = 'absolute';
|
199
|
-
overlay.style.border = '2px solid red';
|
200
|
-
overlay.style.pointerEvents = 'none';
|
201
|
-
overlay.style.zIndex = '9999';
|
202
|
-
document.body.appendChild(overlay);
|
203
|
-
var addEvent = function(elem, type, eventHandle) {
|
204
|
-
if (elem == null || typeof(elem) == 'undefined') return;
|
205
|
-
if (elem.addEventListener) {
|
206
|
-
elem.addEventListener(type, eventHandle, false);
|
207
|
-
} else if (elem.attachEvent) {
|
208
|
-
elem.attachEvent('on' + type, eventHandle);
|
209
|
-
} else {
|
210
|
-
elem['on' + type] = eventHandle;
|
211
|
-
}
|
212
|
-
};
|
213
|
-
|
214
|
-
addEvent(document, 'mousemove', function(e) {
|
215
|
-
e = e || window.event;
|
216
|
-
var element = document.elementFromPoint(e.clientX, e.clientY);
|
217
|
-
window.hoveredElement = element;
|
218
|
-
if (element === overlay) return;
|
219
|
-
var rect = element.getBoundingClientRect();
|
220
|
-
overlay.style.left = (rect.left + (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft) - 5) + 'px';
|
221
|
-
overlay.style.top = (rect.top + (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop) - 5) + 'px';
|
222
|
-
overlay.style.width = (element.offsetWidth + 10) + 'px';
|
223
|
-
overlay.style.height = (element.offsetHeight + 10) + 'px';
|
224
|
-
});
|
225
|
-
"""
|
226
|
-
|
227
|
-
return_script = """
|
228
|
-
return window.hoveredElement ? window.hoveredElement.outerHTML : null;
|
229
|
-
"""
|
230
|
-
|
231
|
-
overlay_script = overlay_script
|
232
|
-
|
233
|
-
|
234
|
-
class Chrome:
|
235
|
-
|
236
|
-
@staticmethod
|
237
|
-
def init():
|
238
|
-
option = Options()
|
239
|
-
option.binary_location = Scheduler.CHROME_PATH
|
240
|
-
service = Service(Scheduler.CHROME_DRIVER_PATH)
|
241
|
-
return webdriver.Chrome(options=option, service=service)
|
242
|
-
|
243
|
-
listen_script = """
|
244
|
-
var overlay = document.createElement('div');
|
245
|
-
overlay.id = 'ck-overlay';
|
246
|
-
overlay.style.position = 'absolute';
|
247
|
-
overlay.style.border = '2px solid red';
|
248
|
-
overlay.style.pointerEvents = 'none';
|
249
|
-
overlay.style.zIndex = '9998';
|
250
|
-
document.body.appendChild(overlay);
|
251
|
-
|
252
|
-
function throttle(func, limit) {
|
253
|
-
let inThrottle;
|
254
|
-
return function() {
|
255
|
-
const args = arguments,
|
256
|
-
context = this;
|
257
|
-
if (!inThrottle) {
|
258
|
-
func.apply(context, args);
|
259
|
-
inThrottle = true;
|
260
|
-
setTimeout(() => inThrottle = false, limit);
|
261
|
-
}
|
262
|
-
};
|
263
|
-
}
|
264
|
-
|
265
|
-
document.addEventListener('mousemove', throttle(function(e) {
|
266
|
-
var element = document.elementFromPoint(e.clientX, e.clientY);
|
267
|
-
window.hoveredElement = element;
|
268
|
-
if(element.id.indexOf('ck-shade') !== -1) return;
|
269
|
-
if (window.hoveredElement) {
|
270
|
-
var rect = element.getBoundingClientRect();
|
271
|
-
overlay.style.left = (rect.left + window.pageXOffset - 5) + 'px';
|
272
|
-
overlay.style.top = (rect.top + window.pageYOffset - 5) + 'px';
|
273
|
-
overlay.style.width = (element.offsetWidth + 10) + 'px';
|
274
|
-
overlay.style.height = (element.offsetHeight + 10) + 'px';
|
275
|
-
}
|
276
|
-
}, 50));
|
277
|
-
"""
|
278
|
-
|
279
|
-
return_script = """
|
280
|
-
return window.hoveredElement ? window.hoveredElement.outerHTML : null;
|
281
|
-
"""
|
282
|
-
|
283
|
-
overlay_script = overlay_script
|
284
|
-
|
285
|
-
|
286
|
-
def callback(xpath):
|
287
|
-
print("Hovered Element XPATH IS :", xpath)
|
288
|
-
|
289
|
-
def get_element(url: str = None, explore: str = "chrome"):
|
290
|
-
global keyboard_listener, ctrl_pressed, g_driver, g_callback, g_result, g_quite_flag, picture_path
|
291
|
-
keyboard_listener = None
|
292
|
-
ctrl_pressed = None
|
293
|
-
g_driver = None
|
294
|
-
g_callback = None
|
295
|
-
g_result = []
|
296
|
-
g_quite_flag = False
|
297
|
-
picture_path = ""
|
298
|
-
|
299
|
-
if explore == "chrome":
|
300
|
-
explore = Chrome
|
301
|
-
else:
|
302
|
-
explore = IE
|
303
|
-
|
304
|
-
if "http" not in url[:5]:
|
305
|
-
url = "http://%s" % url
|
306
|
-
|
307
|
-
get_hovered_element_html(url, explore, callback)
|
308
|
-
|
309
|
-
return g_result, picture_path
|
310
|
-
|
311
|
-
if __name__ == "__main__":
|
312
|
-
# from explore_record_core import get_hovered_element_html, Chrome
|
313
|
-
g_result, picture_path = get_element("weibo.com")
|
314
|
-
print(g_result, picture_path)
|