Anwendungsfälle

Jobbörsen-Scraping mit CAPTCHA-Verarbeitung mit CaptchaAI

Jobbörsen wie Indeed, LinkedIn und Glassdoor setzen CAPTCHAs ein, wenn sie automatisierte Zugriffsmuster erkennen. Rekrutierungsplattformen, Marktforscher und HR-Analysetools benötigen zuverlässige CAPTCHA-Lösungen, um Daten zu Stellenangeboten in großem Umfang zu sammeln.


CAPTCHAs auf wichtigen Jobbörsen

Plattform CAPTCHA-Typ Auslöser Daten verfügbar
Tatsächlich reCAPTCHA v2 Hohes Anfragevolumen Stellenangebote, Gehälter
LinkedIn Cloudflare Challenge Bot-Erkennung Jobs, Unternehmensdaten
Glastür reCAPTCHA v2 Kratzerkennung Bewertungen, Gehälter, Jobs
ZipRecruiter Cloudflare Turnstile Automatisierter Zugriff Stellenangebote
Monster reCAPTCHA v2 Suchseiten Stellenangebote
CareerBuilder reCAPTCHA v3 Einloggen, suchen Stellenangebote, Lebenslaufsuche

Jobbörsen-Scraper mit CAPTCHA-Verwaltung

import requests
import time
import re
from bs4 import BeautifulSoup

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"


def solve_captcha(method, sitekey, pageurl, **kwargs):
    data = {
        "key": CAPTCHAAI_KEY,
        "method": method,
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1,
    }
    data.update(kwargs)
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=data)
    task_id = resp.json()["request"]

    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        r = result.json()
        if r["request"] != "CAPCHA_NOT_READY":
            return r["request"]
    raise TimeoutError("Solve timeout")


class JobBoardScraper:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
            "Accept-Language": "en-US,en;q=0.9",
        })

    def search_jobs(self, base_url, query, location, pages=5):
        """Search job listings across multiple pages."""
        all_jobs = []

        for page in range(pages):
            url = f"{base_url}/jobs?q={query}&l={location}&start={page * 10}"
            resp = self.session.get(url, timeout=30)

            # Check for CAPTCHA
            if self._has_captcha(resp.text):
                resp = self._solve_and_retry(resp.text, url)

            if resp.status_code == 200:
                jobs = self._parse_listings(resp.text)
                all_jobs.extend(jobs)
                print(f"Page {page + 1}: {len(jobs)} jobs found")
            else:
                print(f"Page {page + 1}: Request failed ({resp.status_code})")

            time.sleep(3)  # Rate limit

        return all_jobs

    def _has_captcha(self, html):
        indicators = [
            'data-sitekey=',
            'g-recaptcha',
            'cf-turnstile',
            'captcha-delivery',
        ]
        return any(ind in html.lower() for ind in indicators)

    def _solve_and_retry(self, html, url):
        # Try reCAPTCHA first
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if match:
            sitekey = match.group(1)

            # Detect Turnstile vs reCAPTCHA
            if 'cf-turnstile' in html:
                token = solve_captcha("turnstile", sitekey, url)
                field = "cf-turnstile-response"
            else:
                token = solve_captcha("userrecaptcha", sitekey, url)
                field = "g-recaptcha-response"

            return self.session.post(url, data={field: token})

        return self.session.get(url)

    def _parse_listings(self, html):
        soup = BeautifulSoup(html, "html.parser")
        jobs = []

        for card in soup.select(".job_seen_beacon, .jobsearch-ResultsList > li"):
            title_el = card.select_one("h2 a, .jobTitle a")
            company_el = card.select_one(".companyName, [data-testid='company-name']")
            location_el = card.select_one(".companyLocation, [data-testid='text-location']")
            salary_el = card.select_one(".salary-snippet, .estimated-salary")

            if title_el:
                jobs.append({
                    "title": title_el.get_text(strip=True),
                    "company": company_el.get_text(strip=True) if company_el else "",
                    "location": location_el.get_text(strip=True) if location_el else "",
                    "salary": salary_el.get_text(strip=True) if salary_el else "",
                    "url": title_el.get("href", ""),
                })

        return jobs


