munch-ease-backend/products/woolworths.py

98 lines
2.8 KiB
Python
Raw Normal View History

2025-10-18 03:26:42 +00:00
import re
from typing import Optional, Tuple
2024-01-13 01:54:04 +00:00
2025-10-18 03:26:42 +00:00
import httpx
2024-09-29 05:04:10 +00:00
2024-01-13 01:54:04 +00:00
HEADERS = {
2025-10-18 03:26:42 +00:00
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"DNT": "1",
"Sec-GPC": "1",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Pragma": "no-cache",
"Cache-Control": "no-cache",
2024-01-13 01:54:04 +00:00
}
2025-10-18 03:26:42 +00:00
2024-01-13 01:54:04 +00:00
async def _get_cookies(client):
# Make a request to https://www.woolworths.com.au/ as if we were a normal browser, then return the cookies
2025-10-18 03:26:42 +00:00
response = await client.get(
"https://www.woolworths.com.au/", headers=HEADERS, follow_redirects=True
)
2024-01-13 01:54:04 +00:00
return dict(response.cookies)
2025-10-18 03:26:42 +00:00
def _get_package_size(data: dict) -> Tuple[int, str]:
size = data["Product"]["PackageSize"]
2024-09-29 05:04:10 +00:00
if size:
2025-10-18 03:26:42 +00:00
match = re.match(r"(\d+)(.*)", size)
2024-09-29 05:04:10 +00:00
if match:
return int(match.group(1)), match.group(2)
2025-10-18 03:26:42 +00:00
return 1, "items"
2024-09-29 05:04:10 +00:00
def _get_client() -> httpx.AsyncClient:
return httpx.AsyncClient()
2025-10-18 03:26:42 +00:00
cached_cookies = None
2025-10-18 03:26:42 +00:00
async def _request_url(url: str) -> Optional[dict]:
global cached_cookies
2024-01-13 01:54:04 +00:00
2024-09-29 05:04:10 +00:00
async with _get_client() as client:
if not cached_cookies:
cached_cookies = await _get_cookies(client)
2024-01-13 01:54:04 +00:00
cookies = cached_cookies
2024-01-13 01:54:04 +00:00
try:
2025-10-18 03:26:42 +00:00
response = await client.get(
url, headers=HEADERS, follow_redirects=True, cookies=cookies
)
2024-01-13 01:54:04 +00:00
response.raise_for_status()
return response.json()
except httpx.HTTPError as ne:
2024-01-13 01:54:04 +00:00
print(ne)
cached_cookies = None
2024-01-13 01:54:04 +00:00
return None
2024-09-29 05:04:10 +00:00
2025-10-18 03:26:42 +00:00
def _get_product_details_url(product_id: str) -> str:
return f"https://www.woolworths.com.au/apis/ui/product/detail/{product_id}"
def get_product_id(url: str) -> Optional[str]:
woolies_regex = r"https://www.woolworths.com.au/shop/productdetails/(\d+)/?.*"
2024-01-13 01:54:04 +00:00
match = re.match(woolies_regex, url)
if match:
return match.group(1)
return None
2025-10-18 03:26:42 +00:00
async def scrape(product_id: str) -> Tuple[dict, dict]:
2024-09-29 05:04:10 +00:00
details_url = _get_product_details_url(product_id)
raw_data = await _request_url(details_url)
2025-10-18 03:26:42 +00:00
if raw_data is None:
# Return a minimal structure; callers treat this as raw payload for logging
raw_data = {}
2024-09-29 05:04:10 +00:00
quantity, unit = _get_package_size(raw_data)
product_data = {
2025-10-18 03:26:42 +00:00
"name": raw_data["Product"]["Name"],
"quantity": quantity,
"unit": unit,
"img_small": raw_data["Product"]["SmallImageFile"],
"img_large": raw_data["Product"]["LargeImageFile"],
2024-09-29 05:04:10 +00:00
}
2024-01-13 01:54:04 +00:00
2025-10-18 03:26:42 +00:00
return product_data, raw_data