之前小编开贴有说利用Zblog搜索留下网站文本链接,可以叫低质外链也可以叫网站留痕。对百毒那是没有任何用的。据说对谷歌有点用处。百毒也很少用了,也没有收录到几个Zblog做的网站。效果也就是这样的。
前端需要的JS代码,记得引入JQ哈
- <!-- 外链生成工具-->
- <li>
- <div class="data-group">
- <div style="display: flex; justify-content: space-between;">
- <div style="display: flex; align-items: center; width: 60%; height: 35px; padding-right: 5px;"> <!-- 添加padding-right以减少间距 -->
- <input id="seo-url" placeholder="生成外链目标网址" required placeholder="示例:www.wenyunfang.com" autocomplete="off" style="outline: none; width: 100%; height: 30px;">
- </div>
- <div style="display: flex; align-items: center;height: 35px; padding-left: 5px;"> <!-- 添加padding-left以减少间距 -->
- <b>每页条数:</b><input id="seo-num" placeholder="每页条数" min="1" max="30" value="15" required style="outline: none;height: 30px;width: 50%;text-align: center;">
- </div>
- <div style="display: flex; align-items: center;height: 35px; padding-left: 5px;"> <!-- 添加padding-left以减少间距 -->
- <b>间隔时间:</b><input id="seo-time" placeholder="间隔时间" min="30" max="130" value="30" required style="outline: none;height: 30px;width: 50%;text-align: center;">秒
- </div>
- <button class="seourl" id="seo-startBtn" style="margin-top: 2px;width: 90px;">生成外链</button>
- </div>
- </div>
- </li>
- <div style="margin-left:-9px;border-bottom: 1px solid #cac6c6;"></div>
- <!-- 外链生成工具-->
- <div class="seo-result-box" style="display: none;">
- <div style="display: flex; justify-content: space-between; align-items: center;">
- <div style="display: flex; align-items: center;">当前页数:<span id="seo-currentPage" style="font-weight: 500; color: #333;">1</span></div>
- <div style="display: flex; align-items: center;margin: 0 20px;">共收录<span id="seo-totalf" style="font-weight: 500; color: red;">0</span>条</div>
- <div style="display: flex; align-items: center;">进度:<span id="seo-progress" style="font-weight: 500; color: #2c8df2;">0</span> / <span id="seo-total" style="font-weight: 500; color: red;">0</span> 条</div>
- </div>
- <table id="seo-tableBody">
- <thead>
- <tr>
- <th>序号</th>
- <th>链接</th>
- <th>状态</th>
- </tr>
- </thead>
- <tbody></tbody>
- </table>
- </div>
PHP后端参考我用帝国写的哈,其他的只需要改下常量,变量。如果是框架的,自行把自定义函数集成到类里面去。
- //外链群发工具
- const SeoTool = {
- isRunning: false,
- currentPage: 1,
- tableBody: $('#seo-tableBody tbody'),
- resultBox: $('.seo-result-box'),
- startBtn: $('#seo-startBtn'),
- urlInput: $('#seo-url'),
- numInput: $('#seo-num'),
- timeInput: $('#seo-time'),
- currentPageEl: $('#seo-currentPage'),
- progressEl: $('#seo-progress'),
- totalEl: $('#seo-total'),
- totalElf: $('#seo-totalf'),
- controller: null,
- timeoutId: null,
- init() {
- this.startBtn.on('click', () => this.handleStartStop());
- },
- handleStartStop() {
- if (this.isRunning) {
- this.stopProcess();
- } else {
- const url = this.urlInput.val();
- const num = parseInt(this.numInput.val()) || 15;
- const time = parseInt(this.timeInput.val()) || 30;
- if (time < 30 || time > 130) {
- layer.msg('间隔时间需设置为30-130秒!', { icon: 5, time: 2000, area: 'auto', type: 0, anim: 6 });
- return;
- }
- if (!this.validateUrl(url)) {
- alert('请输入正确的域名(如:www.wenyunfang.com)');
- return;
- }
- this.resetResult();
- this.isRunning = true;
- this.startProcess(url, num, time);
- }
- },
- startProcess(url, num, time) {
- this.resultBox.show();
- this.startBtn.text('停止生成');
- this.controller = new AbortController();
- const signal = this.controller.signal;
- fetch(`/ecmsapi/index.php?mod=duomeiti&act=wlseo&page=${this.currentPage}&num=${num}`, {signal})
- .then(response => {
- if (!this.isRunning) throw new Error('操作已停止');
- return response.json();
- })
- .then(data => {
- if (!this.isRunning) return;
- if (data.code === 1) {
- this.stopProcess();
- alert('所有外链生成完成!');
- return;
- }
- this.tableBody.empty();
- const currentPage = this.currentPage;
- this.updateTable(data, url, currentPage, num);
- this.currentPage++;
- this.timeoutId = setTimeout(() => {
- if (this.isRunning) {
- this.startProcess(url, num, time);
- }
- }, time * 1000);
- })
- .catch(error => {
- if (error.name !== 'AbortError' && error.message !== '操作已停止') {
- this.stopProcess();
- }
- });
- },
- updateTable(urldata, targetUrl, currentPage, num) {
- var rowsHtml = '';
- for (var i = 0; i < urldata.data.length; i++) {
- var link = urldata.data[i];
- var encodedUrl = encodeURIComponent(targetUrl);
- var formattedLink = link.replace('***', encodedUrl);
- var serial = (currentPage - 1) * num + (i + 1);
- var statusId = 'status_' + i;
- rowsHtml += '<tr>';
- rowsHtml += '<td>' + serial + '</td>';
- rowsHtml += '<td><a href="' + formattedLink + '" target="_blank">' + formattedLink.substring(0, 66) + '</a></td>';
- rowsHtml += '<td><span id="' + statusId + '" class="seo-status" style="color:#FF9800;">发布中</span></td>';
- rowsHtml += '<td style="display:none;">';
- rowsHtml += '<iframe ';
- rowsHtml += 'src="' + formattedLink + '" ';
- rowsHtml += 'id="iframe_' + statusId + '" ';
- rowsHtml += 'style="width:0;height:0;border:none;" ';
- rowsHtml += 'onload="document.getElementById(\'' + statusId + '\').textContent=\'已生成\';document.getElementById(\'' + statusId + '\').style.color=\'#4CAF50\';" ';
- rowsHtml += 'onerror="document.getElementById(\'' + statusId + '\').textContent=\'未生成\';document.getElementById(\'' + statusId + '\').style.color=\'#F44336\';" ';
- rowsHtml += '>';
- rowsHtml += '</iframe>';
- rowsHtml += '</td>';
- rowsHtml += '</tr>';
- }
- this.tableBody.html(rowsHtml);
- this.updateProgress(urldata);
- setTimeout(function() {
- var statusElements = document.querySelectorAll('.seo-status');
- for (var i = 0; i < statusElements.length; i++) {
- var statusEl = statusElements[i];
- if (statusEl.textContent === '发布中') {
- statusEl.textContent = '未生成';
- statusEl.style.color = '#F44336';
- }
- }
- }, 5000);
- },
- updateProgress(urldata) {
- const total = urldata.count;
- const completed = (this.currentPage - 1) * urldata.data.length + urldata.data.length;
- this.currentPageEl.text(this.currentPage);
- this.progressEl.text(completed);
- this.totalEl.text(total);
- this.totalElf.text(total);
- },
- stopProcess() {
- this.isRunning = false;
- this.startBtn.text('重新开始');
- if (this.controller) {
- this.controller.abort();
- this.controller = null;
- }
- if (this.timeoutId) {
- clearTimeout(this.timeoutId);
- this.timeoutId = null;
- }
- },
- resetResult() {
- this.tableBody.empty();
- this.progressEl.text('0');
- this.totalEl.text('0');
- this.totalElf.text('0');
- this.resultBox.hide();
- this.currentPage = 1;
- this.timeInput.val(30);
- },
- validateUrl(url) {
- const regex = /^[^http\s/][\w.-]+\.[\w-]{2,}$/;
- return regex.test(url);
- }
- };
- SeoTool.init();
不能照抄,因为小编在帝国的核心函数里面已经把redis链接已经初始化了,自定义函数里面只需要作为全局变量就缓存生成读取都生效了。外链留痕数据TXT文件更新了会自动更新缓存的。
- <?php
- header('Access-Control-Allow-Origin: ' . rtrim($public_r['add_murl'], '/'));
- defined("ECMSAPI_MOD") or exit;
- $redisKey = 'seolinks_api:all_data';
- $redisMetaKey = 'seolinks_api:meta';
- // 检查文件更新并刷新缓存
- function checkAndRefreshCache($filePath, $redis) {
- global $redis, $redisKey, $redisMetaKey;
- $currentMtime = filemtime($filePath);
- $cachedMtime = $redis->hGet($redisMetaKey, 'mtime');
- if ($currentMtime > $cachedMtime) {
- $count = countLines($filePath);
- $redis->multi()
- ->set($redisKey, json_encode(['count' => $count]))
- ->hSet($redisMetaKey, 'mtime', $currentMtime)
- ->hSet($redisMetaKey, 'update_time', time())
- ->exec();
- return $count;
- }
- return false;
- }
- // 计算行数
- function countLines($filePath) {
- if (!file_exists($filePath)) return 0;
- $fp = fopen($filePath, 'rb');
- if (!$fp) return 0;
- $count = 0;
- $prev = null;
- while (($byte = fgetc($fp)) !== false) {
- if ($byte === "\n" || ($prev === "\r" && $byte !== "\n")) {
- $count++;
- }
- $prev = $byte;
- }
- fclose($fp);
- return $count;
- }
- // 获取分页数据
- function getPageData($filePath, $page, $num) {
- $offset = ($page - 1) * $num;
- $data = [];
- $fp = fopen($filePath, 'r');
- for ($i = 0; $i < $offset && !feof($fp); $i++) {
- fgets($fp);
- }
- for ($i = 0; $i < $num && ($line = fgets($fp)) !== false; $i++) {
- $data[] = trim($line);
- }
- fclose($fp);
- return $data;
- }
- // 获取外链留痕数据
- function getData($filePath, $redis) {
- global $redis, $redisKey, $redisMetaKey;
- $cachedData = $redis->get($redisKey);
- if ($cachedData) {
- $data = json_decode($cachedData, true);
- $freshCount = checkAndRefreshCache($filePath, $redis);
- return ['count' => $freshCount !== false ? (int)$freshCount : (int)($data['count'] ?? 0),'cached' => true];
- }
- $count = countLines($filePath);
- $currentMtime = filemtime($filePath);
- $redis->multi()
- ->set($redisKey, json_encode(['count' => $count]))
- ->hMSet($redisMetaKey, ['mtime' => $currentMtime,'create_time' => time(),'update_time' => time()])
- ->exec();
- return [
- 'count' => (int)$count,
- 'cached' => false
- ];
- }
- $filePath = ECMS_PATH.'e/extend/wlseo/links.txt';//外链留痕数据文件
- if (!file_exists($filePath)) {
- die(json_encode(['code' => 404, 'msg' => '文件不存在'], 320));
- }
- try {
- $dataInfo = getData($filePath, $redis);
- $count = $dataInfo['count'];
- $isCached = $dataInfo['cached'];
- } catch (Exception $e) {
- $count = (int)countLines($filePath);
- $isCached = false;
- }
- $page = max(1, (int)($_GET['page'] ?? 1));
- $num = min(30, max(1, (int)($_GET['num'] ?? 15)));
- $maxPage = $count > 0 ? max(1, ceil($count / $num)) : 1;
- if ($page > $maxPage) {
- die(json_encode(['code' => 1, 'msg' => '已完成!'], 320));
- }
- $data = getPageData($filePath, $page, $num);
- $cacheStatus = $isCached ? ($dataInfo['freshCount'] ?? 'hit') : 'miss';
- if (isset($dataInfo['freshCount'])) {
- $cacheStatus = $dataInfo['freshCount'] !== false ? 'refreshed' : 'hit';
- } else {
- $cacheStatus = $isCached ? 'hit' : 'miss';
- }
- echo json_encode([
- 'code' => 200,
- 'title' =>'SEO超级外链工具',
- 'data' => $data,
- 'sitename' => $public_r['sitename'],
- 'count' => $count,
- 'page' => $page,
- 'pagesize' => $num,
- 'maxpage' => $maxPage,
- 'cache_status' => $cacheStatus,
- 'cache_meta' => $redis->hGetAll($redisMetaKey)
- ], 320);