爬虫训练场项目第16课,异步AJAX加载学校清单。
爬虫训练场,让天下没有失效的爬虫,2023年橡皮擦最新专栏。
项目仓库地址 :https://gitcode.net/hihell/spider_playground
博客清单:https://pachong.vip/blog
Bootstrap 实现 ajax 请求
本篇博客的核心是使用 Bootstrap 中的 JavaScript 插件发送 Ajax 请求,下面先看一个示例,展示如何使用 Bootstrap 中的 $.ajax()
方法发送 AJAX 请求,这里使用的 $.ajax()
由 jQuery 插件实现,所以提前在模板文件中进行一下导入。
https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.6.0.min.js
实现本步骤操作,需要在 app/templates/school
目录中新增 ajax_list.html
文件,然后输入下述代码。
{% extends "base.html" %}
{% block script %}
<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
</script>
{% endblock script %}
{% block content %}
{% endblock %}
该文件继承了 base.html
文件,然后在其 script
块中,可以编写JS代码。
使用 jQuery 发送 Ajax 请求的示例代码如下所示,后续将在此基础上实现请求和渲染逻辑。
$.ajax({
type: "POST",
url: "/path/to/server",
data: {
key1: "value1",
key2: "value2"
},
success: function(response) {
console.log("AJAX request succeeded!");
},
error: function(error) {
console.log("AJAX request failed: " + error);
}
});
接下来将刚刚的文件,绑定到视图函数中,代码编写到 school/index.py
中,如下所示。
@s.route('ajax_list')
def ajax_list():
page = 1 # 初始化第一页数据
pagination = pagination_object(page)
return render_template('school/ajax_list.html', pagination=pagination)
这里将分页对象的创建的函数进行了封装,便于后续重复使用,pagination_object()
函数内容如下所示。
def pagination_object(page=1):
# schools = School.query.all()
query = School.query
total = query.count() # 获取数据总量
pagination = Pagination(page, total) # 获取分页对象详细参数
if total != 0:
data_list = query.offset(pagination["offset"]).limit(pagination["page_size"]).all()
else:
data_list = []
pagination["data_list"] = data_list
return pagination
使用 AJAX 渲染数据重点在 JS 部分,核心操作为分页请求,所以首次进入页面,需要对第一页数据进行渲染,补充 ajax_list.html
首次渲染部分代码。
<div class="container" id="school_list">
{% for school in pagination.data_list %}
<div class="row mt-3">
<div class="col">
<div class="d-flex">
<div class="flex-shrink-0">
<a href="#">
<img class="rounded-pill img-thumbnail" width="64" height="64" src="{{school.pic}}" alt="">
</a>
</div>
<div class="flex-grow-1 ms-3">
<h5 class="float-start pe-3">{{school.name}}</h5>
<p class="ms-3">
{% for fea in school.feature.split(',') %}
<span class="badge rounded-pill bg-primary">{{fea}}</span>
{% endfor %}
</p>
<p><em>所在省市:<span class="text-black-50">{{school.province}} -- {{school.city}}</span></em></p>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="container">
<div class="row">
<div class="col">
<span class="text-dark float-end align-middle"
style="line-height: 40px;">合计 {{pagination.total}} 条数据</span>
<ul class="pagination float-end">
<li class="page-item prev" page="{{pagination.prev_page}}">
<a class="page-link" href="#">上一页</a>
</li>
<li class="page-item next" page="{{ pagination.next_page }}"><a class="page-link"
href="#">下一页</a>
</li>
</ul>
</div>
</div>
</div>
上述代码进行了两部分修改,第一个是给 .container
类绑定的 DIV 增加了一个 ID 值,第二个修改是给分页 <li>
标签增加了自定义属性 page
,并分别绑定上一页和下一页的页码,然后取消原来的超链接跳转。
核心 JS 部分
准备工作已经完成,下面的重点在数据 AJAX 请求部分。该部分逻辑分步骤实现。
第一步,给上一页和下一个按钮绑定事件
$(function(){
$('.page-item').on('click',function(){
page = $(this).attr('page');
// 获取数据
get_data(page);
})
})
这部分直接使用 .on()
方法进行绑定即可,注意看上述代码中的 get_data()
,该函数用于发起 AJAX 请求,获取响应,并渲染数据,代码如下。
function get_data(page){
$.ajax({
type: "get",
url: "/ss/api2",
data: {
page: page
},
success: function(response) {
// ajax 请求成功
render_data(response);
// 修改分页数据
$('.prev').attr('page',response["prev_page"]);
$('.next').attr('page',response["next_page"]) ;
console.log("AJAX request succeeded!");
},
error: function(error) {
console.log("AJAX request failed: " + error);
}
});
}
请求的数据接口地址为 /ss/api2
,所以还需要回到 school/index.py
文件中,增加该部分函数。
@s.route('api2')
def ajax_list_school():
page = int(request.args.get("page", 1))
pagination = pagination_object(page)
return jsonify(pagination)
上述代码出现了一个新函数 jsonify()
,在 Python Flask 中,可以使用 jsonify()
函数将数据转换为 JSON 并返回给客户端。
简单的案例如下所示。
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/data')
def get_data():
data = {'key1': 'value1', 'key2': 'value2'}
return jsonify(data)
if __name__ == '__main__':
app.run()
在上面的代码中,我们定义了一个视图函数 get_data()
,该函数创建了一个字典,并使用 jsonify()
函数将其转换为 JSON 并返回给客户端。
继续回到JS代码部分,在接收到数据请求之后,我们依次编写了如下代码,其分为两部分,第一部分是数据渲染,第二部分是分页组件页码变化。
render_data(response);
// 修改分页数据
$('.prev').attr('page',response["prev_page"]);
$('.next').attr('page',response["next_page"]) ;
这里主要看 render_data()
函数的实现,代码如下所示。
function render_data(response){
data_list = response["data_list"];
if(data_list.length>0){
// 清空原有数据
$('#school_list').empty();
$.each(data_list,function(index,item){
// 循环渲染数据
var row = $('<div>', {
'class': 'row mt-3',
'data-custom-attribute': 'value'
});
var col =$('<div>', {
'class': 'col'
});
var d_flex = $('<div>', {
'class': 'd-flex'
});
d_flex.append('<div class="flex-shrink-0"><a href="#"><img class="rounded-pill img-thumbnail" width="64" height="64" src="'+item.pic+'" alt=""></a></div>');
// 生成一下标签代码
var badge = "";
$.each(item.feature.split(','),function(i,f){
badge += ' <span class="badge rounded-pill bg-primary">'+f+'</span> ';
});
d_flex.append('<div class="flex-grow-1 ms-3"><h5 class="float-start pe-3">'+item.name+'</h5><p class="ms-3">'+badge+'</p><p><em>所在省市:<span class="text-black-50">'+item.province+'--'+item.city+'</span></em></p></div>')
col.append(d_flex);
row.append(col);
$('#school_list').append(row);
})
}
}
JS代码使用了循环渲染,拼接HTML DOM 对象的方式实现,其重点代码就是在循环后台发送过来的学校对象数组,然后追加到 ID = school_list
的 DIV 中。
到此,核心代码已经编写完毕,可以进行运行测试。
但是当点击下一页时,控制台出现了如下错误。
TypeError: Object of type 'School' is not JSON serializable
该原因是由于 School
实体直接返回的是对象,不是可JSON序列化的数据,所以要继续进行修改。
找到 model.py
文件,修改 School
类文件,增加一个 to_dict()
方法。
class School(db.Model, EntityBase):
# 前面的字典注意不要丢失
def to_dict(self):
return {
'name': self.name,
'province': self.province,
'city': self.city,
'feature': self.feature,
'hotValue': self.hotValue,
'pic': self.pic,
'category': self.category,
'batchTimes': self.batchTimes
}
然后回到 school/index.py
文件中,在 pagination_object()
函数中增加类型转换代码。
data_list = [row.to_dict() for row in data_list]
pagination["data_list"] = data_list
return pagination
重启项目,再次点击分页,即完成本案例,无刷新 AJAX 请求。
本案例到此结束,已更新到 爬虫训练场 欢迎大家访问学习。
项目同步到代码仓库 https://gitcode.net/hihell/spider_playground
????????????????????????
???? 你正在阅读 【梦想橡皮擦】 的博客
???? 阅读完毕,可以点点小手赞一下
???? 发现错误,直接评论区中指正吧
???? 橡皮擦的第 813 篇原创博客
从订购之日起,案例 5 年内保证更新