【Project】CupFox电影网站数据爬取分析与可视化

发布于:2025-02-10 ⋅ 阅读:(106) ⋅ 点赞:(0)

数据采集清洗与数据存储流程如下图所示。

数据分析与数据可视化流程设计如下

1.使用pymongo从数据库中查询所需的数据。对数据进行处理和分析,进行统计、分类、聚合等操作,提取关键指标和洞察。分析结果可以通过编写Python代码进一步优化、筛选和整理,以便于后续可视化展示。

2.使用pyecharts库生成图表对象(如柱状图、折线图、饼图等),将分析结果可视化。通过render_embed()方法将生成的图表嵌入到HTML模板中。使用Flask框架将图表渲染并传输到前端,展示给用户。用户可以在Web界面上交互式地查看图表,了解数据的动态变化和趋势。

数据分析与数据可视化流程如下图所示。

自动化流程设计

代码管理与版本控制

Gitea:作为代码托管平台,团队通过Gitea进行源代码的管理和版本控制。所有代码变更(如新功能开发、Bug修复)都通过Git提交,并同步到Gitea仓库中。

分支管理:采用Git的分支策略,开发人员在不同的分支上进行开发工作(如feature、bugfix、release等),确保主分支(main)保持稳定,避免开发中断。

持续集成

Jenkins:使用Jenkins实现持续集成,自动化构建和测试代码。当代码提交到Gitea仓库后,Jenkins会检测到变更并自动启动构建流程。

Jenkinsfile:通过编写Jenkinsfile脚本定义CI/CD流水线,确保每次提交代码时都进行构建、编译、单元测试等任务,确保代码质量。

容器化与自动化构建

Docker:项目采用Docker容器化技术,将应用及其所有依赖项打包成容器镜像,确保应用在任何环境下都能一致运行。

Dockerfile:使用Dockerfile定义容器镜像的构建过程,包括操作系统、运行时环境、依赖包等的配置。

自动化构建:Jenkins自动触发Docker镜像构建过程,每次代码提交后,自动创建新的容器镜像,并将其推送到镜像仓库。

自动化流程图下图所示。

总体流程

整个项目流程覆盖了从数据采集、存储、分析、展示到自动化部署和安全保障的各个方面,确保了系统的高效性、可扩展性和安全性。

总体流程如下图所示。

图表设计

系统从以下几个方面进行设计与实现:

  1. 电影更新频率分析

图表类型:折线图

用途:折线图适合展示随时间变化的电影更新频率,尤其是月度更新频率的变化趋势。通过折线图,平台可以清晰地看到电影更新的波动情况,并分析哪些时间段更新较为频繁。

  1. 电影产地热度分析

图表类型:地图

用途:地图适用于展示电影产地的热度分布。通过将不同产地的电影数量和观看量等指标映射到地图上,可以直观地看到各个地区的电影资源热度。

  1. 用户评论关键词提取分析

图表类型:词云图

用途:词云图通过可视化用户评论中的高频关键词,能够帮助发现用户的偏好和需求。关键词出现频率越高,字形越大,能够直观显示哪些主题在评论中讨论得最多。

  1. 导演热度分析

图表类型:柱状图

用途:柱状图适合展示不同导演的影片数量和受欢迎程度,能够帮助平台了解哪些导演的作品更受欢迎。

  1. 影片类型占比分析

图表类型:饼图

用途:饼图适合展示平台中各类型电影的资源占比,能够清晰地展示各类型电影在平台中的分布。

项目优势

本项目采用Python3.8编写,具备以下几个主要优势:

Flask Web可视化页面

本项目使用Flask框架开发的Web可视化页面,相较于传统的静态HTML页面,Flask提供了更强的动态交互能力和灵活性。Flask支持模板渲染、路由管理及RESTful接口,使得前后端分离和数据交互更加便捷高效。

Docker容器技术

使用Docker容器技术部署项目,避免了繁琐的环境配置操作,显著简化了项目迁移和部署的复杂度。Docker容器提供了一种轻量级的虚拟化方式,可以确保应用在不同环境中具有一致的运行效果。此外,容器与宿主机之间实现隔离,提升了服务器的安全性和稳定性,减少了环境配置对生产环境的影响。

MongoDB复制集机制

本项目使用了MongoDB的复制集机制,构建了MongoDB集群,支持读写分离和故障转移。主节点负责写入操作,而从节点用于读取,确保系统在高负载情况下的稳定性和响应速度。复制集机制不仅提升了系统的可用性,还增强了数据的安全性,通过数据备份机制防止数据丢失和服务器故障对业务的影响。

