scrapy爬虫++mysql数据库+tomcat服务器+android客户端项目demo

之前scrapy框架爬取豆瓣电影top250信息爬虫数据存储到mysql数据库写了如何构建一个简单的scrapy项目以及如何将爬取到的数据存储到数据库中,接下来记录一下tomcat服务器端通过j2ee项目实现jdbc(数据库连接)访问这些数据以及通过json实现服务端与android客户端的数据交流。
运行环境

 1. win7-64bit
 2. Apache Tomcat/8.5.12
 3. mysql 5.7.17

创建服务端项目

使用eclipse新建Dynamic web project项目,命名为doubanApp
这里写图片描述

首先整理一下思路:我们需要从数据库中读取关于电影的各种信息,之后将信息存放到对应的对象变量中,将对象变量转化为json格式然后与客户端进行通信。

那么我们共需要四部分:

 1. 数据库部分:数据库的连接,增删查改。
 2. 对象变量:根据从数据库取得的信息初始化变量,以便转化为json通信。
 3. 对象变量的服务:调用编写的数据库增删查改接口,取得对象变量以及转化为json格式。
 4. servlet:客户端访问部分。

根据这几部分创建不同的包,结果如下图
这里写图片描述

服务端

首先编写需要帮助我们处理数据的对象类,在model包中创建java类Movie,其具有的属性与数据库中属性一致,分别为

    private String name;
    private String info;
    private double rating;
    private int num;
    private String quote;
    private String img_url;

对各个属性编写setter与getter,然后重载toString()方法

    @Override
    public String toString()
    {
        return "{\"name\":\"" + this.name + "\", \"info\":\"" + this.info
                + "\", \"rating\":\""+ this.rating + "\", \"num\":\"" + this.num
                + "\", \"quote\":\""+ this.quote + "\", \"img_url\":\""
                + this.img_url + "\"}";
    }

使其能够方便的转化为json格式。

接下来编写数据库部分。
首先配置数据源,在对于项目目录:D:\jee-neon\workstation\doubanApp\WebContent\META-INF中找到context.xml文件(如果没有自行创建即可),修改里面内容,如下

这里写图片描述

