整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

基于 Serverless 快速实现简单版查询工具


友的单位有一个小型的图书室,图书室中摆放了很多的书,每本书都被编号放在对应的区域,为了让大家更快、更容易找到这些书,他联系我,让我帮他弄一个图书查询系统,通过用户输入能模糊匹配到对应的结果,并且提供书籍对应的地点。

功能设计

  • 让朋友把书籍整理并存储到一个 Excel 表格中;
  • 将 Excel 表放到对象存储中,云函数读取这个文件并解析;
  • 根据词语的相似寻找相似的图书;
  • 前端页面通过 MUI 制作,放在对象存储中,并且使用对象存储的 Website 功能;

整体实现

数据形态

Excel 样式主要包括书名和编号,同时下面包括分类的 tab:

基于函数的搜索功能

核心代码实现:

复制代码已复制

import jiebaimport openpyxlfrom gensim import corpora, models, similaritiesfrom collections import defaultdictimport urllib.request with open("/tmp/book.xlsx", "wb") as f:    f.write(        urllib.request.urlopen("https://********").read()    )  top_str = "abcdefghijklmn"book_dict = {}book_list = []wb = openpyxl.load_workbook('/tmp/book.xlsx')sheets = wb.sheetnamesfor eve_sheet in sheets:    print(eve_sheet)    sheet = wb.get_sheet_by_name(eve_sheet)    this_book_name_index = None    this_book_number_index = None    for eve_header in top_str:        if sheet[eve_header][0].value == " 书名 ":            this_book_name_index = eve_header        if sheet[eve_header][0].value == " 编号 ":            this_book_number_index = eve_header    print(this_book_name_index, this_book_number_index)    if this_book_name_index and this_book_number_index:        this_book_list_len = len(sheet[this_book_name_index])        for i in range(1, this_book_list_len):            add_key = "%s_%s_%s" % (                sheet[this_book_name_index][i].value, eve_sheet, sheet[this_book_number_index][i].value)            add_value = {                "category": eve_sheet,                "name": sheet[this_book_name_index][i].value,                "number": sheet[this_book_number_index][i].value            }            book_dict[add_key] = add_value            book_list.append(add_key)  def getBookList(book, book_list):    documents = []    for eve_sentence in book_list:        tempData = " ".join(jieba.cut(eve_sentence))        documents.append(tempData)    texts = [[word for word in document.split()] for document in documents]    frequency = defaultdict(int)    for text in texts:        for word in text:            frequency[word] += 1    dictionary = corpora.Dictionary(texts)    new_xs = dictionary.doc2bow(jieba.cut(book))    corpus = [dictionary.doc2bow(text) for text in texts]    tfidf = models.TfidfModel(corpus)    featurenum = len(dictionary.token2id.keys())    sim = similarities.SparseMatrixSimilarity(        tfidf[corpus],        num_features=featurenum    )[tfidf[new_xs]]    book_result_list = [(sim[i], book_list[i]) for i in range(0, len(book_list))]    book_result_list.sort(key=lambda x: x[0], reverse=True)    result = []    for eve in book_result_list:        if eve[0] >= 0.25:            result.append(eve)    return result  def main_handler(event, context):    try:        print(event)        name = event["body"]        print(name)        base_html = '''<div class='mui-card'><div class='mui-card-header'>{{book_name}}</div><div class='mui-card-content'><div class='mui-card-content-inner'> 分类:{{book_category}}<br> 编号:{{book_number}}</div></div></div>'''        result_str = ""        for eve_book in getBookList(name, book_list):            book_infor = book_dict[eve_book[1]]            result_str = result_str + base_html.replace("{{book_name}}", book_infor['name']) \                .replace("{{book_category}}", book_infor['category']) \                .replace("{{book_number}}", book_infor['number'] if book_infor['number'] else "")        if result_str:            return result_str    except Exception as e:        print(e)    return '''<div class='mui-card' style='margin-top: 25px'><div class='mui-card-content'><div class='mui-card-content-inner'> 未找到图书信息,请您重新搜索。</div></div></div>'''    

同时配置 APIGW:

功能页面

复制代码已复制