DevOps自动化流程

集成Jenkins与Gitea实现了DevOps流程,能够自动化管理代码的构建、测试和部署。结合Docker容器化技术,通过编写Jenkinsfile流水线脚本,将代码打包为Docker容器镜像,支持自动化部署。此流程确保了团队开发的高效协作,同时在多平台(如Kubernetes)上均可运行,大大提升了开发部署和版本更新的效率。

Linux服务器部署与Nginx反向代理

项目部署在Linux服务器上,采用Nginx进行反向代理Flask应用。Nginx不仅能够高效处理大量并发请求,还通过负载均衡和反向代理提高了系统的可扩展性。同时,Nginx还提供了额外的安全性保障,如SSL加密和请求过滤,确保应用的安全性和稳定运行。

工具版本

使用的工具与工具版本如下表x所示。

描述 工具 版本
解释器 Python 3.8
Python库 Flask 3.0.3
Python库 jieba 0.42.1
Python库 pandas 2.0.3
Python库 pyecharts 2.0.6
Python库 beautifulsoup4 4.12.3
Python库 pymongo 4.10.1
Python库 Requests 2.32.3
容器运行时 Docker-ce 27.0.3
数据库 (主从架构) mongodb 7.0.12
CI/CD平台 jenkins 2.462.3
代码仓库 gitea 22.3.1
操作系统 Rocky Linux 9.4
web服务器 nginx 1.25

系统实现

数据采集数据清洗实现

获取所有电影详情页url的关键代码

网站带有请求速度检测反爬,带上请求头,使用time.sleep(random.randint(1, 5)),随机模拟人类访问时间,防止网站拉黑IP。

通过url_get(self,begin_page=1,end_page=3)方法,保存所有的url数据到data_type列表中,返回给video_data_get()方法。

get_url()方法关键代码如下:

for i in range(begin_page, end_page):
    index = i  # 页面数
    url = self.headurl + f"/cupfoxshow/dianying--------{index}---.html"
    
    try:
        data = requests.get(url, headers=self.headers)
        soup = BeautifulSoup(data.text, 'html.parser')
        # 查找所有的 a 标签每页30个url
        elements = soup.find_all('a')
        # 输出每个元素的 href 属性
        for element in elements:
            href = element.get('href')
            if href and '/cupfox/' in href: 
                self.url_path.append(self.headurl + href) -
        print(f"page {i} url add to list succeed")
        time.sleep(random.randint(1, 5))
        
    except Exception as e:
        print("出现异常:", e)

return self.url_path
电影信息数据清洗

requests爬取到网页信息后使用bs4库解析网页,提取需要的数据存入到字典中。

video_data_get()方法关键代码如下:

'''电影信息提取'''
element_info = soup.select(
    'body > div.details > div.container.flex > div.mobile-main.mobile-main-type > div.movie.bj.br.card.border-shadow > div.cf.b-t')

tree2 = element_info[0]

movie_info['名称'] = tree2.find('h1').get_text()
movie_info['图片'] = tree2.find('img').get("src")
movie_info['别名'] = tree2.find('p', class_='cr3').get_text(strip=True).replace("别名:", '').split(",")
movie_info['标签'] = [a.get_text() for a in
                    tree2.select('.scroll-content')[0].find_all('a', target="_blank")]
movie_info['连载'] = tree2.find('p', class_='cr3').find_next('p').get_text(strip=True).replace("连载:", "")
movie_info['导演'] = [a.get_text() for a in
                    tree2.find('p', class_='cr3').find_next('p').find_next('p').find_all("a",target="_blank")]
movie_info['演员'] = [a.get_text() for a in tree2.find_all('p', class_='cr3 starLink')[0].find_all('a')]
movie_info['类型'] = [a.get_text() for a in
                    tree2.find('p', class_='cr3 starLink').find_next("p").find_all('a')]
movie_info['分类'] = tree2.find('p', class_='cr3 starLink').find_next("p").find_next("p").get_text(
    strip=True).replace("分类:", "")
movie_info['更新时间'] = tree2.find('p', class_='cr3 starLink').find_next("p").find_next("p").find_next(
    "p").get_text(strip=True).replace("更新时间:", "")

# 单独提取出影片描述
miaoshu = soup.select(
    "body > div.details > div.container.flex > div.mobile-main.mobile-main-type > div.movie.bj.br.card.border-shadow > div.summary.detailsTxt")
