Files
site-scraper/src/services/scraper.ts
T

106 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { chromium, Browser } from "playwright";
let browser: Browser | null = null;
async function getBrowser(): Promise<Browser> {
if (!browser || !browser.isConnected()) {
browser = await chromium.launch({
headless: true,
args: [
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
"--disable-gpu"
],
});
}
return browser;
}
// Navbar'da atlanacak anahtar kelimeler
const SKIP_KEYWORDS = [
"kariyer", "/ik", "kvkk", "gizlilik", "cerez",
"blog", "haber", "saglikbulteni", "saglik-rehberi",
"rezervasyon", "randevu", "e-sonuc", "checkup",
"wp-admin", "wp-content", "feed", "sitemap",
];
const SKIP_SCHEMES = ["mailto:", "tel:", "javascript:", "#"];
export interface NavLink {
label: string;
url: string;
}
export async function extractNavLinks(targetUrl: string): Promise<NavLink[]> {
const base = new URL(targetUrl);
const b = await getBrowser();
const page = await b.newPage();
try {
await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 30000 });
await page.waitForTimeout(1200); // dropdown'ların render edilmesi için
// Navbar içindeki tüm <a> etiketlerini çek
const rawLinks = await page.evaluate(() => {
const selectors = [
"nav a", "header a",
"[class*='menu'] a", "[class*='navbar'] a",
"[class*='nav-'] a", "[id*='menu'] a", "[id*='nav'] a",
];
const seen = new Set<string>();
const links: { href: string; label: string }[] = [];
for (const sel of selectors) {
document.querySelectorAll<HTMLAnchorElement>(sel).forEach((el) => {
const href = el.href || el.getAttribute("href") || "";
const label = el.textContent?.trim().replace(/\s+/g, " ") || "";
if (href && !seen.has(href)) {
seen.add(href);
links.push({ href, label });
}
});
}
return links;
});
// Filtrele ve temizle
const seen = new Set<string>();
const result: NavLink[] = [];
for (const { href, label } of rawLinks) {
if (!href) continue;
if (SKIP_SCHEMES.some((s) => href.startsWith(s))) continue;
let parsed: URL;
try { parsed = new URL(href, targetUrl); } catch { continue; }
// Sadece aynı domain
if (parsed.hostname !== base.hostname) continue;
const path = parsed.pathname.toLowerCase();
// Atlanacak sayfalar
if (SKIP_KEYWORDS.some((kw) => path.includes(kw))) continue;
// Fragment ve trailing slash temizle
parsed.hash = "";
const normalized = parsed.toString().replace(/\/$/, "");
if (seen.has(normalized)) continue;
seen.add(normalized);
result.push({ label: label || pathToLabel(parsed.pathname), url: normalized });
}
return result;
} finally {
await page.close();
}
}
function pathToLabel(pathname: string): string {
const last = pathname.split("/").filter(Boolean).pop() || "anasayfa";
return last.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
}