<!DOCTYPE html><html><head>    <meta charset="utf-8">    <title> 图书检索系统 </title>    <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">    <meta name="apple-mobile-web-app-capable" content="yes">    <meta name="apple-mobile-web-app-status-bar-style" content="black">     <link rel="stylesheet" href="https://others-1256773370.cos.ap-chengdu.myqcloud.com/booksearch/css/mui.min.css">    <style>        html,        body {            background-color: #efeff4;        }    </style>    <script>        function getResult() {            var UTFTranslate = {                Change: function (pValue) {                    return pValue.replace(/[^\u0000-\u00FF]/g, function (><!DOCTYPE html><html><head>    <meta charset="utf-8">    <title> 图书检索系统 </title>    <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">    <meta name="apple-mobile-web-app-capable" content="yes">    <meta name="apple-mobile-web-app-status-bar-style" content="black">     <link rel="stylesheet" href="https://others-1256773370.cos.ap-chengdu.myqcloud.com/booksearch/css/mui.min.css">    <style>        html,        body {            background-color: #efeff4;        }    </style>    <script>        function getResult() {            var UTFTranslate = {                Change: function (pValue) {                    return pValue.replace(/[^\u0000-\u00FF]/g, function ($0) {                        return escape($0).replace(/(%u)(\w{4})/gi, "&#x$2;")                    });                },                ReChange: function (pValue) {                    return unescape(pValue.replace(/&#x/g, '%u').replace(/\\u/g, '%u').replace(/;/g, ''));                }            };             var xmlhttp;            if (window.XMLHttpRequest) {                // IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码                xmlhttp = new XMLHttpRequest();            } else {                // IE6, IE5 浏览器执行代码                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");            }            xmlhttp.onreadystatechange = function () {                if (xmlhttp.readyState == 4 && xmlhttp.status == 200 && xmlhttp.responseText) {                    document.getElementById("result").innerHTML = UTFTranslate.ReChange(xmlhttp.responseText).slice(1, -1).replace("\"",'"');                }            }            xmlhttp.open("POST", "https://********", true);            xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");            xmlhttp.send(document.getElementById("book").value);        }    </script></head><body><div class="mui-content" style="margin-top: 50px">    <h3 style="text-align: center"> 图书检索系统 </h3>    <div class="mui-content-padded" style="margin: 10px; margin-top: 20px">        <div class="mui-input-row mui-search">            <input type="search" class="mui-input-clear" placeholder=" 请输入图书名 " id="book">        </div>        <div class="mui-button-row">            <button type="button" class="mui-btn mui-btn-numbox-plus" style="width: 100%" onclick="getResult()"> 检索            </button>          </div>    </div>    <div id="result">        <div class="mui-card" style="margin-top: 25px">            <div class="mui-card-content">                <div class="mui-card-content-inner">                    可以在搜索框内输入书籍的全称,或者书籍的简称,系统支持智能检索功能。                </div>            </div>        </div>    </div></div><script src="https://others-1256773370.cos.ap-chengdu.myqcloud.com/booksearch/js/mui.min.js"></script></body></html><) {                        return escape(><!DOCTYPE html><html><head>    <meta charset="utf-8">    <title> 图书检索系统 </title>    <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">    <meta name="apple-mobile-web-app-capable" content="yes">    <meta name="apple-mobile-web-app-status-bar-style" content="black">     <link rel="stylesheet" href="https://others-1256773370.cos.ap-chengdu.myqcloud.com/booksearch/css/mui.min.css">    <style>        html,        body {            background-color: #efeff4;        }    </style>    <script>        function getResult() {            var UTFTranslate = {                Change: function (pValue) {                    return pValue.replace(/[^\u0000-\u00FF]/g, function ($0) {                        return escape($0).replace(/(%u)(\w{4})/gi, "&#x$2;")                    });                },                ReChange: function (pValue) {                    return unescape(pValue.replace(/&#x/g, '%u').replace(/\\u/g, '%u').replace(/;/g, ''));                }            };             var xmlhttp;            if (window.XMLHttpRequest) {                // IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码                xmlhttp = new XMLHttpRequest();            } else {                // IE6, IE5 浏览器执行代码                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");            }            xmlhttp.onreadystatechange = function () {                if (xmlhttp.readyState == 4 && xmlhttp.status == 200 && xmlhttp.responseText) {                    document.getElementById("result").innerHTML = UTFTranslate.ReChange(xmlhttp.responseText).slice(1, -1).replace("\"",'"');                }            }            xmlhttp.open("POST", "https://********", true);            xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");            xmlhttp.send(document.getElementById("book").value);        }    </script></head><body><div class="mui-content" style="margin-top: 50px">    <h3 style="text-align: center"> 图书检索系统 </h3>    <div class="mui-content-padded" style="margin: 10px; margin-top: 20px">        <div class="mui-input-row mui-search">            <input type="search" class="mui-input-clear" placeholder=" 请输入图书名 " id="book">        </div>        <div class="mui-button-row">            <button type="button" class="mui-btn mui-btn-numbox-plus" style="width: 100%" onclick="getResult()"> 检索            </button>          </div>    </div>    <div id="result">        <div class="mui-card" style="margin-top: 25px">            <div class="mui-card-content">                <div class="mui-card-content-inner">                    可以在搜索框内输入书籍的全称,或者书籍的简称,系统支持智能检索功能。                </div>            </div>        </div>    </div></div><script src="https://others-1256773370.cos.ap-chengdu.myqcloud.com/booksearch/js/mui.min.js"></script></body></html><).replace(/(%u)(\w{4})/gi, "&#x;")                    });                },                ReChange: function (pValue) {                    return unescape(pValue.replace(/&#x/g, '%u').replace(/\u/g, '%u').replace(/;/g, ''));                }            };             var xmlhttp;            if (window.XMLHttpRequest) {                // IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码                xmlhttp = new XMLHttpRequest();            } else {                // IE6, IE5 浏览器执行代码                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");            }            xmlhttp.onreadystatechange = function () {                if (xmlhttp.readyState == 4 && xmlhttp.status == 200 && xmlhttp.responseText) {                    document.getElementById("result").innerHTML = UTFTranslate.ReChange(xmlhttp.responseText).slice(1, -1).replace("\"",'"');                }            }            xmlhttp.open("POST", "https://********", true);            xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");            xmlhttp.send(document.getElementById("book").value);        }    </script></head><body><div class="mui-content" style="margin-top: 50px">    <h3 style="text-align: center"> 图书检索系统 </h3>    <div class="mui-content-padded" style="margin: 10px; margin-top: 20px">        <div class="mui-input-row mui-search">            <input type="search" class="mui-input-clear" placeholder=" 请输入图书名 " id="book">        </div>        <div class="mui-button-row">            <button type="button" class="mui-btn mui-btn-numbox-plus" style="width: 100%" onclick="getResult()"> 检索            </button>          </div>    </div>    <div id="result">        <div class="mui-card" style="margin-top: 25px">            <div class="mui-card-content">                <div class="mui-card-content-inner">                    可以在搜索框内输入书籍的全称,或者书籍的简称,系统支持智能检索功能。                </div>            </div>        </div>    </div></div><script src="https://others-1256773370.cos.ap-chengdu.myqcloud.com/booksearch/js/mui.min.js"></script></body></html>