b_cleaned = re.sub(r'\s+', ' ', miaoshu[0].get_text(strip=True))
movie_info['影片描述'] = b_cleaned.replace('展开', '')



'''网友评论提取'''
element = soup.select(
    'body > div.details > div.container.flex > div.mobile-main.mobile-main-type > div:nth-child(6) > ul')
tree1 = element[0]

b_tags = tree1.find_all('b')
p_tags = tree1.find_all('p')

数据存储实现

编写sava_db()类实现数据存储模块,通过传入dict列表,一次性插入多条数据到mongodb中。

其中构造函数通过os库中的getenv方法获取系统中的环境变量修改默认值,能更好的在Linux(容器)中读取环境变量值,而不用修改对应代码来连接mongodb数据库,sava_db()关键代码如下:

class sava_db():
    def __init__(self):
        # 从环境变量中读取配置信息,若未设置则使用默认值
        self.username = os.getenv("MONGO_USERNAME", "root")  # 默认用户名为 root
        self.password = os.getenv("MONGO_PASSWORD", "password123")  # 默认密码为 password123
        self.host = os.getenv("MONGO_HOST", "192.168.100.120")  # 默认主机为 192.168.100.120
        self.port = int(os.getenv("MONGO_PORT", 27017))  # 默认端口为 27017
        self.db = os.getenv("MONGO_DB", "cup_fox_movie")  # 默认数据库为 cup_fox_movie
        self.table = os.getenv("MONGO_TABLE", "movie_data")  # 默认表为 movie_data

    def save_more_data(self, json_list):
        json_list_data = json_list
        myclient = MongoClient(f'mongodb://{self.username}:{self.password}@{self.host}:{self.port}/')
        mydb = myclient[self.db]  # 选中或者创建库
        mycol = mydb[self.table]  # 选中或者创建表
        mydict = json_list_data
        controller_more = mycol.insert_many(mydict)  # 一次插入多条数据
        
        return controller_more

数据分析实现

影片地区与发布年份数据查询

从数据库中查询标签列的所有数据(其中标签列中的数据类型为list),其中包含所有影片地区与影片发布年份数据,可视化部分可以使用“年份”字段做条形图、使用“国家”字段做世界地图,本项目使用“国家”字段做世界地图,关键代码如下:

# 影片地区与发布年份数据
def get_movie_label():
    """
    return: dict
    """
    label_data_list_year = []
    label_data_list_guoji = []
    label_dict = {}
    label_name = collection.find({}, {'标签': 1, '_id': 0})
    for x in label_name:
        if len(x["标签"][0]) == 4:
            label_data_list_year.append(x["标签"][0])
            label_data_list_guoji.append(x["标签"][1])

    label_dict["年份"] = label_data_list_year
    label_dict["国家"] = label_data_list_guoji

    return label_dict
导演参影数据查询

从数据库中查询导演列,(其中导演列中的数据类型为list),一个影片可能包含多个导演,将所有导演存入一个列表,返回包含所有导演的列表,数据可视化部分统计导演参影的top10数据,生成柱状图,关键代码如下:

# 导演参影数据返回
def get_movie_daoyan():
    """
    return: list
    """
    daoyan_data_list = []
    daoyan_name = collection.find({}, {'导演': 1, '_id': 0})
    for x in daoyan_name:
        for y in x["导演"]:
            daoyan_data_list.append(y)

    return daoyan_data_list
影片类型数据查询

从数据库中查询类型列,(其中类型列中的数据类型为list),一个影片可能包含多种类型,将所有类型存入一个列表,返回包含所有类型的列表,数据可视化部分过滤掉占比非常少的类型,生成饼图可以直观的看到网站内电影类型占比,关键代码如下:

# 影片类型占比分布
def get_movie_type():
    """
    return: list
    """
    type_data_list = []
    type_name = collection.find({}, {'类型': 1, '_id': 0})
    for x in type_name:
        for y in x["类型"]:
            type_data_list.append(y)

    return type_data_list
用户评论数据查询

从数据库中查询所有列,获取“【星辰影院】网友评价”的所有数据,将所有评论存入一个字符串,返回包含所有评论的字符串数据,在数据可视化部分使用jieba库分词,提取评论中的关键字,生成词云图,关键代码如下:

