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