From 702a14b634633b383a43be6afece701ecf5223a9 Mon Sep 17 00:00:00 2001 From: ghost Date: Sat, 6 May 2023 07:25:54 +0300 Subject: [PATCH] add mime content type crawling #1 --- README.md | 2 +- crontab/crawler.php | 97 +++++++++++++++++++++++--------------------- database/yggo.mwb | Bin 13968 -> 14611 bytes library/filter.php | 7 ++++ library/mysql.php | 41 +++++++++++-------- public/search.php | 10 ++--- 6 files changed, 87 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 036a036..27cc8c5 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ GET m=SphinxQL * [x] Auto stop crawling on disk quota reached * [x] Transactions support to prevent data loss on queue failures * [x] Distributed index crawling between YGGo nodes trough manifest API -* [ ] MIME Content-type crawler settings +* [x] MIME Content-type crawler settings * [ ] Indexing new sites homepage in higher priority * [ ] Redirect codes extended processing * [ ] Palette image index / filter diff --git a/crontab/crawler.php b/crontab/crawler.php index 7de640d..fff2066 100644 --- a/crontab/crawler.php +++ b/crontab/crawler.php @@ -236,40 +236,39 @@ try { continue; } - // Save image content on data settings enabled - if (!CRAWL_HOST_DEFAULT_META_ONLY) { + // Skip image processing on MIME type not provided + if (!$hostImageContentType = $curl->getContentType()) { - // Skip image processing on MIME type not provided - if (!$contentType = $curl->getContentType()) { - - continue; - } - - // Skip image processing on MIME type not allowed in settings - if (false === strpos($contentType, CRAWL_IMAGE_MIME_TYPE)) { + continue; + } - continue; - } + // Skip image processing on MIME type not allowed in settings + if (false === strpos($hostImageContentType, CRAWL_IMAGE_MIME_TYPE)) { - // Skip image processing without returned content - if (!$content = $curl->getContent()) { + continue; + } - continue; - } + // Skip image processing without returned content + if (!$content = $curl->getContent()) { - // Convert remote image data to base64 string to prevent direct URL call - if (!$hostImageType = @pathinfo($queueHostImageURL, PATHINFO_EXTENSION)) { + continue; + } - continue; - } + // Convert remote image data to base64 string to prevent direct URL call + if (!$hostImageExtension = @pathinfo($queueHostImageURL, PATHINFO_EXTENSION)) { - if (!$hostImageBase64 = @base64_encode($curl->getContent())) { + continue; + } - continue; - } + if (!$hostImageBase64 = @base64_encode($curl->getContent())) { - $hostImagesIndexed += $db->updateHostImageData($hostImage->hostImageId, (string) 'data:image/' . $hostImageType . ';base64,' . $hostImageBase64, time()); + continue; } + + $hostImagesIndexed += $db->updateHostImage($hostImage->hostImageId, + Filter::mime($hostImageContentType), + (!CRAWL_HOST_DEFAULT_META_ONLY ? 'data:image/' . $hostImageExtension . ';base64,' . $hostImageBase64 : null), + time()); } // Process pages crawl queue @@ -344,12 +343,25 @@ try { } } + // Append page with meta robots:noindex value to the robotsPostfix disallow list + if (false !== stripos($metaRobots, 'noindex')) { + + continue; + } + + // Skip page links following by robots:nofollow attribute detected + if (false !== stripos($metaRobots, 'nofollow')) { + + continue; + } + // Update queued page data $hostPagesIndexed += $db->updateHostPage($queueHostPage->hostPageId, - Filter::pageTitle($title->item(0)->nodeValue), - Filter::pageDescription($metaDescription), - Filter::pageKeywords($metaKeywords), - CRAWL_HOST_DEFAULT_META_ONLY ? null : Filter::pageData($content)); + Filter::pageTitle($title->item(0)->nodeValue), + Filter::pageDescription($metaDescription), + Filter::pageKeywords($metaKeywords), + Filter::mime($contentType), + CRAWL_HOST_DEFAULT_META_ONLY ? null : Filter::pageData($content)); // Update manifest registry if (CRAWL_MANIFEST && !empty($metaYggoManifest) && filter_var($metaYggoManifest, FILTER_VALIDATE_URL) && preg_match(CRAWL_URL_REGEXP, $metaYggoManifest)) { @@ -357,25 +369,13 @@ try { $metaYggoManifestCRC32 = crc32($metaYggoManifest); if (!$db->getManifest($metaYggoManifestCRC32)) { - $db->addManifest($metaYggoManifestCRC32, + $db->addManifest($metaYggoManifestCRC32, $metaYggoManifest, (string) CRAWL_MANIFEST_DEFAULT_STATUS, time()); } } - // Append page with meta robots:noindex value to the robotsPostfix disallow list - if (false !== stripos($metaRobots, 'noindex')) { - - continue; - } - - // Skip page links following by robots:nofollow attribute detected - if (false !== stripos($metaRobots, 'nofollow')) { - - continue; - } - // Collect page images if (CRAWL_HOST_DEFAULT_IMAGES_LIMIT > 0) { @@ -402,7 +402,7 @@ try { $imageSrc = $queueHostPage->scheme . '://' . $queueHostPage->name . - ($queueHostPage->port ? ':' . $queueHostPage->port : '') . + ($queueHostPage->port ? ':' . $queueHostPage->port : '') . '/' . trim(ltrim(str_replace(['./', '../'], '', $imageSrc), '/'), '.'); } @@ -466,16 +466,19 @@ try { // Init robots parser $robots = new Robots(($hostRobots ? (string) $hostRobots : (string) CRAWL_ROBOTS_DEFAULT_RULES) . PHP_EOL . ($hostRobotsPostfix ? (string) $hostRobotsPostfix : (string) CRAWL_ROBOTS_POSTFIX_RULES)); - // Save image info + // Save new image info $hostImageId = $db->getHostImageId($hostId, crc32($hostImageURI->string)); if (!$hostImageId && // image not exists - $hostStatus && // host enabled - $robots->uriAllowed($hostImageURI->string) && // src allowed by robots.txt rules - $hostImageLimit > $db->getTotalHostImages($hostId)) { // images quantity not reached host limit + $hostStatus && // host enabled + $robots->uriAllowed($hostImageURI->string) && // src allowed by robots.txt rules + $hostImageLimit > $db->getTotalHostImages($hostId)) { // images quantity not reached host limit // Add host image - if ($hostImageId = $db->addHostImage($hostId, crc32($hostImageURI->string), $hostImageURI->string, time(), null, 200)) { + if ($hostImageId = $db->addHostImage($hostId, + crc32($hostImageURI->string), + $hostImageURI->string, + time())) { $hostImagesAdded++; diff --git a/database/yggo.mwb b/database/yggo.mwb index 9fc68cb51ed13439555a21e324695257a1b6a2a6..504dc0f6d0d2e24ea2e7a686040c8e20f5a10a0a 100644 GIT binary patch literal 14611 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 diff --git a/library/filter.php b/library/filter.php index 9b411a6..c149c63 100644 --- a/library/filter.php +++ b/library/filter.php @@ -9,6 +9,13 @@ class Filter { return trim(urldecode($url)); } + static public function mime(mixed $mime) { + + $mime = (string) $mime; + + return trim($mime); + } + static public function pageTitle(mixed $title) { $title = (string) $title; diff --git a/library/mysql.php b/library/mysql.php index d5c5a8d..6a59fdc 100644 --- a/library/mysql.php +++ b/library/mysql.php @@ -180,13 +180,14 @@ class MySQL { } public function addHostImage(int $hostId, - int $crc32uri, - string $uri, - int $timeAdded, - mixed $timeUpdated = null, - mixed $httpCode = null, - mixed $rank = null, - mixed $data = null) { + int $crc32uri, + string $uri, + int $timeAdded, + mixed $timeUpdated = null, + mixed $httpCode = null, + mixed $mime = null, + mixed $rank = null, + mixed $data = null) { $query = $this->_db->prepare('INSERT INTO `hostImage` ( `hostId`, `crc32uri`, @@ -194,10 +195,11 @@ class MySQL { `timeAdded`, `timeUpdated`, `httpCode`, + `mime`, `rank`, - `data`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'); + `data`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'); - $query->execute([$hostId, $crc32uri, $uri, $timeAdded, $timeUpdated, $httpCode, $rank, $data]); + $query->execute([$hostId, $crc32uri, $uri, $timeAdded, $timeUpdated, $httpCode, $mime, $rank, $data]); return $this->_db->lastInsertId(); } @@ -224,13 +226,14 @@ class MySQL { return $query->rowCount(); } - public function updateHostImageData(int $hostImageId, - string $data, - int $timeUpdated) { + public function updateHostImage(int $hostImageId, + string $mime, + mixed $data, + int $timeUpdated) { - $query = $this->_db->prepare('UPDATE `hostImage` SET `data` = ?, `timeUpdated` = ? WHERE `hostImageId` = ? LIMIT 1'); + $query = $this->_db->prepare('UPDATE `hostImage` SET `mime` = ?, `data` = ?, `timeUpdated` = ? WHERE `hostImageId` = ? LIMIT 1'); - $query->execute([$data, $timeUpdated, $hostImageId]); + $query->execute([$mime, $data, $timeUpdated, $hostImageId]); return $query->rowCount(); } @@ -439,6 +442,7 @@ class MySQL { int $timeAdded, mixed $timeUpdated = null, mixed $httpCode = null, + mixed $mime = null, mixed $rank = null, mixed $metaTitle = null, mixed $metaDescription = null, @@ -451,13 +455,14 @@ class MySQL { `timeAdded`, `timeUpdated`, `httpCode`, + `mime`, `rank`, `metaTitle`, `metaDescription`, `metaKeywords`, - `data`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); + `data`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); - $query->execute([$hostId, $crc32uri, $uri, $timeAdded, $timeUpdated, $httpCode, $rank, $metaTitle, $metaDescription, $metaKeywords, $data]); + $query->execute([$hostId, $crc32uri, $uri, $timeAdded, $timeUpdated, $httpCode, $mime, $rank, $metaTitle, $metaDescription, $metaKeywords, $data]); return $this->_db->lastInsertId(); } @@ -466,14 +471,16 @@ class MySQL { mixed $metaTitle, mixed $metaDescription, mixed $metaKeywords, + string $mime, mixed $data) { $query = $this->_db->prepare('UPDATE `hostPage` SET `metaTitle` = ?, `metaDescription` = ?, `metaKeywords` = ?, + `mime` = ?, `data` = ? WHERE `hostPageId` = ? LIMIT 1'); - $query->execute([$metaTitle, $metaDescription, $metaKeywords, $data, $hostPageId]); + $query->execute([$metaTitle, $metaDescription, $metaKeywords, $mime, $data, $hostPageId]); return $query->rowCount(); } diff --git a/public/search.php b/public/search.php index 3ed1e39..7acdc6b 100644 --- a/public/search.php +++ b/public/search.php @@ -353,17 +353,17 @@ if (!empty($q)) { $db->updateHostImageHttpCode($hostImage->hostImageId, (int) $hostImageHttpCode, time()); if (200 != $hostImageHttpCode) continue; + if (!$hostImageContentType = $hostImageCurl->getContentType()) continue; + if (false === strpos($hostImageContentType, CRAWL_IMAGE_MIME_TYPE)) continue; // Convert remote image data to base64 string to prevent direct URL call - if (!$hostImageType = @pathinfo($hostImageURL, PATHINFO_EXTENSION)) continue; + if (!$hostImageExtension = @pathinfo($hostImageURL, PATHINFO_EXTENSION)) continue; if (!$hostImageBase64 = @base64_encode($hostImageCurl->getContent())) continue; - $hostImageURLencoded = 'data:image/' . $hostImageType . ';base64,' . $hostImageBase64; + $hostImageURLencoded = 'data:image/' . $hostImageExtension . ';base64,' . $hostImageBase64; // Save image content on data settings enabled - if (!CRAWL_HOST_DEFAULT_META_ONLY) { - $db->updateHostImageData($hostImage->hostImageId, (string) $hostImageURLencoded, time()); - } + $db->updateHostImage($hostImage->hostImageId, Filter::mime($hostImageContentType), (!CRAWL_HOST_DEFAULT_META_ONLY ? $hostImageURLencoded : null), time()); // Local image data exists } else {