mirror of
https://github.com/R0m1k3/Priceflow.git
synced 2026-10-22 22:58:11 +02:00
feat: Add core search configuration and service for web scraping, including site-specific selectors, proxies, and user agents.
This commit is contained in:
1 parent
292eb3d12e
commit
bcc3b0900f
2 files changed
+22
-35
No files matched your search
+10
-31
@@ -70,40 +70,14 @@ SITE_CONFIGS = {
|
|||||||
},
|
},
|
||||||
"stokomani.fr": {
|
"stokomani.fr": {
|
||||||
"name": "Stokomani",
|
"name": "Stokomani",
|
||||||
"search_url": "https://www.stokomani.fr/recherche?q={query}",
|
"search_url": "https://www.stokomani.fr/search?options%5Bprefix%5D=last&q={query}",
|
||||||
"product_selector": ".product-miniature a, .js-product-miniature a, a.product-thumbnail",
|
"product_selector": "a[href^='/products/']",
|
||||||
"wait_selector": ".products, .product-miniature",
|
"wait_selector": "a[href^='/products/']",
|
||||||
"category": "Discount",
|
"category": "Discount",
|
||||||
"requires_proxy": False,
|
"requires_proxy": False,
|
||||||
},
|
},
|
||||||
"bmstores.fr": {
|
"bmstores.fr": {
|
||||||
"name": "B&M",
|
"name": "B&M",
|
||||||
"search_url": "https://bmstores.fr/module/ambjolisearch/jolisearch?s={query}",
|
|
||||||
"product_selector": "a.product-thumbnail[href*='/produits/']:not([href*='/c/'])",
|
|
||||||
"wait_selector": ".products, .product-miniature",
|
|
||||||
"category": "Discount",
|
|
||||||
"requires_proxy": False,
|
|
||||||
},
|
|
||||||
"centrakor.com": {
|
|
||||||
"name": "Centrakor",
|
|
||||||
"search_url": "https://www.centrakor.com/catalogsearch/result/?q={query}",
|
|
||||||
"product_selector": ".product-item a.product-item-link, .product-item-info a",
|
|
||||||
"wait_selector": ".products-grid, .product-items",
|
|
||||||
"category": "Déco & Maison",
|
|
||||||
"requires_proxy": False,
|
|
||||||
},
|
|
||||||
"lincroyable.fr": {
|
|
||||||
"name": "L'Incroyable",
|
|
||||||
"search_url": "https://www.lincroyable.fr/recherche-query={query}/?",
|
|
||||||
"product_selector": ".product-miniature a, .js-product-miniature a",
|
|
||||||
"wait_selector": ".products, .product-miniature",
|
|
||||||
"category": "Discount",
|
|
||||||
"requires_proxy": False,
|
|
||||||
},
|
|
||||||
"action.com": {
|
|
||||||
"name": "Action",
|
|
||||||
"search_url": "https://www.action.com/fr-fr/search/?q={query}",
|
|
||||||
"product_selector": "a.product-card__link, .product-card a",
|
|
||||||
"wait_selector": ".product-card, .search-results",
|
"wait_selector": ".product-card, .search-results",
|
||||||
"category": "Discount",
|
"category": "Discount",
|
||||||
"requires_proxy": False,
|
"requires_proxy": False,
|
||||||
@@ -136,8 +110,8 @@ SITE_CONFIGS = {
|
|||||||
"carrefour.fr": {
|
"carrefour.fr": {
|
||||||
"name": "Carrefour",
|
"name": "Carrefour",
|
||||||
"search_url": "https://www.carrefour.fr/s?q={query}",
|
"search_url": "https://www.carrefour.fr/s?q={query}",
|
||||||
"product_selector": "a[href*='/p/'][href$='.html']",
|
"product_selector": "a.product-card-click-wrapper[href^='/p/']",
|
||||||
"wait_selector": ".search-results, .product-grid",
|
"wait_selector": "a.product-card-click-wrapper[href^='/p/']",
|
||||||
"category": "Grande Surface",
|
"category": "Grande Surface",
|
||||||
"requires_proxy": False,
|
"requires_proxy": False,
|
||||||
},
|
},
|
||||||
@@ -216,4 +190,9 @@ COOKIE_ACCEPT_SELECTORS = [
|
|||||||
".popin-close",
|
".popin-close",
|
||||||
"#popin-close",
|
"#popin-close",
|
||||||
"a.close-popin",
|
"a.close-popin",
|
||||||
|
# Centrakor Store Overlay
|
||||||
|
"div.mod-shops button.close",
|
||||||
|
".mod-shops .close",
|
||||||
|
"#shops-modal .close-button",
|
||||||
|
".ui-dialog-titlebar-close",
|
||||||
]
|
]
|
||||||
@@ -234,10 +234,18 @@ class NewSearchService:
|
|||||||
# Phase 1: Parse results
|
# Phase 1: Parse results
|
||||||
initial_results = NewSearchService._parse_results(html_content, site_key, search_url)
|
initial_results = NewSearchService._parse_results(html_content, site_key, search_url)
|
||||||
|
|
||||||
# Phase 2: Scrape details for each result (Sequential or Limited Parallel)
|
# Phase 2: Scrape details for each result (Parallel)
|
||||||
# We yield as soon as one is done
|
# We want to yield results as they complete, not wait for all
|
||||||
for res in initial_results:
|
semaphore = asyncio.Semaphore(3) # Limit concurrency per site
|
||||||
enriched_res = await NewSearchService.scrape_item(res)
|
|
||||||
|
async def scrape_wrapper(res):
|
||||||
|
async with semaphore:
|
||||||
|
return await NewSearchService.scrape_item(res)
|
||||||
|
|
||||||
|
tasks = [scrape_wrapper(r) for r in initial_results]
|
||||||
|
|
||||||
|
for future in asyncio.as_completed(tasks):
|
||||||
|
enriched_res = await future
|
||||||
yield enriched_res
|
yield enriched_res
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
Reference in new issue
Block a user