qrpa 1.0.88__py3-none-any.whl → 1.0.90__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.

Potentially problematic release.


This version of qrpa might be problematic. Click here for more details.

qrpa/db_migrator.py CHANGED
@@ -580,7 +580,7 @@ def create_default_migrator(silent: bool = False) -> DatabaseMigrator:
580
580
  )
581
581
 
582
582
  remote_config = RemoteConfig(
583
- ssh_host="git@e3",
583
+ ssh_host="git@ecslz",
584
584
  temp_dir="/tmp/db_migration",
585
585
  database=remote_db
586
586
  )
qrpa/fun_web.py CHANGED
@@ -146,6 +146,54 @@ def full_screen_shot(web_page: Page, config):
146
146
  web_page.screenshot(path=full_screenshot_image_path, full_page=True)
147
147
  return full_screenshot_image_path
148
148
 
149
+ def fetch_get(page: Page, url: str, headers: Optional[dict] = None, config: Optional[dict] = None) -> dict:
150
+ """
151
+ 发送 HTTP GET 请求,支持自定义 headers 和配置。
152
+
153
+ :param page: Playwright 的 Page 对象
154
+ :param url: 请求地址
155
+ :param headers: 自定义 headers 字典
156
+ :param config: 请求配置字典,可包含 credentials, mode, referrer, referrerPolicy 等
157
+ :return: 服务器返回的 JSON 响应(dict)
158
+ """
159
+ if headers is not None and not isinstance(headers, dict):
160
+ raise ValueError("headers 参数必须是 dict 或 None")
161
+ if config is not None and not isinstance(config, dict):
162
+ raise ValueError("config 参数必须是 dict 或 None")
163
+
164
+ try:
165
+ page.wait_for_load_state('load')
166
+ response = page.evaluate("""
167
+ async ({ url, extraHeaders, config }) => {
168
+ try {
169
+ const defaultHeaders = {
170
+ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
171
+ };
172
+
173
+ const defaultConfig = {
174
+ method: 'GET',
175
+ credentials: 'include',
176
+ mode: 'cors'
177
+ };
178
+
179
+ const headers = Object.assign({}, defaultHeaders, extraHeaders || {});
180
+ const options = Object.assign({}, defaultConfig, config || {}, { headers: headers });
181
+
182
+ const response = await fetch(url, options);
183
+ if (!response.ok) {
184
+ throw new Error(`HTTP ${response.status} - ${response.statusText}`);
185
+ }
186
+ return await response.json();
187
+ } catch (error) {
188
+ return { "error": "fetch_failed", "message": error.message };
189
+ }
190
+ }
191
+ """, {"url": url, "extraHeaders": headers, "config": config})
192
+
193
+ return response
194
+ except Exception as e:
195
+ raise send_exception()
196
+
149
197
  def safe_goto(page, url, **kwargs):
150
198
  caller = inspect.stack()[1]
151
199
  log(f"[DEBUG] goto called from {caller.filename}:{caller.lineno} url={url}")
qrpa/shein_lib.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from .fun_file import read_dict_from_file, write_dict_to_file, read_dict_from_file_ex, write_dict_to_file_ex
2
2
  from .fun_base import log, send_exception, md5_string, get_safe_value, NetWorkIdleTimeout
3
- from .fun_web import fetch, full_screen_shot, safe_goto
3
+ from .fun_web import fetch, fetch_get, full_screen_shot, safe_goto
4
4
  from .time_utils import TimeUtils
5
5
  from .wxwork import WxWorkBot
6
6
 
@@ -36,26 +36,37 @@ class SheinLib:
36
36
  try:
37
37
  current_url = web_page.url
38
38
  log(f"尝试获取页面信息 - URL: {current_url}", self.store_username, self.store_name)
