gomyck-tools 1.3.1__py3-none-any.whl → 1.3.2__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.
- ctools/__init__.py +0 -0
- ctools/aes_tools.py +35 -0
- ctools/api_result.py +55 -0
- ctools/application.py +386 -0
- ctools/b64.py +7 -0
- ctools/bashPath.py +13 -0
- ctools/bottle_web_base.py +169 -0
- ctools/bottle_webserver.py +143 -0
- ctools/bottle_websocket.py +75 -0
- ctools/browser_element_tools.py +314 -0
- ctools/call.py +71 -0
- ctools/cftp.py +74 -0
- ctools/cjson.py +54 -0
- ctools/ckafka.py +159 -0
- ctools/compile_tools.py +18 -0
- ctools/console.py +55 -0
- ctools/coord_trans.py +127 -0
- ctools/credis.py +111 -0
- ctools/cron_lite.py +252 -0
- ctools/ctoken.py +34 -0
- ctools/cword.py +30 -0
- ctools/czip.py +130 -0
- ctools/database.py +185 -0
- ctools/date_utils.py +43 -0
- ctools/dict_wrapper.py +20 -0
- ctools/douglas_rarefy.py +136 -0
- ctools/download_tools.py +57 -0
- ctools/enums.py +4 -0
- ctools/ex.py +31 -0
- ctools/excelOpt.py +36 -0
- ctools/html_soup.py +35 -0
- ctools/http_utils.py +24 -0
- ctools/images_tools.py +27 -0
- ctools/imgDialog.py +44 -0
- ctools/metrics.py +131 -0
- ctools/mqtt_utils.py +289 -0
- ctools/obj.py +20 -0
- ctools/pacth.py +74 -0
- ctools/plan_area_tools.py +97 -0
- ctools/process_pool.py +36 -0
- ctools/pty_tools.py +72 -0
- ctools/resource_bundle_tools.py +121 -0
- ctools/rsa.py +70 -0
- ctools/screenshot_tools.py +127 -0
- ctools/sign.py +20 -0
- ctools/sm_tools.py +49 -0
- ctools/snow_id.py +76 -0
- ctools/str_diff.py +20 -0
- ctools/string_tools.py +85 -0
- ctools/sys_info.py +157 -0
- ctools/sys_log.py +89 -0
- ctools/thread_pool.py +35 -0
- ctools/upload_tools.py +40 -0
- ctools/win_canvas.py +83 -0
- ctools/win_control.py +106 -0
- ctools/word_fill.py +562 -0
- ctools/word_fill_entity.py +46 -0
- ctools/work_path.py +69 -0
- {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/METADATA +1 -1
- gomyck_tools-1.3.2.dist-info/RECORD +62 -0
- gomyck_tools-1.3.2.dist-info/top_level.txt +1 -0
- gomyck_tools-1.3.1.dist-info/RECORD +0 -4
- gomyck_tools-1.3.1.dist-info/top_level.txt +0 -1
- {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/WHEEL +0 -0
@@ -0,0 +1,143 @@
|
|
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()
|
@@ -0,0 +1,75 @@
|
|
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()
|
@@ -0,0 +1,314 @@
|
|
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)
|
ctools/call.py
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
import sched
|
2
|
+
import threading
|
3
|
+
import time
|
4
|
+
from functools import wraps
|
5
|
+
|
6
|
+
# annotation
|
7
|
+
def once(func):
|
8
|
+
"""
|
9
|
+
decorator to initialize a function once
|
10
|
+
:param func: function to be initialized
|
11
|
+
:return: the real decorator for return the result
|
12
|
+
"""
|
13
|
+
initialized = False
|
14
|
+
res = None
|
15
|
+
|
16
|
+
def wrapper(*args, **kwargs):
|
17
|
+
nonlocal initialized, res
|
18
|
+
if not initialized:
|
19
|
+
res = func(*args, **kwargs)
|
20
|
+
initialized = True
|
21
|
+
return res
|
22
|
+
else:
|
23
|
+
return res
|
24
|
+
|
25
|
+
return wrapper
|
26
|
+
|
27
|
+
# annotation
|
28
|
+
def init(func):
|
29
|
+
"""
|
30
|
+
decorator to initialize a function automic
|
31
|
+
:param func: function to be initialized
|
32
|
+
:return: the real decorator for return the result
|
33
|
+
"""
|
34
|
+
res = func()
|
35
|
+
|
36
|
+
def wrapper():
|
37
|
+
return res
|
38
|
+
|
39
|
+
return wrapper
|
40
|
+
|
41
|
+
# annotation
|
42
|
+
def schd(interval_seconds, start_by_call: bool = False):
|
43
|
+
start_flag = False
|
44
|
+
run_flag = False
|
45
|
+
scheduler = sched.scheduler(time.time, time.sleep)
|
46
|
+
|
47
|
+
def decorator(func):
|
48
|
+
@wraps(func)
|
49
|
+
def wrapper(*args, **kwargs):
|
50
|
+
nonlocal start_flag
|
51
|
+
if start_by_call and not start_flag:
|
52
|
+
start_flag = True
|
53
|
+
threading.Thread(target=wrapper, args=args, kwargs=kwargs, daemon=True).start()
|
54
|
+
return
|
55
|
+
|
56
|
+
def job():
|
57
|
+
func(*args, **kwargs)
|
58
|
+
scheduler.enter(interval_seconds, 1, job)
|
59
|
+
|
60
|
+
nonlocal run_flag
|
61
|
+
if not run_flag:
|
62
|
+
scheduler.enter(interval_seconds, 1, job)
|
63
|
+
run_flag = True
|
64
|
+
scheduler.run()
|
65
|
+
else:
|
66
|
+
func(*args, **kwargs)
|
67
|
+
|
68
|
+
if not start_by_call: threading.Thread(target=wrapper, daemon=True).start()
|
69
|
+
return wrapper
|
70
|
+
|
71
|
+
return decorator
|
ctools/cftp.py
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
__author__ = 'haoyang'
|
4
|
+
__date__ = '2025/1/24 08:53'
|
5
|
+
|
6
|
+
import io
|
7
|
+
import time
|
8
|
+
from ftplib import FTP, error_perm, error_temp
|
9
|
+
|
10
|
+
class CFTP:
|
11
|
+
"""
|
12
|
+
with open('xx/xx.md', 'rb') as file:
|
13
|
+
ftp_host = 'x.x.x.x'
|
14
|
+
ftp_username = 'x'
|
15
|
+
ftp_password = 'x'
|
16
|
+
CFTP(ftp_host, ftp_username, ftp_password).upload_file_to_ftp('xx.md', file)
|
17
|
+
"""
|
18
|
+
def __init__(self, host, username, password, timeout=30, max_retries=3, retry_delay=5):
|
19
|
+
self.host = host
|
20
|
+
self.username = username
|
21
|
+
self.password = password
|
22
|
+
self.ftp:FTP = None
|
23
|
+
self.timeout = timeout
|
24
|
+
self.max_retries = max_retries
|
25
|
+
self.retry_delay = retry_delay
|
26
|
+
|
27
|
+
def upload_file_to_ftp(self, file_name, file:io.BytesIO, ftp_directory='/'):
|
28
|
+
if not file_name: raise Exception('文件名不能为空')
|
29
|
+
if not file: raise Exception('文件不能为空')
|
30
|
+
for attempt in range(self.max_retries):
|
31
|
+
try:
|
32
|
+
if not self.ftp:
|
33
|
+
ftp = FTP(self.host, timeout=self.timeout)
|
34
|
+
ftp.login(self.username, self.password)
|
35
|
+
ftp.set_pasv(True)
|
36
|
+
self.ftp = ftp
|
37
|
+
try:
|
38
|
+
self.ftp.cwd(ftp_directory)
|
39
|
+
except error_perm:
|
40
|
+
print(f"FTP 目录不存在:{ftp_directory}")
|
41
|
+
self.ftp.mkd(ftp_directory)
|
42
|
+
print(f"FTP 目录创建成功:{ftp_directory}, 正在切换到目录:{ftp_directory}")
|
43
|
+
self.ftp.cwd(ftp_directory)
|
44
|
+
print(f"正在上传文件:{file_name}")
|
45
|
+
self.ftp.storbinary(f"STOR {file_name}", file)
|
46
|
+
self.ftp.quit()
|
47
|
+
print(f"文件成功上传到 FTP: {file_name}")
|
48
|
+
return
|
49
|
+
except (error_perm, error_temp) as e:
|
50
|
+
try:
|
51
|
+
self.ftp.quit()
|
52
|
+
except Exception:
|
53
|
+
pass
|
54
|
+
self.ftp = None
|
55
|
+
print(f"FTP 错误:{e}")
|
56
|
+
if attempt < self.max_retries - 1:
|
57
|
+
print(f"正在尝试重连... 第 {attempt + 1} 次重试...")
|
58
|
+
time.sleep(self.retry_delay)
|
59
|
+
else:
|
60
|
+
print("重试次数已用尽,上传失败。")
|
61
|
+
raise
|
62
|
+
except Exception as e:
|
63
|
+
try:
|
64
|
+
self.ftp.quit()
|
65
|
+
except Exception:
|
66
|
+
pass
|
67
|
+
self.ftp = None
|
68
|
+
print(f"连接或上传出现异常:{e}")
|
69
|
+
if attempt < self.max_retries - 1:
|
70
|
+
print(f"正在尝试重连... 第 {attempt + 1} 次重试...")
|
71
|
+
time.sleep(self.retry_delay)
|
72
|
+
else:
|
73
|
+
print("重试次数已用尽,上传失败。")
|
74
|
+
raise
|