Trường Hợp Sử Dụng

Xử lý CAPTCHA để kiểm tra đăng ký khóa học đại học

Hệ thống đăng ký khóa học đại học sử dụng reCAPTCHA v2 để bảo vệ các biểu mẫu đăng ký, trang tìm kiếm lớp học và người lập kế hoạch lịch trình. Các nhóm QA kiểm tra các hệ thống này cần xử lý CAPTCHA trong nhiều tình huống kiểm tra — quy trình đăng ký, logic danh sách chờ, chỗ trống và thực thi thời hạn. Dưới đây là cách tích hợp giải pháp CAPTCHA vào kiểm tra đăng ký.

Nơi CAPTCHA xuất hiện trong hệ thống đăng ký

Bước đăng ký loại CAPTCHA Mục đích
Trang đăng nhập sinh viên reCAPTCHA v2 Ngăn chặn việc nhồi thông tin xác thực
Tìm kiếm khóa học/browse reCAPTCHA v2 Hạn chế tra cứu tự động
Thêm các khóa học/drop reCAPTCHA v2 Ngăn chặn đăng ký tự động
Đăng ký danh sách chờ reCAPTCHA v2 Chặn bot đăng ký tự động
lập kế hoạch lịch trình Cloudflare Turnstile Bảo vệ các công cụ tương tác
Yêu cầu bảng điểm reCAPTCHA v2 Xác minh người dùng con người

Kiểm tra đăng ký bằng giải CAPTCHA

import requests
import time
import re

