python-qlv-helper 0.5.7__py3-none-any.whl → 0.7.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.
@@ -11,8 +11,7 @@
11
11
  """
12
12
  from typing import Tuple, Union
13
13
  from playwright_helper.libs.base_po import BasePo
14
- from qlv_helper.utils.ocr_helper import get_image_text
15
- from playwright.async_api import Page, TimeoutError as PlaywrightTimeoutError, Locator
14
+ from playwright.async_api import Page, TimeoutError as PlaywrightTimeoutError, Locator, ElementHandle
16
15
 
17
16
 
18
17
  class LoginPage(BasePo):
@@ -22,104 +21,67 @@ class LoginPage(BasePo):
22
21
  super().__init__(page, url)
23
22
  self.__page = page
24
23
 
25
- async def get_login_username_input(self, timeout: float = 5.0) -> Tuple[bool, Union[Locator, str]]:
24
+ async def get_login_username_input(self, timeout: float = 5.0) -> Locator:
26
25
  """
27
26
  获取登录页面的用户名输入框
28
27
  :param timeout: 超时时间(秒)
29
28
  :return: (是否存在, 错误信息|元素对象)
30
- :return:
31
29
  """
32
30
  selector: str = '//input[@id="UserName"]'
33
- try:
34
- locator = self.__page.locator(selector)
35
- if locator:
36
- await locator.wait_for(state='visible', timeout=timeout * 1000)
37
- return True, locator
38
- else:
39
- return False, '没有找到登录页面中的【用户名】输入框'
40
- except PlaywrightTimeoutError:
41
- return False, f"元素 '{selector}' 未在 {timeout} 秒内找到"
42
- except Exception as e:
43
- return False, f"检查元素时发生错误: {str(e)}"
31
+ return await self.get_locator(selector=selector, timeout=timeout)
44
32
 
45
- async def get_login_password_input(self, timeout: float = 5.0) -> Tuple[bool, Union[Locator, str]]:
33
+ async def get_login_password_input(self, timeout: float = 5.0) -> Locator:
46
34
  """
47
35
  获取登录页面的密码输入框
48
36
  :param timeout: 超时时间(秒)
49
37
  :return: (是否存在, 错误信息|元素对象)
50
- :return:
51
38
  """
52
39
  selector: str = '//input[@id="Password"]'
53
- try:
54
- locator = self.__page.locator(selector)
55
- if locator:
56
- await locator.wait_for(state='visible', timeout=timeout * 1000)
57
- return True, locator
58
- else:
59
- return False, '没有找到登录页面中的【密码】输入框'
60
- except PlaywrightTimeoutError:
61
- return False, f"元素 '{selector}' 未在 {timeout} 秒内找到"
62
- except Exception as e:
63
- return False, f"检查元素时发生错误: {str(e)}"
40
+ return await self.get_locator(selector=selector, timeout=timeout)
64
41
 
65
- async def get_login_number_code_input(self, timeout: float = 5.0) -> Tuple[bool, Union[Locator, str]]:
42
+ async def get_captcha(self, timeout: float = 5.0) -> Locator:
66
43
  """
67
- 获取登录页面的数字验证码输入框,第一层验证码
44
+ 获取验证码Locator对象
68
45
  :param timeout: 超时时间(秒)
69
46
  :return: (是否存在, 错误信息|元素对象)
70
- :return:
71
47
  """
72
- selector: str = '//input[@id="Code"]'
73
- try:
74
- locator = self.__page.locator(selector)
75
- if locator:
76
- await locator.wait_for(state='visible', timeout=timeout * 1000)
77
- return True, locator
78
- else:
79
- return False, '没有找到登录页面中的【数字验证码】输入框'
80
- except PlaywrightTimeoutError:
81
- return False, f"元素 '{selector}' 未在 {timeout} 秒内找到"
82
- except Exception as e:
83
- return False, f"检查元素时发生错误: {str(e)}"
48
+ selector: str = 'xpath=//div[@class="form_row"]/img[@onclick="this.src=this.src+\'?\'"]'
49
+ return await self.get_locator(selector=selector, timeout=timeout)
84
50
 
85
- async def get_login_btn(self, timeout: float = 5.0) -> Tuple[bool, Union[Locator, str]]:
51
+ @staticmethod
52
+ async def get_captcha_type(locator: Locator, timeout: float = 5.0) -> int:
86
53
  """
