美文网首页PHP Dev
搭建一个新闻客户端服务器

搭建一个新闻客户端服务器

作者: 王韩峰 | 来源:发表于2017-02-09 19:40 被阅读468次

    iOS app 源码地址:https://github.com/wanghanfeng/LanYunNews
    php 后台源码地址: https://github.com/wanghanfeng/NewsServer
    目前正在完善中,在此先来记录下开发的过程

    这里主要介绍服务器的搭建,app的实现不做介绍。

    项目分析

    做一个新闻客户端的app最首先需要拿到需要展示的新闻数据,目前来说可行的方案有两种:
    1、写一个爬虫去各大门户的新闻首页上爬去新闻的标题和新闻的url,存入数据库提供给app使用。(后期实现)
    2、使用现成新闻api接口,如聚合数据。因为聚合数据的新闻接口是可以免费使用的,所以我选择使用聚合数据的新闻接口。

    接口测试与改造

    使用post方式向http://v.juhe.cn/toutiao/index 发送数据,必须带上字段type和申请的app key,以下是实例,大家可以使用我的app key,免去申请的麻烦。

    Snip20170209_2.png
    我们可以得到返回回来的json数据,data字段中是一个数组,里面就是返回的30条新闻数据。这个接口有一个缺点,就是只能获取到最新的30条数据,无法获取这之前的新闻数据,如果用户看完了这30条,就没有其他更多的数据可以浏览了。
    真正需要的api应该再增加一个字段page,用来表示我们需要获取到新闻的条目,例如每一页拥有30条数据,0页代表最新的30条,1页代表之后的30条,一次类推。
    现在最大的问题是现有的api没有这个字段,于是考虑搭建一个服务器提供一个新的api。

    mysql数据库建表

    首先分析下收到的新闻数据格式:

    {"uniquekey":"e83c8565b142a3be8897e70323f0e767",
    "title":"魅族商城遭域名劫持?高管在朋友圈呼吁:请友商理性竞争!",
    "date":"2017-02-09 17:23",
    "category":"科技",
    "author_name":"APP皮皮爱应用",
    "url":"http:\/\/mini.eastday.com\/mobile\/170209172306465.html",
    "thumbnail_pic_s":"http:\/\/04.imgmini.eastday.com\/mobile\/20170209\/20170209172306_e49c5df82cb0232c2371932a5e48169e_1_mwpm_03200403.jpeg",
    "thumbnail_pic_s02":"http:\/\/04.imgmini.eastday.com\/mobile\/20170209\/20170209172306_e49c5df82cb0232c2371932a5e48169e_2_mwpm_03200403.jpeg",
    "thumbnail_pic_s03":"http:\/\/04.imgmini.eastday.com\/mobile\/20170209\/20170209172306_e49c5df82cb0232c2371932a5e48169e_3_mwpm_03200403.jpeg"}
    

    一条最新闻最少需要9个字段才可以存储在数据库中,其意义也能很清楚的根据英文名称辨别出来,所以根据其格式建表如下,表名news:

    Snip20170209_5.png

    Tip:
    因为原api接口的特殊性,每次请求都是最新的30条数据,但是新闻数据在短时间内不可能变化的太快,所以当后台每1分钟去请求下原接口并将得到的数据插入数据库中就会出现大量的重复数据,而数据库执行distinct去重操作又是效率非常低下的于是又有如下几个方案:
    1.查看响应头中 Last-Modified 字段,检测文件在浏览器最后的修改时间,如果发现文件未被修改则不插入数据库,反之插入。(原api接口中无此字段,不采用)
    2.对收到的数据做Hash值计算,与上一次的收到的数据Hash值比较,如果不相等则将收到的数据插入数据库,相等则舍弃。(每次计算Hash值时耗时太长,比较明显,不采用)
    3.由于mysql的特点,每次插入数据时检测当前数据的主键与表中数据的主键是否重复,重复则插入失败。因为这是无法避免的耗时,所以可以利用这一点实现去重操作,根据原api返回的uniquekey作为主键,向数据库内插入数据,如果数据库内存在相同新闻那么uniquekey必然重复导致数据库插入失败,达到去重的目的。(采用)

    把uniquekey当做主键,省去了管理主键的麻烦。每条数据的标题不能为空,一旦标题为空在客户端展示是就会出现“无头新闻”用户体验不佳,所以宁肯数据插入失败也不希望它在用户的设备上显示出来。

    php后台

    项目中用到了mysql和php,使用xampp集成化工具比较方便,xampp集成了MySQL(MariaDB)、ProFTPD、Apache Web Server,一键配置比较方便。

    Tip:
    xampp支持Windows、Mac、Linux环境
    初次安装xampp时,MySQL的初始化密码为空。
    笔者建议先将项目在本机上测试后再部署的服务器上。

    这是php项目的入口文件,由于需要改写原有api,在原有的api基础上定制自己的api,所以需要模拟post请求,先向原有api去post得到结果后再进行分析返回给app端。php中没有现成的post函数,于是自己封装了个函数来模拟post请求,在php中实现post请求有很多种方法,笔者的方法是其中之一,仅用做参考。

    //index.php
    <?php
    /**
     * Created by PhpStorm.
     * User: whf
     * Date: 17/2/8
     * Time: 下午4:20
     */
    
    include 'sqlLink.php';
    
    /**
     * 发送post请求
     * @param string $url 请求地址
     * @param array $post_data post键值对数据
     * @return array 
     */
    function send_post($url, $post_data) {
    
        $postdata = http_build_query($post_data);
        $options = array(
            'http' => array(
                'method' => 'POST',
                'header' => 'Content-type:application/x-www-form-urlencoded',
                'content' => $postdata,
                'timeout' => 0.5 * 60 // 超时时间(单位:s)
            )
        );
        $context = stream_context_create($options);
        $body = file_get_contents($url, false, $context);
        //获取响应头
        $header = $http_response_header;
    
        $result = [
            'header' => $header,
            'body' => $body
        ];
        return $result;
    }
    
    $post_data = array(
        'type' => 'keji',
        'key' => '9b36e7f06020e794a3547c5718bc114c'
    );
    $res = send_post('https://v.juhe.cn/toutiao/index', $post_data)['body'];
    
    //打印响应头
    //foreach ($res as $key => $value){
    //    echo $value .'<br/>';
    //}
    
    $arr = json_decode($res,true);
    echo json_encode($arr);
    //把数据写入数据库保存
    foreach ($arr['result']['data'] as $v){
        writeTable('news',$v);
    }
    
    function show($v){
        if (is_array($v)){
            foreach ($v as $key => $value){
                if (is_array($value)){
                    show($value);
                }
                else{
                    echo $key . ' : ' . $value .'<br/>';
                }
            }
        }
    }
    //show($arr['result']['data']);
    

    在获取到数据后直接将数据写入数据库,由数据库来判断当前插入的这一条是否与数据库中其他条目重复。

    send_post($url, $post_data)方法需要传入url和post的body($post_data),返回一个数组,可以使用array['header']来获取响应头,array['body']获取响应体。当然这个函数封装的还是不够完美,缺少请求头参数的传入,由于原有api不需要传入请求头,所以没有实现,现在将send_post函数改造如下,以便达到“完美”:

    /**
     * 发送post请求
     * @param string $url 请求地址
     * @param array $post_data post键值对数据
     * @param array $header_arr post请求头
     * @return array
     */
    function send_post($url,$post_data, $header_arr=null) {
    
        $postdata = http_build_query($post_data);
        $header = '';
        if ($header_arr){
            foreach ($header_arr as $key => $value){
                $header = $header . $key . ':' .$value . '\n';
            }
        }
        
        if (strlen($header)!=0){
            $header = substr($header,0,strlen($header)-1);
        }
        else{
            $header = 'Content-type:application/x-www-form-urlencoded';
        }
        $options = array(
            'http' => array(
                'method' => 'POST',
                'header' => $header,
                'content' => $postdata,
                'timeout' => 0.5 * 60 // 超时时间(单位:s)
            )
        );
        $context = stream_context_create($options);
        $body = file_get_contents($url, false, $context);
        //获取响应头
        $header = $http_response_header;
    
        $result = [
            'header' => $header,
            'body' => $body
        ];
        return $result;
    }
    

    在sqlLink.php文件中,主要是封装了对数据库的操作,由index.php来调用:

    <?php
    /**
     * Created by PhpStorm.
     * User: whf
     * Date: 17/2/8
     * Time: 下午5:46
     */
    
    $conn = mysqli_connect('localhost','root','','news_server');
    mysqli_set_charset($conn,'utf8');
    
    function writeTable($tableName,$item){
        global $conn;
        
        $value = '';
        foreach ($item as $v){
            $value = $value . '"' . $v . '"' . ',';
        }
        //处理当长度不匹配时插入数据库报错
        if (count($item)==7){
            $value = $value . "null" . ',' . "null" . ',';
        }
        if (count($item)==8){
            $value = $value . "null" . ',';
        }
        if (strlen($value)!=0){
            $value = substr($value,0,strlen($value)-1);
        }
        $sql = "insert into $tableName VALUES ($value)";
    //    echo '数据库写入成功!' . $sql . '<br/>';
        if (!mysqli_query($conn,$sql)){
    //        echo '数据库写入失败!' . $sql .'<br/>';
        }
    }
    
    //function readTable($tableName,$)
    

    由于项目实现的功能比较简单,所以采用面向过程编程比较简单。

    项目部署到服务器

    在项目已经测试过之后,便要将其部署到云端服务器上。(笔者的服务器选用的是阿里云的linux系统。)
    使用下面的命令:

    ssh root@42.96.203.33
    

    之后输入密码
    xampp在linux上的安装就不过多介绍了,官网上有安装介绍。
    接下来进入xampp的安装目录

    cd /opt/lampp/htdocs
    

    htdocs文件夹是xampp默认的项目目录,将项目部署在这里,之后就可以通过http://serverName/porjName 来访问项目了,如果想直接访问域名就能访问到项目,就将htdoc下的index.php文件替换成项目的index.php文件(著名php框架CodeIgniter就是这么做的)。

    接下来配置mysql:
    xampp自带mysql,但是自带的mysql并不是全局的,如果想将mysql注册成为全局的环境变量,在用户目录下的.bash_profile文件中增加变量。这里笔者先不设置,切换到mysql的目录下:

    cd /opt/lampp/bin
    

    输入命令 启动mysql(由于xampp自带的mysql没有启用密码,所以可以直接进入)

    ./mysql 
    

    接下来先建立一个数据库:

    create database news_server character set utf8;
    

    选中数据库:

    use news_server;
    

    导入.sql文件,MySQL会自动创建表

    /*
     Navicat Premium Data Transfer
    
     Source Server         : MySQL_Navicat
     Source Server Type    : MySQL
     Source Server Version : 50505
     Source Host           : localhost
     Source Database       : news_server
    
     Target Server Type    : MySQL
     Target Server Version : 50505
     File Encoding         : utf-8
    
     Date: 02/08/2017 19:17:49 PM
    */
    
    SET NAMES utf8;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    --  Table structure for `news`
    -- ----------------------------
    DROP TABLE IF EXISTS `news`;
    CREATE TABLE `news` (
      `uniquekey` varchar(32) NOT NULL,
      `title` varchar(100) NOT NULL,
      `date` varchar(16) DEFAULT NULL,
      `category` varchar(20) DEFAULT NULL,
      `author_name` varchar(20) DEFAULT NULL,
      `url` varchar(256) DEFAULT NULL,
      `thumbnail_pic_s` varchar(256) DEFAULT NULL,
      `thumbnail_pic_s02` varchar(256) DEFAULT NULL,
      `thumbnail_pic_s03` varchar(256) DEFAULT NULL,
      PRIMARY KEY (`uniquekey`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    SET FOREIGN_KEY_CHECKS = 1;
    
    

    这个新闻客户端服务器就已经可以使用了,新的api就是http://serverName/porjName ,可以向这个url上post数据,具体需要实现什么样的功能和参数,可以自己更改php代码来实现。

    现在新闻客户端服务器还不完善,我正在努力完善中,之后再来更新文章。欢迎各位志同道合同志与我共同交流切磋,高手绕行 :)❤

    相关文章

      网友评论

        本文标题:搭建一个新闻客户端服务器

        本文链接:https://www.haomeiwen.com/subject/tdhwittx.html