39
-
39
+
40
+ # 检查是否在认证页面,如果是则直接跳转到目标页面
41
+ if '/auth/SSLS' in current_url:
42
+ log("检测到SSLS认证页面,直接跳转到首页", self.store_username, self.store_name)
43
+ web_page.goto('https://sso.geiwohuo.com/#/home', wait_until='domcontentloaded', timeout=15000)
44
+ web_page.wait_for_timeout(3000)
45
+ current_url = web_page.url
46
+ log(f"跳转后URL: {current_url}", self.store_username, self.store_name)
47
+
40
48
  # 等待导航完成
41
- web_page.wait_for_load_state("networkidle", timeout=8000)
42
-
49
+ web_page.wait_for_load_state("domcontentloaded", timeout=6000)
50
+
43
51
  final_url = web_page.url
44
52
  final_title = web_page.title()
45
53
  log(f"页面稳定 - URL: {final_url}, 标题: {final_title}", self.store_username, self.store_name)
46
54
  break
47
-
55
+
48
56
  except Exception as e:
49
- log(f"第{attempt+1}次等待页面稳定失败: {e}", self.store_username, self.store_name)
50
- if "destroyed" in str(e) or "navigation" in str(e):
57
+ log(f"第{attempt + 1}次等待页面稳定失败: {e}", self.store_username, self.store_name)
58
+ if "crashed" in str(e) or "Target" in str(e):
59
+ log("页面稳定检查时崩溃,直接继续", self.store_username, self.store_name)
60
+ break
61
+ elif "destroyed" in str(e) or "navigation" in str(e):
51
62
  log("检测到导航中断,等待导航完成", self.store_username, self.store_name)
52
- web_page.wait_for_timeout(5000)
63
+ web_page.wait_for_timeout(4000)
53
64
  continue
54
65
  elif attempt == 2:
55
66
  log("页面稳定等待最终失败,继续执行", self.store_username, self.store_name)
56
67
  break
57
68
  web_page.wait_for_timeout(2000)
58
-
69
+
59
70
  web_page.wait_for_timeout(2000)
60
71
 
61
72
  # 定义最大重试次数
@@ -72,13 +83,23 @@ class SheinLib:
72
83
  retries += 1
73
84
 
74
85
  while not web_page.locator('//div[contains(text(),"商家后台")]').nth(1).is_visible():
75
-
86
+
76
87
  try:
77
88
  current_url = web_page.url
78
89
  current_title = web_page.title()
79
90
  log(f"循环检查 - URL: {current_url}, 标题: {current_title}", self.store_username, self.store_name)
91
+
92
+ # 如果在认证页面且出现问题,直接跳转
93
+ if '/auth/SSLS' in current_url:
94
+ log("在主循环中检测到SSLS认证页面,跳转到首页", self.store_username, self.store_name)
95
+ web_page.goto('https://sso.geiwohuo.com/#/home', wait_until='domcontentloaded', timeout=15000)
96
+ web_page.wait_for_timeout(3000)
97
+ continue
98
+
80
99
  except Exception as status_error:
81
100
  log(f"获取页面状态失败: {status_error}", self.store_username, self.store_name)
101
+ if "crashed" in str(status_error):
102
+ break
82
103
 
83
104
  if web_page.locator('xpath=//div[@id="container" and @alita-name="gmpsso"]//button[@type="button" and @id]').nth(0).is_visible():
84
105
  if 'https://sso.geiwohuo.com/#/home' not in web_page.url:
@@ -179,13 +200,13 @@ class SheinLib:
179
200
  except Exception as e:
180
201
  log(f"错误发生: {e}, 重试中...({self.store_username}, {self.store_name})")
181
202
  log(traceback.format_exc())
182
-
203
+
183
204
  # 收集崩溃时的详细信息
184
205
  try:
185
206
  crash_url = web_page.url
186
207
  crash_title = web_page.title()
187
208
  log(f"崩溃时页面信息 - URL: {crash_url}, 标题: {crash_title}", self.store_username, self.store_name)
