この記事では、よくある疑問にできるだけお答えしました。まず最初のステップは、どのような保護システムが使われているかを特定することです。そのために、代表的なキャプチャやボット対策システムの一覧を参照できます。ここには、どの仕組みが使われているかを素早く見分けるための画面イメージや主な特徴がまとめられています。
もしサイトで ComplexImage が使われていることが分かったら、次のステップはその特徴と動作をより詳しく確認することです。同じこの記事の中で、ComplexImage をサイトに導入するための手順書も確認できるので、あなたのサイト上でどのように機能しているのかをしっかり理解できます。これにより、現在の保護を正しく把握できるだけでなく、今後の運用や保守も計画的に行えるようになります。
CapMonster Cloud API を使った一般的な手順:
type - ComplexImageTask
class - recognition
imagesBase64 - base64形式の画像配列。例: [“/9j/4AAQSkZJRgABAQEAAAAAAAD…”];
Task(metadata内) - タスク名(例:dli)。
https://api.capmonster.cloud/createTask{
"clientKey": "API_KEY",
"task": {
"type": "ComplexImageTask",
"class": "recognition",
"imagesBase64": [
"base64"
],
"metadata": {
"Task": "dli" // 必要に応じてタスクを変更してください。利用可能なモジュールのリストは https://docs.capmonster.cloud/docs/captchas/ComplexImageTask-Recognition/ で確認できます
}
}
}
{
"errorId":0,
"taskId":407533072
}https://api.capmonster.cloud/getTaskResult{
"clientKey":"API_KEY",
"taskId": 407533072
}
{
"solution":
{
"answer": "1",
"metadata": {
"AnswerType": "Text"
}
},
"cost": 0.0003,
"status": "ready",
"errorId": 0,
"errorCode": null,
"errorDescription": null
}
// npm install playwright @zennolab_com/capmonstercloud-client
// npx playwright install chromium
import { chromium } from 'playwright';
import { CapMonsterCloudClientFactory, ClientOptions, ComplexImageTaskRecognitionRequest } from '@zennolab_com/capmonstercloud-client';
const API_KEY = "YOUR_API_KEY";
const TARGET_URL = "https://example.com/captcha-page";
async function solveComplexImageTaskPlaywright() {
const browser = await chromium.launch({ headless: false });
const context = await browser.newContext();
const page = await context.newPage();
await page.goto(TARGET_URL);
// CAPTCHA画像を取得
const captchaHandle = await page.$('#captcha'); // 実際のセレクターに置き換え
const captchaBase64 = await captchaHandle.evaluate(img => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
return canvas.toDataURL('image/png').split(',')[1];
});
console.log("Captcha base64:", captchaBase64.substring(0, 50) + "...");
const cmcClient = CapMonsterCloudClientFactory.Create(
new ClientOptions({ clientKey: API_KEY })
);
// CAPTCHAを認識に送信
const citRecognitionRequest = new ComplexImageTaskRecognitionRequest({
imagesBase64: [captchaBase64],
metaData: { Task: 'oocl_rotate' } // CAPTCHAタイプを指定
});
const result = await cmcClient.Solve(citRecognitionRequest);
console.log("Solution received:", result);
// 解答の処理
const solution = result.solution;
if (!solution) {
console.error("No solution received");
return;
}
if (solution.metadata?.AnswerType === "Coordinate") {
// 座標CAPTCHA
const box = await captchaHandle.boundingBox();
for (const point of solution.answer) {
const clickX = box.x + point.X;
const clickY = box.y + point.Y;
console.log(`Clicking at: (${clickX}, ${clickY})`);
await page.mouse.click(clickX, clickY);
}
} else if (solution.metadata?.AnswerType === "Grid") {
// グリッドCAPTCHA(true/false配列)
const box = await captchaHandle.boundingBox();
const gridItems = await page.$$('#captcha_grid div'); // グリッド要素のセレクターを置き換え
const answers = solution.answer;
for (let i = 0; i < answers.length; i++) {
if (answers[i] && gridItems[i]) {
const itemBox = await gridItems[i].boundingBox();
const clickX = itemBox.x + itemBox.width / 2;
const clickY = itemBox.y + itemBox.height / 2;
console.log(`Clicking grid item ${i} at: (${clickX}, ${clickY})`);
await page.mouse.click(clickX, clickY);
}
}
} else {
console.warn("Unknown captcha solution type:", solution.metadata?.AnswerType);
}
// 確認ボタンをクリック(存在する場合)
await page.click('#submit_button'); // 実際のボタンセレクターに置き換え
console.log("Captcha solved.");
}
solveComplexImageTaskPlaywright().catch(console.error);
1. サーバーでCAPTCHA生成
2. CAPTCHAをクライアントに送信
<!--画像グリッドCAPTCHA-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Grid CAPTCHA Demo</title>
<style>
#captchaGrid {
display: grid;
grid-template-columns: repeat(3, 100px);
grid-gap: 10px;
margin-bottom: 10px;
}
.grid-item {
width: 100px;
height: 100px;
background-color: #eee;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: 2px solid transparent;
}
.grid-item.selected {
border-color: blue;
}
</style>
</head>
<body>
<h1>Grid CAPTCHA Demo</h1>
<div id="captchaGrid"></div>
<button id="submitBtn">Submit</button>
<button id="refreshBtn">Refresh CAPTCHA</button>
<p id="result"></p>
<script>
let captchaId;
let answers = []; // クリック用 true/false 配列
async function loadCaptcha() {
const res = await fetch('/captcha'); // サーバーが captchaId と base64画像配列のJSONを返す
const data = await res.json();
captchaId = data.captchaId;
answers = new Array(data.images.length).fill(false);
const grid = document.getElementById('captchaGrid');
grid.innerHTML = '';
data.images.forEach((imgBase64, i) => {
const div = document.createElement('div');
div.className = 'grid-item';
div.style.backgroundImage = `url('data:image/png;base64,${imgBase64}')`;
div.style.backgroundSize = 'cover';
div.addEventListener('click', () => {
answers[i] = !answers[i];
div.classList.toggle('selected', answers[i]);
});
grid.appendChild(div);
});
document.getElementById('result').textContent = '';
}
document.getElementById('refreshBtn').addEventListener('click', loadCaptcha);
document.getElementById('submitBtn').addEventListener('click', async () => {
const res = await fetch('/captcha/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ captchaId, answer: answers })
});
const data = await res.json();
document.getElementById('result').textContent = data.success ? 'Captcha passed!' : 'Captcha failed, try again.';
if (!data.success) loadCaptcha();
});
// 起動時にCAPTCHAをロード
loadCaptcha();
</script>
</body>
</html>
3. ユーザーによる解答入力
4. サーバーへ解答送信:
5. サーバーでの確認
<?php
session_start();
// CAPTCHA TTL(秒)
define('CAPTCHA_TTL', 300); // 5分
// ランダム画像生成
function generateCaptchaImage($text = null) {
$width = 100;
$height = 100;
if (!$text) {
$text = substr(str_shuffle('ABCDEFGHJKLMNPQRSTUVWXYZ23456789'), 0, 2);
}
$image = imagecreatetruecolor($width, $height);
// 背景
$bgColor = imagecolorallocate($image, rand(180, 255), rand(180, 255), rand(180, 255));
imagefilledrectangle($image, 0, 0, $width, $height, $bgColor);
// テキスト
$textColor = imagecolorallocate($image, 0, 0, 0);
$fontSize = 15;
$fontFile = __DIR__ . '/Arial.ttf'; // TTFフォントパス
if (file_exists($fontFile)) {
imagettftext($image, $fontSize, rand(-20,20), 10, 50, $textColor, $fontFile, $text);
} else {
imagestring($image, 5, 10, 40, $text, $textColor);
}
ob_start();
imagepng($image);
$imgData = ob_get_clean();
imagedestroy($image);
return base64_encode($imgData);
}
// グリッドCAPTCHA作成
function generateGridCaptcha() {
$numImages = 9; // 3x3
$images = [];
$solution = [];
for ($i = 0; $i < $numImages; $i++) {
// ランダム解答、正しい画像か否か(例)
$isCorrect = rand(0,1) === 1;
$solution[] = $isCorrect;
// 画像生成(実際のCAPTCHAではオブジェクト追加可)
$text = $isCorrect ? 'OK' : 'NO';
$images[] = generateCaptchaImage($text);
}
return ['images' => $images, 'solution' => $solution];
}
// CAPTCHA生成エンドポイント
if ($_SERVER['REQUEST_METHOD'] === 'GET' && $_SERVER['REQUEST_URI'] === '/captcha') {
$captchaId = uniqid('captcha_', true);
$gridCaptcha = generateGridCaptcha();
$_SESSION['captchas'][$captchaId] = [
'solution' => $gridCaptcha['solution'],
'timestamp' => time()
];
header('Content-Type: application/json');
echo json_encode([
'captchaId' => $captchaId,
'images' => $gridCaptcha['images']
]);
exit;
}
// CAPTCHA検証エンドポイント
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['REQUEST_URI'] === '/captcha/verify') {
$data = json_decode(file_get_contents('php://input'), true);
$captchaId = $data['captchaId'] ?? '';
$answer = $data['answer'] ?? [];
if (!isset($_SESSION['captchas'][$captchaId])) {
echo json_encode(['success' => false, 'message' => 'Captcha expired or not found']);
exit;
}
$captcha = $_SESSION['captchas'][$captchaId];
// TTL確認
if (time() - $captcha['timestamp'] > CAPTCHA_TTL) {
unset($_SESSION['captchas'][$captchaId]);
echo json_encode(['success' => false, 'message' => 'Captcha expired']);
exit;
}
// true/false配列確認
$success = $captcha['solution'] === $answer;
// 確認後CAPTCHA削除
unset($_SESSION['captchas'][$captchaId]);
echo json_encode(['success' => $success]);
exit;
}
// 404
http_response_code(404);
echo 'Not found';
6. 次のステップ
追加の推奨事項
負荷テスト(k6やJMeterなど)実施 — 大量リクエスト時:
既にCAPTCHAや他の保護が設置されたサイトでコードにアクセスできなくても問題ありません。技術の特定は容易です。正しく動作するか確認するには、CapMonster Cloudを隔離テスト環境で使い、トークン処理やロジックが正しく機能しているか確認できます。
画像CAPTCHAの場合、システムを特定し、挙動を学習して保護が機能しているか確認します。この記事では、ComplexImage CAPTCHAの特定方法と接続・再設定方法を示し、保護を維持して機能を管理する方法を紹介しました。