# Usage
scraper = JobBoardScraper(
    proxy="http://user:pass@residential.proxy.com:5000"
)
jobs = scraper.search_jobs(
    base_url="https://jobs.example.com",
    query="python developer",
    location="New York",
    pages=10,
)
print(f"Total jobs collected: {len(jobs)}")

Gehaltsdatenerfassung

import csv


def collect_salary_data(titles, locations, output_file):
    """Collect salary data across job titles and locations."""
    scraper = JobBoardScraper(
        proxy="http://user:pass@residential.proxy.com:5000"
    )

    results = []
    for title in titles:
        for location in locations:
            try:
                jobs = scraper.search_jobs(
                    "https://jobs.example.com",
                    title, location, pages=3,
                )
                salaries = [j["salary"] for j in jobs if j["salary"]]
                results.append({
                    "title": title,
                    "location": location,
                    "listings": len(jobs),
                    "with_salary": len(salaries),
                    "salary_samples": "; ".join(salaries[:5]),
                })
                time.sleep(5)
            except Exception as e:
                results.append({
                    "title": title,
                    "location": location,
                    "error": str(e),
                })

    with open(output_file, "w", newline="") as f:
        writer = csv.DictWriter(
            f, fieldnames=["title", "location", "listings",
                           "with_salary", "salary_samples", "error"],
        )
        writer.writeheader()
        writer.writerows(results)

    return results


# Collect salary data for market analysis
collect_salary_data(
    titles=["Data Engineer", "ML Engineer", "DevOps Engineer"],
    locations=["San Francisco", "New York", "Austin", "Remote"],
    output_file="salary_data.csv",
)

Anti-Erkennungs-Tipps für Jobmärkte

Technik Warum es hilft
Rotierende Wohn-Proxys Verteilt Anfragen über echte IPs
3–5 Sekunden Verzögerung zwischen den Seiten Imitiert die menschliche Surfgeschwindigkeit
Konsistenter User-Agent pro Sitzung Verhindert Fehlanpassungen von Browser-Signalprofilen
Akzeptieren Sie Cookies Jobbörsen verfolgen Sitzungen über Cookies
Suchreihenfolge zufällig festlegen Vermeiden Sie sequentielle Seitenmuster
Beschränkung auf 200 Seiten/day pro Domain Bleiben Sie unter den Erkennungsschwellen

Fehlerbehebung

Problem Ursache Lösung
CAPTCHA bei jeder Suche IP-Adresse markiert oder Rate überschritten Wechseln Sie die IP und fügen Sie längere Verzögerungen hinzu
Leere Ergebnisseite Stattdessen wurde ein CAPTCHA-Block zurückgegeben Erkennen Sie CAPTCHA vor dem Parsen
„Bitte bestätigen Sie, dass Sie ein Mensch sind“ Bot-Erkennung ausgelöst Verwenden Sie einen Wohn-Proxy + realistische UA
Für Gehaltsdaten ist eine Anmeldung erforderlich Plattform-Gating-Inhalte Implementieren Sie eine authentifizierte Sitzung
Andere Ergebnisse als der Browser Location/cookie Unterschiede Passen Sie Accept-Language und Geo-Proxy an

FAQ

Wie viele Stellenangebote kann ich pro Tag durchsuchen?

Mit rotierenden Privat-Proxys und angemessenen Verzögerungen sind 500–2000 Seiten pro Domain erreichbar, ohne auf dauerhafte CAPTCHAs zu stoßen.

Blockieren Jobbörsen das Scraping?

In den meisten Jobbörsen gibt es Bedingungen, die einen automatisierten Zugriff verhindern, die Durchsetzung ist jedoch unterschiedlich. CAPTCHAs sind ihre primäre Verteidigung, die von CaptchaAI übernommen wird.

Welcher Proxy-Typ eignet sich am besten für Jobbörsen?

Rotierende Wohnraum-Proxys bieten das beste Gleichgewicht zwischen Kosten und Erfolgsquote. Rechenzentrums-IPs werden häufig von LinkedIn und Glassdoor blockiert.


Verwandte Leitfäden


Erfassen Sie Arbeitsmarktdaten in großem Maßstab –Holen Sie sich Ihren CaptchaAI-Schlüsselzur automatisierten CAPTCHA-Lösung.

Kommentare sind für diesen Artikel deaktiviert.