qrpa 1.0.89__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,7 +36,7 @@ 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
40
  # 检查是否在认证页面,如果是则直接跳转到目标页面
41
41
  if '/auth/SSLS' in current_url:
42
42
  log("检测到SSLS认证页面,直接跳转到首页", self.store_username, self.store_name)
@@ -44,17 +44,17 @@ class SheinLib:
44
44
  web_page.wait_for_timeout(3000)
45
45
  current_url = web_page.url
46
46
  log(f"跳转后URL: {current_url}", self.store_username, self.store_name)
47
-
47
+
48
48
  # 等待导航完成
49
49
  web_page.wait_for_load_state("domcontentloaded", timeout=6000)
50
-
50
+
51
51
  final_url = web_page.url
52
52
  final_title = web_page.title()
53
53
  log(f"页面稳定 - URL: {final_url}, 标题: {final_title}", self.store_username, self.store_name)
54
54
  break
55
-
55
+
56
56
  except Exception as e:
57
- log(f"第{attempt+1}次等待页面稳定失败: {e}", self.store_username, self.store_name)
57
+ log(f"第{attempt + 1}次等待页面稳定失败: {e}", self.store_username, self.store_name)
58
58
  if "crashed" in str(e) or "Target" in str(e):
59
59
  log("页面稳定检查时崩溃,直接继续", self.store_username, self.store_name)
60
60
  break
@@ -66,7 +66,7 @@ class SheinLib:
66
66
  log("页面稳定等待最终失败,继续执行", self.store_username, self.store_name)
67
67
  break
68
68
  web_page.wait_for_timeout(2000)
69
-
69
+
70
70
  web_page.wait_for_timeout(2000)
71
71
 
72
72
  # 定义最大重试次数
@@ -83,19 +83,19 @@ class SheinLib:
83
83
  retries += 1
84
84
 
85
85
  while not web_page.locator('//div[contains(text(),"商家后台")]').nth(1).is_visible():
86
-
86
+
87
87
  try:
88
88
  current_url = web_page.url
89
89
  current_title = web_page.title()
90
90
  log(f"循环检查 - URL: {current_url}, 标题: {current_title}", self.store_username, self.store_name)
91
-
91
+
92
92
  # 如果在认证页面且出现问题,直接跳转
93
93
  if '/auth/SSLS' in current_url:
94
94
  log("在主循环中检测到SSLS认证页面,跳转到首页", self.store_username, self.store_name)
95
95
  web_page.goto('https://sso.geiwohuo.com/#/home', wait_until='domcontentloaded', timeout=15000)
96
96
  web_page.wait_for_timeout(3000)
97
97
  continue
98
-
98
+
99
99
  except Exception as status_error:
100
100
  log(f"获取页面状态失败: {status_error}", self.store_username, self.store_name)
101
101
  if "crashed" in str(status_error):
@@ -200,13 +200,13 @@ class SheinLib:
200
200
  except Exception as e:
201
201
  log(f"错误发生: {e}, 重试中...({self.store_username}, {self.store_name})")
202
202
  log(traceback.format_exc())
203
-
203
+
204
204
  # 收集崩溃时的详细信息
205
205
  try:
206
206
  crash_url = web_page.url
207
207
  crash_title = web_page.title()
208
208
  log(f"崩溃时页面信息 - URL: {crash_url}, 标题: {crash_title}", self.store_username, self.store_name)
209
-
209
+
210
210
  # 尝试截图保存崩溃现场
211
211
  try:
212
212
  screenshot_path = f"crash_screenshot_{self.store_username}_{int(time.time())}.png"
@@ -214,19 +214,19 @@ class SheinLib:
214
214
  log(f"已保存崩溃截图: {screenshot_path}", self.store_username, self.store_name)
215
215
  except:
216
216
  log("无法截取崩溃时的页面截图", self.store_username, self.store_name)
217
-
217
+
218
218
  except:
219
219
  log("无法获取崩溃时的页面信息", self.store_username, self.store_name)
220
-
220
+
221
221
  # 检查特定类型的错误
222
222
  if any(keyword in str(e).lower() for keyword in ['memory', 'out of memory', 'oom']):
223
223
  log("检测到内存相关崩溃", self.store_username, self.store_name)
224
-
224
+
225
225
  if "destroyed" in str(e) or "navigation" in str(e):
226
226
  log("检测到导航中断,等待页面稳定后重试", self.store_username, self.store_name)
227
227
  web_page.wait_for_timeout(5000)
228
228
  continue
229
-
229
+
230
230
  if 'crashed' in str(e) or 'Target' in str(e):
231
231
  log("检测到页面或目标崩溃,直接退出当前循环", self.store_username, self.store_name)
232
232
  raise e
@@ -241,6 +241,44 @@ class SheinLib:
241
241
  # web_page.wait_for_load_state("networkidle")
242
242
  web_page.wait_for_timeout(3000)
243
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
+
244
282
  # 获取质检报告pdf地址
245
283
  def get_qc_report_url(self, deliverCode, purchaseCode):
246
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.89
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=H6WDWkOwVHKAAruUTKGl4bAa-Sa51ymquJ3_uTsP-G4,132924
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.89.dist-info/METADATA,sha256=bv8NCzAvKtdDjkXzd5ofc3SD0StLMNGPGtjuZIjZAHc,231
28
- qrpa-1.0.89.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
- qrpa-1.0.89.dist-info/top_level.txt,sha256=F6T5igi0fhXDucPPUbmmSC0qFCDEsH5eVijfVF48OFU,5
30
- qrpa-1.0.89.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