87
- 获取登录页面的登录按钮
54
+ 获取验证码的类型
55
+ :param locator: 验证码Locator对象
88
56
  :param timeout: 超时时间(秒)
89
57
  :return: (是否存在, 错误信息|元素对象)
90
- :return:
91
58
  """
92
- selector: str = '//input[@class="login-btn"]'
93
- try:
94
- locator = self.__page.locator(selector)
95
- if locator:
96
- await locator.wait_for(state='visible', timeout=timeout * 1000)
97
- return True, locator
98
- else:
99
- return False, '没有找到登录页面中的【登录】按钮'
100
- except PlaywrightTimeoutError:
101
- return False, f"元素 '{selector}' 未在 {timeout} 秒内找到"
102
- except Exception as e:
103
- return False, f"检查元素时发生错误: {str(e)}"
59
+ img_src: str = await locator.get_attribute(name="src", timeout=timeout)
60
+ img_src_slice = img_src.split("=")
61
+ return int(img_src_slice[-1][0])
104
62
 
105
- async def get_number_code(self, timeout: float = 5.0) -> Tuple[bool, str]:
63
+ async def get_captcha_image(self, timeout: float = 5.0) -> ElementHandle:
106
64
  selector: str = '//div[@id="signup_forms"]//img'
107
- return await get_image_text(page=self.__page, selector=selector, timeout=timeout)
65
+ locator: Locator = await self.get_locator(selector=selector, timeout=timeout)
66
+ return await locator.element_handle(timeout=timeout * 1000)
108
67
 
109
- async def is_exist_login_warn(self, timeout: float = 5.0) -> bool:
110
- selector: str = '//p[@class="login_warn"]'
111
- try:
112
- locator = self.__page.locator(selector)
113
- if locator:
114
- text: str = await locator.text_content(timeout=timeout * 1000)
115
- if text.strip() != "":
116
- return True
117
- else:
118
- return False
119
- else:
120
- return False
121
- except (PlaywrightTimeoutError, Exception):
122
- return False
68
+ async def get_login_captcha_input(self, timeout: float = 5.0) -> Locator:
69
+ """
70
+ 获取登录页面的验证码输入框
71
+ :param timeout: 超时时间(秒)
72
+ :return: (是否存在, 错误信息|元素对象)
73
+ """
74
+ selector: str = '//input[@id="Code"]'
75
+ return await self.get_locator(selector=selector, timeout=timeout)
76
+
77
+ async def get_login_btn(self, timeout: float = 5.0) -> Locator:
78
+ """
79
+ 获取登录页面的登录按钮
80
+ :param timeout: 超时时间(秒)
81
+ :return: (是否存在, 错误信息|元素对象)
82
+ """
83
+ selector: str = '//input[@class="login-btn"]'
84
+ return await self.get_locator(selector=selector, timeout=timeout)
123
85
 
124
86
  async def get_wechat_entrance(self, timeout: float = 5.0) -> Tuple[bool, Union[Locator, str]]:
125
87
  selector: str = '//img[@src="/images/weixin.png"]'
@@ -9,9 +9,8 @@
9
9
  # Copyright ©2011-2025. Hunan xxxxxxx Company limited. All rights reserved.
10
10
  # ---------------------------------------------------------------------------------------------------------
11
11
  """
12
- from typing import Tuple, Union
12
+ from playwright.async_api import Page, Locator
13
13
  from playwright_helper.libs.base_po import BasePo
14
- from playwright.async_api import Page, TimeoutError as PlaywrightTimeoutError, Locator
15
14
 
16
15
 
17
16
  class MainPage(BasePo):
@@ -23,48 +22,18 @@ class MainPage(BasePo):
23
22
  self.url = url
24
23
  self.__page = page
25
24
 
