-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdetails.py
More file actions
185 lines (150 loc) · 6.17 KB
/
details.py
File metadata and controls
185 lines (150 loc) · 6.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
from intuned_browser import go_to_url
from playwright.async_api import BrowserContext, Page
from utils.types_and_schemas import EcommereceDetailsParams, ProductDetails, Size
async def extract_price_info(page: Page) -> dict:
"""
Extracts price information including regular price, sale price, and currency.
Replace selectors with appropriate ones for your store.
"""
price = None
sale_price = None
sale_offer = None
# Check for sale price (old price exists)
# Replace selector with appropriate one for your store
old_price_locator = page.locator("div.prices span.old")
if await old_price_locator.count() > 0:
price = (await old_price_locator.first.inner_text()).strip()
price = price.replace(",", ".")
# Get current/sale price - replace selector
sale_locator = page.locator("div.prices span.value, div.prices span.current")
sale_price = (await sale_locator.first.inner_text()).strip() if await sale_locator.count() > 0 else None
sale_price = sale_price.replace(",", ".") if sale_price else None
# Get sale offer text - replace selector
sale_offer_locator = page.locator("div.prices span.promotion")
sale_offer = (
(await sale_offer_locator.first.inner_text()).strip()
if await sale_offer_locator.count() > 0
else None
)
else:
# No sale, regular price only - replace selector
price_locator = page.locator("div.prices span.value, div.prices span.current")
price = (await price_locator.first.inner_text()).strip() if await price_locator.count() > 0 else None
price = price.replace(",", ".") if price else None
return {
"price": price,
"sale_price": sale_price,
"sale_offer": sale_offer,
}
async def extract_sizes(page: Page) -> list[Size]:
"""
Extracts available sizes from the product page.
Replace selectors with appropriate ones for your store.
"""
sizes: list[Size] = []
# Replace selector with appropriate one for your store
size_container_selector = "div.size-container > ul > li"
size_locator = page.locator(size_container_selector)
if await size_locator.count() > 0:
size_elements = await size_locator.all()
for size_element in size_elements:
size_text = await size_element.inner_text()
check_available = await size_element.get_attribute("class")
# Check if size is available (not sold out)
is_available = True
if check_available and "sold-out" in check_available:
is_available = False
sizes.append(
Size(
size=size_text.strip() if size_text else "",
is_available=is_available,
)
)
else:
# No size options, single size product
sizes.append(Size(size="OneSize", is_available=True))
return sizes
async def extract_description(page: Page) -> str | None:
"""
Extracts product description.
Replace selector with appropriate one for your store.
"""
# Replace selector with appropriate one for your store
description_locator = page.locator("ul.content-list")
if await description_locator.count() > 0:
return await description_locator.first.inner_text()
return None
async def extract_shipping_and_returns(page: Page) -> str | None:
"""
Extracts shipping and returns information.
Replace selector with appropriate one for your store.
"""
# Replace selector with appropriate one for your store
shipping_locator = page.locator("#accordion-pdp-content-shipping-return div")
if await shipping_locator.count() > 0:
return await shipping_locator.first.inner_text()
return None
async def find_entity(page: Page, url: str) -> None:
await go_to_url(page=page, url=url)
try:
await page.wait_for_selector("#onetrust-accept-btn-handler", timeout=60000)
await page.click("#onetrust-accept-btn-handler")
print("Accepted cookies")
await page.wait_for_timeout(1000)
except Exception:
pass
async def automation(
page: Page,
params: EcommereceDetailsParams,
context: BrowserContext | None = None,
**_kwargs,
) -> ProductDetails:
"""
Fetches product details from a product page.
Extracts title, price, sizes, description, and shipping/returns info.
Example params:
{
"name": "Product Name",
"price": "$99.00",
"details_url": "https://www.example.com/product/item-1"
}
"""
await page.set_viewport_size({"width": 1280, "height": 800})
params = EcommereceDetailsParams(**params)
if not params.details_url:
raise ValueError("Params with details_url are required for this automation")
details_url = params.details_url
print(f"Fetching product details: {details_url}")
await find_entity(page, details_url)
# Dismiss any modals - replace with appropriate action for your store
try:
await page.keyboard.press("Escape")
except Exception:
pass
# Wait for product title to load - replace selector
await page.wait_for_selector("h1.product-name", timeout=60000)
# Extract title - replace selector
title_locator = page.locator("h1.product-name")
title = (
await title_locator.first.inner_text() if await title_locator.count() > 0 else params.name
)
# Extract price information
price_info = await extract_price_info(page)
# Extract sizes
sizes = await extract_sizes(page)
# Extract description
description = await extract_description(page)
# Extract shipping and returns
shipping_and_returns = await extract_shipping_and_returns(page)
product_details: ProductDetails = ProductDetails(
source_url=details_url,
name=title.strip() if title else "",
price=price_info.get("price", ""),
sale_price=price_info.get("sale_price", None),
sale_offer=price_info.get("sale_offer", None),
sizes=sizes,
description=description,
shipping_and_returns=shipping_and_returns,
)
print(f"Extracted details for: {product_details.name}")
return product_details