Crawler Configuration
v3.0.0Simulation simulates complex depths, outbound scopes, and OCR targets inside our sandbox.
Scans embedded images using Tesseract OCR to extract text/email signatures.
Downloads and parses attached PDF, DOCX, DOC, and TXT files found on pages.
Ignores web page text and images. Strictly crawls pages to locate & extract emails from PDF/Word document files only.
Live Dynamic Domain & OCR Engine Mapper
Engine idle... Ready to parse.Extracted Email Contacts Dataset
Aggregated email matches from HTML parsing and Tesseract OCR decodes. 0 Unique / 0 Total
| Match ID | Found Email Address | Discovery Source URL | Extraction Type | Context / Sector | Depth Level |
|---|---|---|---|---|---|
| No target crawls initiated yet. Configure and trigger the engine on the dashboard to fetch data. | |||||
Local Scraping Database Audit
Reports standard mock SQL jobs tracking, timestamps, success vectors, and OCR count metrics.
| Job ID | Target domain | Trigger Date/Time | Max Depth | Extracted Emails | Engine Outcome |
|---|
Production PHP & MySQL Crawler with OCR Capability
Ready to deploy on LAMP/XAMPP. Includes cURL multi-thread traversal, DOM image extractor, and Tesseract PHP OCR integration guides.
Server Pre-requisites
- PHP version 8.0 or greater
- Tesseract OCR binary installed on server (Linux: `apt-get install tesseract-ocr`, Windows: setup EXE configured in system PATH)
- Composer package `thiagoalessio/tesseract-ocr` loaded
- PDO, GD/Imagick, and cURL extensions enabled
<?php
/**
* PRODUCTION-GRADE WEB CRAWLER & EMAIL EXTRACTOR WITH MYSQL INTEGRATION & SERVER SIDE OCR
* Language: PHP 8.0+ (cURL, DOMDocument, PDO, Tesseract-OCR)
* Composer installation: composer require thiagoalessio/tesseract-ocr
*/
header('Content-Type: text/html; charset=utf-8');
// --- DATABASE CONFIGURATION ---
define('DB_HOST', 'localhost');
define('DB_NAME', 'email_crawler_db');
define('DB_USER', 'root');
define('DB_PASS', '');
try {
$db = new PDO("mysql:host=" . DB_HOST, DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
$db->exec("CREATE DATABASE IF NOT EXISTS " . DB_NAME);
$db->exec("USE " . DB_NAME);
$db->exec("CREATE TABLE IF NOT EXISTS crawl_jobs (
id INT AUTO_INCREMENT PRIMARY KEY,
start_url VARCHAR(255) NOT NULL,
max_depth INT DEFAULT 2,
pages_limit INT DEFAULT 25,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;");
$db->exec("CREATE TABLE IF NOT EXISTS extracted_emails (
id INT AUTO_INCREMENT PRIMARY KEY,
job_id INT,
email VARCHAR(150) NOT NULL,
source_url VARCHAR(255) NOT NULL,
extraction_type VARCHAR(50) DEFAULT 'DOM Text',
discovered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (job_id) REFERENCES crawl_jobs(id) ON DELETE CASCADE,
UNIQUE KEY unique_email_url (email, source_url)
) ENGINE=InnoDB;");
} catch (PDOException $e) {
die("Database Connection Failed: " . htmlspecialchars($e->getMessage()));
}
// --- CORE CRAWLING & OCR LOGIC ---
use thiagoalessio\TesseractOCR\TesseractOCR;
class ServerEmailCrawler {
private $startUrl;
private $maxDepth;
private $maxPages;
private $jobId;
private $pdo;
private $enableOcr = true;
private $visited = [];
private $pagesProcessed = 0;
public function __construct($pdo, $startUrl, $maxDepth = 2, $maxPages = 20, $enableOcr = true) {
$this->pdo = $pdo;
$this->startUrl = rtrim(filter_var($startUrl, FILTER_SANITIZE_URL), '/');
$this->maxDepth = intval($maxDepth);
$this->maxPages = intval($maxPages);
$this->enableOcr = $enableOcr;
$stmt = $this->pdo->prepare("INSERT INTO crawl_jobs (start_url, max_depth, pages_limit) VALUES (?, ?, ?)");
$stmt->execute([$this->startUrl, $this->maxDepth, $this->maxPages]);
$this->jobId = $this->pdo->lastInsertId();
}
public function start() {
echo "Starting crawl on " . htmlspecialchars($this->startUrl) . "\n";
$this->crawl($this->startUrl, 1);
}
private function crawl($url, $depth) {
if ($depth > $this->maxDepth || $this->pagesProcessed >= $this->maxPages || isset($this->visited[$url])) {
return;
}
$this->visited[$url] = true;
$this->pagesProcessed++;
$html = $this->fetchWithCurl($url);
if (!$html) return;
// 1. Text Extraction
$emails = $this->extractEmails($html);
foreach ($emails as $email) {
$this->saveEmail($email, $url, 'DOM HTML Text');
}
// 2. OCR Image Extraction
if ($this->enableOcr) {
$images = $this->extractImages($html, $url);
foreach ($images as $imgUrl) {
$imgData = $this->fetchWithCurl($imgUrl);
if ($imgData) {
$tmpFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'ocr_tmp_' . md5($imgUrl);
file_put_contents($tmpFile, $imgData);
try {
// Scan downloaded image using local tesseract binaries
$ocrText = (new TesseractOCR($tmpFile))->run();
$ocrEmails = $this->extractEmails($ocrText);
foreach ($ocrEmails as $ocrEmail) {
$this->saveEmail($ocrEmail, $url, 'OCR Image Extraction (' . basename($imgUrl) . ')');
}
} catch (\Exception $e) {
// Failed to OCR (e.g. invalid format or binaries missing)
}
@unlink($tmpFile);
}
}
}
// Recursion
if ($depth < $this->maxDepth) {
$links = $this->extractLinks($html, $url);
foreach ($links as $link) {
$this->crawl($link, $depth + 1);
}
}
}
private function fetchWithCurl($url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$res = curl_exec($ch);
curl_close($ch);
return $res;
}
private function extractEmails($text) {
preg_match_all('/[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,6}/i', $text, $matches);
return array_unique($matches[0] ?? []);
}
private function extractImages($html, $baseUrl) {
$images = [];
$dom = new DOMDocument();
@$dom->loadHTML($html);
foreach ($dom->getElementsByTagName('img') as $img) {
$src = $img->getAttribute('src');
if ($src) {
if (strpos($src, 'http') !== 0) {
$src = rtrim($baseUrl, '/') . '/' . ltrim($src, '/');
}
$images[] = $src;
}
}
return array_slice(array_unique($images), 0, 10); // Limit to 10 images per page
}
private function extractLinks($html, $baseUrl) {
$links = [];
$dom = new DOMDocument();
@$dom->loadHTML($html);
foreach ($dom->getElementsByTagName('a') as $a) {
$href = $a->getAttribute('href');
if ($href && strpos($href, '#') !== 0 && strpos($href, 'mailto:') !== 0) {
if (strpos($href, 'http') !== 0) {
$href = rtrim($baseUrl, '/') . '/' . ltrim($href, '/');
}
$links[] = $href;
}
}
return array_slice(array_unique($links), 0, 15);
}
private function saveEmail($email, $url, $type) {
try {
$stmt = $this->pdo->prepare("INSERT IGNORE INTO extracted_emails (job_id, email, source_url, extraction_type) VALUES (?, ?, ?, ?)");
$stmt->execute([$this->jobId, $email, $url, $type]);
} catch (\PDOException $e) {}
}
}
?>