在本实训中,将利用正则表达式及其它的Python模块实现从互联网页面中获取关于电视连续剧《长征》的评论数据,并展示到对应的影视作品详情页中。具体步骤如下:
- 查看关于电视连续剧《长征》评论数据的互联网页面(本实训中选择的是豆瓣电影中的相关评论),在浏览器中打开相关评论数据页面后,利用浏览器的调试工具(如:chrome浏览器的DevTools工具)定位要获取的评论信息在网页文档中的相关位置结构,如图1所示。从调试工具的元素界面可看到,评论数据是位一个具有样式“review-list”的div中,每条评论的相关内容在一个子div中,如图2所示。
- 定义从网页爬取评论数据的函数:在项目根目录下的utils包中新建一个Python脚本文件get_comments_from_douban.py,在文件中定义一个函数get_comments()用于从豆瓣电影的用户评论页面中爬取相关的评论数据,函数接收一个评论数据页面的URL参数,在函数中利用Python的标准库urllib实现对页面的数据请求,然后定义相应的数据提取正则表达式模式用于从请求到的页面html代码中提取对应的评论内容,具体的代码实现如图3所示。
import re from urllib import request # 用于从豆瓣网对应页面获取相关评论信息的函数 def get_comments(target_url): # 模拟浏览器请求网页时,设置请求头伪装 headers = {'User-Agent': ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0Safari/537.36 ')} try: req = request.Request(target_url, headers=headers) # 创建Request对象时通过headers参数设置请求头 # 调用urlopen()请求目标页面,请求成功会返回一个响应对象 response = request.urlopen(req) # 可通过uropen方法返回的响应对象的read()、readline()、readlines()等方法读取响应数据 html = response.read().decode('utf-8') # 定义提取相关信息的正则表达式模式 comment_reg = r'(?s)<div data-cid=.+?class="hidden">' # 用于提取评论信息的html代码块 cmt_avator_src_reg = r'src="([^"]+)"' # 提取用户头像的链接 cmt_user_reg = r'<a href="([^"]+)" class="name">([^<]+)</a>' # 提取评论用户的豆瓣网用户主页及昵称 cmt_datetime_reg = r'<span content="([^"]+)" class="main-meta">([^<]+)</span>' # 提取评论的发表时间 cmt_title_reg = r'<h2><a href="([^"]+)">([^<]+)</a></h2>' # 提取评论标题及评论详情页链接地址 cmt_content_reg = r'<div class="short-content">\s*(.*?)\s* ' # 提取评论的简短文本内容 cmt_list = re.findall(comment_reg, html) # 先提取所有评论信息的html代码块 comments = [] # 用于存储所有评论信息的列表 for c in cmt_list: # 循环处理每一条评论 cmt = { 'data_src': target_url, # 评论数据的来源 'avator_src': re.search(cmt_avator_src_reg, c).group(1), # 用户头像的链接 'user_page_url': re.search(cmt_user_reg, c).group(1), # 评论用户的豆瓣网用户主页 'user_nick': re.search(cmt_user_reg, c).group(2), # 评论用户的昵称 'cmt_datetime': re.search(cmt_datetime_reg, c).group(1), # 评论的发表时间 'cmt_page_url': re.search(cmt_content_reg, c).group(1), # 评论的简短文本 'cmt_title': re.search(cmt_title_reg, c).group(2), # 评论标题 'cmt_content': re.search(cmt_content_reg, c, re.DOTALL).group(1) } comments.append(cmt) return comments except Exception as e: print(e) return None
-
修改video_detail_page()函数:修改项目根目录下的views子目录中的cz_video.py文件,在视图函数video_detail_page()添加获取评论数据的代码,通过调用图3中定义好的get_comments()方法从评论页面中获取数据,并添加到影视作品对象的comments属性中,最后渲染到模板文件中,如图4所示。需要说明的是,在图中所示的代码中,因为本示例项目的影视作品详情只有关于电视连续剧《长征》的示例数据,因此在加载剧情评论数据时,只是简单在第31行代码处进行了一个判断当前的影视作品对象ID是否指定ID的操作,所以当前的示例代码只能加载关于电视连续剧《长征》的评论信息,其它的影视作品如需加载,则须自行修改对应代码才能实现。
from flask import Blueprint, render_template from models.video_models import Video from utils.get_comments_from_douban import get_comments from utils.load_data import load_data_from_json_file video = Blueprint('video', __name__, url_prefix='/video') # 创建名为”video“的蓝图 @video.route('/video_list') def video_list_page(): target_file = r"static/jsons/cz_video.json" # 构造示例数据文件的路径字符串 # 调用 utils.load_data块下的load_data_from_json_file方法,从json文件加载数据 video_list = load_data_from_json_file(target=target_file) # 利用render_template方法,将相关数据渲染到模板文件中 return render_template("/video/cz_video_list.html", video_list=video_list) @video.route('/video_detail/<vid>/<fname>') def video_detail_page(vid, fname): target_file = r"static\jsons\video\\" + fname # 构造示例数据文件的路径字符串 # 调用 utils.load_data模块下的load_data_from_json_file方法,从json文件加毂数据 video_detail = load_data_from_json_file(target=target_file) if video_detail and video_detail[0]['video_id'] == vid: vd = Video(**video_detail[0]) if vid == 'v00100001': # 获取关于电视连续剧《长征》的评论信息 urls = [ "https://movie.douban.com/subject/2156727/reviews?sort=time&start=0", "https://movie.douban.com/subject/2156727/reviews?sort=time&start=20" ] temp = [] for url in urls: result = get_comments(url) temp.extend(result) vd.comments = temp else: vd = None # 利用render_template方法,将相关数据渲染到模板文件中 return render_template("/video/cz_video_detail.html", video=vd)
-
修改影视作品详情页的模板文件,添加“剧情评论”模块:修改项目根目录下的templates/video子目录中的cz_video_detail.html模板文件,在原有代码的“参考网址”模块的结束标签(</section>)后面,控制标签{% else %}前面的代码区中添加一个“剧情评论”模块,在该模块中编写相应代码,实现将视图函数中传递过来的影视作品详情对象中的剧情评论信息展示出来,如图5所示。在图中所示的代码中,首先在第129行代码处判断comments属性是否存在,如果comments属性存在,则在第131行-第146行代码中,对评论数据列表中每一条评论数据循环处理,将相关的信息展示到页面中。因为关于评论用户的头像、豆瓣网用户主页、豆瓣网对应的评论详情页面的URL地址并不在本应用中,因此,将对应的URL直接赋值给对应标签的src或href属性,完成以上步骤的操作后刷新页面,运行效果如图6所示。
<!-- 影视作品·剧情评论 --> <section class="mt-1 bg-army"> <div class="container py-4"> <div class="row"> <div class="block-title-w"> <h2 class="block-title">{{video.video_name}}·剧情评论</h2> <span class="icon-title"><span></span> <img src="{{ url_for('static', filename='images/star.png') }}" width="30" height="30"></span> </div> {% if video.comments %} <div class="row"> {% for c in video.comments %} <div class="mb-2 p-1"> <div class="p-1 h-100 bg-light rounded-3"> <div class="fw-bold fs-4 mt-2 text-danger ps-2"> <a href="{{ c.user_page_url }}"><img class="rounded" width="32" src="{{ c.avator_src }}"/></a> <span class="icon-title">{{c.user_nick}}</span> <span>{{ c.cmt_datetime }}</span> </div> <div class="cz-loss-decs p-2"> <a href="{{c.cmt_page_url}}"><p class="h3 fw-bold">{{c.cmt_title}}</p> <p class="h5 text-dark">数据来源:{{c.data_src }}</p> <p class="fs-4 text-dark">{{ c.cmt_content }}</p></a> </div> </div> </div> {% endfor %} </div> {% else %} <h4 class="text-center m-5 p-5">暂无评论...</h4> {% endif %} </div> </div> </section>