用AI開發(fā)網(wǎng)站,效率翻倍,新網(wǎng)站1天就搞定!
共 17770字,需瀏覽 36分鐘
·
2024-06-04 19:38
之前一直想做個網(wǎng)站,反正五一也不出去,正好這幾天可以開發(fā)一下。
為了盡快上線,有些功能讓Ai來實現(xiàn)的,沒想到1天就搞完了。
太快樂了!??
做的什么?
從去年開始,Ai領(lǐng)域突飛猛進的發(fā)展,國內(nèi)各種大模型以及Ai應(yīng)用層出不窮,不計其數(shù),但是我們能了解到的也就阿里、百度、騰訊、字節(jié)等這些大廠的AI應(yīng)用,但是仍然有很多優(yōu)秀的應(yīng)用大家無法接觸到,更無法使用到。
所以打工人急需一個AI工具導航網(wǎng)站,方便打工人更快速便捷的找到目標應(yīng)用,同時也能了解到更多其他AI應(yīng)用,提高我們打工人的效率。
下面是我用1天時間開發(fā)的AI導航網(wǎng)站:
網(wǎng)址:dai.bigerfe.com
技術(shù)選型
-
必須考慮SEO,因為要獲取自然流量 -
內(nèi)容更新頻率較低,誰沒事會改簡介,logo啥的,所以管理后臺也不太需要 -
考慮到我的服務(wù)器,空間有限,資源有限,所以框架啥的也不適用,不然裝一堆npm依賴,給我磁盤干爆了
綜上,最后決定使用最成熟,最沒有新意的技術(shù) ??。
node+Ai+koa2+ejs+bootstrap3+jQuery+file系統(tǒng)+pm2+ng+圖床
整體思路
首先需要考慮數(shù)據(jù)源從哪來,導航網(wǎng)站的數(shù)據(jù)說多不多,說少不少,一條條添加,能把人干報廢了。
還是找一個好點的網(wǎng)站,爬一爬吧,不過這樣的網(wǎng)站大多數(shù)都是服務(wù)的直出。
目標網(wǎng)站就不說了,市面上有很多。
-
數(shù)據(jù)源集合,也就是拿到網(wǎng)站Ai分類信息以及分類對應(yīng)的頁面url,還有分類頁面是否有分頁,要落地成一個配置文件
-
詳情頁面不需要提前獲取,每次打開頁面時即時保存文件即可,下次訪問走文件讀取
-
跳轉(zhuǎn)到目標應(yīng)用官網(wǎng),不需要提前獲取,訪問時需要簡單識別下html meta標簽
為了提高效率,有些功能必須讓AI來完成。
分類信息頁面url
一個分類下對應(yīng)多子分類,每個子分類的url,以及子分類列表還有分頁。
這里借助了Kimi - AI 來處理,把頁面的分類html輸入給Kimi,再輸入一些規(guī)則和要求,讓他輸出json格式的數(shù)據(jù),包括分類名稱,分類別名(用來當作路由),遠程地址。
如下:
下面這些分類很多,手動整理很費時間,起碼得2個小時,讓Ai只需要半個小時。不過有訓練不出來的風險。
//分類信息
module.exports = [
{
typeName: 'Ai工具箱',
typeId:0,
alias:'ai-write',
child:[
{
typeName: 'Ai寫作對話',
update:false,
alias:'ai-write',
remote:['https://www.xxx.com/ai-write/','https://www.xxx.com/ai-write/list_664_2/'],
items:[]
},
{
typeName:'Ai繪畫生成',
alias:'ai-image',
update:false,
remote:['https://www.xxx.com/ai-image/','https://www.xxx.com/ai-image/list_660_2/'],
items:[]
},
{
typeName:'Ai視頻生成',
alias:'ai-shipin',
update:false,
remote:['https://www.xxx.com/ai-shipin/'],
items:[]
}
]
}
]
......
Ai應(yīng)用列表獲取
這里就必須要使用cheerio了 ,可以在node里像使用jq一樣獲取數(shù)據(jù)。需要排除一些干擾信息,找到每個item的關(guān)鍵信息過濾就行。
//獲取頁面里的列表網(wǎng)站
async function execPageHtml(pageHtml) {
// 使用cheerio加載HTML字符串
const $ = cheerio.load(pageHtml);
// 選取并提取h1標簽中的文本內(nèi)容
const itemBoxs = $('.line-big');
let lastResult = [];
for (let i = 0; i < itemBoxs.length; i++) {
const list = await getSiteBaseInfo(itemBoxs[i], $);
lastResult = lastResult.concat(list);
}
return lastResult;
}
//獲取網(wǎng)站的基本信息 title、url、logo、簡介
async function getSiteBaseInfo(itemBox, $) {
const result = [];
const items = $(itemBox).find('a');
for (let i = 0; i < items.length; i++) {
const target = $(items[i]);
const url = target.attr('href');
//圖片上傳到七牛,下面說
const imgInfo = await qiniu.uploadRemote($(target.find('img')[0]).attr('src'));
if (url.indexOf('/site/') > -1) { //必須是站點
const obj = {
title: $(target.find('strong')[0]).text(),
url,
img: imgInfo ? imgInfo.key : '',
desc: $(target.find('.text-default')[0]).text(),
originId: url.substring(url.lastIndexOf('/') + 1).replace('.html', '')
};
result.push(obj);
}
}
return result;
}
數(shù)據(jù)存儲
網(wǎng)站列表不經(jīng)常更新,也沒必要搞個管理后臺,所以在完成上面步驟后都會寫入文件。
規(guī)則是按照分類別名存儲:
function saveConToFile(filename, obj) {
fs.writeFile(path.resolve(__dirname, `./data/${filename}.txt`), JSON.stringify(obj), (err) => {
if (err) throw err;
console.log('文件寫入成功!');
});
}
圖片存儲
有個兩個圖片需要進行處理,內(nèi)容抄人家的,圖片再用人家的,這老臉真掛不住了~。
-
列表的logo圖 -
詳情頁的圖片
其實想過放在自己的服務(wù)器上,但想了下不至于這么想不開吧。雖然很容易,但并不正確。
最后,放在七牛,還有些免費的空間,足夠用了。
這種云平臺,有非常完善的sdk,可以輔助開發(fā)者快速對接。
還能夠直接讀取遠程圖片直接保存,返回遠程預覽地址,真的香。
1. 需要先安裝 qiniu npm包
2. 需要配置accessKey、secretKey、bucket
3. 需要獲取 uploadToken
const qiniu = require('qiniu');
const path = require('path');
const encode = require('./encode');
var accessKey = 'xxxxxxsYzzzRcxhNMXV0dQJTtDrK';
var secretKey = 'xxxxxxV_-xNoqXAt8Uqkim'
var bucket = 'xxxxx';
var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
var putPolicy = new qiniu.rs.PutPolicy({
scope: bucket
});
var uploadToken = putPolicy.uploadToken(mac);
async function uploadRemote(remoteUrl,isMd5Name = true) {
if(!remoteUrl) return '';
let config = {
// 空間對應(yīng)的機房
//zone: qiniu.zone.Zone_z1,
// 是否使用https域名
useHttpsDomain: false,
// 上傳是否使用cdn加速
useCdnDomain: true
}
var bucketManager = new qiniu.rs.BucketManager(mac, config);
var resUrl = remoteUrl;
var bucket = "appandroid";
var fileType = remoteUrl.substring(remoteUrl.lastIndexOf('.'));
var key = remoteUrl.substring(remoteUrl.lastIndexOf('/') + 1);
if(isMd5Name){
key = encode.md5(key) + fileType;
}
return new Promise(resolve=>{
bucketManager.fetch(resUrl, bucket,'daai/' + key, function(err, respBody, respInfo) {
if (err) {
console.log(err);
resolve(null);
} else {
if (respInfo.statusCode == 200) {
console.log(respBody.key);
resolve(respBody);
} else {
resolve(null);
}
}
});
})
}
module.exports = {
uploadRemote
}
圖片懶加載
一個分類下的應(yīng)用很多,上百條是有的,logo需要做下加載優(yōu)化,不然頁面打開特別慢。
這里直接問AI就可以了,馬上給你結(jié)果:
$(document).ready(function() {
var lazyImages = [];
$("img.js-lazy-load").each(function() {
var img = $(this);
lazyImages.push(img);
});
function loadImg(){
var scrollTop = $(window).scrollTop();
for (var i = lazyImages.length - 1; i >= 0; i--) {
var img = lazyImages[i];
if ((img.offset().top - $(window).height()) < scrollTop) {
img.attr('src', img.attr('src'));
lazyImages.splice(i, 1);
}
}
if (!lazyImages.length) {
$(window).off('scroll');
}
}
$(window).scroll(function() {
loadImg();
});
loadImg();
網(wǎng)站tdk
如果不設(shè)置TDK,爬蟲可能無法知道你的網(wǎng)站做啥的。
-
網(wǎng)站有主標題,在每一個頁面的結(jié)尾
舉例:
<title>懂AI | 一站式AI導航</title>
-
關(guān)鍵詞也一樣
舉例:
<meta name="keywords" content="Leonardo.ai,懂AI" />
-
描述需要定制,根據(jù)每個頁面定
所以這里需要搞一個中間件來處理通用的tdk對象,在ejs任何一個頁面上都能獲取。
定制的描述,就在他所屬的controller內(nèi)改寫。
看代碼:
//commoninfo 中間件
module.exports= async function (ctx,next) {
//獲得基礎(chǔ)通用數(shù)據(jù)并綁定到 ctx 上
ctx.CommonInfo = {
tdk:{
title:'ai人工智能,gpt人工智能,大Ai工具導航',
key:'大Ai工具導航',
desc:'大Ai工具導航是一個不斷增長的AI人工智能工具資源庫,xxxx'
}
};
await next();
}
在搜索controller內(nèi)重寫。
module.exports = async (ctx) => {
const { key = '' } = ctx.query;
ctx.CommonInfo.tdk.title = '搜索-' + key;
const links = await searchApp(key);
ejsRender.call(ctx, 'newpages/search/search.html', {
...ctx.CommonInfo,
links: links || [],
skey: key
});
}
搜索
我用的最多的就是搜索,能很快定位到目標網(wǎng)站。
搜索就比較暴力了,根據(jù)路徑配置,和本地文件內(nèi)容,計算出全集,然后進行過濾。
更好的是加個緩存,沒必要每次都重新讀取,浪費資源(當有了自己的服務(wù)器,每個資源都是非常寶貴的)。
//url配置
module.exports = [
{
typeName: 'Ai工具箱',
typeId:0,
alias:'ai-write',
child:[
{
typeName: 'Ai寫作對話',
update:false,
alias:'ai-write',
remote:['https://www.xxx.com/ai-write/','https://www.xxx.com/ai-write/list_664_2/'],
items:[]
},
{
typeName:'Ai繪畫生成',
alias:'ai-image',
update:false,
remote:['https://www.xxx.com/ai-image/','https://www.xxx.com/ai-image/list_660_2/'],
items:[]
},
{
typeName:'Ai視頻生成',
alias:'ai-shipin',
update:false,
remote:['https://www.xxx.com/ai-shipin/'],
items:[]
}
]
}
]
//搜索方法
async function searchApp(key) {
if(!allAppInfo.length){
for (let i = 0; i < urlConfig.length; i++) {
const len = urlConfig[i].child.length;
const childs = urlConfig[i].child;
for (let j = 0; j < len; j++) {
const child = childs[j];
const alias = child.alias;
const linksStr = fs.readFileSync(path.resolve(__dirname, '../common/data/'+alias+'.txt'));
const list = JSON.parse(linksStr);
allAppInfo = allAppInfo.concat(list);
}
}
}
return allAppInfo.filter(item=>item.title.indexOf(key)>-1 || item.desc.indexOf(key)>-1);
}
部署
這里直接使用pm2+nginx即可。
但是端口得是能自定義,從命令中傳遞過去。
//獲取端口
const port = process.env.PORT || config.defaultPort
//pm2命令
PORT=7560 pm2 start app.js -n bigai-7560 -o --watch -- env:production
最后
網(wǎng)站就介紹到這里,技術(shù)很古老,再結(jié)合下AI,效率真的高,我的宗旨就是穩(wěn)定,好用就ok。
也可以點擊左下角原文鏈接來體驗下。
動動你發(fā)財?shù)男∈郑o點個贊??!
