|
|
@ -34,6 +34,52 @@ def parse_divar_url(divar_url): |
|
|
query_params = dict(urllib.parse.parse_qsl(parsed.query)) |
|
|
query_params = dict(urllib.parse.parse_qsl(parsed.query)) |
|
|
return city, category, query_params |
|
|
return city, category, query_params |
|
|
|
|
|
|
|
|
|
|
|
def extract_price_from_widget(d): |
|
|
|
|
|
""" |
|
|
|
|
|
Extracts human-readable price string from various Divar widget structures. |
|
|
|
|
|
""" |
|
|
|
|
|
if not isinstance(d, dict): |
|
|
|
|
|
return '' |
|
|
|
|
|
|
|
|
|
|
|
data = d.get('data', {}) if isinstance(d.get('data'), dict) else {} |
|
|
|
|
|
|
|
|
|
|
|
# 1. Direct price string or dict in d or data |
|
|
|
|
|
raw_price = d.get('price') or data.get('price') or data.get('price_text') or data.get('price_str') |
|
|
|
|
|
if isinstance(raw_price, str) and raw_price.strip(): |
|
|
|
|
|
return raw_price.strip() |
|
|
|
|
|
elif isinstance(raw_price, dict): |
|
|
|
|
|
text = raw_price.get('text') or raw_price.get('price_text') or raw_price.get('value') |
|
|
|
|
|
if text: |
|
|
|
|
|
return str(text).strip() |
|
|
|
|
|
|
|
|
|
|
|
# 2. Check action_log server_side_params |
|
|
|
|
|
action_log = data.get('action_log', {}) if isinstance(data.get('action_log'), dict) else {} |
|
|
|
|
|
server_params = action_log.get('server_side_params', {}) if isinstance(action_log.get('server_side_params'), dict) else {} |
|
|
|
|
|
if 'price' in server_params: |
|
|
|
|
|
p_val = server_params.get('price') |
|
|
|
|
|
if p_val: |
|
|
|
|
|
return f"{p_val} تومان" if str(p_val).isdigit() else str(p_val) |
|
|
|
|
|
|
|
|
|
|
|
# 3. Check middle_description_text, bottom_description_text, top_description_text |
|
|
|
|
|
middle_desc = str(data.get('middle_description_text') or d.get('middle_description_text') or '').strip() |
|
|
|
|
|
bottom_desc = str(data.get('bottom_description_text') or d.get('bottom_description_text') or '').strip() |
|
|
|
|
|
top_desc = str(data.get('top_description_text') or d.get('top_description_text') or '').strip() |
|
|
|
|
|
|
|
|
|
|
|
price_keywords = ['تومان', 'ریال', 'توافقی', 'رایگان', 'ودیعه', 'اجاره', 'قیمت'] |
|
|
|
|
|
|
|
|
|
|
|
for text in [middle_desc, bottom_desc, top_desc]: |
|
|
|
|
|
if text and any(kw in text for kw in price_keywords): |
|
|
|
|
|
return text |
|
|
|
|
|
|
|
|
|
|
|
persian_digits = '۰۱۲۳۴۵۶۷۸۹0123456789' |
|
|
|
|
|
if middle_desc and any(char in middle_desc for char in persian_digits): |
|
|
|
|
|
return middle_desc |
|
|
|
|
|
|
|
|
|
|
|
if bottom_desc and any(char in bottom_desc for char in persian_digits): |
|
|
|
|
|
return bottom_desc |
|
|
|
|
|
|
|
|
|
|
|
return middle_desc or bottom_desc or top_desc or '' |
|
|
|
|
|
|
|
|
def extract_widgets_from_dict(d, found_ads): |
|
|
def extract_widgets_from_dict(d, found_ads): |
|
|
""" |
|
|
""" |
|
|
Recursively searches a JSON structure for dictionaries matching |
|
|
Recursively searches a JSON structure for dictionaries matching |
|
|
@ -62,12 +108,7 @@ def extract_widgets_from_dict(d, found_ads): |
|
|
'' |
|
|
'' |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
price = ( |
|
|
|
|
|
d.get('price') or |
|
|
|
|
|
d.get('data', {}).get('price_text') or |
|
|
|
|
|
d.get('data', {}).get('price') or |
|
|
|
|
|
'' |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
price = extract_price_from_widget(d) |
|
|
|
|
|
|
|
|
image_url = '' |
|
|
image_url = '' |
|
|
image_data = d.get('image') or d.get('data', {}).get('image') or d.get('data', {}).get('image_url') |
|
|
image_data = d.get('image') or d.get('data', {}).get('image') or d.get('data', {}).get('image_url') |
|
|
@ -185,6 +226,9 @@ def run_crawl_pipeline(run_id, force=False): |
|
|
'url': f"https://divar.ir/v/{token}" |
|
|
'url': f"https://divar.ir/v/{token}" |
|
|
} |
|
|
} |
|
|
) |
|
|
) |
|
|
|
|
|
if not created and ad_info['price'] and (not ad.price or ad.price == 'Not specified'): |
|
|
|
|
|
ad.price = ad_info['price'] |
|
|
|
|
|
ad.save(update_fields=['price']) |
|
|
|
|
|
|
|
|
# Check if an evaluation already exists for this crawler on this ad |
|
|
# Check if an evaluation already exists for this crawler on this ad |
|
|
eval_exists = AdEvaluation.objects.filter(ad=ad, crawl_task=task).exists() |
|
|
eval_exists = AdEvaluation.objects.filter(ad=ad, crawl_task=task).exists() |
|
|
|