[微信小程序][JAVA][Python][SQL Server][网络爬虫] 基于微信小程序的北邮信息门户通知查询系统

最近在忙着期末考试,等期末考完之后再对各个模块进行详细补充~

1 项目目标

目前项目目标都已经实现。

1.1 基础目标

(1)实现北邮信息门户校内通知、学术讲座、公示公告、校园新闻的查询
(2)将从北邮信息门户校内通知、学术讲座、公示公告、校园新闻的内容更新到数据库
(3)将微信小程序与数据库进行连接,使得小程序可以实时调取数据库内容
(4)在微信小程序上实现搜索功能
(5)在微信小程序上实现用户登录功能

1.2 进阶目标

(1)用户可以在微信小程序上对重要通知进行收藏和取消收藏
(2)将用户收藏的通知与用户微信号挂钩,使用户可以随时读取自己收藏的内容

2 技术栈及相关环境要求

2.1 技术栈

  1. 微信小程序
    用户“无需下载,用完即走”,增加了用户使用的便捷性
    不受手机操作系统的限制,只需要开发一套程序

  2. 腾讯云
    支撑微信小程序云开发,可以将数据操作与用户绑定

  3. JAVA 服务器
    开发的服务器稳定性和安全性都在长期实践中被得以证明

  4. Microsoft SQL Server 数据库
    具备完全Web支持,具有易用性和良好的性价比

  5. Python
    具有海量的第三方库,实现功能迅速、稳定、便捷

2.2 系统运行环境

2.2.1 软件环境

分类名称版本语种
PC 操作系统Windows 10家庭中文版简体中文
移动端操作系统iOS/Android/简体中文
应用平台微信小程序/简体中文
数据库平台Microsoft SQL Server2019Sql
服务器端IntelliJ IDEA2020.1Java
Tomcat9.0.44
数据爬取端Spyder3.0.0Python
开发环境Python3.7 及以上Python
Java14.0.1Java

2.2.2 硬件平台

设备名称设备要求
智能手机/平板电脑安装微信(IOS:7.0.20 及以上版本,Andriod: 7.0.22 及以上版本)
PC装有 python 3.7 及以上版本、Java 14.0.1 及 以上版本、 Apache Tomcat 对应版本 、 Microsoft SQL Server 数据库 2019及以上版本

2.2.3 开发环境

分类名称版本语种
PC 操作系统Windows 10家庭中文版简体中文
开发平台微信小程序开发者工具Stable 1.03.2101150JavaScript/WXSS/ WXML
开发平台IntelliJ IDEA2020.1Java
开发平台Tomcat9.0.44Java
开发平台Spyder3.0.0Python
数据库平台Microsoft SQL Server2019Sql

3 设计思想

整个系统被划分为四个部分:Python 爬虫程序、Microsoft SQL Server 数据库、Java Web的Tomcat 以及微信小程序。划分基于每一个部分对系统的贡献和使用的语言。

Python 由于其具有海量的第三方库,可以迅速、稳定、便捷地实现爬虫程序和数据库的更新,因此选用Python 作为数据的获取和处理部分的主要语言。

Microsoft SQL Server 的是一个具备完全Web 支持的数据库产品,提供了对可扩展标记语言(XML)的核心支持以及在Internet 上和防火墙外进行查询的能力,具有易用性和良好的性价比,这些都是我们选择其作为数据库的原因。

Java 用于Web 开发具有天然的优势。长期以来Java 开发者为其提供了良好的开发基础,使得其具有成熟的设计模式,而且还有成熟的框架,可以用很多表达式以及标签来展示我们需要的内容;Java 开发的稳定性和安全性都在长期实践中被得以证明,因此我们选用Java 作为连接数据库和微信小程序的程序设计语言。

微信小程序从出生以来一直受到开发者的青睐,其背靠10 亿+微信用户,用户“无需下载,用完即走”,增加了用户使用的便捷性,降低了使用门槛。此外,微信小程序的开发不受手机操作系统的限制,只需要开发一套程序即可在不同操作系统的手机进行展示。基于此,我们使用微信小程序作为我们的前端展示工具。

整体技术路线如下图所示

4 模块设计

4.1 Python爬虫及数据库更新模块