# 网友评论 整体拼接成字符串
def get_movie_remark():
    """
    return: string
    """
    remarks = ""
    remark_name = collection.find({})

    for x in remark_name:
        remark_sources = [x.get("【星辰影院】网友评价", "")] #所有评论插入列表
        for remark in remark_sources:
            if remark:
                remarks += remark

    return remarks
电影网站更新时间查询

从数据库中查询更新时间列,获取“更新时间”的所有数据,将所有数据以“-”分割获取月份数据,返回包含所有月份数据的字符串列表,在可视化部分生成折线图,关键代码如下:

# 电影网站更新时间分布
def get_movie_date():
    """
    return: list
    """
    data_list = []
    update_time = collection.find({}, {'更新时间': 1, '_id': 0})
    for i in update_time:
        data_list.append(i['更新时间'].split("-")[1])  # 获取月份数据
        
    return data_list

数据可视化实现

影片产地国家分布图

地图可视化数据获取,关键代码如下:

guojia = show_mongo_data.get_movie_label()["国家"]
city_data = [1] * len(show_mongo_data.get_movie_label()["国家"])

地图可视化效果,显示各国电影资源分布,如图所示。

导演参影Top10数量图

导演参影柱状图可视化数据获取,关键代码如下:

directors = show_mongo_data.get_movie_daoyan()
directors = [i for i in directors if len(i) > 0]
director_count = pd.Series(directors).value_counts().head(10)

导演参影柱状图效果,展示热门导演参影频率,如图所示。

影片类型占比饼图

影片类型饼图可视化数据获取,关键代码如下:

film_types = show_mongo_data.get_movie_type()
film_type_counts = pd.Series(film_types).value_counts().head(10)
data = [(index, count) for index, count in film_type_counts.items()]

影片类型占比饼图效果,反映网站内资源类型分布,如图所示。

用户评论关键词分析图

用户评论关键词词云图可视化数据获取,关键代码如下:

reviews = jieba.lcut(show_mongo_data.get_movie_remark())
word_counts = Counter(reviews)

#进一步清洗jieba库提取的数据
word_data = [(word, count) for word, count in word_counts.items() if
         len(word) > 1 and not re.match(r'^[a-zA-Z0-9]+$', word)]
word_data = [(word, count) for word, count in word_data if count > 5]
word_data = sorted(word_data, key=lambda x: x[1], reverse=True)[:500]

用户评论关键词词云图效果,生成词云图展示用户关注点,如图所示。

电影网站影片更新时间分布图

电影网站影片更新时间分布可视化数据获取,关键代码如下:

monther = dict(Counter(show_mongo_data.get_movie_date()))
dates = [f"2024-{item}" for item in monther.keys()]
yearly_counts = list(monther.values())

电影网站影片更新时间分布折线图效果,以折线图呈现更新规律,如图所示。

可视化大屏

自动化流程实现

使用git命令将代码推送到gitea代码仓库中,jenkins会读取gitea中的Jenkinsfile文件,并检查语法,通过后会自动运行stages中声明的shell脚本。

效果图

爬虫模块自动部署,效果图如图所示。

数据可视化模块自动部署,效果图如图所示。

Jenkinsfile流水线脚本代码

爬虫模块自动构建与部署,流水线脚本代码如下:

pipeline {
    agent any

    environment {
        IMAGE_NAME = 'cupfox_project'
        IMAGE_VERSION='v5'
        CONTAINER_NAME='cupfox'
        MONGO_CONTAINER_NETWORK='mongodb_data_set_db_network'
    }

    stages {
        stage('Build') {
            steps {
                echo "构建爬虫模块镜像..."
                sh 'docker build -t $IMAGE_HUB/$IMAGE_NAME:$IMAGE_VERSION .'
            }
        }

        stage('Push Image') {
            steps {
                echo "推送爬虫模块容器镜像到注册中心..."
                sh 'docker images $IMAGE_HUB/$IMAGE_NAME:$IMAGE_VERSION'
                sh 'docker push $IMAGE_HUB/$IMAGE_NAME:$IMAGE_VERSION'
            }
        }

        stage('Deploy') {
            steps {
                echo "部署爬虫模块到服务器..."
                // 停止并删除已存在的容器
                sh '''
                    docker ps -q -f name=$CONTAINER_NAME | xargs -r docker stop
                    docker ps -aq -f name=$CONTAINER_NAME | xargs -r docker rm
                '''
                // 启动新的容器
                sh 'docker run -id --network $MONGO_CONTAINER_NETWORK --name $CONTAINER_NAME  $IMAGE_HUB/$IMAGE_NAME:$IMAGE_VERSION'
                // 查看容器状态和日志
                sh 'docker ps -a'
                sh 'sleep 10'
                sh 'docker logs $CONTAINER_NAME'
            }
        }
    }
}

