本例结合我的前两篇博客,使用jquery moblie框架搭建了一个简单的手机版博客园;源码已放在github上了,地址https://github.com/guasses/moblie-blog-garden,如果有更新的话,会第一时间提交到github。

声明

本例仅供学习交流使用,不参与任何商业活动。

前言

本例结合我的前两篇博客 使用node.js爬取博客园首页的博客 和 使用原生node.js搭建HTTP服务器,支持MP4视频、图片传输,支持下载rar文件,使用jquery moblie框架搭建了一个简单的手机版博客园。


jquery 移动端ui 库 mobile jquery_数据

jquery 移动端ui 库 mobile jquery_jQuery_02

服务器端输出日志如下:

jquery 移动端ui 库 mobile jquery_jquery_03

源码我放在github上了,地址https://github.com/guasses/moblie-blog-garden,如果有更新的话,我会第一时间提交到github。

使用node.js爬取博客园首页的博客中讲到如何爬取博客园首页中的博客并写入到文件中,而这次不直接写入文件,本来我是打算写入到数据库中,然后增量爬取数据,然后我搞不太懂怎么增量爬,就想定时全量爬。但是定时全量取博客园又有一个问题,如果定时爬取的间隔时间太长,此应用不能及时得到更新;如果定时爬取时间间隔太短,又浪费大量的服务器资源,包括我这边的服务器和博客园的服务器,“双赢?”。不会增量爬,全量爬又不完美,然后我就想如果客户端请求哪一页服务器就现爬哪一页的数据,然后发送的客户端,这样好不好呢?说做就做。

目录结构

moblie-blog-garden
    ├── css
    │   ├── index.css
    │   └── jquery.moblie-1.4.5.min.css
    ├── get_data.js
    ├── index.html
    ├── js
    │   ├── index.js
    │   ├── jquery.min.js
    │   └── jquery.moblie-1.4.5.min.js
    ├── router.js
    ├── server.js
    └── txt
        └── main_blog_list.txt

index.html

首先在网页中添加 jQuery Mobile,建议在</head>前添加css样式,在</body>前先引入jQuery库,然后引入jQuery Moblie库。

<!-- meta使用viewport以确保页面可自由缩放 -->
<meta name="viewport" content="width=device-width, initial-scale=1">

<!-- 引入 jQuery Mobile 样式 -->
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css">

<!-- 引入 jQuery 库 -->
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>

<!-- 引入 jQuery Mobile 库 -->
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>

index.html页面很简单,仅有一个fixed固定的头和尾,页尾部分显示翻页按钮和当前页数;内容部分使用ajax加载数据。

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>博客园</title>
    <link rel="stylesheet" href="css/jquery.mobile-1.4.5.min.css">
    <link rel="stylesheet" href="css/index.css">
</head>
<body>
    <div data-role="page" id="home">
        <div data-role="header" data-position="fixed">
            <h1>博客园</h1>
        </div>
        <div data-role="content">
            <ul data-role="listview" data-inset="true" id='content'>
               <!--插入页面数据-->
            </ul>
        </div> 
        <div data-role="footer" data-position="fixed">
            <div data-role="navbar">
                <ul>
                    <li><button data-role="button" class="button" id="pre">上一页</button></li>
                    <li><button data-role="button" class="button" id="page">第1页</button></li>
                    <li><button data-role="button" class="button" id="next">下一页</button></li>
                </ul> 
            </div>
        </div>
    </div>
    <script src="js/jquery.min.js"></script>
    <script src="js/jquery.mobile-1.4.5.min.js"></script>
    <script src="js/index.js"></script>
</body>
</html>

index.css中设置样式加不加的也无所谓了,所以这里就不贴出它的代码了。

index.js

index.js中添加了一个getData的函数,用来发送post请求,服务器根据请求的页数返回相应的数据,客户端解析数据,拼接html字符串并添加html代码到DOM文档中,最后修改页脚的页数。使用jquery moblie事件方法监听上一页和下一页按钮的触控(点击)事件,递增或递减页数并调用getData函数获取数据,因为总共就1-200页数据,所以不能超过200页。

$(function(){                     //当文档加载完成时调用,jquery方法
    let page = 1;
    getData(page);
    $("#next").on("tap",function(){//下一页按钮点击事件
        ++page;
        if(page < 201){            
            getData(page);         //ajax获取数据
        }else{              
            page = 1;              //如果页数大于200页就跳转到第一页
            getData(page);         //因为博客园翻页就200页,再多就获取不到了
        }
    });
    $('#pre').on("tap",function(){//上一页按钮点击事件
        --page;
        if(page < 1){               //页数不能小于1页
            page = 200;
            getData(page);
        }else{
            getData(page);
        }
    })
});
/**
 * @description desc 根据传入的第几页发送获取第几页的数据到服务器,然后加载数据
 * @param page  int 第几页
 */
