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.
- {python_qlv_helper-0.5.7.dist-info → python_qlv_helper-0.7.2.dist-info}/METADATA +8 -6
- {python_qlv_helper-0.5.7.dist-info → python_qlv_helper-0.7.2.dist-info}/RECORD +17 -16
- qlv_helper/config/url_const.py +2 -0
- qlv_helper/controller/main_page.py +18 -2
- qlv_helper/controller/order_detail.py +74 -5
- qlv_helper/controller/order_table.py +173 -4
- qlv_helper/controller/user_login.py +106 -102
- qlv_helper/controller/wechat_login.py +50 -0
- qlv_helper/http/order_page.py +161 -14
- qlv_helper/po/domestic_activity_order_page.py +91 -0
- qlv_helper/po/login_page.py +36 -74
- qlv_helper/po/main_page.py +7 -38
- qlv_helper/po/order_detail_page.py +17 -0
- qlv_helper/utils/ocr_helper.py +38 -43
- {python_qlv_helper-0.5.7.dist-info → python_qlv_helper-0.7.2.dist-info}/WHEEL +0 -0
- {python_qlv_helper-0.5.7.dist-info → python_qlv_helper-0.7.2.dist-info}/licenses/LICENSE +0 -0
- {python_qlv_helper-0.5.7.dist-info → python_qlv_helper-0.7.2.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python_qlv_helper
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: qlv helper python package
|
|
5
5
|
Author-email: ckf10000 <ckf10000@sina.com>
|
|
6
6
|
License: Apache License
|
|
@@ -210,14 +210,16 @@ Project-URL: Issues, https://github.com/ckf10000/qlv-helper/issues
|
|
|
210
210
|
Requires-Python: >=3.12
|
|
211
211
|
Description-Content-Type: text/markdown
|
|
212
212
|
License-File: LICENSE
|
|
213
|
-
Requires-Dist: playwright==1.56.0; python_version >= "3.12"
|
|
214
|
-
Requires-Dist: playwright-stealth==2.0.0; python_version >= "3.12"
|
|
215
|
-
Requires-Dist: ddddocr==1.5.6; python_version >= "3.12"
|
|
213
|
+
Requires-Dist: playwright==1.56.0; python_version >= "3.12"
|
|
214
|
+
Requires-Dist: playwright-stealth==2.0.0; python_version >= "3.12"
|
|
215
|
+
Requires-Dist: ddddocr==1.5.6; python_version >= "3.12"
|
|
216
216
|
Requires-Dist: aiohttp==3.13.2; python_version >= "3.12"
|
|
217
217
|
Requires-Dist: beautifulsoup4==4.14.2; python_version >= "3.12"
|
|
218
|
-
Requires-Dist: airtest==1.3.6; python_version >= "3.12"
|
|
218
|
+
Requires-Dist: airtest==1.3.6; python_version >= "3.12"
|
|
219
219
|
Requires-Dist: python_http_helper>=0.2.0; python_version >= "3.12"
|
|
220
|
-
Requires-Dist: python_playwright_helper>=0.
|
|
220
|
+
Requires-Dist: python_playwright_helper>=0.3.0; python_version >= "3.12"
|
|
221
|
+
Requires-Dist: python_ocr_helper>=0.0.1; python_version >= "3.12"
|
|
222
|
+
Requires-Dist: flight_helper>=0.2.9
|
|
221
223
|
Dynamic: license-file
|
|
222
224
|
|
|
223
225
|
# qlv-helper
|
|
@@ -1,35 +1,36 @@
|
|
|
1
|
-
python_qlv_helper-0.
|
|
1
|
+
python_qlv_helper-0.7.2.dist-info/licenses/LICENSE,sha256=WtjCEwlcVzkh1ziO35P2qfVEkLjr87Flro7xlHz3CEY,11556
|
|
2
2
|
qlv_helper/__init__.py,sha256=5DCc5JhfdsgtIuFWgkxPOW5VVKZ8RPikQLGIuyZX6_Y,465
|
|
3
3
|
qlv_helper/config/__init__.py,sha256=0pKLgui-sC6yMNBBuuTTLkUGhPybiJQSTKTbi66alvg,465
|
|
4
4
|
qlv_helper/config/custom_exception.py,sha256=uYme0iseQt_dP-Y6-hZ_H2OA2OQR0Kp5PmmZvhEZlEc,805
|
|
5
|
-
qlv_helper/config/url_const.py,sha256=
|
|
5
|
+
qlv_helper/config/url_const.py,sha256=EoLHOtlO3Ob2WSMMv6TBrl7eJCfJCB844dwmD_k86BM,630
|
|
6
6
|
qlv_helper/controller/__init__.py,sha256=cOJA0xMIytv17oICzPYqWLaSy-Ro2Ceeti0hHhsUj6Y,468
|
|
7
7
|
qlv_helper/controller/domestic_activity_order.py,sha256=MlmsDVsMBWq2h4Yjh1rhO372Z3p8tu2-4IZGP-nkfr8,1136
|
|
8
|
-
qlv_helper/controller/main_page.py,sha256=
|
|
9
|
-
qlv_helper/controller/order_detail.py,sha256=
|
|
10
|
-
qlv_helper/controller/order_table.py,sha256=
|
|
11
|
-
qlv_helper/controller/user_login.py,sha256=
|
|
8
|
+
qlv_helper/controller/main_page.py,sha256=p_-nXAIptNrx1SUgma7oPB-cqFFHYdCNvXCfsesIQFc,1855
|
|
9
|
+
qlv_helper/controller/order_detail.py,sha256=pdxN6ahsoeJq2XqBNRUwLrGG12WSy737fU17fhJMjgg,25621
|
|
10
|
+
qlv_helper/controller/order_table.py,sha256=unLL1xMKK1xvm_iewpdMcUPZc6AcBcLq0hAGXAdjdhU,15373
|
|
11
|
+
qlv_helper/controller/user_login.py,sha256=oXO1otTGuK3_r07fKdncQE3Is2w-kiI-mWmJyFvsVyE,5915
|
|
12
|
+
qlv_helper/controller/wechat_login.py,sha256=0u9H6tJiQO28nT5AG2Ot7EWFmpBqFJD_9dd12snsJIQ,2196
|
|
12
13
|
qlv_helper/http/__init__.py,sha256=yDh1xi_o7ohXqDAzLu62qCWIGk4_aD1dhnUaCon3klM,484
|
|
13
14
|
qlv_helper/http/main_page.py,sha256=LTpwrG8H_NqwCa3185irgcGd6JQkChAk7HQsDM-TNTI,1519
|
|
14
|
-
qlv_helper/http/order_page.py,sha256=
|
|
15
|
+
qlv_helper/http/order_page.py,sha256=Sti3dxZIN1U9z0e7NwyJ6_TrNdI-N3mRFnUN8WpN3FM,21674
|
|
15
16
|
qlv_helper/http/order_table_page.py,sha256=IaXn5wjqPi1aRXHz0kucdHEdZswUWAZfECC13y57y8k,14440
|
|
16
17
|
qlv_helper/po/__init__.py,sha256=eDr06o0eYapBsYpOhA11bbxzs2F0dsuDjOKmxk_2HVE,480
|
|
17
|
-
qlv_helper/po/domestic_activity_order_page.py,sha256=
|
|
18
|
-
qlv_helper/po/login_page.py,sha256=
|
|
19
|
-
qlv_helper/po/main_page.py,sha256=
|
|
20
|
-
qlv_helper/po/order_detail_page.py,sha256=
|
|
18
|
+
qlv_helper/po/domestic_activity_order_page.py,sha256=El63U0GI2PU9WGIkGxPgCE1Fp1yCsw2v1H6cZhSaG4c,8509
|
|
19
|
+
qlv_helper/po/login_page.py,sha256=JbapNFwGCei3K3mpfhte1TVNeqzG7yHsCtp5KiKv_6g,4271
|
|
20
|
+
qlv_helper/po/main_page.py,sha256=0_tqZILZnLS6y3chg9ERTHDtg86pW0aIQ1vNuPFLVG8,1752
|
|
21
|
+
qlv_helper/po/order_detail_page.py,sha256=0Xyi9zxnSHv4OfFAlPPz_D0S5S0ZHKLphUUT8R4svzk,14414
|
|
21
22
|
qlv_helper/po/wechat_auth_page.py,sha256=a4YZlM5JOS0l3CNJm_oJFBhZY7AbNOpdIwilSjAO5bY,3171
|
|
22
23
|
qlv_helper/utils/__init__.py,sha256=rGzBkUf1tslG4WRPQjVWTVuwWG76pkckuKO_6K4sEus,465
|
|
23
24
|
qlv_helper/utils/browser_utils.py,sha256=mKoqSEz1vFrVemp9cgI4R5UhA4k7i0Cd9cWsIXJZ6E4,986
|
|
24
25
|
qlv_helper/utils/datetime_utils.py,sha256=BaDJKuH-yqc2NF9KYN66zUYUEJ9ZRHj09AV4-gILf3o,606
|
|
25
26
|
qlv_helper/utils/file_handle.py,sha256=_dJ9Yk8esttJYsjdBMZAkjZTDQh5QYVPXjLRRyWUMh0,1087
|
|
26
27
|
qlv_helper/utils/html_utils.py,sha256=i5oOFYETH3kDS9-rSyGu1SHFTkfZvAPPQ4za76BCdVA,1962
|
|
27
|
-
qlv_helper/utils/ocr_helper.py,sha256=
|
|
28
|
+
qlv_helper/utils/ocr_helper.py,sha256=vpIokr07Utmpsb78MHF83UcXL90A8BPbLgjMg4kONyA,3343
|
|
28
29
|
qlv_helper/utils/po_utils.py,sha256=SwQKL58HERGG2Weou_AwY_TQoYSvgi0gvaVCJBput_k,3516
|
|
29
30
|
qlv_helper/utils/stealth_browser.py,sha256=srNOYJOboYo30TvW5OP5TaVpg4jgHm9GxqmYnuwcUQU,3140
|
|
30
31
|
qlv_helper/utils/type_utils.py,sha256=S5FXUje2mbDuq27LU05WymxNu1VGOLBUV3tuqcx51dE,3792
|
|
31
32
|
qlv_helper/utils/windows_utils.py,sha256=Cvedsk1c2ujgPNVxszz8XWANkvEr8G9kne6povtZRU4,2866
|
|
32
|
-
python_qlv_helper-0.
|
|
33
|
-
python_qlv_helper-0.
|
|
34
|
-
python_qlv_helper-0.
|
|
35
|
-
python_qlv_helper-0.
|
|
33
|
+
python_qlv_helper-0.7.2.dist-info/METADATA,sha256=LuxKTcs6bOcTbrthW-Q3N35aT9nF7sTKDDZS0uae9bQ,14825
|
|
34
|
+
python_qlv_helper-0.7.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
35
|
+
python_qlv_helper-0.7.2.dist-info/top_level.txt,sha256=0pYdhD8SfBcC57LzLYGHY7cwwPqdqAkB1twysCJh5OA,11
|
|
36
|
+
python_qlv_helper-0.7.2.dist-info/RECORD,,
|
qlv_helper/config/url_const.py
CHANGED
|
@@ -10,13 +10,29 @@
|
|
|
10
10
|
# ---------------------------------------------------------------------------------------------------------
|
|
11
11
|
"""
|
|
12
12
|
import aiohttp
|
|
13
|
+
from logging import Logger
|
|
14
|
+
from playwright.async_api import Page
|
|
13
15
|
from typing import Dict, Any, Optional
|
|
16
|
+
from qlv_helper.po.main_page import MainPage
|
|
14
17
|
from qlv_helper.http.main_page import get_main_page_html, parser_head_title
|
|
15
18
|
|
|
16
19
|
|
|
20
|
+
async def open_main_page(
|
|
21
|
+
*, page: Page, logger: Logger, qlv_protocol: str, qlv_domain: str, timeout: float = 60.0, **kwargs: Any
|
|
22
|
+
) -> MainPage:
|
|
23
|
+
url_prefix = f"{qlv_protocol}://{qlv_domain}"
|
|
24
|
+
main_url = url_prefix + "/"
|
|
25
|
+
await page.goto(main_url)
|
|
26
|
+
|
|
27
|
+
main_po = MainPage(page=page, url=main_url)
|
|
28
|
+
await main_po.url_wait_for(url=main_url, timeout=timeout)
|
|
29
|
+
logger.info(f"即将进入首页,页面URL<{main_url}>")
|
|
30
|
+
return main_po
|
|
31
|
+
|
|
32
|
+
|
|
17
33
|
async def get_main_info_with_http(
|
|
18
34
|
domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5, enable_log: bool = True,
|
|
19
|
-
cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None
|
|
35
|
+
cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None, **kwargs: Any
|
|
20
36
|
) -> Dict[str, Any]:
|
|
21
37
|
response = await get_main_page_html(
|
|
22
38
|
domain=domain, protocol=protocol, retry=retry, timeout=timeout, enable_log=enable_log,
|
|
@@ -27,4 +43,4 @@ async def get_main_info_with_http(
|
|
|
27
43
|
|
|
28
44
|
html = response.get("data")
|
|
29
45
|
response["message"] = parser_head_title(html=html)
|
|
30
|
-
return response
|
|
46
|
+
return response
|
|
@@ -16,7 +16,9 @@ from datetime import datetime
|
|
|
16
16
|
import qlv_helper.config.url_const as url_const
|
|
17
17
|
from typing import Dict, Any, List, cast, Optional
|
|
18
18
|
from qlv_helper.po.order_detail_page import OrderDetailPage
|
|
19
|
-
from
|
|
19
|
+
from flight_helper.models.dto.procurement import FillProcurementInputDTO
|
|
20
|
+
from qlv_helper.http.order_page import parser_order_info, get_order_page_html, parser_order_flight_table, \
|
|
21
|
+
fill_procurement_info_with_http, fill_itinerary_info_with_http, fill_procurement_dto_with_http
|
|
20
22
|
from playwright.async_api import Page, Locator, Error as PlaywrightError, TimeoutError as PlaywrightTimeoutError
|
|
21
23
|
|
|
22
24
|
|
|
@@ -95,7 +97,7 @@ async def order_unlock(
|
|
|
95
97
|
lock_state, lock_btn = await page.get_order_lock_state_btn(timeout=timeout)
|
|
96
98
|
if "强制解锁" in lock_state:
|
|
97
99
|
if is_force is False:
|
|
98
|
-
raise EnvironmentError(f"
|
|
100
|
+
raise EnvironmentError(f"被他人锁定,无需解锁处理")
|
|
99
101
|
await lock_btn.click(button="left")
|
|
100
102
|
logger.info(f"订单详情页面,用户操作【{lock_state}】按钮点击完成")
|
|
101
103
|
try:
|
|
@@ -135,7 +137,7 @@ async def order_locked(
|
|
|
135
137
|
lock_state, lock_btn = await page.get_order_lock_state_btn(timeout=timeout)
|
|
136
138
|
if "强制解锁" in lock_state:
|
|
137
139
|
if is_force is False:
|
|
138
|
-
raise EnvironmentError(f"
|
|
140
|
+
raise EnvironmentError(f"被他人锁定,不做加锁处理")
|
|
139
141
|
await lock_btn.click(button="left")
|
|
140
142
|
logger.info(f"订单详情页面,用户操作【{lock_state}】按钮点击完成")
|
|
141
143
|
try:
|
|
@@ -317,7 +319,7 @@ async def fill_itinerary(
|
|
|
317
319
|
diff = kwargs_passengers.difference(current_passengers)
|
|
318
320
|
if diff:
|
|
319
321
|
raise RuntimeError(
|
|
320
|
-
f"
|
|
322
|
+
f"传递回填票号的乘客证件信息<{kwargs_passengers}>与订单实际乘客信息<{current_passengers}>不一致"
|
|
321
323
|
)
|
|
322
324
|
passenger_itinerary_locator = cast(Locator, None)
|
|
323
325
|
for passenger_locator in passenger_itinerary_locators:
|
|
@@ -333,7 +335,7 @@ async def fill_itinerary(
|
|
|
333
335
|
await passenger_itinerary_locator.press("Enter")
|
|
334
336
|
logger.info(f"订单<{order_id}>,本次的票号回填完成")
|
|
335
337
|
else:
|
|
336
|
-
raise RuntimeError(f"
|
|
338
|
+
raise RuntimeError(f"回填票号过程异常,回填失败")
|
|
337
339
|
|
|
338
340
|
|
|
339
341
|
async def first_open_page_fill_itinerary(
|
|
@@ -413,6 +415,7 @@ async def first_open_page_my_order_unlock(
|
|
|
413
415
|
lock_state, lock_btn = await order_detail_po.get_order_lock_state_btn(timeout=timeout)
|
|
414
416
|
elif "锁定" in lock_state:
|
|
415
417
|
logger.info(f"订单<{order_id}>,处于无锁状态,无需解锁处理")
|
|
418
|
+
return order_detail_po
|
|
416
419
|
elif "操作" in lock_state:
|
|
417
420
|
await lock_btn.click(button="left")
|
|
418
421
|
await asyncio.sleep(2)
|
|
@@ -425,3 +428,69 @@ async def first_open_page_my_order_unlock(
|
|
|
425
428
|
await asyncio.sleep(2)
|
|
426
429
|
logger.info(f"订单详情页面,日志记录栏,【{lock_state}】按钮点击完成")
|
|
427
430
|
return order_detail_po
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
async def fill_procurement_with_http(
|
|
434
|
+
*, order_id: int, qlv_domain: str, amount: float, pre_order_id: str, platform_user_id: str, user_password: str,
|
|
435
|
+
passengers: List[str], fids: str, pids: List[str], transaction_id: str, qlv_protocol: str = "http",
|
|
436
|
+
retry: int = 1, timeout: int = 5, enable_log: bool = True, cookie_jar: Optional[aiohttp.CookieJar] = None,
|
|
437
|
+
playwright_state: Dict[str, Any] = None, data_list: Optional[List[Dict[str, Any]]] = None, **kwargs: Any
|
|
438
|
+
) -> Dict[str, Any]:
|
|
439
|
+
return await fill_procurement_info_with_http(
|
|
440
|
+
order_id=order_id, qlv_domain=qlv_domain, amount=amount, pre_order_id=pre_order_id,
|
|
441
|
+
platform_user_id=platform_user_id, user_password=user_password, passengers=passengers, fids=fids, pids=pids,
|
|
442
|
+
transaction_id=transaction_id, qlv_protocol=qlv_protocol, retry=retry, timeout=timeout, enable_log=enable_log,
|
|
443
|
+
cookie_jar=cookie_jar, playwright_state=playwright_state, data_list=data_list
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
async def fill_procurement_with_http_callback(
|
|
448
|
+
*, fill_procurement_dto: FillProcurementInputDTO, retry: int = 1, timeout: int = 5, enable_log: bool = True,
|
|
449
|
+
cookie_jar: Optional[aiohttp.CookieJar] = None, playwright_state: Dict[str, Any] = None,
|
|
450
|
+
data_list: Optional[List[Dict[str, Any]]] = None, **kwargs: Any
|
|
451
|
+
) -> Dict[str, Any]:
|
|
452
|
+
return await fill_procurement_dto_with_http(
|
|
453
|
+
fill_procurement_dto=fill_procurement_dto, retry=retry, timeout=timeout, enable_log=enable_log,
|
|
454
|
+
cookie_jar=cookie_jar, playwright_state=playwright_state, data_list=data_list
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
async def fill_itinerary_with_http(
|
|
459
|
+
*, page: Page, logger: Logger, qlv_protocol: str, qlv_domain: str, order_id: int, retry: int = 1,
|
|
460
|
+
passengers: List[Dict[str, Any]], timeout: float = 20.0, cookie_jar: Optional[aiohttp.CookieJar] = None,
|
|
461
|
+
playwright_state: Dict[str, Any] = None, enable_log: bool = True, **kwargs: Any
|
|
462
|
+
) -> bool:
|
|
463
|
+
# 1. 打开页面
|
|
464
|
+
order_detail_po = await open_order_detail_page(
|
|
465
|
+
page=page, logger=logger, protocol=qlv_protocol, domain=qlv_domain, order_id=order_id, timeout=timeout
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
# 2. 获取采购信息的流水id
|
|
469
|
+
purchase_transaction_ids = await order_detail_po.get_purchase_info_transaction_id(timeout=timeout)
|
|
470
|
+
purchase_transaction_ids = [x for x in purchase_transaction_ids if x != "0"]
|
|
471
|
+
if purchase_transaction_ids:
|
|
472
|
+
flag = True
|
|
473
|
+
purchase_transaction_ids.sort()
|
|
474
|
+
purchase_transaction_id = purchase_transaction_ids[0]
|
|
475
|
+
for passenger in passengers:
|
|
476
|
+
pid = passenger.get("pid")
|
|
477
|
+
tid = passenger.get("tid")
|
|
478
|
+
p_name = passenger.get("p_name")
|
|
479
|
+
itinerary_id = passenger.get("itinerary_id")
|
|
480
|
+
try:
|
|
481
|
+
response = await fill_itinerary_info_with_http(
|
|
482
|
+
order_id=order_id, qlv_domain=qlv_domain, pid=pid, tid=tid, transaction_id=purchase_transaction_id,
|
|
483
|
+
itinerary_id=itinerary_id, retry=retry, qlv_protocol=qlv_protocol, timeout=int(timeout),
|
|
484
|
+
enable_log=enable_log, cookie_jar=cookie_jar, playwright_state=playwright_state
|
|
485
|
+
)
|
|
486
|
+
if response == 200 or "OK" in response.get("data"):
|
|
487
|
+
logger.info(f"订单<{order_id}>,乘客<{p_name}>票号<{itinerary_id}>回填成功")
|
|
488
|
+
else:
|
|
489
|
+
logger.warning(
|
|
490
|
+
f'订单<{order_id}>,乘客<{p_name}>票号<{itinerary_id}>回填失败:{response.get("data")}')
|
|
491
|
+
flag = False
|
|
492
|
+
except (Exception,):
|
|
493
|
+
flag = False
|
|
494
|
+
return flag
|
|
495
|
+
else:
|
|
496
|
+
raise EnvironmentError("还未填写采购信息,暂时不能回填票号")
|
|
@@ -10,10 +10,17 @@
|
|
|
10
10
|
# ---------------------------------------------------------------------------------------------------------
|
|
11
11
|
"""
|
|
12
12
|
import asyncio
|
|
13
|
+
from copy import deepcopy
|
|
14
|
+
from logging import Logger
|
|
13
15
|
from aiohttp import CookieJar
|
|
14
|
-
from
|
|
16
|
+
from datetime import datetime, timedelta
|
|
17
|
+
from playwright.async_api import Page, Locator
|
|
18
|
+
import qlv_helper.config.url_const as url_const
|
|
15
19
|
from http_helper.client.async_proxy import HttpClientFactory
|
|
16
20
|
from qlv_helper.utils.html_utils import parse_pagination_info
|
|
21
|
+
from qlv_helper.utils.type_utils import safe_convert_advanced
|
|
22
|
+
from typing import Optional, Dict, Any, Callable, List, cast, Tuple
|
|
23
|
+
from qlv_helper.po.domestic_activity_order_page import DomesticActivityOrderPage
|
|
17
24
|
from qlv_helper.http.order_table_page import get_domestic_activity_order_page_html, get_domestic_ticket_outed_page_html, \
|
|
18
25
|
parse_order_table, get_domestic_unticketed_order_page_html
|
|
19
26
|
|
|
@@ -28,7 +35,7 @@ async def _get_paginated_order_table(
|
|
|
28
35
|
cookie_jar: Optional[CookieJar],
|
|
29
36
|
playwright_state: Dict[str, Any],
|
|
30
37
|
table_state: str,
|
|
31
|
-
fetch_page_fn: Callable[..., Any],
|
|
38
|
+
fetch_page_fn: Callable[..., Any], # 拿到第一页/分页 HTML 的函数
|
|
32
39
|
) -> Dict[str, Any]:
|
|
33
40
|
"""通用分页表格抓取(支持并发)"""
|
|
34
41
|
|
|
@@ -79,7 +86,7 @@ async def _get_paginated_order_table(
|
|
|
79
86
|
)
|
|
80
87
|
if resp.get("code") == 200:
|
|
81
88
|
return parse_order_table(html=resp["data"], table_state=table_state)
|
|
82
|
-
except (Exception,
|
|
89
|
+
except (Exception,):
|
|
83
90
|
return list() # 抓取失败则返回空,不影响整体
|
|
84
91
|
return list()
|
|
85
92
|
|
|
@@ -111,6 +118,7 @@ async def _get_paginated_order_table(
|
|
|
111
118
|
response["data"] = pagination_info
|
|
112
119
|
return response
|
|
113
120
|
|
|
121
|
+
|
|
114
122
|
async def get_domestic_activity_order_table(
|
|
115
123
|
domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5, enable_log: bool = True,
|
|
116
124
|
cookie_jar: Optional[CookieJar] = None, playwright_state: Dict[str, Any] = None
|
|
@@ -144,6 +152,7 @@ async def get_domestic_ticket_outed_table(
|
|
|
144
152
|
fetch_page_fn=get_domestic_ticket_outed_page_html
|
|
145
153
|
)
|
|
146
154
|
|
|
155
|
+
|
|
147
156
|
async def get_domestic_unticketed_order_table(
|
|
148
157
|
domain: str, protocol: str = "http", retry: int = 1, timeout: int = 5, enable_log: bool = True,
|
|
149
158
|
cookie_jar: Optional[CookieJar] = None, playwright_state: Dict[str, Any] = None
|
|
@@ -158,4 +167,164 @@ async def get_domestic_unticketed_order_table(
|
|
|
158
167
|
playwright_state=playwright_state,
|
|
159
168
|
table_state="proccessing",
|
|
160
169
|
fetch_page_fn=get_domestic_unticketed_order_page_html
|
|
161
|
-
)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
async def open_domestic_activity_order_page(
|
|
174
|
+
*, page: Page, logger: Logger, qlv_protocol: str, qlv_domain: str, timeout: float = 20.0
|
|
175
|
+
) -> DomesticActivityOrderPage:
|
|
176
|
+
url_prefix = f"{qlv_protocol}://{qlv_domain}"
|
|
177
|
+
domestic_activity_order_url = url_prefix + url_const.domestic_activity_order_url
|
|
178
|
+
await page.goto(domestic_activity_order_url)
|
|
179
|
+
|
|
180
|
+
domestic_activity_order_po = DomesticActivityOrderPage(page=page, url=domestic_activity_order_url)
|
|
181
|
+
await domestic_activity_order_po.url_wait_for(url=domestic_activity_order_url, timeout=timeout)
|
|
182
|
+
logger.info(f"即将进入国内活动订单页面,页面URL<{domestic_activity_order_url}>")
|
|
183
|
+
return domestic_activity_order_po
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
async def pop_will_expire_domestic_activity_order(
|
|
187
|
+
*, page: Page, logger: Logger, qlv_protocol: str, qlv_domain: str, last_minute_threshold: int,
|
|
188
|
+
timeout: float = 20.0, **kwargs: Any
|
|
189
|
+
) -> Tuple[List[Dict[str, Any]], bool]:
|
|
190
|
+
# 1. 打开国内活动订单页面
|
|
191
|
+
domestic_activity_order_po = await open_domestic_activity_order_page(
|
|
192
|
+
page=page, logger=logger, qlv_protocol=qlv_protocol, qlv_domain=qlv_domain, timeout=timeout
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# TODO 暂时不考虑分页的情况
|
|
196
|
+
# 2. 获取table所有tr的Locator对象
|
|
197
|
+
trs_locator = await domestic_activity_order_po.get_flight_table_trs_locator(timeout=timeout)
|
|
198
|
+
trs_locator = await trs_locator.all()
|
|
199
|
+
table_data = list()
|
|
200
|
+
feilds = {
|
|
201
|
+
"to_from": "", "urgant_state": "", "order_id": 0, "pre_order_id": "", "aduit_pnr": "", "child_pnr": "",
|
|
202
|
+
"payment_time": "", "last_time_ticket": "", "dat_dep": "", "code_dep": "", "code_arr": "", "flight_no": "",
|
|
203
|
+
"cabin": "", "policy": "", "total_people": 0, "total_adult": 0, "total_child": 0, "receipted": 0.00,
|
|
204
|
+
"stat_opration": "", "more_seats": "", "operation_info": "", "substitute_btn_locator": ""
|
|
205
|
+
}
|
|
206
|
+
pre_pop_orders = list()
|
|
207
|
+
is_pop = False
|
|
208
|
+
for tr_locator in trs_locator[1:]:
|
|
209
|
+
row_locator = await domestic_activity_order_po.get_flight_table_trs_td(locator=tr_locator, timeout=timeout)
|
|
210
|
+
tds_locators = await row_locator.all()
|
|
211
|
+
sub_feilds = list()
|
|
212
|
+
copy_feilds = deepcopy(feilds)
|
|
213
|
+
for index, td_locator in enumerate(tds_locators):
|
|
214
|
+
try:
|
|
215
|
+
text = (await td_locator.inner_text()).strip()
|
|
216
|
+
if index == 0:
|
|
217
|
+
copy_feilds["to_from"] = text
|
|
218
|
+
sub_feilds.append("to_from")
|
|
219
|
+
elif index == 1:
|
|
220
|
+
order_id = await domestic_activity_order_po.get_flight_table_td_order_id(
|
|
221
|
+
locator=td_locator, timeout=timeout
|
|
222
|
+
)
|
|
223
|
+
urgant_state = await domestic_activity_order_po.get_flight_table_td_urgant(
|
|
224
|
+
locator=td_locator, timeout=timeout
|
|
225
|
+
)
|
|
226
|
+
copy_feilds["order_id"] = safe_convert_advanced(value=order_id)
|
|
227
|
+
copy_feilds["urgant_state"] = urgant_state
|
|
228
|
+
sub_feilds.extend(["order_id", "urgant_state"])
|
|
229
|
+
elif index == 2:
|
|
230
|
+
copy_feilds["pre_order_id"] = text
|
|
231
|
+
sub_feilds.append("pre_order_id")
|
|
232
|
+
elif index == 3:
|
|
233
|
+
text = text.replace("\xa0", "")
|
|
234
|
+
text_slice = text.split("|")
|
|
235
|
+
copy_feilds["aduit_pnr"] = text_slice[0].strip()
|
|
236
|
+
copy_feilds["child_pnr"] = text_slice[1].strip()
|
|
237
|
+
sub_feilds.extend(["aduit_pnr", "child_pnr"])
|
|
238
|
+
elif index == 4:
|
|
239
|
+
copy_feilds["payment_time"] = text
|
|
240
|
+
sub_feilds.append("payment_time")
|
|
241
|
+
elif index == 5:
|
|
242
|
+
continue
|
|
243
|
+
elif index == 6:
|
|
244
|
+
copy_feilds["last_time_ticket"] = text
|
|
245
|
+
sub_feilds.append("last_time_ticket")
|
|
246
|
+
elif index == 7:
|
|
247
|
+
continue
|
|
248
|
+
elif index == 8:
|
|
249
|
+
text = text.replace("\xa0", "|")
|
|
250
|
+
text_slice = [i for i in text.split("|") if i.strip()]
|
|
251
|
+
ctrip = text_slice[1].split("-")
|
|
252
|
+
copy_feilds["dat_dep"] = text_slice[0].strip()
|
|
253
|
+
copy_feilds["code_dep"] = ctrip[0].strip()
|
|
254
|
+
copy_feilds["code_arr"] = ctrip[1].strip()
|
|
255
|
+
copy_feilds["flight_no"] = text_slice[2].strip()
|
|
256
|
+
copy_feilds["cabin"] = text_slice[3].strip()
|
|
257
|
+
sub_feilds.extend(["dat_dep", "code_dep", "code_arr", "flight_no", "cabin"])
|
|
258
|
+
elif index == 9:
|
|
259
|
+
text = text.replace("\xa0", "")
|
|
260
|
+
text = text.replace(">", "")
|
|
261
|
+
text = text.replace("<", "")
|
|
262
|
+
text = text.replace("&", "")
|
|
263
|
+
text = text.replace("<br>", "\n")
|
|
264
|
+
copy_feilds["policy"] = text
|
|
265
|
+
sub_feilds.append("policy")
|
|
266
|
+
elif index == 10:
|
|
267
|
+
text = text.replace("【 ", "|")
|
|
268
|
+
text = text.replace("/", "|")
|
|
269
|
+
text = text.replace("】", "")
|
|
270
|
+
text_slice = text.split("|")
|
|
271
|
+
copy_feilds["total_people"] = safe_convert_advanced(value=text_slice[0].strip())
|
|
272
|
+
copy_feilds["total_adult"] = safe_convert_advanced(value=text_slice[1].strip())
|
|
273
|
+
copy_feilds["total_child"] = safe_convert_advanced(value=text_slice[2].strip())
|
|
274
|
+
sub_feilds.extend(["total_people", "total_adult", "total_child"])
|
|
275
|
+
elif index == 11:
|
|
276
|
+
copy_feilds["receipted"] = safe_convert_advanced(value=text)
|
|
277
|
+
sub_feilds.append("receipted")
|
|
278
|
+
elif index == 12:
|
|
279
|
+
copy_feilds["stat_opration"] = text
|
|
280
|
+
sub_feilds.append("stat_opration")
|
|
281
|
+
elif index == 13:
|
|
282
|
+
copy_feilds["more_seats"] = safe_convert_advanced(value=text)
|
|
283
|
+
sub_feilds.append("more_seats")
|
|
284
|
+
elif index == 14:
|
|
285
|
+
text_slice = text.split(" ")
|
|
286
|
+
operation_info = dict(
|
|
287
|
+
lock_btn_locator=cast(Locator, None), pop_btn_locator=cast(Locator, None), locked=""
|
|
288
|
+
)
|
|
289
|
+
if "锁定" in text_slice[0]:
|
|
290
|
+
lock_btn_locator = await domestic_activity_order_po.get_flight_table_td_operation_lock_btn(
|
|
291
|
+
locator=td_locator, timeout=timeout
|
|
292
|
+
)
|
|
293
|
+
operation_info["lock_btn_locator"] = lock_btn_locator
|
|
294
|
+
else:
|
|
295
|
+
operation_info["locked"] = text_slice[0].strip()
|
|
296
|
+
if "踢出" in text:
|
|
297
|
+
pop_btn_locator = await domestic_activity_order_po.get_flight_table_td_operation_pop_btn(
|
|
298
|
+
locator=td_locator, timeout=timeout
|
|
299
|
+
)
|
|
300
|
+
operation_info["pop_btn_locator"] = pop_btn_locator
|
|
301
|
+
copy_feilds["operation_info"] = operation_info
|
|
302
|
+
sub_feilds.append("operation_info")
|
|
303
|
+
elif index == 15:
|
|
304
|
+
copy_feilds[
|
|
305
|
+
"substitute_btn_locator"
|
|
306
|
+
] = await domestic_activity_order_po.get_flight_table_td_operation_substitute_btn(
|
|
307
|
+
locator=td_locator, timeout=timeout)
|
|
308
|
+
sub_feilds.append("substitute_btn_locator")
|
|
309
|
+
except (Exception,) as e:
|
|
310
|
+
logger.error(f"第<{index + 1}>列数据处理异常,原因:{e}")
|
|
311
|
+
if len(sub_feilds) == 22:
|
|
312
|
+
table_data.append(copy_feilds)
|
|
313
|
+
if datetime.strptime(
|
|
314
|
+
copy_feilds.get("last_time_ticket"), "%Y-%m-%d %H:%M:%S"
|
|
315
|
+
) < datetime.now() + timedelta(minutes=last_minute_threshold):
|
|
316
|
+
pre_pop_orders.append(copy_feilds)
|
|
317
|
+
for pre_pop_order in pre_pop_orders:
|
|
318
|
+
order_id = pre_pop_order.get("order_id")
|
|
319
|
+
operation_info = pre_pop_order.get("operation_info")
|
|
320
|
+
pop_btn_locator = operation_info.get("pop_btn_locator")
|
|
321
|
+
last_time_ticket = pre_pop_order.get("last_time_ticket")
|
|
322
|
+
if pop_btn_locator and isinstance(pop_btn_locator, Locator):
|
|
323
|
+
minute = (datetime.strptime(last_time_ticket, "%Y-%m-%d %H:%M:%S") - datetime.now()).total_seconds() / 60
|
|
324
|
+
await pop_btn_locator.click()
|
|
325
|
+
if is_pop is False:
|
|
326
|
+
is_pop = True
|
|
327
|
+
logger.info(
|
|
328
|
+
f"订单<{order_id}>,距离最晚出票时限: {last_time_ticket},仅剩<{minute}>分钟,已将工单剔出活动订单"
|
|
329
|
+
)
|
|
330
|
+
return table_data, is_pop
|