数据可视化模块,动态更新并快速发布,流水线脚本代码如下:

pipeline {
    agent any

    environment {
        IMAGE_NAME = 'cupfox_project_visuali'
        IMAGE_VERSION='v5'
        CONTAINER_NAME='cupfox_visuali'
        MONGO_CONTAINER_NETWORK='mongodb_data_set_db_network'
    }

    stages {
        stage('Build') {
            steps {
                echo "构建数据可视化模块镜像..."
                sh 'docker build -t $IMAGE_HUB/$IMAGE_NAME:$IMAGE_VERSION .'
            }
        }

        stage('Push Image') {
            steps {
                echo "推送数据可视化模块容器镜像到注册中心......"
                sh 'docker images $IMAGE_HUB/$IMAGE_NAME:$IMAGE_VERSION'
                sh 'docker push $IMAGE_HUB/$IMAGE_NAME:$IMAGE_VERSION'
            }
        }

        stage('Deploy') {
            steps {
                echo "部署数据可视化模块到服务器..."
                // 停止并删除已存在的容器
                sh '''
                    docker ps -q -f name=$CONTAINER_NAME | xargs -r docker stop
                    docker ps -aq -f name=$CONTAINER_NAME | xargs -r docker rm
                '''
                // 启动新的容器
                sh 'docker run -id  --network $MONGO_CONTAINER_NETWORK  --name $CONTAINER_NAME -p 38000:5000 $IMAGE_HUB/$IMAGE_NAME:$IMAGE_VERSION'
                // 查看容器状态和日志
                sh 'docker ps -a'
                sh 'sleep 10'
                sh 'docker logs $CONTAINER_NAME'
            }
        }
    }
}
Dockerfile编写

爬虫模块容器构建代码如下:

FROM python:3.8-slim-buster

COPY ./requirements.txt /data/


RUN apt-get update && apt-get install -y  net-tools \
    && pip install -r /data/requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ \
    && apt-get autoremove -y \
    && rm -rf /var/lib/apt/lists/* /root/.cache/pip


ENV MONGO_USERNAME="root"
ENV MONGO_PASSWORD="password123"
ENV MONGO_HOST="172.25.0.2"
ENV MONGO_PORT=27017
ENV MONGO_DB="cup_fox_movie"
ENV MONGO_TABLE="movie_data"

WORKDIR /data

COPY . /data/

ENTRYPOINT ["python", "-u", "/data/main.py"]

数据可视化模块容器构建代码如下:

FROM python:3.8-slim-buster

COPY ./requirements.txt /data/

RUN apt-get update && apt-get install -y  net-tools \
    && pip install -r /data/requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ \
    && apt-get autoremove -y \
    && rm -rf /var/lib/apt/lists/* /root/.cache/pip

ENV MONGO_USERNAME="root"
ENV MONGO_PASSWORD="password123"
ENV MONGO_HOST="172.25.0.3"
ENV MONGO_PORT=27017
ENV MONGO_DB="cup_fox_movie"
ENV MONGO_TABLE="movie_data"

WORKDIR /data

COPY . /data/

EXPOSE 5000

ENTRYPOINT ["python", "-u", "/data/app.py"]

NginxWeb服务器配置

配置文件

nginx反向代理服务器38000端口,重定向到443https端口,flask_38000.conf具体配置代码如下:


server {
    listen 80;
    server_name flask.localserver.local;

    location / {
        return 301 https://$host$request_uri;  # 将 HTTP 请求重定向到 HTTPS
    }
}

server {
    listen 443 ssl;
    server_name flask.localserver.local;

    ssl_certificate /etc/nginx/ssl/server.crt;  # 证书文件路径
    ssl_certificate_key /etc/nginx/ssl/server.key;  # 私钥文件路径

    ssl_session_cache shared:SSL:5m;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';

    location / {
        proxy_pass http://127.0.0.1:38000; #反向代理到本地38000 Docker映射端口
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

数据可视化大屏

通过配置Nginx反向代理,将流量从HTTP重定向至HTTPS,提升安全性,修改本地hosts文件解析服务器IP到“flask.localserver.local”访问结果如图所示。


网站公告

今日签到

点亮在社区的每一天
去签到