效果展示

为了便于朋友使用,我将这个页面用 Webview 封装成一个 APP,整体效果如下:

总结

这是一个低频使用的 APP,如果是构建在传统服务器上,不是一个明智的选择,而云函数的按量付费,对象存储与 APIGW 的融合,完美解决了资源浪费的问题,同时借用云函数的 APIGW 触发器,可以很简单轻松的替代传统的 Web 框架和部分服务器软件的安装和使用、维护等。这个例子非常小,但却是一个有趣的小工具,除了图书查询之外,我们还可以继续拓展构建其它系统,例如成绩查询等。

关注我并转发此篇文章,私信我“领取资料”,即可免费获得InfoQ价值4999元迷你书!

MLHttpRequest(oXMLHttpRequest) 对象属性

onreadystatechange 状态改变的事件触发器,只写;

XMLHttpRequest.onreadystatechange = funcMyHandler;

readyState 对象状态(integer),只读;

XMLHttpRequest.readyState;

0 = 未初始化 1 = 初始化 2 = 已发送 3 = 已接收部分数据 4 = 完成

0 (未初始化) 对象已建立, 但是尚未初始化---创建ajax对象(new XMLHttpRequest)

1 (初始化) 对象已初始化, 尚未调用send方法---初始化ajax对象(xmlHttp.open())

2 (发送数据) send方法已调用, 但是当前的状态及http头未知----发送请求(xmlHttp.send())

3 (数据传送中) 已接收部分数据, 因为响应及http头不全, 这时通过responseBody和responseText获取部分数据会出现错误,

4 (完成) 数据接收完毕, 此时可以通过通过responseBody和responseText获取完整的回应数据

responseText 服务器进程返回数据的文本版本,只读;(text/html)

strValue = XMLHttpRequest.responseText;(text/html)

strXML = XMLHttpRequest.responseXML;(text/xml)

status 服务器返回的状态码, 如:404 = "文件末找到" 、200 ="成功"

XMLHttpRequest.status;

常用的值有:

200 表示请求成功

202 表示被接收

400 表示错误的请求

404 表示资源未找到

500 表示内部服务器错误, 如php代码错误等

statusText 服务器返回的状态文本信息,如:ok、not found

strValue = XMLHttpRequest.statusText;