之后在database包中创建文件InitDatabase.java文件,用于初始化数据库连接。在构造方法中获得数据源,之后编写getConnection()方法及各种 close 方法。

    package database;

    import java.sql.*;
    import javax.naming.*;
    import javax.sql.*;

    public class InitDatabase {

        private DataSource ds = null;

        //初始化数据源
        public InitDatabase() throws Exception
        {
            Context ctx = new InitialContext();
            ds = (DataSource)ctx.lookup("java:comp/env/jdbc/douban");
        }

        //获得与数据库的连接
        public Connection getConnection()throws Exception
        {
            return ds.getConnection();
        }

        //关闭连接
        public void closeConnection(Connection con)
        {
            try
            {
                if(con!=null)
                {
                    con.close();
                }
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }

        //关闭准备语句
        public void closePrepStmt(PreparedStatement prepStmt)
        {
            try
            {
                if(prepStmt!=null)
                {
                    prepStmt.close();
                }
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }

        //关闭查询结果集
        public void closeResultSet(ResultSet rs)
        {
            try
            {
                if(rs!=null)
                {
                    rs.close();
                }
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }

    }

其中PreparedStatement用于准备待执行的sql语句,ResultSet用于存放查询结果。

继续在database包中创建文件MovieDB.java,用于实现增删查改。
首先我们想到在客户端我们会通过电影名查询获得电影的全部信息,以及将250条电影信息分页显示等,暂时先编写这两个方法。
方法编写大同小异,首先初始化连接,执行sql语句,将返回结果存在对象中后返回给调用者。

package database;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import model.Movie;

public class MovieDB extends InitDatabase{

    public MovieDB() throws Exception
    {
        super();
    }

    //根据电影名获得电影信息
    public Movie getMovie(String name)
    {
        Connection con = null;
        PreparedStatement prepStmt = null;
        ResultSet rs = null;
        Movie movie = null;
        try
        {
            //初始化连接
            con = getConnection();

            //构造sql语句
            String query = "select * from doubanmovie where name = ?";
            prepStmt = con.prepareStatement(query);
            prepStmt.setString(1, name);

            //执行查询
            rs = prepStmt.executeQuery();

            //将查询结果存储在对象中
            while(rs.next())
            {
                movie = new Movie(rs.getString(1),
                        rs.getString(2),
                        rs.getDouble(3),
                        rs.getInt(4),
                        rs.getString(5),
                        rs.getString(6));
            }
        }

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

        finally
        {
            closeConnection(con);
            closePrepStmt(prepStmt);
            closeResultSet(rs);
        }

        return movie;
    }

    //获得某一页电影信息
    public List<Movie> getMovies(int page)
    {
        Connection con = null;
        PreparedStatement prepStmt = null;
        ResultSet rs = null;
        Movie movie = null;
        ArrayList<Movie> movies = new ArrayList<>();
        try
        {
            //初始化连接
            con = getConnection();

            //构造sql语句
            int lastPage = page-1;
            String query = "select * from doubanmovie limit ?, 10";
            prepStmt = con.prepareStatement(query);
            prepStmt.setInt(2, page*10);

            //执行查询
            rs = prepStmt.executeQuery();
            while(rs.next())
            {   
                //将查询结果存储在对象中
                movie = new Movie(rs.getString(1),
                        rs.getString(2),
                        rs.getDouble(3),
                        rs.getInt(4),
                        rs.getString(5),
                        rs.getString(6));
                movies.add(movie);
            }
        }

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

        finally
        {
            closeConnection(con);
            closePrepStmt(prepStmt);
            closeResultSet(rs);
        }

        return movies;
    }
}

实现了从数据库取得数据后,我们还有编写服务方法来由servlet调用访问数据库,在service包中创建文件JsonService.java,编写方法getMovieString()getMovieArray()分别取得对应电影信息。

package service;

import java.awt.List;
import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONObject;

import database.MovieDB;
import model.Movie;

public class JsonService {

    //由电影名获得电影描述字符串
    public String getMovieString(String name) throws Exception
    {
        //从数据库中取得电影
        Movie movie = new MovieDB().getMovie(name);

        //转化为json格式
        JSONObject jsonObject = new JSONObject();
        jsonObject.put(name, movie);
        return jsonObject.toString();
    }

    //获得某一页电影信息字符串
    public String getMovieArray(int page) throws Exception
    {
        //从数据库中取得电影
        ArrayList<Movie> movies = (ArrayList<Movie>) new MovieDB().getMovies(page);
        JSONArray jsonMovies = new JSONArray();

        //转化为json格式
        for( int i = 0 ; i < movies.size() ; ++i)
        {
            JSONObject jsonMovie = new JSONObject(movies.get(i).toString());
            jsonMovies.put(i+1, jsonMovie);
        }
        return jsonMovies.toString();
    }
}

最后编写servlet,在servlet包中创建文件JsonServlet.java,类JsonServlet继承自HttpServlet,同时重载方法doGet(HttpServletRequest request, HttpServletResponse response)doPost(HttpServletRequest request, HttpServletResponse response)这里使GET与POST方法执行相同的操作。首先规定编码方式,在http请求中获得请求参数,根据参数执行不同操作,然后将结果返回给请求者。

package servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import service.JsonService;

public class JsonServlet extends HttpServlet{

    public JsonServlet()
    {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        //设置文档格式及编码方式
        response.setContentType("text/html;charset=utf-8");
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");

        //获得输出文档writer
        PrintWriter out = response.getWriter();

        //获得请求参数
        String name = request.getParameter("name");
        int pageNum = Integer.parseInt(request.getParameter("page"));

        //没有参数时打印对应错误
        String movieString = "Error";
        String movieArray = "Error2";
        try 
        {
            //这里规定一次访问只能有一个参数
            if(name == null)
            {
                //获取某一页电影信息
                movieArray = new JsonService().getMovieArray(pageNum);
                out.println(movieArray);
            }
            else
            {
                //获取某一个电影信息
                movieString = new JsonService().getMovieString(name);
                out.println(movieString);
            }
        }

        catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        //写出缓冲区
        out.flush();
        out.close();
    }
}

最后,在目录:D:\jee-neon\workstation\doubanApp\WebContent\WEB-INF下编辑web.xml文件(如果没有自行创建即可),在<web-app>元素内添加访问url
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/douban</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<servlet>
<servlet-name>JsonServlet</servlet-name>
<servlet-class>servlet.JsonServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>JsonServlet</servlet-name>
<url-pattern>/JsonServlet</url-pattern>
</servlet-mapping>

到此,服务端代码就以及编写完毕。

客户端

客户端重要说明如何实现网络访问以及json解析。
首先创建项目后,在MainActivity中初始化变量private String baseURL = "http://localhost:8080/doubanApp/JsonServlet";

访问网络的步骤为首先根据url开启网络连接,设置网络访问方法,超时时间等属性,然后接受从服务端返回的内容,将其进行处理。android规定,在主线程中不能进行网络访问,所以我们在主线程中只做一些ui的更改,费时的网络操作开启子线程来完成。

public void sendRequest()
    {
        //开启线程访问网络
        new Thread(new Runnable() {
            @Override
            public void run() {

                HttpURLConnection connection = null;
                BufferedReader reader = null;
                try
                {
                    //获得访问url
                    URL url = new URL(baseURL + "?page=" + pageNum);

                    //开启网络连接
                    connection = (HttpURLConnection)url.openConnection();

                    //设置请求方法
                    connection.setRequestMethod("GET");

                    //设置超时时间
                    connection.setConnectTimeout(8000);
                    connection.setReadTimeout(8000);

                    //读入服务端返回内容
                    InputStream in = connection.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(in));
                    StringBuilder response = new StringBuilder();
                    String line;
                    while((line = reader.readLine()) != null)
                    {
                        response.append(line);
                    }

                    //解析json数据
                    parseJson(response.toString());
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
                finally
                {
                    if(reader != null)
                    {
                        try
                        {
                            reader.close();
                        }
                        catch (Exception e)
                        {
                            e.printStackTrace();
                        }
                    }
                    if(connection != null)
                    {
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }

解析Json数据也比较简单,这里解析从服务端获得的一页电影字符串信息。

    private void parseJson(final String response)
    {
        Movie movie;
        try
        {
            //从服务端返回字符串初始化JSONArray
            JSONArray movies = new JSONArray(response);
            //movieList.clear();

            //从JSONArray中取得JSONObject初始化movie对象
            for( int i = 0 ; i < movies.length()-1 ; ++i)
            {
                JSONObject movieJson = movies.getJSONObject(i+1);
                movie = new Movie(movieJson.getString("name"), movieJson.getString("info"),
                        movieJson.getDouble("rating"), movieJson.getInt("num"),
                        movieJson.getString("quote"), movieJson.getString("img_url"));
                //Log.w(TAG, movie.getName());
                //movieList.add(movie);
            }
            showResponse();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

这里我将获得的movie对象添加到movieList这样一个列表中,然后在showResponse()方法中进行处理。
这里我使用了ListView来显示信息,然后对每一项电影名称编写了点击事件
这里写图片描述
这里写图片描述

总结

通过这么一个小demo尝试了一下简单的网络项目,学习了爬虫,数据库的使用,服务端客户端网络通信,客户端简单界面编写及信息显示,有这么一个小项目过程作为参考,相信之后在写其他项目时逻辑步骤应该会更加清晰吧

猜你喜欢

转载自blog.csdn.net/ljm_9615/article/details/77000465