qrpa 1.0.49__tar.gz → 1.0.50__tar.gz
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.
Potentially problematic release.
This version of qrpa might be problematic. Click here for more details.
- {qrpa-1.0.49 → qrpa-1.0.50}/PKG-INFO +1 -1
- {qrpa-1.0.49 → qrpa-1.0.50}/pyproject.toml +1 -1
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/shein_lib.py +173 -2
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa.egg-info/PKG-INFO +1 -1
- {qrpa-1.0.49 → qrpa-1.0.50}/README.md +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/RateLimitedSender.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/__init__.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/db_migrator.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/feishu_bot_app.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/feishu_client.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/feishu_logic.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/fun_base.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/fun_excel.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/fun_file.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/fun_web.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/fun_win.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/mysql_module/__init__.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/mysql_module/shein_return_order_model.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/shein_daily_report_model.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/shein_excel.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/shein_mysql.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/shein_sqlite.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/shein_ziniao.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/temu_chrome.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/temu_excel.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/temu_lib.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/time_utils.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/time_utils_example.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa/wxwork.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa.egg-info/SOURCES.txt +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa.egg-info/dependency_links.txt +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/qrpa.egg-info/top_level.txt +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/setup.cfg +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/setup.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/tests/test_db_migrator.py +0 -0
- {qrpa-1.0.49 → qrpa-1.0.50}/tests/test_wxwork.py +0 -0
|
@@ -27,8 +27,179 @@ class SheinLib:
|
|
|
27
27
|
|
|
28
28
|
self.deal_auth()
|
|
29
29
|
|
|
30
|
-
# 处理鉴权
|
|
31
30
|
def deal_auth(self):
|
|
31
|
+
"""处理登录鉴权流程"""
|
|
32
|
+
web_page = self.web_page
|
|
33
|
+
|
|
34
|
+
# 配置常量
|
|
35
|
+
MAX_RETRIES = 5
|
|
36
|
+
CAPTCHA_WAIT_INTERVAL = 5
|
|
37
|
+
PAGE_LOAD_TIMEOUT = 5000
|
|
38
|
+
SHORT_TIMEOUT = 1000
|
|
39
|
+
|
|
40
|
+
# 页面元素选择器 - 集中管理
|
|
41
|
+
selectors = {
|
|
42
|
+
'merchant_backend' : '//div[contains(text(),"商家后台")]',
|
|
43
|
+
'auth_confirm_btn' : '//div[@id="container" and @alita-name="gmpsso"]//button[@type="button" and @id]',
|
|
44
|
+
'captcha_text' : '//div[text()="验证码"]',
|
|
45
|
+
'username_input' : '//input[@name="username"]',
|
|
46
|
+
'login_btn' : '//button[contains(@class,"login_btn")]',
|
|
47
|
+
'product_management': '//span[contains(text(),"商品管理")]',
|
|
48
|
+
'auth_title' : '//h1[contains(text(),"鉴权")]'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# URL常量
|
|
52
|
+
home_url = 'https://sso.geiwohuo.com/#/home'
|
|
53
|
+
base_url = 'https://sso.geiwohuo.com'
|
|
54
|
+
|
|
55
|
+
retries = 0
|
|
56
|
+
wait_count = 0
|
|
57
|
+
is_captcha_notification_sent = False
|
|
58
|
+
|
|
59
|
+
log("开始鉴权处理", self.store_username, self.store_name)
|
|
60
|
+
|
|
61
|
+
while retries < MAX_RETRIES:
|
|
62
|
+
try:
|
|
63
|
+
retries += 1
|
|
64
|
+
log(f"鉴权处理第{retries}次尝试", self.store_username, self.store_name)
|
|
65
|
+
|
|
66
|
+
# 主循环:等待商家后台页面加载
|
|
67
|
+
while True:
|
|
68
|
+
try:
|
|
69
|
+
# 检查商家后台是否已经可见
|
|
70
|
+
if web_page.locator(selectors['merchant_backend']).nth(1).is_visible(timeout=SHORT_TIMEOUT):
|
|
71
|
+
log('商家后台已可见,鉴权处理完成', self.store_username, self.store_name)
|
|
72
|
+
return
|
|
73
|
+
except:
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
# 1. 处理鉴权确认弹窗
|
|
77
|
+
try:
|
|
78
|
+
auth_btn = web_page.locator(selectors['auth_confirm_btn']).nth(0)
|
|
79
|
+
if auth_btn.is_visible(timeout=SHORT_TIMEOUT):
|
|
80
|
+
log("检测到鉴权确认按钮,点击确定", self.store_username, self.store_name)
|
|
81
|
+
auth_btn.click()
|
|
82
|
+
web_page.wait_for_load_state("load")
|
|
83
|
+
web_page.wait_for_timeout(SHORT_TIMEOUT)
|
|
84
|
+
continue
|
|
85
|
+
except:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
# 2. 处理验证码等待
|
|
89
|
+
try:
|
|
90
|
+
if web_page.locator(selectors['captcha_text']).is_visible(timeout=SHORT_TIMEOUT):
|
|
91
|
+
wait_count += 1
|
|
92
|
+
log(f'等待验证码输入: 第{wait_count}次', self.store_username, self.store_name)
|
|
93
|
+
|
|
94
|
+
# 只发送一次验证码通知
|
|
95
|
+
if not is_captcha_notification_sent:
|
|
96
|
+
try:
|
|
97
|
+
img_path = full_screen_shot(web_page, self.config)
|
|
98
|
+
wx_bot = WxWorkBot(self.config.wxwork_bot_exception)
|
|
99
|
+
wx_bot.send_img(img_path)
|
|
100
|
+
wx_bot.send_text(f'{self.store_username},{self.store_name} 需要登录验证码')
|
|
101
|
+
is_captcha_notification_sent = True
|
|
102
|
+
log("验证码通知已发送", self.store_username, self.store_name)
|
|
103
|
+
except Exception as notify_error:
|
|
104
|
+
log(f"发送验证码通知失败: {notify_error}", self.store_username, self.store_name)
|
|
105
|
+
|
|
106
|
+
time.sleep(CAPTCHA_WAIT_INTERVAL)
|
|
107
|
+
continue
|
|
108
|
+
except:
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
# 3. 处理登录按钮点击
|
|
112
|
+
try:
|
|
113
|
+
if web_page.locator(selectors['username_input']).is_visible(timeout=SHORT_TIMEOUT):
|
|
114
|
+
log("检测到用户名输入框,准备点击登录按钮", self.store_username, self.store_name)
|
|
115
|
+
web_page.wait_for_timeout(PAGE_LOAD_TIMEOUT)
|
|
116
|
+
web_page.locator(selectors['login_btn']).click()
|
|
117
|
+
web_page.wait_for_load_state("load")
|
|
118
|
+
web_page.wait_for_timeout(PAGE_LOAD_TIMEOUT)
|
|
119
|
+
continue
|
|
120
|
+
except:
|
|
121
|
+
pass
|
|
122
|
+
|
|
123
|
+
# 4. 检查商品管理菜单(成功标识)
|
|
124
|
+
try:
|
|
125
|
+
if web_page.locator(selectors['product_management']).nth(1).is_visible(timeout=SHORT_TIMEOUT):
|
|
126
|
+
log('检测到商品管理菜单,鉴权处理成功', self.store_username, self.store_name)
|
|
127
|
+
return
|
|
128
|
+
except:
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
# 5. 获取当前页面信息用于状态判断
|
|
132
|
+
current_title = web_page.title()
|
|
133
|
+
current_url = web_page.url
|
|
134
|
+
|
|
135
|
+
log(f'当前页面状态: {current_title} | {current_url}', self.store_username, self.store_name)
|
|
136
|
+
|
|
137
|
+
# 6. 处理已知的成功状态页面
|
|
138
|
+
success_titles = ['SHEIN全球商家中心', '后台首页', '商家后台']
|
|
139
|
+
if any(title in current_title for title in success_titles) and home_url in current_url:
|
|
140
|
+
log(f'检测到目标页面: {current_title},跳出循环', self.store_username, self.store_name)
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
# 7. 处理需要重定向的页面
|
|
144
|
+
if 'mrs.biz.sheincorp.cn' in current_url and '商家后台' in current_title:
|
|
145
|
+
log('检测到旧后台地址,重定向到新地址', self.store_username, self.store_name)
|
|
146
|
+
web_page.goto(home_url)
|
|
147
|
+
web_page.wait_for_load_state("load")
|
|
148
|
+
web_page.wait_for_timeout(3000)
|
|
149
|
+
continue
|
|
150
|
+
|
|
151
|
+
# 8. 处理鉴权页面
|
|
152
|
+
try:
|
|
153
|
+
if web_page.locator(selectors['auth_title']).is_visible(timeout=SHORT_TIMEOUT):
|
|
154
|
+
log('检测到鉴权页面,刷新重试', self.store_username, self.store_name)
|
|
155
|
+
web_page.reload()
|
|
156
|
+
web_page.wait_for_load_state('load')
|
|
157
|
+
web_page.wait_for_timeout(3000)
|
|
158
|
+
continue
|
|
159
|
+
except:
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
# 9. 处理SHEIN主页
|
|
163
|
+
if current_title == 'SHEIN':
|
|
164
|
+
log('当前在SHEIN主页,跳转到商家后台', self.store_username, self.store_name)
|
|
165
|
+
web_page.goto(home_url)
|
|
166
|
+
web_page.wait_for_load_state("load")
|
|
167
|
+
web_page.wait_for_timeout(3000)
|
|
168
|
+
continue
|
|
169
|
+
|
|
170
|
+
# 默认等待,然后继续下一轮检查
|
|
171
|
+
web_page.wait_for_load_state("load")
|
|
172
|
+
web_page.wait_for_timeout(SHORT_TIMEOUT)
|
|
173
|
+
|
|
174
|
+
# 如果跳出了内层while循环,说明达到了某种成功状态
|
|
175
|
+
log('内层循环结束,鉴权处理完成', self.store_username, self.store_name)
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
except Exception as e:
|
|
179
|
+
error_msg = str(e)
|
|
180
|
+
log(f"鉴权处理异常: {error_msg}, 第{retries}次重试", self.store_username, self.store_name)
|
|
181
|
+
|
|
182
|
+
# 处理页面崩溃或特定认证错误
|
|
183
|
+
if 'crashed' in error_msg.lower() or 'https://sso.geiwohuo.com//#/auth/SSLS' in web_page.url:
|
|
184
|
+
log('检测到页面崩溃或认证错误,重新导航到基础URL', self.store_username, self.store_name)
|
|
185
|
+
try:
|
|
186
|
+
web_page.goto(base_url)
|
|
187
|
+
web_page.wait_for_load_state('load')
|
|
188
|
+
web_page.wait_for_timeout(3000)
|
|
189
|
+
except Exception as nav_error:
|
|
190
|
+
log(f'重新导航失败: {nav_error}', self.store_username, self.store_name)
|
|
191
|
+
|
|
192
|
+
# 达到最大重试次数
|
|
193
|
+
if retries >= MAX_RETRIES:
|
|
194
|
+
log(f"达到最大重试次数{MAX_RETRIES},鉴权处理失败", self.store_username, self.store_name)
|
|
195
|
+
break
|
|
196
|
+
|
|
197
|
+
time.sleep(2) # 异常后等待2秒再重试
|
|
198
|
+
|
|
199
|
+
log('鉴权处理结束', self.store_username, self.store_name)
|
|
200
|
+
|
|
201
|
+
# 处理鉴权
|
|
202
|
+
def deal_auth_old(self):
|
|
32
203
|
web_page = self.web_page
|
|
33
204
|
|
|
34
205
|
# 定义最大重试次数
|
|
@@ -112,7 +283,7 @@ class SheinLib:
|
|
|
112
283
|
except Exception as e:
|
|
113
284
|
log(f"错误发生: {e}, 重试中...({self.store_username}, {self.store_name})")
|
|
114
285
|
if 'crashed' in str(e) or 'https://sso.geiwohuo.com//#/auth/SSLS' in web_page.url:
|
|
115
|
-
web_page.
|
|
286
|
+
web_page.goto('https://sso.geiwohuo.com')
|
|
116
287
|
web_page.wait_for_load_state('load')
|
|
117
288
|
web_page.wait_for_timeout(3000)
|
|
118
289
|
retries += 1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|