26
- async def get_confirm_btn_with_system_notice_dialog(self, timeout: float = 5.0) -> Tuple[bool, Union[Locator, str]]:
25
+ async def get_confirm_btn_with_system_notice_dialog(self, timeout: float = 5.0) -> Locator:
27
26
  """
28
27
  获取系统通知弹框中的确认按钮,注意这个地方,存在多个叠加的弹框,因此用last()方法,只需定位到最上面的那个弹框就行
29
28
  :return:
30
29
  """
31
30
  selector: str = "//div[@class='CommonAlert'][last()]//a[@class='CommonAlertBtnConfirm']"
32
- try:
33
- locator = self.__page.locator(selector)
34
- if locator:
35
- await locator.wait_for(state='visible', timeout=timeout * 1000)
36
- return True, locator
37
- else:
38
- return False, '没有找到首页中的【系统提醒-确定】按钮'
39
- except PlaywrightTimeoutError:
40
- return False, f"元素 '{selector}' 未在 {timeout} 秒内找到"
41
- except Exception as e:
42
- return False, f"检查元素时发生错误: {str(e)}"
31
+ return await self.get_locator(selector=selector, timeout=timeout)
43
32
 
44
- async def get_level1_menu_order_checkout(self, timeout: float = 5.0) -> Tuple[bool, Union[Locator, str]]:
33
+ async def get_level1_menu_order_checkout(self, timeout: float = 5.0) -> Locator:
45
34
  selector: str = "//span[contains(normalize-space(), '订单出票')]"
46
- try:
47
- locator = self.__page.locator(selector)
48
- if locator:
49
- await locator.wait_for(state='visible', timeout=timeout * 1000)
50
- return True, locator
51
- else:
52
- return False, '没有找到首页中的【订单出票】左侧一级导航菜单'
53
- except PlaywrightTimeoutError:
54
- return False, f"元素 '{selector}' 未在 {timeout} 秒内找到"
55
- except Exception as e:
56
- return False, f"检查元素时发生错误: {str(e)}"
35
+ return await self.get_locator(selector=selector, timeout=timeout)
57
36
 
58
- async def get_level2_menu_order_checkout(self, timeout: float = 5.0) -> Tuple[bool, Union[Locator, str]]:
37
+ async def get_level2_menu_order_checkout(self, timeout: float = 5.0) -> Locator:
59
38
  selector: str = "//a[@menuname='国内活动订单']"
60
- try:
61
- locator = self.__page.locator(selector)
62
- if locator:
63
- await locator.wait_for(state='visible', timeout=timeout * 1000)
64
- return True, locator
65
- else:
66
- return False, '没有找到首页中的【国内活动订单】左侧二级导航菜单'
67
- except PlaywrightTimeoutError:
68
- return False, f"元素 '{selector}' 未在 {timeout} 秒内找到"
69
- except Exception as e:
70
- return False, f"检查元素时发生错误: {str(e)}"
39
+ return await self.get_locator(selector=selector, timeout=timeout)
@@ -273,3 +273,20 @@ class OrderDetailPage(BasePo):
273
273
  """
274
274
  selector: str = '//legend//input[@name="PolicyName"]'
275
275
  return await self.get_locator(selector=selector, timeout=timeout)
276
+
277
+ async def get_purchase_info_transaction_id(self, timeout: float = 5.0) -> List[str]:
278
+ """
279
+ 获取订单详情页面的采购信息流水
280
+ :param timeout:
281
+ :return:
282
+ """
283
+ # selector: str = '//tr[@class="PurchaseInfoClass"]'
284
+ selector: str = '//table[@id="PurchaseInfos"]/tbody/tr'
285
+ loc: Locator = await self.get_locator(selector=selector, timeout=timeout)
286
+ locators = await loc.all()
287
+ transaction_ids = list()
288
+ for locator in locators:
289
+ transaction_id = await locator.get_attribute("transactionid")
290
+ if transaction_id:
291
+ transaction_ids.append(transaction_id.strip())
292
+ return transaction_ids
@@ -9,11 +9,13 @@
9
9
  # Copyright ©2011-2025. Hunan xxxxxxx Company limited. All rights reserved.
10
10
  # ---------------------------------------------------------------------------------------------------------
11
11
  """
12
+ import json
13
+ import asyncio
12
14
  import ddddocr
