"""LinkedIn Jobs adapter — scroll to load all cards on first page only.""" from __future__ import annotations from loguru import logger from gimme_job.adapters.base import ManifestDrivenAdapter from gimme_job.adapters.registry import register from gimme_job.models.dto import JobPostingCandidate, RawJobCard @register("linkedin") class LinkedInAdapter(ManifestDrivenAdapter): def collect_cards(self, page, manifest) -> list[RawJobCard]: """Scroll each card into view to trigger lazy-loading, then extract.""" self._scroll_to_load(page) return super().collect_cards(page, manifest) def normalize(self, raw: RawJobCard) -> JobPostingCandidate: return super().normalize(raw) @staticmethod def _scroll_to_load(page) -> None: """Scroll each job card into view to force LinkedIn's virtual list to render.""" try: page.evaluate("""() => { const cards = Array.from(document.querySelectorAll('li[data-occludable-job-id]')); cards.forEach(card => card.scrollIntoView({ behavior: 'instant', block: 'center' })); }""") page.wait_for_timeout(800) page.evaluate("window.scrollTo(0, 0)") page.wait_for_timeout(400) except Exception as e: logger.debug(f"[linkedin] scroll_to_load failed: {e}")