该部分包含一个portalToSQL 类。该类用于将门户中特定的消息更新到Microsoft SQL Server 数据库中,该类的设计如下图所示。

主体代码具体实现如下:

class portalToSQL:
    
    # 基础信息
    username = ''               # 信息门户登录用户名
    password = ''               # 信息门户登录密码
    server = "127.0.0.1"        # 服务器地址,该处为本地地址
    sqlUsername = ""            # 数据库用户名
    sqlPassword = ""            # 数据库密码
    database = ""               # 数据库名
    
    
    '''
    Input:      loginUrl:   登录网址
                cookkie:    登录cookie
                infoUrl:    信息所在的网址
                infoHref:   信息所在的Href标识
                lableName:  预存入的数据库的表名
    Output:     None
    Function:   构造函数,初始化
    '''
    def __init__(self, loginUrl, cookie, infoUrl, infoHref, lableName):     
        self.loginUrl = loginUrl
        self.cookie = cookie
        self.infoUrl = infoUrl
        self.infoHref = infoHref
        self.lableName = lableName
    
    '''
    Input:      HTTP GET请求 loginUrl 所得到的内容的字符串 str
    Output:     包含本次登录的网站信息的字典 dic (包括用户名、密码、Lt等参数)
    Function:   从 loginUrl 里获取本次登录所特有的参数,如 lt, execution 等
    '''
    def getLt(self, str):
        lt=bs(str,'html.parser')
        dic={}
        for inp in lt.form.find_all('input'):
            if(inp.get('name'))!=None:
                dic[inp.get('name')]=inp.get('value')
        return dic
    
    '''
    Input:      目标地址 objectUrl,实例化的 Session 对象
    Output:     含有标题、日期和文章主体的字典 result
    Function:   获得所需要爬取的页面的标题、日期和文章主体
    '''
    def getNewsDetail(self, objectUrl, session):
        result = {}
        res = session.get(objectUrl, headers=self.cookie)
        res.encoding = 'utf-8'
        soup = bs(res.text, 'html.parser')
        result['Title'] = soup.select('.text-center')[0].text
        
        date=[]
        # 学术讲座和其他通知的标签不同,故做两种处理。'singlemeta text-center'为学术讲座的格式。
        if soup.find_all(class_ = 'pmeta ptime'):
            for j in soup.find_all(class_ = 'pmeta ptime'):
                date.append(j.text)
                eachDate = ''
            for i in date:
                eachDate = i
        else:
            for j in soup.find_all(class_ = 'singlemeta text-center'):
                date.append(j.text)
                eachDate = ''
            for i in date:
                eachDate = i
            eachDate = (eachDate.split(' ')[-1] + "    " + eachDate.split(' ')[-2])
        result['date'] = eachDate
        article = []
        image = []
        tmp = ''
        allP = soup.select('.singleinfo #vsb_content .v_news_content p')
                           
        # 插入图片                           
        allPicture = soup.select('.singleinfo #vsb_content .v_news_content p img')
        try:
            for pictures in allPicture:
                imgsUrl = 'http://my.bupt.edu.cn' + pictures.get('src')
                image.append(imgsUrl)
        except:
            pass # 解决不含img信息时的报错问题
        
        for p in allP:
            article.append('        ' + p.text.strip() + '\n')
            
        for i in range(0,len(article)):
            tmp += article[i] 
        result['article'] = tmp
        
        tmp = ''
        for unitImg in range(0, len(image)):   
            if unitImg == len(image) - 1:
                tmp += image[unitImg]
            else:
                tmp += image[unitImg] + ','
        result['image'] = tmp
        return result
    
    # 将数据写入数据库
    # 连接数据库
    '''
    Input:      需要写入数据库的 DataFrame 型数据 df
    Output:     写入数据库的结果
    Function:   将 df 写入 database 数据库的 lablename 表中
    '''
    def toSqlServer(self, df):
        connect = pymssql.connect(self.server, self.sqlUsername, self.sqlPassword, self.database)
        # 一次插入多条数据
        cols = ','.join(df.columns)
        val = (tuple(i) for i in df.values) # 这里需要转成tuple类型才能写入到数据库中
        sqlstr = "INSERT INTO {}({}) VALUES ({})".format(self.lableName, cols, ','.join(['%s']*len(df.columns)))

        try:
            with connect.cursor() as cursor:
                cursor.executemany(sqlstr, val)
                sqlDel = 'Delete T From (Select Row_Number() Over(Partition By title order By date) As RowNumber,* From {})T Where T.RowNumber > 1'.format(self.lableName)
                cursor.execute(sqlDel)
                sqlDelNull = 'delete from {} where (datalength (article) = 0 or datalength (article) is null) and (datalength (image) = 0 or datalength (image) is null)'.format(self.lableName)
                cursor.execute(sqlDelNull)
            connect.commit()
            print('>>> 插入数据成功,表 {} 共插入 {} 行数据'.format(self.lableName, len(df)))
            print('>>> 表 {} 删除重复数据、空白数据成功'.format(self.lableName))
        except Exception as e:
            print('>>> 插入数据失败', e)
            connect.rollback()
        finally:
            connect.close()
    
    '''
    Input:      None
    Output:     含有需要爬取信息的 Excel 文件并更新到数据库
    Function:   将所需要爬取的信息保存到 Excel 文件和 SQL Server 数据库
    '''
    def getInformation(self):
        #模拟一个浏览器头
        header={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0'}
    
        #实例化session
        session = requests.Session()
        session.cookies = cookielib.CookieJar()
        response=session.get(self.loginUrl, headers=header)
        # 得到含有输入的用户名、密码、Lt的字典格式
        dic = self.getLt(response.text)

        # 更新post信息
        postdata={
                'username':self.username,
                'password':self.password,
                'lt':dic['lt'],
                'execution':dic['execution'],
                '_eventId':'submit',
                'rmShown':'1'
                }

        #携带登陆数据,以post方式登录,
        response = session.post(self.loginUrl, data=postdata, headers=header)
        
        #用 GET 方式访问“校内通知”的页面
        res = session.get(self.infoUrl, headers=self.cookie)
        #用 beautifulsoup 解析 html
        soup = bs(res.text,'html.parser')
    
        # 获取各个通知的详细URL
        url = []
        urls = soup.find_all(href = re.compile(self.infoHref))
        isPrompt = False # 让提示代码只执行一次
        for j in urls:
            if(('http://my.bupt.edu.cn/' + j.get('href')) not in url):
                url.append('http://my.bupt.edu.cn/' + j.get('href'))
                if url != [] and isPrompt == False:
                    print(">>> 网址爬取成功")
                    isPrompt = True
                    
                
        news_total=[]
        for i in range(0, len(url)):
            newsary = self.getNewsDetail(url[i], session) # 读取网址中的内容详情
            news_total.append(newsary)
        df = pd.DataFrame(news_total)
        self.toSqlServer(df) # 将数据存入数据库

该类中包含四个功能函数,其中getLt(str) 函数用于从loginUrl 里获取本次登录所特有的参数,如lt, execution 等; getNewsDetaill(objectUrl, session) 函数用于获得所需要爬取的页面的标题、日期和文章主体; toSqlServer(df) 函数用于将df 写入database 数据库的lablename 表中; getInformation() 函数用于将所需要爬取的信息保存到SQL Server 数据库。设计图如下图所示。

该部分的接口描述如下

接口名称输入信息输出信息异常处理
toSqlServer需要存入数据库的DataFrame 类型数据dfNoneTry: 将数据插入对应表中Except: 无法插入,则提示插入数据失败,并显示报错结果
getInformaion登录页面的URL、登录页面后获得的cookie、需要爬取消息列表对应的URL、需要爬取的消息对应的Href、对应的数据库中的表名获取的URL和是否成功插入数据Try: 成功获取信息Except: 获取失败,则提示执行失败的模块,并显示报错结果

4.2 JAVA Tomcat服务器模块

该部分包含9个类,设计图如下图所示。

该模块接口描述如下

接口名称输入信息输出信息异常处理
informationDatabase需要连入的数据库及列表名查询到的数据结果Try: 将数据存入数组中 Except: 无法存入,则提示失败,并显示报错结果
userDatabase需要连入的数据库及列表名查询到的数据结果Try: 将数据存入数组中 Except: 无法存入,则提示失败,并显示报错结果
http://localhost:8080/userServer/userServlet用户名username 密码password成功:success 失败:errorTry:在网页上显示数据内容 Except:打印错误报告
http://localhost:8080/articleServer/notificationServletNone返回对应JSON数据Try:在网页上显示数据内容 Except:打印错误报告
http://localhost:8080/articleServer/newsServletNone返回对应JSON数据Try:在网页上显示数据内容 Except:打印错误报告
http://localhost:8080/articleServer/announcementServletNone返回对应JSON数据Try:在网页上显示数据内容 Except:打印错误报告
http://localhost:8080/articleServer/lectureServletNone返回对应JSON数据Try:在网页上显示数据内容 Except:打印错误报告

返回的JSON参数格式如下表所示

参数名参数类型说明注意事项
titleString信息标题None
dateString信息发布日期若为学术讲座,该部分包含主办学院
articleString信息主体None
imageArray图片的 URL 地址None

主体代码实现如下:

package database;

import model.Information;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.fastjson.*;

public class informationDatabase {
    String lableName = "";
    Connection ct = null;
    PreparedStatement pestmt = null;
    String jsonOutput = "";
    private List<Information> information = new ArrayList<Information>();
    public informationDatabase(String lableName) {
        try {
            this.lableName = lableName;
            Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
            ct = DriverManager.getConnection("jdbc:sqlserver://localhost:1433;databaseName=schoolNews", "这里输入数据库用户名", "这里输入数据库密码");
            if (ct != null) {
                System.out.println("数据库连接成功");
            } else {
                System.out.println("数据库连接失败");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 获取通知信息并存储到Notification数组中
    public String getInformation() throws SQLException {
        try {
            System.out.println("正在打包发送 校内通知");

            pestmt = ct.prepareStatement("select * from " + this.lableName + " order by date desc");
            ResultSet rs = pestmt.executeQuery(); // 将数据库响应的查询结果放在rs中

            while(rs.next()) {
            	//System.out.println(rs.getString(1)+",");	//标题
            	//System.out.println(rs.getString(2)+",");
            	//System.out.println(rs.getString(3)+",");
                String[] imgUrls = rs.getString(4).split(","); // 将image字符串按逗号分割,成为数组
                Information information = new Information(rs.getString(1),rs.getString(2),rs.getString(3),imgUrls);
                this.information.add(information);
            }
            jsonOutput = JSON.toJSONString(information);
            System.out.println(jsonOutput);
            return jsonOutput;
        } catch (Exception e) {
            e.printStackTrace();
            return "转换JSON出错";
        } finally {
            ct.close();
            pestmt.close();
        }
    }
}
package database;


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import model.User;

public class userDatabase {
    Connection ct = null;
    PreparedStatement pestmt = null;
    public userDatabase(){
        try{
            Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
            ct=DriverManager.getConnection("jdbc:sqlserver://localhost:1433;databaseName=schoolNews", "数据库用户名", "数据库密码");
            if(ct != null){
                System.out.println("数据库连接成功");
            }
            else{
                System.out.println("数据库连接失败");
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    // 确认登录信息
    public User checkUser(String username,String password) throws SQLException{
        try{
            System.out.println("正在确认登录信息");
            // 该处报错原因:nvarchar和ntext的类型不匹配。解决方案参考:https://blog.csdn.net/weixin_34343000/article/details/92824471
            pestmt=ct.prepareStatement("select * from [User] where convert(nvarchar(255),username)=? and convert(nvarchar(255),password)=?");
            pestmt.setString(1, username);
            pestmt.setString(2, password);
            ResultSet rs=pestmt.executeQuery(); // 将数据库响应的查询结果放在rs中
            System.out.println("数据库响应结果为:" + rs.toString());

            User user = new User();

            while(rs.next()){
                user.setUsername(rs.getString(1));//第一个属性
                user.setPassword(rs.getString(2));//第二个属性
                System.out.println("用户信息为:" + user.getUsername() + " " + user.getPassword());
                return user;	///查到就返回对象
            }
            return null;
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }finally{
            ct.close();
            pestmt.close();
        }
    }
}


4.3 前端登录页面

本模块实现登陆功能。用户输入用户名(学号)与密码,前端进行简单的表单校验后, 通过 login 接口将数据传到数据库进行账号与密码的比对,如果成功的话,将跳转到消息页 面,反之将提醒用户账号与密码错误。

  • 输入:用户名(学号)与密码
  • 输出:登陆成功与否的信息
  • 异常处理:用户名或密码为空时,前端将告知用户名或密码为空,这时数据将不被传到后端
  • 安全处理:密码传输过程采用国密SM3加密

该部分主体代码实现如下:

  login: function (e) {
    var that = this
    let password =  e.detail.value.password
    let username =  e.detail.value.username
    var unameNULL = (username.length === 0) ? true : false   // 判断用户名是否为空
    var pwdVaild = (password.length < 6) ? false : true      // 判断密码是否大于6位
    var unameSpace = username.lastIndexOf(' ')               // 判断用户名中有没有空格,值为-1时没有
    var pwdSpace = password.lastIndexOf(' ')                 // 判断密码中有没有空格,值为-1时没有
    console.log(pwdVaild,unameNULL)
    console.log(sm3('nihao'))
    // 用户名不为空以及密码大于6位且其中都没有空格时,才传递登录表单
    if(!unameNULL && pwdVaild && unameSpace === -1 && pwdSpace === -1){
      password = sm3(password)  // 上传前,先基于国密算法sm3将密码加密
      console.log(password)
      wx.request({
        url: 'http://localhost:8080/userServer/userServlet', //后端的url地址  
        // 传给后端的数据 —— 用户名与加密后的密码
        data: {
          username: username,
          password: password,
        },
        method: 'GET',
        header: {
          'content-type': 'application/json' // 默认值
        },
        // res为从后端获取的数据
        success: function (res) {
          console.log(res)
          console.log(res.statusCode === 200);
          // 只要后端返回的状态码以2开头,即请求成功的情况
          if (res.statusCode === 200) {
            // 登录成功,将跳转至首页
            if(res.data === 'success') {
              wx.showToast({
                title: '登录成功!',
                duration: 1000
              })
              wx.switchTab({
                url: '../index/index',
              })
            } 
            // 这个是登录失败的处理,界面将显示提示字段,告知用户是哪个部分出错
            else that.setData({ flag: true })
          }
          else {
            wx.showToast({
              title: '服务器异常',
            })
          }
        },
        fail: function (err) {
          wx.showToast({
            title: '网络异常!',
          })
          console.log("失败!!!!!!");
        }
      })
    } 
    // 设置标志位,不管什么时候都进行
    this.setData({
      unameNULL: unameNULL,
      pwdVaild: pwdVaild,
      unameSpace: unameSpace,
      pwdSpace: pwdSpace
    })
  },

4.4 前端信息展示模块

渲染效果上,利用组件 mp-cells & mp-cell 组件为用户带来一致的视觉体验。将事先设计一个信息展示的模板(信息详情页),以确保所有的信息都可以规范地展示出来,让用户的视觉体验更加统一。

逻辑实现上,四种类型的消息每一个都对应着一个列表。运用条件渲染语句 wx:for(类 似于 C/C++中的 for 语句)来对列表数据进行循环渲染,以实现消息的展示效果。使用搜索 时,利用条件渲染语句 wx:if 将主页面隐藏,只留下搜索部分。利用 JS 的字符串方法将搜 索内容与全局消息进行匹配,将结果放入列表当中,由条件渲染语句 wx:for 来展示。当用 户点击对应信息后,由小程序自带的函数来获取这条消息对应的列表索引。获取这条消息的 详情信息,随后携带其详情信息跳转到信息详情页。

该部分主体代码实现如下:

  <van-tab title="校内通知">
    <mp-cells>
      <mp-cell wx:for="{{noticeList}}" id="{{index}}" link hover="true" bindtap="getDetail">
        <view style="font-size:14px;margin-bottom:8px">
          <view style="margin:4px;font-weight:bolder">{{item.title}}</view>
          <text style="margin:4px">{{item.date}}</text>
        </view>
      </mp-cell>
    </mp-cells>
  </van-tab>
  <van-tab title="校内新闻">
    <mp-cells>
      <mp-cell wx:for="{{newList}}" id="{{index}}" link hover="true" bindtap="getDetail">
        <view style="font-size:14px;margin-bottom:8px">
          <view style="margin:4px;font-weight:bolder">{{item.title}}</view>
          <text style="margin:4px">{{item.date}}</text>
        </view>
      </mp-cell>
    </mp-cells>
  </van-tab>
  <van-tab title="公示公告">
    <mp-cells>
      <mp-cell wx:for="{{signiList}}" id="{{index}}" link hover="true" bindtap="getDetail">
        <view style="font-size:14px;margin-bottom:8px">
          <view style="margin:4px;font-weight:bolder">{{item.title}}</view>
          <text style="margin:4px">{{item.date}}</text>
        </view>
      </mp-cell>
    </mp-cells>
  </van-tab>
  <van-tab title="学术讲座">
    <mp-cells>
      <mp-cell wx:for="{{academicList}}" id="{{index}}" link hover="true" bindtap="getDetail">
        <view style="font-size:14px;margin-bottom:8px">
          <view style="margin:4px;font-weight:bolder">{{item.title}}</view>
          <view style="margin:4px">{{item.tutor}}</view>
          <text style="margin:4px">{{item.date}}</text>
        </view>
      </mp-cell>
    </mp-cells>
  </van-tab>
</van-tabs>

4.5 前端个人信息展示模块

本模块展示用户的个人信息。同时提供用户个人收藏消息的管理

该部分主体实现代码如下

<mp-cells>
  <mp-cell id="avatar" title="头像:">
    <image class="userinfo-avatar" src="{{avatar}}" mode="cover"></image>
  </mp-cell>
  <mp-cell id="nickname" title="昵称: ">
    <text style="margin-left:55vw">{{nickname}}</text>
  </mp-cell>
  <mp-cell id="sex" title="性别:">
    <text style="margin-left:65vw">{{sex}}</text>
  </mp-cell>
  <mp-cell title="收藏管理" hover="true" link url="../collectInfo/collectInfo"></mp-cell>
</mp-cells>

4.6 前端收藏管理模块

本模块提供收藏模块的管理功能(查看收藏的消息,或者对消息取消收藏),分为信息的展示页以及信息详情页两部分。

该部分主体实现代码如下

  getDetail: function(e){
    console.log(e)
    var index = parseInt(e.currentTarget.id)
    var tab = this.data.nowTab
    var info = this.data.collectionList[index]
    var title = info.title
    var date = info.date
    var article = encodeURIComponent(info.article)
    var image = info.image
    var like = true                           // 本部分的like肯定是true
    var id = ''
    var collectList = this.data.collectionList
    for(var i = 0;i < collectList.length; i++){
      if(title === collectList[i].title){
        id = collectList[i]._id
      }
    }
    if(tab === '学术讲座') 
      tutor = info.tutor
    wx.navigateTo({
      url: '../noticeDetail/noticeDetail?title=' + title + '&date=' + date + '&id=' + id + 
      '&article=' + article + '&tab=' + tab + '&image=' + image + '&like=' + like,
      success(res){
        console.log(res)
      },
      fail(err){
        console.log(err)
      }
    })
  },
  
  request: function(){
    var that = this
    DB.get({
      _openid: app.globalData.openid,
      success(res){
        that.setData({
          collectionList: res.data
        })
        console.log(that.data.collectionList)
      },
      fail(err){
        console.log(err)
      }
    })
  },

5 数据库与数据结构设计

本系统内使用的数据库系统为 Microsoft SQL Server 2019 数据库,数据库中包含表 User, schoolAnnouncements, schoolArticles, schoolLectures 和 schoolNews 五张表,分别用于 用户登录鉴权、公告公示、校内通知、学术讲座和校内新闻的存储。

五张表中,User 表的数据结构设计如下表所示

列名数据类型允许 Null 值
usernamenchar(10)
passwordntext

schoolAnnouncements, schoolArticles, schoolLectures 和 schoolNews 表的数据结构设计如下表所示

列名数据类型允许 Null 值
titlenvarchar(50)
datenvarchar(50)
articlentext
imagentext

数据存储设计:

  • 访问方法:程序登录数据库后,通过 SQL 语句直接取用;
  • 每次取用整组数据,对整组数据进行搜索,最终将得到的数据在程序中处理;
  • 所有数据存储在电脑硬盘中,由程序按期删除;
  • 数据库内容的保密通过连接数据库时所使用的用户名、密码所对应的权限来进行区分。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值