class RegistrationTester:
    def __init__(self, api_key, portal_url):
        self.api_key = api_key
        self.portal_url = portal_url
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        })

    def test_login(self, student_id, password):
        """Test login flow with CAPTCHA handling."""
        login_page = self.session.get(f"{self.portal_url}/login")

        site_key = self._extract_site_key(login_page.text)
        token = self._solve_recaptcha(site_key, f"{self.portal_url}/login")

        response = self.session.post(f"{self.portal_url}/login", data={
            "studentId": student_id,
            "password": password,
            "g-recaptcha-response": token
        })

        return {
            "status": response.status_code,
            "redirected": response.url != f"{self.portal_url}/login",
            "success": "dashboard" in response.url or response.status_code == 200
        }

    def test_course_search(self, term, department, level=None):
        """Test course search returns correct results."""
        params = {"term": term, "dept": department}
        if level:
            params["level"] = level

        response = self.session.get(
            f"{self.portal_url}/courses/search", params=params
        )

        if self._has_captcha(response.text):
            site_key = self._extract_site_key(response.text)
            token = self._solve_recaptcha(
                site_key, f"{self.portal_url}/courses/search"
            )
            response = self.session.post(
                f"{self.portal_url}/courses/search",
                data={**params, "g-recaptcha-response": token}
            )

        courses = self._parse_courses(response.text)
        return {
            "count": len(courses),
            "courses": courses,
            "all_match_dept": all(
                c["department"] == department for c in courses
            )
        }

    def test_enrollment(self, course_id, section):
        """Test enrolling in a course section."""
        enroll_page = self.session.get(
            f"{self.portal_url}/enroll/{course_id}/{section}"
        )

        if self._has_captcha(enroll_page.text):
            site_key = self._extract_site_key(enroll_page.text)
            token = self._solve_recaptcha(
                site_key,
                f"{self.portal_url}/enroll/{course_id}/{section}"
            )
        else:
            token = None

        form_data = {
            "courseId": course_id,
            "section": section,
            "confirm": "true"
        }
        if token:
            form_data["g-recaptcha-response"] = token

        response = self.session.post(
            f"{self.portal_url}/enroll/submit",
            data=form_data
        )

        return {
            "status": response.status_code,
            "enrolled": "success" in response.text.lower(),
            "waitlisted": "waitlist" in response.text.lower(),
            "error": self._extract_error(response.text)
        }

    def test_seat_availability(self, course_ids):
        """Test seat availability display for multiple courses."""
        results = []
        for course_id in course_ids:
            response = self.session.get(
                f"{self.portal_url}/courses/{course_id}"
            )

            if self._has_captcha(response.text):
                site_key = self._extract_site_key(response.text)
                token = self._solve_recaptcha(
                    site_key, f"{self.portal_url}/courses/{course_id}"
                )
                response = self.session.post(
                    f"{self.portal_url}/courses/{course_id}",
                    data={"g-recaptcha-response": token}
                )

            availability = self._parse_availability(response.text)
            results.append({"course_id": course_id, **availability})

        return results

    def _has_captcha(self, html):
        return "g-recaptcha" in html or "recaptcha" in html.lower()

    def _extract_site_key(self, html):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if match:
            return match.group(1)
        raise ValueError("reCAPTCHA site key not found")

    def _solve_recaptcha(self, site_key, page_url):
        resp = requests.post("https://ocr.captchaai.com/in.php", data={
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": page_url,
            "json": 1
        })
        task_id = resp.json()["request"]

        for _ in range(60):
            time.sleep(3)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
                "json": 1
            })
            data = result.json()
            if data["status"] == 1:
                return data["request"]

        raise TimeoutError("reCAPTCHA solve timed out")

    def _parse_courses(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")

        def text_or_empty(node):
            return node.text.strip() if node and node.text else ""

        courses = []
        for row in soup.select(".course-row, tr.course"):
            courses.append({
                "id": row.get("data-course-id", ""),
                "department": text_or_empty(row.select_one(".dept")),
                "number": text_or_empty(row.select_one(".number")),
                "title": text_or_empty(row.select_one(".title")),
                "seats": text_or_empty(row.select_one(".seats"))
            })
        return courses

    def _parse_availability(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")

        def text_or_none(node):
            return node.text.strip() if node and node.text else None

        return {
            "total_seats": text_or_none(soup.select_one(".total-seats")),
            "enrolled": text_or_none(soup.select_one(".enrolled-count")),
            "available": text_or_none(soup.select_one(".available-seats")),
            "waitlist": text_or_none(soup.select_one(".waitlist-count"))
        }

    def _extract_error(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")
        error = soup.select_one(".error-message, .alert-danger")
        return error.text.strip() if error else None

Bộ chạy thử nghiệm (JavaScript)

class RegistrationTestSuite {
  constructor(apiKey, portalUrl) {
    this.apiKey = apiKey;
    this.portalUrl = portalUrl;
    this.results = [];
  }

  async runAll(testCredentials) {
    const tests = [
      () => this.testLogin(testCredentials),
      () => this.testCourseSearch('Fall2025', 'CS'),
      () => this.testEnrollment('CS101', '001'),
      () => this.testDropCourse('CS101', '001'),
      () => this.testWaitlistFlow('CS201', '001')
    ];

    for (const test of tests) {
      try {
        const result = await test();
        this.results.push({ ...result, passed: true });
      } catch (error) {
        this.results.push({ name: test.name, error: error.message, passed: false });
      }
    }

    return this.generateReport();
  }

  async solveRecaptcha(pageUrl, html) {
    const match = html.match(/data-sitekey="([^"]+)"/);
    if (!match) return null;

    const submitResp = await fetch('https://ocr.captchaai.com/in.php', {
      method: 'POST',
      body: new URLSearchParams({
        key: this.apiKey,
        method: 'userrecaptcha',
        googlekey: match[1],
        pageurl: pageUrl,
        json: '1'
      })
    });
    const { request: taskId } = await submitResp.json();

    for (let i = 0; i < 60; i++) {
      await new Promise(r => setTimeout(r, 3000));
      const result = await fetch(
        `https://ocr.captchaai.com/res.php?key=${this.apiKey}&action=get&id=${taskId}&json=1`
      );
      const data = await result.json();
      if (data.status === 1) return data.request;
    }
    throw new Error('reCAPTCHA solve timed out');
  }

  generateReport() {
    const passed = this.results.filter(r => r.passed).length;
    const failed = this.results.filter(r => !r.passed).length;

    return {
      total: this.results.length,
      passed,
      failed,
      passRate: `${((passed / this.results.length) * 100).toFixed(1)}%`,
      details: this.results
    };
  }
}

// Usage
const suite = new RegistrationTestSuite('YOUR_API_KEY', 'https://register.university.edu');
const report = await suite.runAll({ studentId: 'test123', password: 'testpass' });
console.log(`Tests: ${report.passed}/${report.total} passed (${report.passRate})`);

Các kịch bản thử nghiệm kích hoạt CAPTCHA

Kịch bản thử nghiệm tần số CAPTCHA tác động
Đăng nhập bằng nhiều tài khoản Cao Mỗi lần đăng nhập sẽ kích hoạt CAPTCHA
Kỳ thi tuyển sinh đồng thời Cao Bảo vệ chống bot
Tìm kiếm khóa học nhanh chóng Trung bình Truy vấn có giới hạn tỷ lệ
Tạo lịch trình Thấp Tương tác một phiên
Xác thực điều kiện tiên quyết Thấp Một phần của quy trình tuyển sinh

Khắc phục sự cố

Vấn đề Nguyên nhân Cách xử lý
CAPTCHA trên mỗi lần đăng nhập thử nghiệm Không tái sử dụng phiên Xác thực một lần, chạy thử nghiệm trong phiên
Đăng ký không thành công sau CAPTCHA Mã thông báo CSRF đã hết hạn Trích xuất mã thông báo CSRF mới bằng CAPTCHA
Kiểm tra dữ liệu đặt lại giữa các lần chạy Cổng thông tin xóa đăng ký kiểm tra Sử dụng môi trường thử nghiệm chuyên dụng
Không tìm thấy khóa trang web reCAPTCHA SPA hiển thị CAPTCHA một cách linh hoạt Sử dụng tự động hóa trình duyệt để truy cập DOM được hiển thị

Câu hỏi thường gặp

Tôi có nên sử dụng môi trường thử nghiệm không có CAPTCHA không?

Lý tưởng nhất là môi trường dàn dựng sẽ vô hiệu hóa CAPTCHA. Khi điều đó là không thể (hoặc khi kiểm tra hành vi sản xuất), CaptchaAI sẽ xử lý việc giải reCAPTCHA v2 để các bài kiểm tra chạy mà không cần can thiệp thủ công.

Làm cách nào để xử lý CAPTCHA trong quy trình thử nghiệm CI/CD?

Thêm CaptchaAI làm phần phụ thuộc thử nghiệm. Khi bài kiểm tra gặp CAPTCHA, hãy giải quyết nó thông qua API và tiếp tục. Điều này giúp cho các bài kiểm tra đầu cuối luôn đáng tin cậy mà không cần tắt các tính năng bảo mật.

Tôi có thể kiểm tra quá trình đăng ký bằng CAPTCHA không?

Vâng. CaptchaAI xử lý việc giải reCAPTCHA song song để kiểm tra tải. Gửi nhiều yêu cầu giải quyết đồng thời cho mỗi buổi học mô phỏng.

bài viết liên quan

Các bước tiếp theo

Hệ thống đăng ký thử nghiệm từ đầu đến cuối —lấy khóa API CaptchaAI của bạnvà xử lý CAPTCHA của cổng thông tin đại học trong bộ thử nghiệm của bạn.

Os comentários estão desativados para este artigo.