Ajax引擎对象中的方法

abort() 停止当前请求

XMLHttpRequest.abort();

getAllResponseHeaders() 作为字符串返回完整的headers

strValue = XMLHttpRequest.getAllResponseHeaders();

getResponseHeader() 作为字符串返回单个的header标签

strValue = XMLHttpRequest.getResponseHeader();

如:XMLHttpRequest.getResponseHeader("Server")

open("method","URL"[,asyncFlag[,"userName"[, "password"]]]) 设置未决的请求的目标 URL,方法,和其他参数

method:http方法, 例如:POST、GET、PUT及PROPFIND。大小写不敏感。

URL:请求的URL地址,可以为绝对地址也可以为相对地址。

asyncFlag[可选]:布尔型,指定此请求是否为异步方式,默认为true。如果为真, 当状态改变时会调用onreadystatechange属性指定的回调函数。

异步:同一个时间点允许执行多个进程。(true)

同步:同一个时间点只允许执行一个进程。(false)

userName[可选]:如果服务器需要验证,此处指定用户名,如果未指定,当服务器需要验证时,会弹出验证窗口。

password[可选]:验证信息中的密码部分,如果用户名为空,则此值将被忽略。

send(content) 发送请求

只有当ajax对象的send方法被调用时,才会发送请求

注意:

使用get将数据传给服务器,则服务器就使用$_GET

就直接通过url将数据传给服务器,发送请求使用send(null)语句


使用POST时一定要使用 xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

发送请求使用send(url)语句;

通常在数据不多的时候才有GET方式,而数据量稍大的时候采用POST方式

setRequestHeader("header", "value") 设置header并和请求一起发送

xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");


完整实例:

<!DOCTYPE html>
<html>
<head>
<title>XMLHttpRequest</title>
<script language="javascript">
var xmlHttp;
function createXMLHttpRequest(){
      if(window.ActiveXObject)
      xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
      else if(window.XMLHttpRequest)
      xmlHttp = new XMLHttpRequest();
}
function startRequest(){
      createXMLHttpRequest();
      var sUrl = "sync.php?" + new Date().getTime(); //地址不断的变化,解决IE缓存问题
      xmlHttp.onreadystatechange = function(){
      if(xmlHttp.readyState == 4 && xmlHttp.status == 200)
      alert("服务器返回: " + xmlHttp.responseText);
      }
      xmlHttp.open("GET",sUrl,true);
      xmlHttp.send(null);
}
</script>
</head>
<body>
<input type="button" value="测试异步通讯" onClick="startRequest()">
</body>
</html>

服务器端代码(文件名:sync.php)

<?php
header('Content-type: text/html;charset=utf-8');
echo "异步测试成功,很高兴";
?>


服务器端在返回数据时可不可以使用return代替echo?

答:不可以

原因如下:虽然return与echo都有返回的含义, 但是两者有很大的区别。return代表返回数据到服务器端, 而echo语句代表返回数据到客户端。

又由于Ajax属于客户端语言, 其接收的数据必须能返回到客户端, 否则是接收不到任何数据的。

ajax

avaScript 最早诞生于1995年,当时主要用来处理输入验证操作,随着时间的发展它成为了市面上浏览器必备的一项功能。如今JavaScript不仅仅局限于简单的数据校验,它已经具备与浏览器窗口及其内容几乎所有方面的交互能力,已经是一个功能全面的编程语言,能够处理复杂的交互和计算。

JavaScript 简史

1995年Netscape 公司的布兰登.艾奇(Brendan Eich)着手开发一种名为 LiveScript的脚步语言。主要用于 Netscape Navigator 浏览器上,为了赶在浏览器发布前完成Livescript 的开发,Netscape 便和 Sun 公司合作,因此Netscape 为了搭上 Java的顺风车,把Livescrip改名JavaScript。

由于JavaScript 1.0版的巨大成功,成为了市场的领袖型公司,在当时的背景下,微软在其ie 3中加入了名为JScript的脚本语言,随着微软的加入,市场上出现多个不同版本的JavaScript,导致每个版本差异巨大,没有统一的标准,引起了许多问题。

于是在1997年,以JavaScript 1.1为基础版本提交给欧洲计算机制造商协会(ECMA)。随后,该协会推出了ECMAScript标准,各浏览器厂商都将此标准作为实现各自JavaScript的实现基础,也在不同程度上取得成功。

JavaScript 的实现

虽然通常JavaScript 和 ECMAScript被认为是相同的含义,但是JavaScript却比ECMA规定的内容要多得多。一个完整的JavaScript 由以下组成:

  • 核心 (ECMAScript)
  • 文档对象模型(DOM)
  • 浏览器对象模型(BOM)