13
15
  import requests
14
- from typing import Union, Tuple
16
+ from typing import Tuple
15
17
  from aiohttp import ClientSession
16
- from playwright.async_api import Page, TimeoutError as PlaywrightTimeoutError
18
+ from ocr_helper.core.baidu import ImageContentOCR
17
19
 
18
20
  # 复用 OCR 实例,不用每次都重新加载模型(更快)
19
21
  _ocr = ddddocr.DdddOcr(show_ad=False)
@@ -39,45 +41,38 @@ async def async_fetch_and_ocr_captcha(url: str) -> Tuple[str, bytes]:
39
41
  return result, img_bytes
40
42
 
41
43
 
42
- def recognize_captcha(image: Union[str, bytes]) -> str:
43
- """
44
- 识别验证码图片,返回识别文本。
45
- 参数:
46
- image: 图片路径 str,或图片的二进制 bytes
47
- 返回:
48
- 识别出的验证码字符串
49
- """
50
- try:
51
- # 如果是路径,读取文件
52
- if isinstance(image, str):
53
- with open(image, "rb") as f:
54
- img_bytes = f.read()
44
+ async def get_image_text(image_path: str, captcha_type: int, api_key, secret_key: str) -> str:
45
+ if captcha_type == 0:
46
+ with open(image_path, "rb") as f:
47
+ img_bytes = f.read()
48
+ for _ in range(100):
49
+ text = _ocr.classification(img_bytes).strip()
50
+ if len(text) == 4:
51
+ return text
52
+ raise RuntimeError("ddddocr识别验证码失败")
53
+ else:
54
+ api = ImageContentOCR(api_key=api_key, secret_key=secret_key)
55
+ response = await api.get_access_token(is_end=False)
56
+ if not response.get("access_token"):
57
+ raise RuntimeError(f"获取百度API的认证Token失败,原因:{response}")
58
+ token = response.get("access_token")
59
+ response = await api.submit_request(
60
+ question='图片中的文字是什么,如果含有运算信息,请将运算结果返回。注意给我返回一个json格式数据包,例如:{"content":"xxxx", result: xxx}, 如果无运算信息,设置为空串就行',
61
+ image_path=image_path,
62
+ token=token,
63
+ is_end=False
64
+ )
65
+ task_id: str = response.get("result", dict()).get("task_id")
66
+ if not task_id:
67
+ raise RuntimeError(f"提交图片至百度API接口失败,原因:{response}")
68
+ await asyncio.sleep(delay=10)
69
+ response = await api.get_result(task_id=task_id, token=token, is_end=True)
70
+ if response.get("result").get("ret_code") == 0 and response.get("result").get("ret_msg") == "success":
71
+ description = response.get("result").get("description")
72
+ description = json.loads(description[description.find("{"):description.find("}") + 1])
73
+ if description.get("result"):
74
+ return str(description.get("result")).strip()
75
+ else:
76
+ return description.get("content").strip()
55
77
  else:
56
- img_bytes = image
57
-
58
- result = _ocr.classification(img_bytes)
59
- return result
60
-
61
- except Exception as e:
62
- raise RuntimeError(f"OCR 识别失败: {e}")
63
-
64
-
65
- async def get_image_text(page: Page, selector: str, timeout: float = 5.0) -> Tuple[bool, str]:
66
- try:
67
- # 找到 img
68
- locator = page.locator(selector)
69
- if locator:
70
- img = await locator.element_handle(timeout=timeout * 1000)
71
-
72
- # 直接截图获取原始图片字节,不刷新图片
73
- img_bytes = await img.screenshot(timeout=timeout * 1000)
74
-
75
- # OCR 识别
76
- text = _ocr.classification(img_bytes)
77
- return True, text.strip()
78
- else:
79
- return False, f'没有找到当前页面中的【{selector}】图片'
80
- except PlaywrightTimeoutError:
81
- return False, f"元素 '{selector}' 未在 {timeout} 秒内找到"
82
- except Exception as e:
83
- return False, f"检查元素时发生错误: {str(e)}"
78
+ raise RuntimeError(f"调用百度API,获取图片识别结果失败,原因{response}")