Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
者:HelloGitHub-追夢人物
搜索是一個復雜的功能,但對于一些簡單的搜索任務,我們可以使用 Django Model 層提供的一些內置方法來完成。現在我們來為我們的博客提供一個簡單的搜索功能。
博客文章通常包含標題和正文兩個部分。當用戶輸入某個關鍵詞進行搜索后,我們希望為用戶顯示標題和正文中含有被搜索關鍵詞的全部文章。整個搜索的過程如下:
整個過程就是這樣,下面來看看 Django 如何用實現這些過程。
先來回顧一下我們的 Django 博客的 Post(文章)模型:
blog/models.py
class Post(models.Model):
# 標題
title = models.CharField("標題", max_length=70)
# 正文
body = models.TextField("正文")
# 其他屬性...
def __str__(self):
return self.title
先看到第 1 步,用戶在搜索框輸入搜索關鍵詞,因此我們要在博客上為用戶提供一個搜索表單,HTML 表單代碼大概像這樣:
templates/base.html
<form role="search" method="get" id="searchform" action="{% url 'blog:search' %}">
<input type="search" name="q" placeholder="搜索" required>
<button type="submit"><span class="ion-ios-search-strong"></span></button>
</form>
特別注意這里 <input type="search" name="q" placeholder="搜索" required> 中的 name 屬性,當用戶在這個 input 中輸入搜索內容并提交表單后,鍵入的數據會以鍵值對的形式提交服務器,這個鍵的名字就是通過 name 屬性指定的。這樣服務器就可以根據 name 的值來取得用戶輸入的內容。
用戶輸入了搜索關鍵詞并點擊了搜索按鈕后,數據就被發送給了 Django 后臺服務器。表單的 action 屬性的值為 {% url 'blog:search' %}(雖然我們還沒有寫這個視圖函數),表明用戶提交的結果將被發送給 blog 應用下 search 視圖函數對應的 URL。
搜索的功能將由 search 視圖函數提供,代碼寫在 blog/views.py 里:
blog/views.py
from django.contrib import messages
def search(request):
q = request.GET.get('q')
if not q:
error_msg = "請輸入搜索關鍵詞"
messages.add_message(request, messages.ERROR, error_msg, extra_tags='danger')
return redirect('blog:index')
post_list = Post.objects.filter(Q(title__icontains=q) | Q(body__icontains=q))
return render(request, 'blog/index.html', {'post_list': post_list})
首先我們使用 request.GET.get('q') 獲取到用戶提交的搜索關鍵詞。用戶通過表單 get 方法提交的數據 Django 為我們保存在 request.GET 里,這是一個類似于 Python 字典的對象,所以我們使用 get 方法從字典里取出鍵 q 對應的值,即用戶的搜索關鍵詞。這里字典的鍵之所以叫 q 是因為我們的表單中搜索框 input 的 name 屬性的值是 q,如果修改了 name 屬性的值,那么這個鍵的名稱也要相應修改。
接下來我們做了一個小小的校驗,如果用戶沒有輸入搜索關鍵詞而提交了表單,我們就無需執行查詢,我們給給用戶發一條錯誤提醒消息,這里使用了 django messages 應用,這在 交流的橋梁:評論功能中講過。然后將用戶重定向到首頁。這里的 redirect 函數也在那篇教程中講過。
如果用戶輸入了搜索關鍵詞,我們就通過 filter 方法從數據庫里過濾出符合條件的所有文章。這里的過濾條件是 title__icontains=q,即 title 中包含(contains)關鍵字 q,前綴 i 表示不區分大小寫。這里 icontains 是查詢表達式(Field lookups),我們在之前也使用過其他類似的查詢表達式,其用法是在模型需要篩選的屬性后面跟上兩個下劃線。Django 內置了很多查詢表達式,建議過一遍 Django 官方留個印象,了解每個表達式的作用,以后碰到相關的需求就可以快速定位到文檔查詢其用途 Field lookups。
此外我們這里從 from django.db.models 中引入了一個新的東西:Q 對象。Q 對象用于包裝查詢表達式,其作用是為了提供復雜的查詢邏輯。例如這里 Q(title__icontains=q) | Q(body__icontains=q) 表示標題(title)含有關鍵詞 q 或者正文(body)含有關鍵詞 q ,或邏輯使用 | 符號。如果不用 Q 對象,就只能寫成 title__icontains=q, body__icontains=q,這就變成標題(title)含有關鍵詞 q 且正文(body)含有關鍵詞 q,就達不到我們想要的目的。
有了視圖函數后記得把視圖函數映射到相應了 URL,如下。
blog/urls.py
urlpatterns = [
# 其他 url 配置
path('search/', views.search, name='search'),
]
大功告成,在導航欄嘗試輸入一些關鍵詞,看看效果吧!
當然這樣的搜索功能是非常簡略的,難以滿足一些復雜的搜索需求。編寫一個搜索引擎是一個大工程,好在 django-haystack 這款第三方 app 為我們完成了全部工作。使用它我們可以實現更加復雜的搜索功能,比如全文檢索、按搜索相關度排序、關鍵字高亮等等類似于百度搜索的功能,功能十分強大。當然其使用也會復雜一些,下一篇教程將向大家介紹 django-haystack 結合 Elasticsearch 搜索引擎的使用方法。
『講解開源項目系列』——讓對開源項目感興趣的人不再畏懼、讓開源項目的發起者不再孤單。跟著我們的文章,你會發現編程的樂趣、使用和發現參與開源項目如此簡單。歡迎留言聯系我們、加入我們,讓更多人愛上開源、貢獻開源
文分享的技術,是用Web做一個二分查找算法的小網頁,包括所有的代碼。
通過二分查找的原理,做一個閉環Web小應用,這個網頁包括HTML、CSS和JavaScript的配合。通過制作這個網頁,老鐵們可以熟悉網頁設計、練習算法的落地應用,做到學以致用。
最終的二分算法網頁效果展示 最終的二分算法網頁效果展示
本文代碼分為3個部分:
核心JavaScript代碼講解(包括調用函數JS代碼);
HTML網頁設計講解;
CSS的美化講解。
第1部分 JavaScript代碼講解
本案例的JavaScript代碼包括核心的算法代碼和調用代碼。
先介紹核心算法代碼:
function binarySearch(nums, target) //定義了一個名為 binarySearch 的函數,
//該函數接受兩個參數,nums 是已排序數組,target 是要查找的目標值。
{
let left = 0;//聲明兩個變量 left 和 right,它們分別代表搜索范圍的左邊界和右邊界。
//初始時,左邊界為數組的第一個元素索引(0),右邊界為數組的最后一個元素索引。
let right = nums.length - 1;
while (left <= right) //使用循環來執行二分查找,條件是左邊界小于等于右邊界。
{
let middle = left + Math.floor((right - left) / 2);
//計算當前搜索范圍的中間位置,使用 Math.floor 函數確保取整。
if (nums[middle] > target) //如果中間位置的元素值大于目標值。
{
right = middle - 1;//將右邊界更新為中間位置的前一個位置,縮小搜索范圍至左半部分。
}
else if (nums[middle] < target) //如果中間位置的元素值小于目標值。
{
left = middle + 1;//將左邊界更新為中間位置的后一個位置,縮小搜索范圍至右半部分。
}
else
{
return middle;//直接返回中間位置的索引,表示找到目標值。
}
}
return -1;
}
這段代碼的含義是:先定義兩個邊界索引,左邊界初始值為0,右邊界初始值為數組的長度。循環條件是左邊界的數值小于右邊界。
二分查找原理演示
二分查找是很基礎的算法,很容易搜索到,在此不做更多的原理講解。只要記住查找的數組必須有序排列即可。
由于這個算法要在Web端調用,因此還需要進行調用代碼的編寫:
function calculateBinarySearch()
{
const inputNums = document.getElementById("output").value.split(" ").map(Number);
// 獲取頁面上 id 為 "output" 的元素的值,將其以空格分割成數字數組并映射為數字類型
const inputTarget = parseInt(document.getElementById("textBox1").value);
// 獲取頁面上 id 為 "textBox1" 的元素的值,將其解析為整數,作為二分查找的目標值
const result = binarySearch(inputNums, inputTarget);
// 調用二分查找函數 binarySearch,傳入數字數組和目標值,并將結果保存在變量 result 中
if (result !== -1)
// 如果結果不等于 -1,表示找到了目標值
{
document.getElementById("textBox2").value = `數字${inputTarget}排在第${result + 1}位`;
// 在頁面上 id 為 "textBox2" 的元素中顯示目標值在數組中的位置(索引 + 1)
}
else
{
document.getElementById("textBox2").value = "這個數字不存在";
// 如果結果等于 -1,表示目標值不存在于數組中
}
}
在本案例中,顯示25個數字的窗口是一個文本框“output”,一個輸入查找數字的文本框“textBox1”和一個輸出查找結果的文本框“textBox2”。
調用代碼是將隨機生成的25個數字提取成數組,把textBox1里輸入的數字也進行必要的清洗,然后作為二分查找函數的輸入值,進行計算,結算結果顯示在textBox2中。
顯示結果要進行一下必要的美化,做一下人機交互的設計。
如何生成25個隨機數字,我會在另一篇文章仔細講解,在此不做贅述。
第2部分 HTML代碼講解
本案例的HTML包括一個標題文件(其中包含一個LOGO圖片)、1個展示25個數字的文本框output,1個輸入文本框textBox1和1個輸出文本框textBox2。還有兩個按鈕,分別控制生成25個數字和清除25個數字。
<h1>
<img src="logo.png" alt="Logo" width="130" height="130">JavaScript編程練習:二分算法演示
</h1>
<textarea id="output" readonly>
</textarea>
<textarea id="textBox2" readonly>
</textarea>
<br>
<div class="button-container">
<button onclick="generateNumbers()">生成數字</button>
<button onclick="clearText()">清空數字</button>
</div>
<input type="text" id="textBox1">
<div class="color-box-container">
<div class="color-box" id="whiteBox" onclick="changeBackgroundColor('white')"></div>
<div class="color-box" id="blueBox" onclick="changeBackgroundColor('rgba(247, 160, 255, 0.348)')"></div>
<div class="color-box" id="pinkBox" onclick="changeBackgroundColor('rgba(202, 251, 189, 0.966)')"></div>
</div>
<a href="index.html">
<button id="backToHomeButton">回到主頁</button>
</a>
第3部分 CSS代碼講解
CSS代碼考慮到了手機豎屏的應用。注意按鈕使用了群組,本案例還有顏色代碼塊,純裝飾用,不是必須的。
在說這個之前,想必大家應該都比較了解搜索引擎了,它就是通過用戶在瀏覽器輸入框中輸入文本,從而顯示一些結果,你覺得哪項符合你要搜索的內容,你就點擊哪項。
瀏覽器:360瀏覽器
編輯器:Sublime Text 3
插件:Jquery-3.2.1.Min.Js
由于是要實現一個網頁搜索引擎,所以我們需要借用網頁三劍客(Html+Css+Javascript),然后實現這一功能。
我們可以先看看百度的搜索引擎:
可以看到,這個搜索框的部分設置,比如關閉自動完成功能。然后我們在隨便搜索內容來查看它的變化:
image
可以看到某些我們查詢的關鍵字,于是我們便發現了請求規律:
https://www.baidu.com/s?+查詢字符參數
這就構成了我們的一個完整的get請求,而且這里面有很多關鍵字參數可以省略掉,只需要保留重要的一部分就好了。于是,經試驗,得出如下結論:
https://www.baidu.com/s?wd=keyword
這個才是請求的接口地址,只需將keyword參數替換為任意搜索關鍵字即可實現查詢并跳轉到相應結果頁面。
看過之前寫的Html系列的文章,你將不再對此感到困惑。
<html>
<head>
<title></title>
<style type="text/css">
*{ 內外邊距初始時為0
margin:0;
padding:0
}
input{
width:300px;
height:30px
}
span{
position:absolute; 絕對定位
background-color:red; 背景顏色
border:1px solid gray; 邊框設置
width:60px;
height:32px;
text-align:center 文字位置
}
span:hover{ 鼠標懸停時的樣式
background-color:blue
}
</style>
</head>
<body>
<input type="text" name="" placeholder="請輸入要搜索的內容"> 文本框
<span>search</span> 搜索按鈕
</body>
</html>
編寫完成后進入瀏覽器查看,即可看到:
可以看到,已經有點瀏覽器搜索框的意思了。
<script src='jquery-3.2.1.min.js'></script>
這個是重中之重,打開瀏覽器,network,繼續分析:
可以看到搜索結果就在里面。然后打開這個請求的url地址,經過多次實驗,發現就只有圖中標記的參數有變化:
image.png
所以我們可以得出結論,我們只需要改變這兩個值即可。
于是我先創建一個腳本標簽,不用它的時候隨時可以清除,避免占用內存,導致頁面打開遲緩,性能降低:
var script=document.createElement('script'); 創建script的標簽
script.id='jsonp'; 設置id為jsonp
script.src='https://www.baidu.com/sugrec?prod=pc&cb=getData&wd='+wd; 設置它的地址
document.body.appendChild(script); 添加script元素到body中
然后等它不用了,隨時將它刪除:
var script=document.createElement('script'); 創建script的標簽
script.id='jsonp'; 設置id為jsonp
script.src='https://www.baidu.com/sugrec?prod=pc&cb=getData&wd='+wd; 設置它的地址
document.body.appendChild(script); 添加script元素到body中
我們在瀏覽器可以看到,只要一輸入文本,它就會彈出對應的選項讓我們選擇,那么這是如何辦到的了?
<script>
function getlist(wd){ /*獲取下拉列表*/
var script=document.createElement('script'); /*創建script的標簽*/
script.id='jsonp'; /*設置id為jsonp*/
script.src='https://www.baidu.com/sugrec?prod=pc&cb=getData&wd='+wd; /* 設置它的地址*/
document.body.appendChild(script); /*添加script元素到body中*/
}
function getData(data){ /*獲取數據*/
var script=document.querySelector('#jsonp'); /*選擇id為jsonp的元素*/
script.parentNode.removeChild(script); /*從這個元素的父元素中刪除這個元素*/
$('ol').html(''); /* 設置有序列表的值為空*/
var da=data.g; /* 獲取搜索的結果*/
if(da){ /*結果存在的話就將結果放到li標簽中*/
da.forEach(function(item,index){
$('<li><a target="_blank" href ="https://www.baidu.com/s?wd='+item.q+'">'+item.q+'</a></li>').appendTo('ol');
})
}
}
/* 判斷鍵盤是否按下*/
$('input:text').keyup(function(){
var wd=$(this).val(); /* 輸入框的值*/
if(wd==''){ /*如果值是空,那么就隱藏,否則顯示*/
$('ol').css('display','none');
$('ol').css('zIndex',-10);
}else{
$('ol').css('display','block');
$('ol').css('zIndex',20);
}
getlist(wd);
});
</script>
可以看到,搜索結果已經出來了,而且有序列表下的"li"標簽也都對應的生成了。
我們可以看到,結果終于出來,但是我想給它個序列號,這樣就可以知道搜索結果有多少個了。要設置的標記方式有很多種,可以以數字開頭,也可以是大小寫字母或者羅馬時間。在這里我選擇數字,很簡單。
終于非常完美的實現了這一功能,是不是很驚艷了,趕快去試下吧。
看到這里相信大家應該都知道這個功能已經算是完成了,我們只需要隨便點擊哪個li標簽都可以訪問到相應的頁面。于是,我決定添加一個刷新的功能,屬于重連服務器的那種刷新:
<span onclick='window.location.reload()'>search</span> 點擊后立即刷新
總的來說,對于初學者小白是個很不錯的練手項目,希望大家能從中有所收獲。
需要源碼的小伙伴,后臺回復“搜索引擎”四個字即可獲取。
****看完本文有收獲?請轉發分享給更多的人****
想學習更多Python網絡爬蟲與數據挖掘知識,可前往專業網站:http://pdcfighting.com/
*請認真填寫需求信息,我們會在24小時內與您取得聯系。