Checks done
This commit is contained in:
+78
-18
@@ -147,7 +147,7 @@ def extract_agent_posts(agent_result: str, page_text: str) -> list[dict]:
|
|||||||
if isinstance(item, dict) and item.get('content'):
|
if isinstance(item, dict) and item.get('content'):
|
||||||
posts.append({
|
posts.append({
|
||||||
'content': item.get('content', ''),
|
'content': item.get('content', ''),
|
||||||
'author': item.get('author', ''),
|
'author': item.get('author', '') or item.get('publisher', '') or '',
|
||||||
'url': item.get('url', ''),
|
'url': item.get('url', ''),
|
||||||
'date': item.get('date', ''),
|
'date': item.get('date', ''),
|
||||||
})
|
})
|
||||||
@@ -155,11 +155,20 @@ def extract_agent_posts(agent_result: str, page_text: str) -> list[dict]:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
if not posts:
|
if not posts:
|
||||||
lines = [l.strip() for l in page_text.split('\n') if l.strip()]
|
lines = [l.strip() for l in agent_result.split('\n') if l.strip()]
|
||||||
for line in lines:
|
cur = []
|
||||||
if len(line) > 30 and not any(skip in line.lower() for skip in
|
for l in lines:
|
||||||
['facebook', 'messenger', 'notification', 'comment', 'like', 'share']):
|
if l.startswith(('1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.', '- ', '* ')):
|
||||||
posts.append({'content': line[:500], 'author': '', 'url': '', 'date': ''})
|
if cur:
|
||||||
|
combined = ' '.join(cur)
|
||||||
|
if len(combined) > 30:
|
||||||
|
posts.append({'content': combined[:500], 'author': '', 'url': '', 'date': ''})
|
||||||
|
cur = []
|
||||||
|
cur.append(l)
|
||||||
|
if cur:
|
||||||
|
combined = ' '.join(cur)
|
||||||
|
if len(combined) > 30:
|
||||||
|
posts.append({'content': combined[:500], 'author': '', 'url': '', 'date': ''})
|
||||||
|
|
||||||
return posts
|
return posts
|
||||||
|
|
||||||
@@ -443,6 +452,18 @@ def _parse_fb_date(block: list[str]) -> str:
|
|||||||
pass
|
pass
|
||||||
return datetime.now().strftime('%Y-%m-%d')
|
return datetime.now().strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
|
||||||
|
def _is_within_days(date_str: str, max_days: int = 3) -> bool:
|
||||||
|
"""Check if date is within max_days from now. Empty/unparseable = keep."""
|
||||||
|
if not date_str:
|
||||||
|
return True
|
||||||
|
try:
|
||||||
|
dt = datetime.strptime(date_str.strip(), '%Y-%m-%d')
|
||||||
|
return (datetime.now() - dt).days <= max_days
|
||||||
|
except ValueError:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _clean_fb_text(text: str) -> str:
|
def _clean_fb_text(text: str) -> str:
|
||||||
cleaned_lines = []
|
cleaned_lines = []
|
||||||
for l in text.split('\n'):
|
for l in text.split('\n'):
|
||||||
@@ -475,13 +496,26 @@ def _extract_posts_from_elements(elements: list[dict], base_url: str) -> list[di
|
|||||||
seen_texts.add(dekey)
|
seen_texts.add(dekey)
|
||||||
request_score = 2 if is_request(text) else 0
|
request_score = 2 if is_request(text) else 0
|
||||||
post_url = el.get('url') or base_url
|
post_url = el.get('url') or base_url
|
||||||
|
|
||||||
|
# Prefer JS-extracted date, fall back to text parsing
|
||||||
|
js_date = (el.get('date') or '').strip()
|
||||||
|
if js_date:
|
||||||
|
# Try ISO datetime from <time datetime>
|
||||||
|
try:
|
||||||
|
dt = datetime.fromisoformat(js_date.replace('Z', '+00:00'))
|
||||||
|
date_str = dt.strftime('%Y-%m-%d')
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
date_str = _parse_fb_date([js_date])
|
||||||
|
else:
|
||||||
|
date_str = _parse_fb_date(lines)
|
||||||
|
|
||||||
posts.append({
|
posts.append({
|
||||||
"title": text[:300],
|
"title": text[:300],
|
||||||
"content": text[:1000],
|
"content": text[:1000],
|
||||||
"author": el.get('author', ''),
|
"author": el.get('author', ''),
|
||||||
"url": post_url,
|
"url": post_url,
|
||||||
"source": "facebook",
|
"source": "facebook",
|
||||||
"date": _parse_fb_date(lines),
|
"date": date_str,
|
||||||
"_score": request_score,
|
"_score": request_score,
|
||||||
})
|
})
|
||||||
posts.sort(key=lambda p: p.get('_score', 0), reverse=True)
|
posts.sort(key=lambda p: p.get('_score', 0), reverse=True)
|
||||||
@@ -588,24 +622,42 @@ async def _get_article_elements(page) -> list[dict]:
|
|||||||
const key = text.substring(0, 80);
|
const key = text.substring(0, 80);
|
||||||
if (seenTexts.has(key)) continue;
|
if (seenTexts.has(key)) continue;
|
||||||
seenTexts.add(key);
|
seenTexts.add(key);
|
||||||
const links = el.querySelectorAll('a');
|
|
||||||
|
// --- Publisher name ---
|
||||||
let author = '';
|
let author = '';
|
||||||
for (const a of links) {
|
const nameEl = el.querySelector('h4, strong, a[role="link"]');
|
||||||
const t = (a.innerText || '').trim();
|
if (nameEl) {
|
||||||
if (t && t.length > 2 && t.length < 80 && /^[a-zA-ZÀ-ÿ][a-zA-ZÀ-ÿ .'-]+$/.test(t)) {
|
author = (nameEl.innerText || '').trim();
|
||||||
author = t;
|
}
|
||||||
break;
|
if (!author) {
|
||||||
|
const links = el.querySelectorAll('a');
|
||||||
|
for (const a of links) {
|
||||||
|
const t = (a.innerText || '').trim();
|
||||||
|
if (t && t.length > 1 && t.length < 60 && /^[A-Za-zÀ-ÿ]/.test(t) && !t.includes('facebook') && !t.includes('/')) {
|
||||||
|
author = t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Post URL ---
|
||||||
let postUrl = '';
|
let postUrl = '';
|
||||||
for (const a of links) {
|
for (const a of el.querySelectorAll('a')) {
|
||||||
const h = (a.getAttribute('href') || '');
|
const h = (a.getAttribute('href') || '');
|
||||||
if (h.includes('/posts/') || h.includes('/photo/') || h.includes('/video/') || h.includes('/groups/')) {
|
if (h.includes('/posts/') || h.includes('/photo/') || h.includes('/video/') || h.includes('/groups/') || h.includes('/permalink/')) {
|
||||||
postUrl = h.startsWith('http') ? h : 'https://www.facebook.com' + h;
|
postUrl = h.startsWith('http') ? h : 'https://www.facebook.com' + h;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
results.push({ text, author, url: postUrl });
|
|
||||||
|
// --- Date from <time datetime> ---
|
||||||
|
let date = '';
|
||||||
|
const timeEl = el.querySelector('time');
|
||||||
|
if (timeEl) {
|
||||||
|
date = timeEl.getAttribute('datetime') || timeEl.getAttribute('aria-label') || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push({ text, author, url: postUrl, date });
|
||||||
}
|
}
|
||||||
if (results.length > 0) break;
|
if (results.length > 0) break;
|
||||||
}
|
}
|
||||||
@@ -804,6 +856,9 @@ async def _scrape_with_firefox(profile_path: str, force: bool) -> dict:
|
|||||||
seen.add(key)
|
seen.add(key)
|
||||||
deduped.append(p)
|
deduped.append(p)
|
||||||
|
|
||||||
|
# Filter to last 3 days only
|
||||||
|
deduped = [p for p in deduped if _is_within_days(p.get('date', ''), 3)]
|
||||||
|
|
||||||
leads = deduped[:20]
|
leads = deduped[:20]
|
||||||
if leads:
|
if leads:
|
||||||
leads = await classify_leads(leads)
|
leads = await classify_leads(leads)
|
||||||
@@ -847,10 +902,12 @@ async def _scrape_with_agent(force: bool = False) -> dict:
|
|||||||
2. Use the Facebook search to find: {query}
|
2. Use the Facebook search to find: {query}
|
||||||
3. Scroll through the results
|
3. Scroll through the results
|
||||||
4. For each relevant post, extract and list:
|
4. For each relevant post, extract and list:
|
||||||
|
- The publisher/author name (who posted it)
|
||||||
- The post text content
|
- The post text content
|
||||||
- The author name
|
|
||||||
- The post URL (if visible)
|
- The post URL (if visible)
|
||||||
5. Collect as many posts as you can (aim for 5-10 per search)
|
- The post date
|
||||||
|
5. ONLY include posts from the last 3 days
|
||||||
|
6. Collect as many posts as you can (aim for 5-10 per search)
|
||||||
|
|
||||||
When done, return the data as a JSON list with keys: content, author, url, date.""",
|
When done, return the data as a JSON list with keys: content, author, url, date.""",
|
||||||
llm=make_ollama(num_ctx=32000, temperature=0.3),
|
llm=make_ollama(num_ctx=32000, temperature=0.3),
|
||||||
@@ -878,6 +935,9 @@ When done, return the data as a JSON list with keys: content, author, url, date.
|
|||||||
seen.add(key)
|
seen.add(key)
|
||||||
deduped.append(p)
|
deduped.append(p)
|
||||||
|
|
||||||
|
# Filter to last 3 days only
|
||||||
|
deduped = [p for p in deduped if _is_within_days(p.get('date', ''), 3)]
|
||||||
|
|
||||||
leads = deduped[:20]
|
leads = deduped[:20]
|
||||||
if leads:
|
if leads:
|
||||||
leads = await classify_leads(leads)
|
leads = await classify_leads(leads)
|
||||||
|
|||||||
Reference in New Issue
Block a user