1、ECMAScript

ECMAScript 其实与web浏览器没有依赖关系,它不依赖任何环境,web浏览器只是其实现的一种宿主环境。此外还有Node 和 Flash 也都是宿主环境,都实现了各自的ECMAScript 脚本。

ECMAScript 主要有以下核心内容:

  • 语法
  • 类型
  • 语句
  • 关键字
  • 保留字
  • 操作符
  • 对象
  • 模块化

2015年6月17日,ECMAScript 6发布正式版本,即ECMAScript 2015,简称ES6,ES6增添了许多必要的特性,例如:模块和类,以及一些实用特性,例如Maps、Sets、Promises、生成器(Generators)等。尽管ES6做了大量的更新,但是它依旧完全向后兼容以前的版本。

截止目前,ES6是各大浏览器支持较好的且稳定的一个版本,以后关于JavaScript的知识都会在ES6的语法基础上介绍。

2、文档对象模型(DOM)

文档对象模型(Document Object Model),是针对XML及其基础上扩展的HTML的应用程序编程接口(API)。DOM把整个网页映射成为一个树状的多层节点结构。页面中每个组成部分都是某种类型的节点,这些节点有包含不同的数据。如下面这个html页面:

<html>
  <head>
  	<title>标题</title>
  </head>
  <body>
  	<p>段落文字</p>
  </body>
 </html>

通过DOM构建的节点树结构,我们可以很方便的控制页面的内容,比如删除、添加、修改任何节点的内容。

DOM主要内容

DOM 作为一种API,它也是有规范的,1998年DOM成为W3C的推荐标准,DOM从1.0到如今的3.0,其实现了更加丰富的接口,主要有如下这些模块:

  • DOM 视图:定义跟踪不同文档的视图的接口;
  • DOM 事件:定义了事件和事件处理的接口;
  • DOM样式:定义基于css的应用样式接口;
  • DOM遍历范围:定义了遍历和操作文档的接口;
  • 统一加载和保存文档的方法;
  • DOM验证: 验证文档的方法;
  • 同时开始支持XML1.0规范,涉及XML Infoset、XPath和XMl Base;

其它DOM标准

处理DOM核心和DOM HTML接口外,另外其它语言也发布了针对自己的DOM标准。比如下面几种:

  • SVG:可伸缩矢量图;
  • MathMl:数学标记语言;
  • SMIL:同步多媒体集成语言;

还有一些语言,比如 Mozilla 的XUL(XML 用户界面语言),但是只有上面几种是W3C推荐的标准。

如今,大部分浏览器已经全部支持DOM 1.0 、2.0版,3.0版也大部分都支持,所以我们可以放心的使用,尤其是最近IE浏览器的彻底退出舞台,浏览器兼容问题少了许多。

浏览器对象模型(BOM)

早期Netscape 浏览器和IE浏览器,都支持访问和操作浏览器窗口对象模型。开发者可以控制页面以外的部分,不同于其它,BOM作为JavaScript的实现却没有一个相关的标准。这个问题直到HTML5出现才得到解决,HTML5致力于把BOM纳入正式的规范中。

BOM从根本上讲,只处理浏览器窗口和框架,但人们都习惯把针对浏览器的JavaScript扩展归于BOM的范畴,下面是一些扩展:

  • 弹出浏览器窗口的功能;
  • 移动、缩放和关闭浏览器窗口;
  • 提高浏览器详细信息的navigator对象;
  • 提高浏览器加载的页面详细信息的location对象;
  • 提高显示屏分辨率信息的的screen对象;
  • 对cookies的支持;
  • XMLHttpRequest 和 IE中的ActiveXObject对象;

由于BOM一直以来没有统一的规范,各个浏览器都有自己的实现,所以差距比较大,但是随着HTML5的发展,BOM朝着兼容性更高的方向发展。

总结

JavaScript 是一种专门为网页设计的脚本语言,其主要由以下三个部分组成:

  • ECMAScript ,由ECMA-262定义,提高核心功能;
  • 文档对象模型(DOM),提供访问和操作网页内容的接口;
  • 浏览器对象模型(BOM),提高和浏览器窗口交互的方法;

在当前主流浏览器中,对于ES6的大部分功能都得到了支持,随着技术的发展,JavaScript越来越完善,功能也更加强大。

参考资料:

《JavaScript 高级程序设计》第三版

MDN JavaScript 教程 —— https://developer.mozilla.org/zh-CN/docs/Web/JavaScript