function getData(page){
    //请求特定的文件并发送请求的页数
    $.post("txt/main_blog_list.txt",{id:page},function(data,status){
        if(data){
            data = JSON.parse(data);  //服务器返回的数据为字符串,我们把它转换为JSON数组
            let html = ""             //每次请求后都清空html字符串
            for(let i=0;i<data.length;i++){//因为是一个JSON数组,需要遍历它
                html+=`<li data-role="list-divider" class="ui-li-divider ui-bar-inherit ui-li-has-count ui-first-child">
                ${data[i]['time']}<span class="ui-li-count ui-body-inherit">${data[i]['view']}${data[i]['comment']}</span></li>
                <li>
                    <a href="${data[i]['link']}" class="ui-btn ui-btn-icon-right ui-icon-carat-r">
                        <h2>${data[i]['title']}</h2>
                        <p>${data[i]['summary']}</p>
                        <p class="ui-li-aside">${data[i]['author']}</p>
                    </a>    
                </li>`;                  //这里使用模板字符串,不懂的可以百度一下
                $('#content').html(html);//添加html到DOM
            }
            $("#page").text(`第${page}页`);//修改页脚页数
        }
    });
}

使用原生node.js搭建HTTP服务器,支持MP4视频、图片传输,支持下载rar文件中提到如何搭建服务器,我这里就贴出改动的代码,就不全部贴出来了。

server.js

首先需要额外引入两个模块。

//querystring 模块提供用于解析和格式化 URL 查询字符串的实用工具
var querystring = require('querystring');
//自定义模块,下面会提到
var getData = require('./get_data.js');

http.createServer函数中的if语句后面添加else if语句,接收客户端发送过来的数据,并使用querystring模块的parse函数将字符串解析为键值对,例:id=1就解析为{id:1};然后调用get_data.js文件中的getData函数,getData函数使用回调函数返回爬取到的数据,然后发送数据到客户端。

if(pathname == "/"){
  pathname = "/index.html";
}else if(pathname == "/txt/main_blog_list.txt"){
  let post = "";
  request.on("data",function(chunk){
    post += chunk;
   });
   request.on("end",function(){
     //将查询字符串str解析为键值对的集合
     post = querystring.parse(post);
      //调用get_data.js中getData函数
      getData(post['id'],function(data){
        response.write(JSON.stringify(data));
        response.end();
      });
  });
}

get_data.js

使用node.js爬取博客园首页的博客中说到如何爬取博客园200页的数据,这里把该博客中的index.js文件改名为get_data.js文件,并把两个函数合二为一,直接使用回调函数返回数据。

//引入第三方模块
const superagent = require('superagent');
const cheerio = require('cheerio');

/**
 * @description desc 获取页面数据并写入到文件
 * @param i desc 页数
 * @return array 返回博客数组列表
 */
function getData(i,callback){
    superagent.post("")
        .send({CategoryId: 808,     //发送数据到服务器
            CategoryType: "SiteHome",
            ItemListActionName: "PostList",
            PageIndex: i,           //根据i获取相应的页面数据
            ParentCategoryId: 0,
            TotalPostCount: 4000})
        .end((error,response)=>{    //页面访问失败返回error,response为服务器返回的数据
            if(error){
                console.log(`访问第${i}页失败!`+error);
                return false;
            }else{
                console.log(`开始解析第${i}页数据`);
                //解析DOM
                let blogList = [];        //新建空列表,用来放一个页面的博客
                let $ = cheerio.load(response.text);
                //获取所有class="post_item_body"的元素,然后遍历每一个元素
                $('.post_item>.post_item_body').each((index,element)=>{
                    //用键值对存放数据
                    let blog = {
                        //trim()方法用于删除字符串的头尾空格
                        link:$(element).find('.titlelnk').attr('href').trim(),
                        title:$(element).find('.titlelnk').text().trim(),
                        summary:$(element).find('.post_item_summary').text().trim(),
                        author:$(element).find('.lightblue').text(),
                        time:$(element).find('.post_item_foot').clone().children().remove().end().text().trim(),
                        comment:$(element).find('.article_comment>.gray').text().trim(),
                        view:$(element).find('.article_view>.gray').text().trim()
                    }
                    //添加到数组尾部
                    blogList.push(blog);
                });
                callback(blogList);//回调函数
            }
        });
}
module.exports = getData; //暴露getData函数

router.js中的内容没有变过,可以在上一篇博客中找到。至此,一个简单的手机版博客园就完成了,以后还会添加分类功能以及更多功能,大家有什么建议尽管提出。