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
基于Python+Django+MySQL+HTML的豆瓣影視劇推薦系統(tǒng)
打開系統(tǒng)界面, 登錄頁面,用戶打開瀏覽器并訪問系統(tǒng)的登錄頁面,可以看到主要功能包括:
輸入賬戶名稱和密碼進(jìn)行登入
在影視劇推薦系統(tǒng)中,可以看到按照熱度排序的電影名稱包括影視劇的時間,名稱等信息,右邊有基于內(nèi)容推薦幾個影視劇
點擊一個影視劇的圖片可以進(jìn)入詳情頁面,可以查看影視劇的上映日期,主演等信息,還有影視劇的簡介信息。
對影視劇進(jìn)行評論發(fā)表,可以看到評論的數(shù)據(jù)。
對影視劇進(jìn)行打分,可以看到打分的數(shù)據(jù)。
查看電影的分類情況。
文件結(jié)構(gòu)
本項目在python3.7下通過測試,具體可以查看requirements.txt(或者r.txt)中的環(huán)境要求,在這里出一個簡單的項目使用教程,一般項目中的requirements.txt中包含了項目的python依賴環(huán)境,在安裝好python的前提下只需要在cmd窗口中pip install -r requirements.txt有時候因為路徑問題會提示requirements這個文件不存在,可以改為完整的路徑,比如c:\requirements.txt,對于本項目只需要運行app.py,然后再瀏覽器打開地址就好啦。在pycharm的配置更為方便,可以不用每次都在終端輸入命令使用。為了加快安裝下載速度可以更換為國內(nèi)源,使用命令為 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
有需要的小伙伴可以通過后臺聯(lián)系方式獲取,如果加不上可以后臺留言留下聯(lián)系方式,不經(jīng)常看后臺,但是看到了會回復(fù)的~,源碼獲取只收取很少的錢錢,除非是標(biāo)記了For Free的。
PConline 雜談]最近,豆瓣App截圖暗含水印的新聞,繃緊了很多人的神經(jīng)。有網(wǎng)友發(fā)現(xiàn),在豆瓣App中截圖,當(dāng)中竟然隱藏著肉眼難以識別的水印,水印的內(nèi)容則是截圖者的UID等信息!這意味著,在豆瓣App中截屏,根據(jù)水印很容易就可以追查到截屏者的某層身份。對此,豆瓣回應(yīng)稱,這是豆瓣小組的防搬運機(jī)制。
豆瓣App截圖帶有隱藏水印,默認(rèn)難以察覺,某些手機(jī)開啟夜間模式后可以清楚看到
豆瓣解釋,豆瓣小組長開啟了內(nèi)容防搬運設(shè)置后,在對小組內(nèi)容進(jìn)行截圖時,截圖上將自動生成經(jīng)加密的截圖用戶 ID、被截圖帖子 ID、截圖時間信息。而網(wǎng)友發(fā)現(xiàn),這個信息對于截屏者來說是難以察覺的,水印文字的顏色和背景相似,只有開啟夜間模式后才能比較顯眼地觀察到。
實際上,豆瓣App的這種做法,被業(yè)內(nèi)稱之為加“盲水印”。顧名思義,盲水印很難被覺察,但懂得其中竅門的話,就可以通過一些技術(shù)手段檢測、還原水印。盲水印一般應(yīng)用于一些對保密有需求的場合,例如企業(yè)內(nèi)部。企業(yè)在內(nèi)部論壇或者聊天工具加上員工信息身份的盲水印,如果員工截圖發(fā)到外網(wǎng),通過盲水印就很容易定位到泄密者,作出處理。
豆瓣顯然不是一個內(nèi)部使用的App,現(xiàn)在一個面向公眾的應(yīng)用,居然啟用了追查身份作震懾手段的防泄密機(jī)制,這正是讓很多網(wǎng)友感到震驚之處。
據(jù)了解,如果豆瓣小組啟用了“防搬運”功能,會在主貼底部有明顯提示的字眼。但即便如此,豆瓣盲水印帶來的影響,也已揮之不去了——豆瓣能這么做,其他App要不要也跟著做?這樣做是不是能帶來一些什么好處?
今天,就來簡單聊聊盲水印和互聯(lián)網(wǎng)社區(qū)的話題吧。
我們先來簡單了解一下盲水印的相關(guān)技術(shù)。
豆瓣使用的盲水印,其策略是融入背景色,雖然看似比較隱秘,但其實這還遠(yuǎn)算不上防不勝防。
某些圖片盲水印,用肉眼根本無法察覺,需要使用特定的算法還原,才能觀測到。同時,這類盲水印還非常難以去除,就算對打了盲水印的圖片反轉(zhuǎn)、裁剪、遮擋、涂抹等處理,水印依然可以被算法還原出來。
a是原始圖像,b是加了盲水印的圖像,肉眼看不出區(qū)別
這類盲水印,既做到了“盲”的隱秘——水印對其他人不可見,也做到了“印”的牢靠——想要追蹤的話,無論圖片經(jīng)過了怎樣的處理,保證絕大部分情況下水印不會被破壞。
這類盲水印是怎樣實現(xiàn)的呢?原理并不復(fù)雜,通常是將圖片進(jìn)行離散余弦、小波或者傅里葉變換,得到圖片的頻譜信息,再將水印的編碼信息疊加到圖片的頻譜上,然后再進(jìn)行一次逆變換,生成的圖片就帶有幾乎無法檢測、但又可以確切還原出來的盲水印了。
一種基于離散傅里葉變換添加盲水印的方法
而這樣的盲水印制作也非常簡單,網(wǎng)絡(luò)上就有開源算法可以實現(xiàn),下面以“blind_watermark”為例。
blind_watermark:https://blindwatermark.github.io/blind_watermark/#/zh/
是一個關(guān)于盲水印的開源項目,依賴于Python運行。安裝了blind_watermark后,只需要簡單的幾行命令,就可以為圖片添加文字或者另一張圖作為盲水印。
blind_watermark向圖片添加文字盲水印的案例
添加了盲水印后的圖,即便經(jīng)過多種修改,依然可以還原出水印。而這一切,都是可以用開源免費的方案實現(xiàn)的。
加了水印的圖即使經(jīng)過各種修改,也依然可以提取出水印
除了圖片可以添加水印,文字也是可以作標(biāo)記的。下面舉個“text_blind_watermark”的例子。
text_blind_watermark(demo):https://www.guofei.site/pictures_for_blog/app/text_watermark/v1.html
text_blind_watermark為文字加“盲水印”的原理也很簡單,在文字當(dāng)中穿插特殊的符號(demo演示的是空格),解碼算法識別出特定符號,就可以解出隱藏的信息了。或許demo中的空格鍵肉眼看上去還比較明顯,但如果所使用的是更加隱秘的字符呢?字符分布更加稀疏呢?恐怕就非常難以覺察了。
簡而言之,無論是圖片還是文字,都可以用已有的成熟方案輕易添加“盲水印”。只要某個App有這個心思,完全可以0成本使用強(qiáng)力的盲水印,而且還不會被察覺到,不至于像豆瓣一樣引發(fā)輿情。
從這個角度來看,豆瓣的水印方案只能算是小兒科了。如果啟用更強(qiáng)力的盲水印,用戶甚至完全無法察覺,這才是真正的“防不勝防”。
正如前文所說,盲水印主要用于防泄密,企業(yè)內(nèi)部頁面、電影放映畫面等都是盲水印的常見應(yīng)用場合。由于盲水印不可見且?guī)в行姑苷叩男畔ⅲ虼怂ǔS糜谧凡椤⒄饝匦姑苷撸ㄋ^“抓內(nèi)鬼”),而從某個層面來說,盲水印具備區(qū)分“敵我”的功能。
例如企業(yè)內(nèi)部會議投影,加水印可以防泄密(圖為必捷網(wǎng)絡(luò)產(chǎn)品演示)
這就很微妙了。豆瓣作為一個面向公眾的App,為何竟然有“抓內(nèi)鬼”的需求,甚至說竟然有區(qū)分“敵我”的需求?
豆瓣官方的說法是,加入盲水印是為了“防搬運”,這似乎并不能完全平息輿論。如果是一些版權(quán)網(wǎng)站,例如小說網(wǎng)站、視頻網(wǎng)站啟用盲水印“防搬運”,是說得通的,但即使是版權(quán)網(wǎng)站,也往往只在水印中添加版權(quán)信息,或者禁止復(fù)制文字或下載內(nèi)容,水印也通常不會摻雜用戶個人信息,且往往會明確告知水印的存在。而豆瓣作為一個社區(qū)App,使用個人信息作為盲水印“防搬運”,似乎多少有點不妥。
文字版權(quán)網(wǎng)站保護(hù)內(nèi)容不被抄襲的手段往往是禁止復(fù)制(例如起點),為何豆瓣要在截圖加水印?
這次啟用盲水印機(jī)制的是豆瓣的SNS社區(qū)豆瓣小組,而并非豆瓣全站。豆瓣小組分為不同主題的小組,各個小組需要申請加入才能發(fā)表討論,而其管理員“小組長”則可以審核入組申請,也可以決定小組成員的去留。如果小組長啟用“防搬運”,意味著可以通過截圖的盲水印追查到截圖者的身份,并對其進(jìn)行處理。
換言之,如果小組長認(rèn)為組員截圖導(dǎo)致組內(nèi)討論內(nèi)容產(chǎn)生了傳播,是不恰當(dāng)?shù)模瑒t可將該組員禁言。盲水印“防搬運”機(jī)制,會促使組員不再外泄小組內(nèi)所討論的內(nèi)容。加之豆瓣小組長本來就有刪帖等權(quán)限,如此一來,小組內(nèi)的討論內(nèi)容會更容易變得更具符合小組長的管理意愿,組內(nèi)的觀點也會變得更趨同。
顯然,如果豆瓣小組想要團(tuán)結(jié)意見觀點相似的人,盲水印會是一個非常立竿見影的機(jī)制。我們可以想象,如果QQ群微信群中也啟用了盲水印機(jī)制,那么群員很有可能就不再敢輕易截圖,將群聊記錄留證或公諸于眾,否則一旦被發(fā)現(xiàn),就面臨著被踢出群聊的風(fēng)險。有了“抓內(nèi)鬼”的能力,網(wǎng)絡(luò)社群管理者的威權(quán),也就得以加強(qiáng)。
豆瓣小組加入盲水印,有利建立起更加整齊劃一的討論基調(diào)、聚集起觀點近似的人群,這或許非常符合豆瓣想要打造的社區(qū)氛圍。但是盲水印畢竟包含個人信息,作為一個公眾App,在這方面使用個人信息,并可能間接致使相關(guān)信息廣泛傳播,多少有侵犯隱私的嫌疑。或許正因如此,豆瓣小組已會明確提示已開啟防搬運功能,用戶不至于完全沒有知情權(quán)。
但是,從技術(shù)的角度來看,App對截圖加入盲水印且不被察覺,并不是一件困難的事。如果有更多網(wǎng)絡(luò)社區(qū)想要打造價值觀趨同的圈子,認(rèn)同這種運營理念,神不知鬼不覺加入更加強(qiáng)力的盲水印,也是有可能的。
盲水印作為追查內(nèi)部泄密者的手段,這次如此大規(guī)模用于公眾社區(qū),無可避免會引來議論紛紛。作為普通用戶,或許大家并不希望個人信息用于這等用途,希望有關(guān)部門能夠嚴(yán)加監(jiān)管,進(jìn)一步規(guī)范平臺對個人信息的使用吧。
個寫博客的朋友想讓我?guī)兔Λ@取一下豆瓣上的Top250的電影數(shù)據(jù),說是做個什么電影推薦榜,沒辦法之后硬著頭皮拿出我那一小點點的Python爬蟲技術(shù)來完成人家的需求了。當(dāng)然了也是在不違法的情況下進(jìn)行的。
要爬取豆瓣電影排名信息,我們可以使用Python中的Request庫來發(fā)送一個請求,然后使用一些HTML解析工具例如BeautifulSoup或者是通過Lxml庫來對HTML頁面進(jìn)行解析,然后將解析到的結(jié)果打印出來。
import requests
from bs4 import BeautifulSoup
def crawl_douban_movies(url):
# 發(fā)送 HTTP GET 請求獲取頁面內(nèi)容
response=requests.get(url)
if response.status_code==200:
# 使用 BeautifulSoup 解析頁面內(nèi)容
soup=BeautifulSoup(response.text, 'html.parser')
# 找到電影列表
movie_list=soup.find_all('div', class_='item')
for movie in movie_list:
# 獲取電影名稱和評分
title=movie.find('span', class_='title').text
rating=movie.find('span', class_='rating_num').text
print(f"電影:{title},評分:{rating}")
else:
print("請求失敗")
if __name__=="__main__":
# 豆瓣電影 Top 250 頁面 URL
url="https://movie.douban.com/top250"
crawl_douban_movies(url)
是不是有點簡單了?在這個例子中我們通過requests.get()發(fā)送HTTP GET請求獲取豆瓣電影Top250頁面的HTML內(nèi)容。然后,使用BeautifulSoup解析頁面內(nèi)容,提取出電影名稱和評分,并打印出來。
運行程序之后,發(fā)現(xiàn)居然給我來了個請求失敗?這是為什么呢?是網(wǎng)絡(luò)請求不通?還是說豆瓣網(wǎng)站對相關(guān)的操作有所限制呢?
經(jīng)過驗證發(fā)現(xiàn)豆瓣網(wǎng)站可能設(shè)置了反爬蟲機(jī)制,檢測到了爬蟲行為并阻止了請求。為了規(guī)避這種情況,我嘗試設(shè)置請求頭信息,來模擬正常的瀏覽器訪問。
為了模擬正常瀏覽器的訪問操作,所以添加了請求頭信息,將代碼升級成如下的樣子。
import requests
from bs4 import BeautifulSoup
def crawl_douban_movies(url):
# 添加 User-Agent 請求頭信息
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'
}
# 發(fā)送帶有請求頭信息的 HTTP GET 請求
response=requests.get(url, headers=headers)
if response.status_code==200:
# 使用 BeautifulSoup 解析頁面內(nèi)容
soup=BeautifulSoup(response.text, 'html.parser')
# 找到電影列表
movie_list=soup.find_all('div', class_='item')
for movie in movie_list:
# 獲取電影名稱和評分
title=movie.find('span', class_='title').text
rating=movie.find('span', class_='rating_num').text
print(f"電影:{title},評分:{rating}")
else:
print("請求失敗")
if __name__=="__main__":
# 豆瓣電影 Top 250 頁面 URL
url="https://movie.douban.com/top250"
crawl_douban_movies(url)
與之前不同的是,我們添加了User-Agent請求頭信息。這樣這個請求就是模擬瀏覽器發(fā)送的。應(yīng)該算是正常請求了。果然,運行代碼之后,結(jié)果如下。
正當(dāng)我以為這樣就可以的時候,朋友居然說為什么沒有導(dǎo)演的信息,為什么沒有那個簡單的評語的信息呀?我勒個去?還要這么麻煩么?這就不得不讓我去分析一下頁面了
打開網(wǎng)頁開發(fā)這工具,簡單的分析頁面之后,有了這樣的結(jié)果。如下所示。
導(dǎo)演信息,在一個div里面,并且class叫做bd,在這個div里面有個p標(biāo)簽,這個p標(biāo)簽中就是導(dǎo)演的信息。那么這樣我們就可以通過如下的操作來獲取了。
# 獲取導(dǎo)演信息
directors=movie.find('div', class_='bd').find('p').text.split('\n')[1].strip().split('\xa0\xa0\xa0')
那么評語信息又在什么地方呢?
簡單查找之后發(fā)現(xiàn),評語在一個span標(biāo)簽中這就簡單了,我們可以通過如下的方式來進(jìn)行獲取。
quote=movie.find('span', class_='inq').text if movie.find('span', class_='inq') else ''
整體代碼修改變成了如下的樣子。
import requests
from bs4 import BeautifulSoup
def crawl_douban_movies(url):
# 添加 User-Agent 請求頭信息
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'
}
# 發(fā)送帶有請求頭信息的 HTTP GET 請求
response=requests.get(url, headers=headers)
if response.status_code==200:
# 使用 BeautifulSoup 解析頁面內(nèi)容
soup=BeautifulSoup(response.text, 'html.parser')
# 找到電影列表
movie_list=soup.find_all('div', class_='item')
for movie in movie_list:
# 獲取電影名稱
title=movie.find('span', class_='title').text
# 獲取導(dǎo)演信息
directors=movie.find('div', class_='bd').find('p').text.split('\n')[1].strip().split('\xa0\xa0\xa0')
director=directors[0].strip().split(':')[-1]
# 獲取評語
quote=movie.find('span', class_='inq').text if movie.find('span', class_='inq') else ''
print(f"電影:{title},導(dǎo)演:{director},評語:{quote}")
else:
print("請求失敗")
if __name__=="__main__":
# 豆瓣電影 Top 250 頁面 URL
url="https://movie.douban.com/top250"
crawl_douban_movies(url)
運行上述代碼之后,結(jié)果如下所示,我心想,這下應(yīng)該就可以了吧,然后人家說評分沒有了,我去,這東西還能難得我么?我就把評分的代碼給復(fù)制粘貼到這段代碼中。
將評分的獲取代碼復(fù)制粘貼完成之后,得到了如下的結(jié)果
我就說這是不是很完美了,他居然說還不行?他還要下面的時間信息、產(chǎn)地信息這些?我去這咋玩?
其實獲取時間信息和獲取產(chǎn)地信息的方式跟上面的操作是一樣的。只需要通過查看代碼,找到對應(yīng)的HTML標(biāo)識就可以提取到對應(yīng)的信息。
在獲取電影詳細(xì)信息的時候遇到了格式處理的問題,如下所示。
# 獲取電影詳情信息
details=movie.find('div', class_='bd').find('p').text.split('\n')
info=[i.strip() for i in details if i.strip() !='']
#print("獲取到信息",info) # 打印詳情信息列表
# 提取時間、產(chǎn)地和劇情信息
if len(info) >=2:
year_region=info[1].split('\xa0/\xa0')
year=year_region[0].strip()
region=year_region[1].strip()
plot=info[1].strip()
else:
year="未知"
region="未知"
plot="未知"
要獲取電影的詳細(xì)頁面 URL,需要從每個電影條目中提取鏈接信息。豆瓣電影條目的鏈接通常包含在a標(biāo)簽的href屬性中如下圖所示。
通過如下的操作來獲取到對應(yīng)屬性中的數(shù)據(jù)
detail_url=movie.find('a')['href']
最終獲取完成的電影信息如下圖所示。
到這里,人家的需求才算提完,原來現(xiàn)在電影博主都這么卷的了么?想要這么多信息,居然不自己整理,讓我這個小嘍嘍來幫他實現(xiàn)。真實有天賦呀?
在滿足了他所有的要求之后,最終我們給出詳細(xì)的代碼
import requests
from bs4 import BeautifulSoup
def crawl_douban_movies(url):
# 添加 User-Agent 請求頭信息
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'
}
# 發(fā)送帶有請求頭信息的 HTTP GET 請求
response=requests.get(url, headers=headers)
if response.status_code==200:
# 使用 BeautifulSoup 解析頁面內(nèi)容
soup=BeautifulSoup(response.text, 'html.parser')
# 找到電影列表
movie_list=soup.find_all('div', class_='item')
for movie in movie_list:
# 獲取電影名稱
title=movie.find('span', class_='title').text
rating=movie.find('span', class_='rating_num').text
# 獲取電影詳細(xì)頁面鏈接
detail_url=movie.find('a')['href']
# 獲取導(dǎo)演信息
directors=movie.find('div', class_='bd').find('p').text.split('\n')[1].strip().split('\xa0\xa0\xa0')
director=directors[0].strip().split(':')[-1]
# 獲取評語
quote=movie.find('span', class_='inq').text if movie.find('span', class_='inq') else ''
# 獲取電影詳情信息
details=movie.find('div', class_='bd').find('p').text.split('\n')
info=[i.strip() for i in details if i.strip() !='']
#print("獲取到信息",info) # 打印詳情信息列表
# 提取時間、產(chǎn)地和劇情信息
if len(info) >=2:
year_region=info[1].split('\xa0/\xa0')
year=year_region[0].strip()
region=year_region[1].strip()
plot=info[1].strip()
else:
year="未知"
region="未知"
plot="未知"
print(f"電影:{title}\n評分:{rating}\n導(dǎo)演:{director}\n評語:{quote}\n時間:{year}\n產(chǎn)地:{region}\n劇情:{plot}\n詳細(xì)頁面鏈接:{detail_url}\n")
else:
print("請求失敗")
if __name__=="__main__":
# 豆瓣電影 Top 250 頁面 URL
url="https://movie.douban.com/top250"
crawl_douban_movies(url)
通過上述代碼,我們就可以獲取到豆瓣電影TOP250的所有電影信息,當(dāng)然這里需要手動的將頁碼信息進(jìn)行添加,例如第二頁的URL就會變成https://movie.douban.com/top250?start=25&filter=樣子,在實際操作的時候我們可以自己進(jìn)行調(diào)整。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。