真人男女直接做的視頻網(wǎng)站深圳華強(qiáng)北新聞最新消息今天
這個本來是想做視頻的,所以是以講稿的形式寫的。最后沒做視頻,但是覺得這篇文還是值得記錄一下。真的要多記錄,不然一些不常用的東西即使做過幾個月又有點陌生了。
文章目錄
- 爬蟲 SCRAPY
- xpath
- 后端 DJANGO
- 前端 REACT
Hello大家好這里是小魚,最近我做了一個前后端分離的實時數(shù)據(jù)大屏,因為是自己做全棧開發(fā),所以從零開始學(xué)了不少新的知識,想在這里分享或者說記錄一下,這個教程會盡量短,但是從數(shù)據(jù)獲取,到后端項目和前端項目,包括項目部署到云服務(wù)器都會講到,那我們開始吧。
爬蟲 SCRAPY
首先我們要考慮數(shù)據(jù)大屏的數(shù)據(jù)從哪來,數(shù)據(jù)大屏的重要優(yōu)勢,一個是多端顯示(比如做好了之后無論在手機(jī)/電腦還是電視這種更大的顯示器都可以看),再一個就是實時性,一般數(shù)據(jù)大屏不同于那種一次性生成的數(shù)據(jù)可視化圖,數(shù)據(jù)大屏的數(shù)據(jù)是實時更新的。
給公司做項目的話可能會有現(xiàn)成的實時數(shù)據(jù)給到你,那我們做自己項目就要考慮實時數(shù)據(jù)從哪來,有一些網(wǎng)站會向外提供免費開放的數(shù)據(jù)api接口,直接調(diào)用就可以拿到數(shù)據(jù),這是一個方法。但是我今天想做的是全頭到尾不依賴其他第三方,獨立的寫一個完整的項目。
我這邊想到的獲取數(shù)據(jù)的方法就是爬蟲,先疊個甲,咋們這個視頻只是用于交流學(xué)習(xí),不涉及商業(yè)行為,如有侵權(quán)聯(lián)系我刪除。
我選的框架是scrapy,當(dāng)然你也可以用你熟悉的框架,那我們就直接開始。
首先安裝scrapy,安裝過程大家根據(jù)自己的操作系統(tǒng)搜索一下,到命令行輸入scrapy有如下顯示則安裝成功。
使用scrapy的命令創(chuàng)建項目
scrapy startproject douban
這時候可以用編輯器打開這個項目,能看到目錄結(jié)構(gòu)是這樣的:
里面有一個 spiders 目錄,我們后面爬蟲代碼的主要邏輯就寫在這個目錄里。
Items 文件用來定義我們爬取到的數(shù)據(jù)字段和類型。
pipelines 文件用來執(zhí)行保存數(shù)據(jù)的操作,后面我們會在其中寫和數(shù)據(jù)庫連接相關(guān)的代碼。
middlewares 文件是中間件,我們這次用不上可以先忽略。
settings 文件顧名思義是設(shè)置文件,里面可以配置的東西很多,后面再詳細(xì)。
好,看完目錄現(xiàn)在來寫主要的代碼,我想要爬取的是豆瓣現(xiàn)在正在上映電影的評價,我們知道豆瓣的評分相對來說還是比較能代表口碑的,它又是一個會實時變化的值,那我們對數(shù)據(jù)爬取之后存入自己的數(shù)據(jù)庫,就可以在前端展示當(dāng)前線上電影評價排行還有單個電影的評價趨勢等數(shù)據(jù)圖表,方便自己挑選電影。
我們使用這個命令創(chuàng)建一個爬蟲主文件,scrapy genspider main_dou 'https://movie.douban.com/cinema/nowplaying/yichang/'命令。
這時會看到spiders目錄下有一個main_dou.py文件,里面已經(jīng)給出了大致的架構(gòu),
里面有一個用scrapy.Spider類創(chuàng)建的子類,并有三個屬性和一個方法。
name 是唯一名稱
allow_domains 是搜索的域名范圍,規(guī)定只爬取這個域名下的網(wǎng)頁
從 start_urls 開始抓取數(shù)據(jù)
parse(self, response) :每個初始URL完成下載后將被調(diào)用,調(diào)用的時候傳入從每一個URL傳回的Response對象來作為唯一參數(shù),負(fù)責(zé)解析返回的網(wǎng)頁數(shù)據(jù)(response.body),提取結(jié)構(gòu)化數(shù)據(jù)(生成item)
首先在settings中我們將user agent換一下
然后我們輸入命令跑一下看能不能成功。
抓出來是亂碼,是header的問題。
xpath
只需要懂三個符號
/ 直接子集(根元素)
// 可以跳級(不考慮位置)
@ 屬性訪問
我們直接在瀏覽器F12,在dom里可以按xpath語法搜索,
這里輸入的意思就是找屬性里id為nowplaying的div,是不是很簡單。
這樣就找到了所有有上映影片信息的li標(biāo)簽,可以看到標(biāo)簽里有影片信息,我們提取對應(yīng)屬性就行。
最后的代碼是這樣,extract()方法用于從XPath結(jié)果中提取文本值,這樣就爬到了所有影片名字和打分。
接下來我們要將數(shù)據(jù)存到數(shù)據(jù)庫中,實現(xiàn)持久化。
我們在items文件里定義好數(shù)據(jù)字段,然后在主程里拼好每一組數(shù)據(jù)
接下來我們要連接數(shù)據(jù)庫,我這里用的mongodb,你們也可以使用自己熟悉的數(shù)據(jù)庫比如mysql等。
在settings里寫好數(shù)據(jù)庫的地址,庫名表名,賬號密碼等信息,在pipelines文件里進(jìn)行注入。如果你也是用的mongodb,可以和我一樣配置,不過其實別的數(shù)據(jù)庫也差不多的。
再跑一下項目,發(fā)現(xiàn)數(shù)據(jù)庫里已經(jīng)成功寫入數(shù)據(jù)了,我們這一步就完成了。
再只需要讓這個scrapy項目按時自動執(zhí)行我們數(shù)據(jù)庫就有實時數(shù)據(jù)了,這個我們后面部署的部分再講。
后端 DJANGO
我們已經(jīng)有了數(shù)據(jù),現(xiàn)在需要開發(fā)一個后端項目,對數(shù)據(jù)庫的數(shù)據(jù)進(jìn)行操作,并暴露api給前端。
因為我平時用python比較多,后端框架選型的時候就選了Django,當(dāng)然也可以選你熟悉的框架。Django其實是一款前后端不分離的框架,將前端代碼寫在django的templates文件夾里。前后端冗雜在一塊會比較難維護(hù),針對前后端分離的情況,Django退出了DRF(Django Rest Framework)。那我們一步步來做。
首先在開發(fā)后端項目之前可以整一個虛擬環(huán)境,因為后端項目依賴的庫會比較多,而各個后端項目之間如果庫的版本各不相同就會比較麻煩,所以開啟一個虛擬環(huán)境保證這個項目的依賴都是獨立的。
sudo pip3 install virtualenv
virtualenv douban-env
source env/bin/activate
安裝django
pip3 install django==3.2.1
pip3 install djangorestframework==3.12.4
django-admin.py startproject be_douban
cd be_douban
python3 manage.py startapp douban
此時的目錄結(jié)構(gòu)是這樣的
douban那個文件夾是我們創(chuàng)建出來的app,我們先到settings中注冊這個app。
為了操作mongodb,我們需要安裝djongo
我們來看一下DRF這個架構(gòu)(這里借用一下大佬的圖,圖源見水印)
DRF是將數(shù)據(jù)庫的東西通過ORM的映射取出來,通過view和serializers文件綁定REST接口,當(dāng)前端請求時,返回序列化好的json。
我們從Modal開始修改:Model文件里定義了數(shù)據(jù)的結(jié)構(gòu)和關(guān)系,類似于數(shù)據(jù)庫表的描述。我們把之前爬蟲存在數(shù)據(jù)庫里的字段再這邊再聲明一下。
from djongo import models# Create your models here.class Douban(models.Model):_id = models.CharField(max_length=128, primary_key=True)name = models.CharField(max_length=128)score = models.FloatField()time = models.IntegerField()class Meta:db_table = 'spider_douban'
然后是序列化器,新建一個serializer.py文件,因為我們直接需要所有字段,這邊f(xié)ields直接__all__就行,如果不需要所有的,可以自己用數(shù)組定義 例如 fields = [‘_id’, ‘station’, ‘time’]
from douban.models import Douban
from rest_framework.serializers import ModelSerializerclass DoubanSerializer(ModelSerializer):class Meta:model = Doubanfields = '__all__'
最后是視圖view文件,定義了API的行為,我們主要對數(shù)據(jù)處理的邏輯寫在這,我們是需要一個接口拿到最新的所有電影的分?jǐn)?shù),然后在前端進(jìn)行排行,那么就這么寫。
from rest_framework.decorators import api_view
from rest_framework.response import Response
from douban.models import Douban
from douban.serializers import DoubanSerializer
from django.db.models import Max@api_view(['GET'])
def movie_lasttime_list(request):if request.method == 'GET':# 使用Djongo查詢獲取最后一次時間的時間戳last_time = Douban.objects.aggregate(Max('time'))['time__max']# 使用過濾器獲取最后一次時間的所有數(shù)據(jù)movies = Douban.objects.filter(time=last_time)# 將查詢到的數(shù)據(jù)序列化并返回serializer = DoubanSerializer(movies, many=True)return Response(serializer.data)
之后定義一下url,就是api的路徑,我們在douban app下創(chuàng)建一個url文件
from django.urls import include, path
from douban import viewsurlpatterns = [path('api/current', views.movie_lasttime_list),
]
之后在主url文件中引入這個app的url文件
from django.contrib import admin
from django.urls import path, includeurlpatterns = [path('admin/', admin.site.urls),path('', include('douban.urls')),
]
現(xiàn)在就做完了,可以跑項目了,第一次要執(zhí)行一下遷移命令
python3 manage.py makemigrations
python3 manage.py migrate
python3 manage.py runserver
這時候看到
證明已經(jīng)跑起來了
瀏覽器輸入對應(yīng)url可以看到
我們的接口就寫好了。
除了最新數(shù)據(jù),我們還需要一個趨勢數(shù)據(jù),就不一步步講解了,和上面的一樣,展示一下view文件的邏輯:
@api_view(['GET'])
def movie_scores_by_name(request):if request.method == 'GET':movies = Douban.objects.all() # 獲取所有電影數(shù)據(jù)movie_scores = {} # 創(chuàng)建一個字典來存儲電影名稱和對應(yīng)的時間和分?jǐn)?shù)列表for movie in movies:if movie.name in movie_scores:movie_scores[movie.name].append({"time": movie.time, "score": movie.score})else:movie_scores[movie.name] = [{"time": movie.time, "score": movie.score}]return Response(movie_scores)
前端 REACT
接下來看前端部分。
一般經(jīng)常寫前端的人都有自己熟悉的腳手架,我也有一套 react + ts + less + webpack 的腳手架。但是為了簡單,畢竟這個教程不是一個前端各種技術(shù)棧的介紹,我們就是用最簡單的官方的腳手架實現(xiàn)一下,它內(nèi)部封裝了webpack。
npm install -g create-react-app
create-react-app fe_douban
我們 npm start 看到下面這個頁面就是項目跑起來了:
這是原始的目錄,我們稍微調(diào)整一下結(jié)構(gòu)。把src下的文件刪掉只保留index.js和index.css,再創(chuàng)建一個container和component文件夾分別放容器和組件。
引入echarts,在命令行輸入npm install echarts,可以看到package.json文件里已經(jīng)有Echarts的依賴了
而echarts怎么用呢,我們在component目錄下創(chuàng)建一個bar-chart文件夾,新建index.ts文件。用函數(shù)組件的寫法吧,在useEddect里面對echart實例進(jìn)行操作,簡單來說就是先init初始化,然后定義Option,我們圖表的所有配置都在option里面,這部分就不詳述了,可以去Echarts官網(wǎng)看例子,還有手冊,每個配置項都很詳細(xì),我就直接展示了。
import React, { useEffect, useRef } from 'react';
import * as echarts from 'echarts';import './index.css';const mockData = [{"_id": "8c7c957e237ff7ec2f848908fbea9817","name": "奧本海默","score": 8.7,"time": 1693390018}, {"_id": "21d53373fa67f1866993492cfb782d17","name": "燃冬","score": 6.2,"time": 1693390018}
]const BarChart = (props) => {const chartRef = useRef();let data = props.data || mockData;data.sort((a, b) => a.score - b.score);console.log(data)useEffect(() => {const chart = echarts.init(chartRef.current); //echart初始化容器let option = {title: {text: props.title ? props.title : '',textStyle: {color: '#aad8f8',fontWeight: 300,fontStyle: 'italic'}},color: ['#faeea0'],grid: {left: '15%'},tooltip: {trigger: 'axis'},xAxis: {type: 'value',axisLabel: {color: '#ffffff'}},yAxis: {type: 'category',data: data.map((item) => item.name)},series: [{data: data.map((item) => item.score),label: {show: true,position: 'inside',color: '#ffffff'},type: 'bar'}]};chart.setOption(option);const echartsResize = () => {echarts.init(chartRef.current).resize();};window.addEventListener('resize', echartsResize);return () => window.removeEventListener('resize', echartsResize);}, [props]);return <div className='bar-chart-container' ref={chartRef}></div>;
};export default BarChart;
寫好BarChart組件后在container創(chuàng)建一個homePage=,在其中引用之前寫好的組件,并傳入數(shù)據(jù)。數(shù)據(jù)是通過fetch方法在我們后端接口取得,具體代碼如下:
import React from "react";import './index.css'
import BarChart from "../../component/bar-chart";class HomePage extends React.Component {state = {barData: null}componentDidMount() {fetch("http://127.0.0.1:8000/api/current").then(res => res.json()).then(data => {this.setState({barData: data});}, (e) => console.log(e))}render() {return (<div className="home-page">{/* <LineChart /> */}<BarChart title={"今日在映電影評分排名"} data={this.state.barData} /></div>)}
}export default HomePage;
現(xiàn)在打開頁面,已經(jīng)可以看到排名后的電影和評分了。這樣整個流程就走通了。
最后將爬蟲項目,前后端都部署到云服務(wù)器,將爬蟲設(shè)置為定時任務(wù),前端可以用 nginx,后端用 uwsgi + nginx。就可以了。