188
-
209
+
189
210
  # 尝试截图保存崩溃现场
190
211
  try:
191
212
  screenshot_path = f"crash_screenshot_{self.store_username}_{int(time.time())}.png"
@@ -193,19 +214,19 @@ class SheinLib:
193
214
  log(f"已保存崩溃截图: {screenshot_path}", self.store_username, self.store_name)
194
215
  except:
195
216
  log("无法截取崩溃时的页面截图", self.store_username, self.store_name)
196
-
217
+
197
218
  except:
198
219
  log("无法获取崩溃时的页面信息", self.store_username, self.store_name)
199
-
220
+
200
221
  # 检查特定类型的错误
201
222
  if any(keyword in str(e).lower() for keyword in ['memory', 'out of memory', 'oom']):
202
223
  log("检测到内存相关崩溃", self.store_username, self.store_name)
203
-
224
+
204
225
  if "destroyed" in str(e) or "navigation" in str(e):
205
226
  log("检测到导航中断,等待页面稳定后重试", self.store_username, self.store_name)
206
227
  web_page.wait_for_timeout(5000)
207
228
  continue
208
-
229
+
209
230
  if 'crashed' in str(e) or 'Target' in str(e):
210
231
  log("检测到页面或目标崩溃,直接退出当前循环", self.store_username, self.store_name)
211
232
  raise e
@@ -220,6 +241,44 @@ class SheinLib:
220
241
  # web_page.wait_for_load_state("networkidle")
221
242
  web_page.wait_for_timeout(3000)
222
243
 
244
+ # 获取用户信息
245
+ def get_user(self, uuid=None):
246
+ log(f'获取用户信息:{self.store_username} {self.store_name}')
247
+
248
+ # 生成 uuid 参数,如果没有提供则使用时间戳
249
+ if uuid is None:
250
+ import time
251
+ uuid = str(int(time.time() * 1000))
252
+
253
+ url = f"https://sso.geiwohuo.com/sso-prefix/auth/getUser?uuid={uuid}"
254
+
255
+ # 设置请求头,根据 Chrome 请求
256
+ headers = {
257
+ "gmpsso-language": "CN",
258
+ "origin-url" : "https://sso.geiwohuo.com/#/home/",
259
+ "x-sso-scene" : "gmpsso"
260
+ }
261
+
262
+ # 特定于此请求的配置
263
+ fetch_config = {
264
+ "credentials" : "omit",
265
+ "referrer" : "https://sso.geiwohuo.com/",
266
+ "referrerPolicy": "strict-origin-when-cross-origin"
267
+ }
268
+
269
+ response_text = fetch_get(self.web_page, url, headers, fetch_config)
270
+ error_code = response_text.get('code')
271
+ if str(error_code) != '0':
272
+ raise send_exception(json.dumps(response_text, ensure_ascii=False))
273
+ info = response_text.get('info', {})
274
+ log(info)
275
+ cache_file = f'{self.config.auto_dir}/shein_user.json'
276
+ info['store_username'] = self.store_username
277
+ info['store_name'] = self.store_name
278
+ write_dict_to_file_ex(cache_file, {self.store_username: info}, [self.store_username])
279
+
280
+ return info
281
+
223
282
  # 获取质检报告pdf地址
224
283
  def get_qc_report_url(self, deliverCode, purchaseCode):
225
284
  log(f'获取质检报告:{deliverCode} {purchaseCode}')
qrpa/shein_ziniao.py CHANGED
@@ -5,7 +5,7 @@
5
5
  import os
6
6
  import platform
7
7
  import shutil
8
- import time
8
+ import time, datetime
9
9
  import traceback
10
10
  import uuid
11
11
  import json
@@ -74,7 +74,7 @@ class ZiniaoClient:
74
74
  """启动客户端"""
75
75
  try:
76
76
  if self.is_windows:
77
- cmd = [self.client_path, '--run_type=web_driver', '--ipc_type=http', '--port=' + str(self.socket_port)]
77
+ cmd = [self.client_path, '--run_type=web_driver', '--show_sidb=true', '--ipc_type=http', '--port=' + str(self.socket_port)]
78
78
  elif self.is_mac:
79
79
  cmd = ['open', '-a', self.client_path, '--args', '--run_type=web_driver', '--ipc_type=http',
80
80
  '--port=' + str(self.socket_port)]
@@ -272,6 +272,73 @@ class ZiniaoTaskManager:
272
272
  self.browser = browser
273
273
  self.config = config
274
274
 
275
+ def daily_cleanup_superbrowser(self, browser_id):
276
+ """
277
+ 每天删除一次SuperBrowser缓存文件夹
278
+
279
+ Args:
280
+ browser_id (str): 浏览器ID,如 '26986387919128'
281
+ """
282
+
283
+ # 获取本地AppData路径
284
+ local_appdata = os.getenv('LOCALAPPDATA')
285
+ if not local_appdata:
286
+ log("错误: 无法获取LOCALAPPDATA路径")
287
+ return False
288
+
289
+ # 构建路径
290
+ cache_path = os.path.join(local_appdata, 'SuperBrowser')
291
+ target_folder = os.path.join(cache_path, f'User Data\\Chromium_{browser_id}')
292
+ flag_file = os.path.join(cache_path, f'User Data\\cleanup_flag_{browser_id}.txt')
293
+
294
+ # 检查目标文件夹是否存在
295
+ if not os.path.exists(target_folder):
296
+ log(f"目标文件夹不存在: {target_folder}")
297
+ return False
298
+
299
+ # 获取当前日期
300
+ today = datetime.date.today()
301
+ today_str = today.strftime('%Y-%m-%d')
302
+
303
+ # 检查标志文件
304
+ need_cleanup = True
305
+
306
+ if os.path.exists(flag_file):
307
+ try:
308
+ # 读取标志文件中的日期
309
+ with open(flag_file, 'r', encoding='utf-8') as f:
310
+ last_cleanup_date = f.read().strip()
311
+
312
+ # 如果是今天已经清理过,则跳过
313
+ if last_cleanup_date == today_str:
314
+ log(f"今天({today_str})已经清理过,跳过删除操作")
315
+ need_cleanup = False
316
+
317
+ except Exception as e:
318
+ log(f"读取标志文件时出错: {e}")
319
+ # 如果读取出错,继续执行清理
320
+
321
+ if need_cleanup:
322
+ try:
323
+ # 删除目标文件夹
324
+ log(f"正在删除文件夹: {target_folder}")
325
+ shutil.rmtree(target_folder)
326
+ log("删除成功!")
327
+
328
+ # 创建/更新标志文件
329
+ os.makedirs(os.path.dirname(flag_file), exist_ok=True)
330
+ with open(flag_file, 'w', encoding='utf-8') as f:
331
+ f.write(today_str)
332
+
333
+ log(f"已创建标志文件: {flag_file}")
334
+ return True
335
+
336
+ except Exception as e:
337
+ log(f"删除文件夹时出错: {e}")
338
+ return False
339
+
340
+ return True
341
+
275
342
  def run_single_store_task(self, browser_info: Dict[str, Any],
276
343
  run_func: Callable, task_key: str,
277
344
  just_store_username: Optional[List[str]] = None,
@@ -282,6 +349,10 @@ class ZiniaoTaskManager:
282
349
  store_name = browser_info.get("browserName")
283
350
  store_username = browser_info.get("store_username")
284
351
 
352
+ # 删除浏览器缓存,一天一删
353
+ browser_id = browser_info.get("browserId")
354
+ self.daily_cleanup_superbrowser(browser_id)
355
+
285
356
  retry_count = 0
286
357
  while True:
287
358
  try:
qrpa/time_utils.py CHANGED
@@ -260,20 +260,31 @@ class TimeUtils:
260
260
 
261
261
  @staticmethod
262
262
  def get_dates_from_first_of_month_to_yesterday() -> List[str]:
263
- """获取从本月第一天到昨天的日期列表"""
263
+ """获取从本月第一天到昨天的日期列表
264
+ 如果今天是本月第一天,则返回上个月的日期列表
265
+ """
264
266
  today = datetime.today()
265
- first_day_of_month = today.replace(day=1)
266
267
  yesterday = today - timedelta(days=1)
267
268
 
268
- date_list = []
269
- current_date = first_day_of_month
269
+ # 如果今天是本月第一天,取上个月
270
+ if today.day == 1:
271
+ # 找到上个月最后一天
272
+ last_month_last_day = today - timedelta(days=1)
273
+ # 上个月第一天
274
+ first_day_of_last_month = last_month_last_day.replace(day=1)
275
+ start_date = first_day_of_last_month
276
+ end_date = last_month_last_day
277
+ else:
278
+ start_date = today.replace(day=1)
279
+ end_date = yesterday
270
280
 
271
- while current_date <= yesterday:
281
+ date_list = []
282
+ current_date = start_date
283
+ while current_date <= end_date:
272
284
  date_list.append(current_date.strftime('%Y-%m-%d'))
273
285
  current_date += timedelta(days=1)
274
286
 
275
287
  return date_list
276
-
277
288
  # ==================== 月份相关 ====================
278
289
 
279
290
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qrpa
3
- Version: 1.0.88
3
+ Version: 1.0.90
4
4
  Summary: qsir's rpa library
5
5
  Author: QSir
6
6
  Author-email: QSir <1171725650@qq.com>
@@ -1,30 +1,30 @@
1
1
  qrpa/RateLimitedSender.py,sha256=hqvb1qspDFaW4RsLuVufylOrefkMgixANKeBaGEqYb4,1421
2
2
  qrpa/__init__.py,sha256=FjyNLjEnR4Y9_0scrWy7foJmDI4oiqSNyeAohPQTQvE,1148
3
- qrpa/db_migrator.py,sha256=2VmhzcMsU0MKpl-mNCwKyV8tLTqyEysSpP27-S_rQZ8,21862
3
+ qrpa/db_migrator.py,sha256=6lz1MdSQzN3Co00uwy0EwIXjWrblgL39hHJUw18sf5I,21865
4
4
  qrpa/feishu_bot_app.py,sha256=6r2YqCAMUN7y3F7onoABRmHC7S-UPOLHwbhsQnQ3IRc,9452
5
5
  qrpa/feishu_client.py,sha256=gXvyhf7r-IqeDhPjM01SfsGf17t8g1ZwUAkhymBkBeg,17544
6
6
  qrpa/feishu_logic.py,sha256=yuwb-LeZiHKGlz-W8JobinorHonVa8L-5h12WxnU7_Q,67508
7
7
  qrpa/fun_base.py,sha256=lMzqPwsbVfe968CUR2MVNImzIskIUZqPCE2tWxYqb5o,10728
8
8
  qrpa/fun_excel.py,sha256=-GFtjg0LeiiXrpTU5RtMlBUHl05mqzdHCzIxlssT59U,117385
9
9
  qrpa/fun_file.py,sha256=yzjDV16WL5vRys7J4uQcNzIFkX4D5MAlSCwxcD-mwQo,11966
10
- qrpa/fun_web.py,sha256=Ep2ddMwJix_KNpCiHEjSo7tWu-g55Nlu1QtOXJf6iZw,6531
10
+ qrpa/fun_web.py,sha256=Mv0m2P5fh2-U4DzRoVivqRcXD56BJ_Svj6TZznx49YU,8645
11
11
  qrpa/fun_win.py,sha256=-LnTeocdTt72NVH6VgLdpAT9_C5oV9okeudXG6CftMA,8034
12
12
  qrpa/shein_daily_report_model.py,sha256=H8oZmIN5Pyqe306W1_xuz87lOqLQ_LI5RjXbaxDkIzE,12589
13
13
  qrpa/shein_excel.py,sha256=1F9QMMsaihDfwr0ajSSQdXksBCjuKSPwMynfP7Jlkso,124825
14
- qrpa/shein_lib.py,sha256=On9tT6vVeOKH56ukgYrT7eVcnqo_0bgghWFhpvr5z5c,131491
14
+ qrpa/shein_lib.py,sha256=5rsxoS8rI8xKprjEVmEzaNRrTcCm4FU2eGcpfhf-TzE,134142
15
15
  qrpa/shein_mysql.py,sha256=Sgz6U0_3f4cT5zPf1Ht1OjvSFhrVPLkMxt91NV-ZPCM,3005
16
16
  qrpa/shein_sqlite.py,sha256=ZQwD0Gz81q9WY7tY2HMEYvSF9r3N_G_Aur3bYfST9WY,5707
17
- qrpa/shein_ziniao.py,sha256=hjEgK5gNWytAkvTvJQKEy3A3L-R0sArgnSBk85m41ow,18703
17
+ qrpa/shein_ziniao.py,sha256=4CsOPZXRDEqHJvCGyb8imCPqSH5Ln5fGqPUFqLuFgCg,21276
18
18
  qrpa/temu_chrome.py,sha256=CbtFy1QPan9xJdJcNZj-EsVGhUvv3ZTEPVDEA4-im40,2803
19
19
  qrpa/temu_excel.py,sha256=2hGw76YWzkTZGyFCuuUAab4oHptYX9a6U6yjpNsL7FE,6990
20
20
  qrpa/temu_lib.py,sha256=hYB59zsLS3m3NTic_duTwPMOTSxlHyQVa8OhHnHm-1g,7199
21
- qrpa/time_utils.py,sha256=ef0hhbN_6b-gcnz5ETIVOoxemIMvcxGVGGIRnHnGaBo,29564
21
+ qrpa/time_utils.py,sha256=KFXnF1pMWACAKOSy483riPqvTsMjhQtFJAIhQsZKaMk,30075
22
22
  qrpa/time_utils_example.py,sha256=shHOXKKF3QSzb0SHsNc34M61wEkkLuM30U9X1THKNS8,8053
23
23
  qrpa/wxwork.py,sha256=gIytG19DZ5g7Tsl0-W3EbjfSnpIqZw-ua24gcB78YEg,11264
24
24
  qrpa/mysql_module/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  qrpa/mysql_module/shein_product_model.py,sha256=qViI_Ik3SkXXxqJ1nXjimvfB_a5uiwW9RXL0fOreBao,18880
26
26
  qrpa/mysql_module/shein_return_order_model.py,sha256=Zt-bGOH_kCDbakW7uaTmqqo_qTT8v424yidcYSfWvWM,26562
27
- qrpa-1.0.88.dist-info/METADATA,sha256=-ppgc5XGhffpjLxLSBlNMLy9OKv7BGMjpS0BG7rgo-c,231
28
- qrpa-1.0.88.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
- qrpa-1.0.88.dist-info/top_level.txt,sha256=F6T5igi0fhXDucPPUbmmSC0qFCDEsH5eVijfVF48OFU,5
30
- qrpa-1.0.88.dist-info/RECORD,,
27
+ qrpa-1.0.90.dist-info/METADATA,sha256=5_HyMIZcanl7560yxaMsi9O4YjuP2nlvjtMIzsSEJXM,231
28
+ qrpa-1.0.90.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
+ qrpa-1.0.90.dist-info/top_level.txt,sha256=F6T5igi0fhXDucPPUbmmSC0qFCDEsH5eVijfVF48OFU,5
30
+ qrpa-1.0.90.dist-info/RECORD,,
File without changes