16. 你很勇哦,这么点数据就敢用异步加载?

爬虫训练场项目第16课,异步AJAX加载学校清单。
爬虫训练场,让天下没有失效的爬虫,2023年橡皮擦最新专栏。
项目仓库地址 :https://gitcode.net/hihell/spider_playground
博客清单:https://pachong.vip/blog

文章目录

    • Bootstrap 实现 ajax 请求
    • 核心 JS 部分

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 值,第二个修改是给分页

  • 标签增加了自定义属性 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 = $('
    ', { 'class': 'row mt-3', 'data-custom-attribute': 'value' }); var col =$('
    ', { 'class': 'col' }); var d_flex = $('
    ', { 'class': 'd-flex' }); d_flex.append(''); // 生成一下标签代码 var badge = ""; $.each(item.feature.split(','),function(i,f){ badge += ' '+f+' '; }); d_flex.append('
    '+item.name+'

    '+badge+'

    所在省市:'+item.province+'--'+item.city+'

    '
    ) 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 年内保证更新

    • ⭐️ Python 爬虫 120,点击订购 ⭐️
    • ⭐️ 爬虫 100 例教程,点击订购 ⭐️
  • 你可能感兴趣的:(爬虫补充[暂免费],javascript,前端,bootstrap,爬虫,python)