文章目录
一. 项目概述
1.1 核心需求
-
选取3-5个代表性的新闻网站(比如新浪新闻、网易新闻等,或者某个垂直领域权威性的网站比如经济领域的雪球财经、东方财富等,或者体育领域的腾讯体育、虎扑体育等等)建立爬虫,针对不同网站的新闻页面进行分析,爬取出编码、标题、作者、时间、关键词、摘要、内容、来源等结构化信息,存储在数据库中。
-
建立网站提供对爬取内容的分项全文搜索,给出所查关键词的时间热度分析。
1.2 技术要求
-
必须采用Node.JS实现网络爬虫
-
必须采用Node.JS实现查询网站后端,HTML+JS实现前端(尽量不要使用任何前后端框架)
二. demo展示
2.1 项目框架
![](https://i-blog.csdnimg.cn/blog_migrate/26c53b9fa952aa85aeaae226e0d771b9.png)
2.2 演示demo视频
(如果上面的视频看起来不太清楚,可能是因为被压缩了,老师可以转到b站 web 编程 demo 看~qwq)
三. 网站分析
本项目选取了人民网,网易新闻网和中国新闻网三个网站,网站包含的结构化信息较为全面。我们首先需要对将要爬取的新闻页面进行分析,下面以人民网为例,
新闻主页面
通过F12键可以看到页面的源码,在网站主页面中,每条新闻都在一个<h3>或者<li>标签内,我们可以获取到每条新闻的 title 和 url 。标签 a 表示超链接,并设置了 href 属性,可链接到详情页面。
![](https://i-blog.csdnimg.cn/blog_migrate/796196512e83069886b0dba178057ff3.png)
新闻详情页
点击超链接,进入新闻详情页,我们可以获取到新闻的 keywords, publishdate, author 等字段。
![](https://i-blog.csdnimg.cn/blog_migrate/2435a6cd750356e044e797c49cf6a572.png)
整体来说,网站的DOM结构如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/9eb9fe97256beb73063c376da3faf7bc.png)
四.数据爬取
4.1 爬虫整体结构
![](https://i-blog.csdnimg.cn/blog_migrate/4e194202aec3544c9c4411e7aaee7166.png)
爬虫整体结构如上图所示,先读取种子页面,分析出种子页面里的所有新闻链接,再爬取所有未被爬取过的新闻链接的内容,分析新闻页面内容,并解析出结构化数据,最后将结构化数据保存到mysql数据库中,本项目爬取了三个新闻网站,共计近1700条。
4.2 使用的工具包
本项目使用的模块包括 request, cheerio, mysql, fs, date-utils, iconv-lite, node-schedule 等。
-
使用 request 模块来获取网页内容,request是服务端发起请求的工具包,是一个用来简化 HTTP 请求操作的模块。
-
使用 cheerio 模块来提取网页中的数据,cheerio 是一个 jQuery Core 的子集,其实现了 jQuery Core 中浏览器无关的 DOM 操作 API。简单来说,cheerio就是服务器端的jQuery,去掉了jQuery的一些效果类和请求类等等功能后,仅保留核心对dom操作的部分,因此能够对dom进行和jQuery一样方便的操作。它是我们筛选数据的利器——把多余的html标签去掉,只留下我们想要的内容的重要工具。
-
使用 mysql 模块来将数据储存到数据库,mysql 模块内置了连接池机制。
-
fs(file system)模块是nodejs提供的用于访问本地文件系统的功能模块,它使得运行于nodejs环境下的JavaScript具备直接读写本地文件的能力。
-
date-utils 是日期时间插件模块。
-
iconv-lite 用于各种字符集间高效的转码。
-
node-schedule 库用来完成定时器功能
var fs = require('fs');
var myRequest = require('request');
var myCheerio = require('cheerio');
var myIconv = require('iconv-lite');
require('date-utils');
var mysql = require('./mysql.js');
var schedule = require('node-schedule');
4.3 数据爬取具体流程
4.3.1 新闻链接爬取
- 引入必要的模块 fs, request, cheerio, iconv-lite, date-utils, mysql ,并定义要访问的网站。
人民网
var source_name = "人民网";
var domain = 'http://www.people.com.cn/';
var myEncoding = "GBK";
var seedURL = 'http://www.people.com.cn/';
网易新闻
var source_name = "网易新闻";
var domain = 'https://news.163.com/';
var myEncoding = "utf-8";
var seedURL = 'https://news.163.com/';
中国新闻网
var source_name = "中国新闻网";
var domain = 'http://www.chinanews.com/';
var myEncoding = "utf-8";
var seedURL = 'http://www.chinanews.com/';
- 根据不同网页源码中不同字段的结构定义新闻页面里具体的元素的读取方式,定义哪些url可以作为新闻页面。我们要注意到不同网站中不同字段结构不同,所以我们需要对不同网站定义不同的正则表达式进行匹配。下面列出本项目爬取的三个网站页面爬取格式的正则表达式。
人民网
var seedURL_format = "$('a')";
var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var title_format = "$('title').text()";
var date_format = " $('meta[name=\"publishdate\"]').eq(0).attr(\"content\")";
var author_format = " $('meta[name=\"author\"]').eq(0).attr(\"content\")";
var content_format = "$('.fl text_con_left').text()";
var desc_format = " $('meta[name=\"description\"]').eq(0).attr(\"content\")";
var source_format = " $('meta[name=\"source\"]').eq(0).attr(\"content\")";
var url_reg = /\/(\w{2})\/(\d{4})\/(\d{4})\/(\w{5,10})-(\w{8,15}).html/;
var regExp = /(\d{4}\-\d{2}\-\d{2})/
网易新闻
var seedURL_format = "$('a')";
var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var title_format = "$('title').text()";
var date_format = "$('html#ne_wrap').attr(\"data\-publishtime\")";//
var author_format = "$('.ep-editor').text()";
var content_format = "$('#endText').text()";
var desc_format = " $('meta[name=\"description\"]').eq(0).attr(\"content\")";
var source_format = "$('#ne_article_source').text()";
var url_reg = /\/(\d{2})\/(\d{4})\/(\d{2})\/([A-Z0-9]{16}).html/;
var regExp = /((\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2})|(\d{4}年\d{1,2}月\d{1,2}日)/
中国新闻网
var seedURL_format = "$('a')";
var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var title_format = "$('title').text()";
var date_format = "$('#pubtime_baidu').text()";
var author_format = "$('#editor_baidu').text()";
var content_format = "$('.left_zw').text()";
var desc_format = " $('meta[name=\"description\"]').eq(0).attr(\"content\")";
var source_format = "$('#source_baidu').text()";
var url_reg = /\/(\d{4})\/(\d{2})-(\d{2})\/(\d{7}).shtml/;
var regExp = /((\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2})|(\d{4}年\d{1,2}月\d{1,2}日)/
- 构造一个模仿浏览器的 request,鉴于 node.js 异步执行的特性,利用 request 模块异步 fetch url 可以追求更高的并发,更高的处理性能,这也是 node.js 适合用来写爬虫的原因之一。
//防止网站屏蔽我们的爬虫
var headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.65 Safari/537.36'
}
//request模块异步fetch url
function request(url, callback) {
var options = {
url: url,
encoding: null,
headers: headers,
timeout: 10000 //
}
myRequest(options, callback)
};
- 读取种子页面,解析出种子页面里所有的<a href>链接
使用request模块爬取网页内容,第一个参数是将要发送请求的网址 url ,在项目中赋值为主页面seedURL。第二个参数是回调函数(callback)。回调函数的内容是非阻塞的,体现了Node.JS“异步编程”的特性,加快了代码运行的速度。
request(seedURL, function(err, res, body) { //读取种子页面
// try {
//用iconv转换编码
var html = myIconv.decode(body, myEncoding);
//console.log(html);
//准备用cheerio解析html
var $ = myCheerio.load(html, { decodeEntities: true });
// } catch (e) { console.log('读种子页面并转码出错:' + e) };
var seedurl_news;
try {
seedurl_news = eval(seedURL_format);
} catch (e) { console.log('url列表所处的html块识别出错:' + e) };
- 遍历种子页面里所有的<a href>链接
首先规整化所有链接,统一把字符串转换为小写,再把不同的url格式整理成统一的以 http:// 开头的格式;接着判断该url是否符合预先定义的新闻URL的正则表达式,并查找该 url 是否未被爬取过,若符合则爬取。
seedurl_news.each(function(i, e) { //遍历种子页面里所有的a链接
var myURL = "";
try {
//得到具体新闻url
var href = "";
href = $(e).attr("href");
if (href == undefined) return;
if (href.toLowerCase().indexOf('http://') >= 0) myURL = href; //http://开头的
else if (href.startsWith('//')) myURL = 'http:' + href; 开头的
else myURL = seedURL.substr(0, seedURL.lastIndexOf('/') + 1) + href; //其他
} catch (e) { console.log('识别种子页面中的新闻链接出错:' + e) }
if (!url_reg.test(myURL)) return; //检验是否符合新闻url的正则表达式
//console.log(myURL);
var fetch_url_Sql = 'select url from china where url=?';
var fetch_url_Sql_Params = [myURL];
mysql.query(fetch_url_Sql, fetch_url_Sql_Params, function(qerr, vals, fields) {
if (vals.length > 0) {
console.log('URL duplicate!')
} else newsGet(myURL); //读取新闻页面
});
});
4.3.2 新闻内容解析
-
读取具体的新闻页面,构造一个空的fetch对象用于存储数据。
先用iconv进行转码,接着准备用cheerio解析html_news,并构建json对象准备存储网页中的元素。
request(myURL, function(err, res, body) { //读取新闻页面
//try {
var html_news = myIconv.decode(body, myEncoding); //用iconv转换编码
//console.log(html_news);
//准备用cheerio解析html_news
var $ = myCheerio.load(html_news, { decodeEntities: true });
myhtml = html_news;
//} catch (e) { console.log('读新闻页面并转码出错:' + e);};
console.log("转码读取成功:" + myURL);
//动态执行format字符串,构建json对象准备写入文件或数据库
var fetch = {};
fetch.title = "";
fetch.content = "";
fetch.publish_date = (new Date()).toFormat("YYYY-MM-DD");
//fetch.html = myhtml;
fetch.url = myURL;
fetch.source_name = source_name;
fetch.source_encoding = myEncoding; //编码
fetch.crawltime = new Date();
- 根据正则表达式匹配读取新闻页面中的元素并保存到 fetch 对象里,这里需要注意的是不同新闻网站的结构不同,某些字段不一定都有,所以fetch对象中有的字段为空。
if (keywords_format == "") fetch.keywords = source_name; // eval(keywords_format); //没有关键词就用sourcename
else fetch.keywords = eval(keywords_format);
if (title_format == "") fetch.title = ""
else fetch.title = eval(title_format); //标题
if (date_format != "") fetch.publish_date = eval(date_format); //刊登日期
console.log('date: ' + fetch.publish_date);
if(fetch.keywords!=undefined && fetch.publish_data!=" "){
fetch.publish_date = regExp.exec(fetch.publish_date)[0];
fetch.publish_date = fetch.publish_date.replace('年', '-')
fetch.publish_date = fetch.publish_date.replace('月', '-')
fetch.publish_date = fetch.publish_date.replace('日', '')
fetch.publish_date = new Date(fetch.publish_date).toFormat("YYYY-MM-DD");
if (author_format == "") fetch.author = source_name; //eval(author_format); //作者
else fetch.author = eval(author_format);
if (content_format == "") fetch.content = "";
else fetch.content = eval(content_format).replace("\r\n" + fetch.author, ""); //内容,是否要去掉作者信息自行决定
if (source_format == "") fetch.source = fetch.source_name;
else fetch.source = eval(source_format).replace("\r\n", ""); //来源
if (desc_format == "") fetch.desc = fetch.title;
else fetch.desc = eval(desc_format).replace("\r\n", ""); //摘要
- 使用insert语句将 fetch 对象插入 mysql 数据库中
var fetchAddSql = 'INSERT INTO china(url,source_name,source_encoding,title,' +
'keywords,author,publish_date,crawltime,content) VALUES(?,?,?,?,?,?,?,?,?)';
var fetchAddSql_Params = [fetch.url, fetch.source_name, fetch.source_encoding,
fetch.title, fetch.keywords, fetch.author, fetch.publish_date,
fetch.crawltime.toFormat("YYYY-MM-DD HH24:MI:SS"), fetch.content
];
//执行sql,数据库中fetch表里的url属性是unique的,不会把重复的url内容写入数据库
mysql.query(fetchAddSql, fetchAddSql_Params, function(qerr, vals, fields) {
if (qerr) {
console.log(qerr);
}
}); //mysql写入
- 爬取结果展示:
![](https://i-blog.csdnimg.cn/blog_migrate/fb8c6b14b17e581a6fca3291a99a3cc9.png)
四. 数据库设计
数据库 schema 各字段属性
![](https://i-blog.csdnimg.cn/blog_migrate/89583a5b3bf5f07a76eadd1eecf92585.png)
数据库索引关系
![](https://i-blog.csdnimg.cn/blog_migrate/f04a7720e942c20d440488f4acda44a7.png)
数据库china表中字段包括id_fetches,url,source_name,source_encoding,title,keywords,author,publish_date,crawltime,content,createtime.
-
id_fetches为唯一主键PRIMARY KEY,表示新闻的唯一标识id
-
url也限制为唯一键UNIQUE KEY,表示新闻的链接
-
source_name表示新闻网站来源,在此项目中source_name包括三种value:人民网,网易新闻,中国新闻网
-
source_encoding表示编码方式,此项目中编码方式为:人民网 GBK; 网易新闻 utf-8 ;中国新闻网 utf-8
-
title表示新闻的标题
-
keywords表示新闻的关键词
-
author表示新闻的作者,编辑
-
publish_date表示新闻的出版时间
-
crawltime表示新闻的爬取时间
-
content表示新闻的具体内容
-
create time存储当前的时间戳
数据展示:
![](https://i-blog.csdnimg.cn/blog_migrate/5fc5bd537c89e73c297ac891251f4e27.png)
五. 搜索网站部分
5.1 分项搜索功能
分项搜索功能,可根据 title, keywords, author 进行新闻的搜索,采用字符串匹配的方法,将查询结果以表格形式展现出来。
根据前端输入跳转到相应路由,构建sql语句进行mysql数据库查询,结果以json格式返回。
router.get('/process_get_keywords', function(request, response) {
//sql字符串和参数
var fetchSql = "select url,source_name,title,author,publish_date " +
"from china where keywords like '%" + request.query.keywords + "%'";
mysql.query(fetchSql, function(err, result, fields) {
response.writeHead(200, {
"Content-Type": "application/json"
});
response.write(JSON.stringify(result));
response.end();
});
});
查询结果:
5.2 时间热度分析
个人理解的时间热度分析即时间维度上的含特定语义或关键词的新闻规模数量的变化情况,统计每个时间点含特定关键词的新闻的数量,以折线图和饼状图的形式可视化在页面中。
所以思路是根据输入的value,构建sql语句查询含该关键词的新闻,并按出版时间publish_date排序。构建字典word_freq,对于排序好的新闻,遍历每条新闻,对keywords先用replace方法去除一些无用的字符,再用正则表达式匹配出publish_date,搜索出keywords数量,添加到publish_date对应的word_freq上。遍历完成即可得到含特定关键词的新闻数量随时间维度变化的数值表示。再用echarts做出折线图和饼状图,定义好option中title,grid,x轴,y轴等属性,得到可视化效果展示在前端。
实现流程
- 获取关键词随日期变化的出现次数
const regex_c = /[\t\s\r\n\d\w]|[\+\-\(\),\.。,!?《》@、【】"'::%-\/“”]/g;
var regex_d = /\w{3}\s(.*?) 2021/;
var freqchange = function(vals, keyword) {
var regex_k = eval('/'+keyword+'/g');
var word_freq = {};
vals.forEach(function (data){
var content = data["keywords"].replace(regex_c,'');
var publish_date = regex_d.exec(data['publish_date'])
if(publish_date!=null){
publish_date=publish_date[1];
var freq = content.match(regex_k).length;// 直接搜这个词。
word_freq[publish_date] = (word_freq[publish_date] + freq ) || 0;
}
});
console.log(word_freq);
return word_freq;
};
exports.freqchange = freqchange;
- Echarts 作图,对于查询结果 data 按照作图数据格式{name,value}进行解析,并将可视化结果同时以折线图和饼状图展示出来。通过title属性设置图表标题,通过grid属性设置图标布局,通过xAxis和yAxis设置坐标轴属性,通过series和data设置图标中的数据。
let newdata = [];
for (var key in data) {
newdata.push({name:key,value:data[key]});
};
console.log(newdata);
var myChart = echarts.init(document.getElementById("main1"));
option = {
title: {
text: '\"'+$("#analyze_text").val()+'\"'+'该词在新闻中的出现次数随时间变化图'+' '+'\"'+$("#analyze_text").val()+'\"'+'该词在新闻中的出现次数饼状图',
left:'6%'
},
grid:[
{x:'2%',y:'20%',width:'25%',height:'48%'},
{x2:'7%',y:'0%',width:'47%',height:'47%'},
],
xAxis: [
{
gridIndex:0,
type: 'category',
data: Object.keys(data)
},
],
yAxis: [
{
gridIndex: 0,
type: 'value',
}
],
series: [{
data: Object.values(data),
type: 'line',
itemStyle : { normal: {label : {show: true}}}
},
{
name: '访问来源',
type: 'pie', // 设置图表类型为饼图
radius: '55%', // 饼图的半径,外半径为可视区尺寸(容器高宽中较小一项)的 55% 长度。
data:newdata,
}],
};
if (option && typeof option === "object") {
myChart.setOption(option, true);
}
- 分析”人民“关键词的时间热度
六. 网站设计
Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性创建各种 Web 应用和丰富的 HTTP 工具。
使用 Express 可以快速地搭建一个完整功能的网站。
Express 框架核心特性:
- 可以设置中间件来响应 HTTP 请求。
- 定义了路由表用于执行不同的 HTTP 请求动作。
- 可以通过向模板传递参数来动态渲染 HTML 页面。
6.1 前端设计
前端主要由 html + css + js 构成
search.html
header部分引用了css文件和js文件,包括 jquery,echarts 及用于时间热度分析的 line.js等。在public文件夹中有三个文件夹,images放项目需要用到的图片,javascripts存放前端页面需要的js文件,stylesheets文件夹则放置用来美化页面的css文件。
<header>
<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>Login</title>
<link rel="stylesheet" href="stylesheets/style.css">
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.0.2/echarts.min.js"></script>
<script src="javascripts/line.js" type="text/javascript"></script>
</header>
定义 form 表单,包括 title, keywords,author 及 hot analyze 查询的属性。
<form action="">
<h1>Search for Free</h1>
<div class="form">
<br><input class="text-input" type="text" name="title_text" id="title_text" placeholder="Title">
<input class="form-submit" type="button" value="查询" id="title_button">
<br><input class="text-input" type="text" name="keywords_text" id="keywords_text" placeholder="Keywords">
<input class="form-submit" type="button" value="查询" id="keywords_button">
<br><input class="text-input" type="text" name="author_text" id="author_text" placeholder="Author">
<input class="form-submit" type="button" value="查询" id="author_button">
<!-- 作图 -->
<br><input class="text-input" type="text" name="analyze_text" id="analyze_text" placeholder="Heat Analysis">
<input class="form-submit" type="button" value="查询" id="analyze_button" onclick="lineFun()">
</div>
</form>
js 部分,客户端点击操作触发路由的跳转并传递参数。以获取title为例,当用户输入title所含关键词并点击button时会自动跳转到/process_get_title路由,并将返回得到的数据data以表格形式append进record2,最终表格在前端显示出来。
$("#title_button").click(function() {
$.get('/process_get_title?title=' + $("#title_text").val(), function(data) {
$("#record2").empty();
$("#record2").append('<tr class="cardLayout"><th>url</th><th>source_name</th>' +
'<th>title</th><th>author</th><th>publish_date</th></tr>');
for (let list of data) {
let table = '<tr class="cardLayout"><td>';
Object.values(list).forEach(element => {
table += (element + '</td><td>');
});
$("#record2").append(table + '</td></tr>');
}
});
});
style.css
CSS不仅可以静态地修饰网页,还可以配合各种脚本语言动态地对网页各元素进行格式化。本项目前端的css代码如下,其中font指定了字体,background设置了背景,border-collapse 设置取消表格边框,border-bottom 设置表格横线,页面设计以突出文字为核心,表格格式仅起辅助作用,故设计以简约风格为主。
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
background: url('../images/24hr-Earth_11.jpeg');
background-repeat: no-repeat;
background-size: 100% auto;
}
a {
color: #00B7FF;
}
#login-box {
width: 32%;
height: auto;
margin: 0 auto;
margin-top: 15%;
text-align: center;
background: #00000090;
padding: 20px 50px;
}
#login-box h1 {
color: #fff;
}
#login-box .form .item {
margin-top: 15px;
}
#login-box .form .item i {
font-size: 18px;
color: #fff;
}
#login-box .form .text-input {
width: 180px;
font-size: 18px;
border: 0;
border-bottom: 2px solid #fff;
padding: 5px 10px;
background: #ffffff00;
color: #fff;
}
#login-box .form .form-submit {
margin-top: 15px;
width: 100px;
height: 30px;
font-size: 20px;
font-weight: 400;
color: #fff;
border: 0;
border-radius: 15px;
/*background: #1abc9c;*/
/*background-image: linear-gradient(to right, #43e97b 0%, #38f9d7 100%);*/
background-image: linear-gradient(to right, #e4afcb 0%, #b8cbb8 0%, #b8cbb8 0%, #e2c58b 30%, #c2ce9c 64%, #7edbdc 100%);
}
.cardLayout table{
width:70%;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);/* 阴影 */
border-collapse:collapse;/* 取消表格边框 */
letter-spacing:1px;/* 文字缩进 */
}
.cardLayout table,th,tr,td{
border-bottom:1px solid #dedede;/* 表格横线 */
padding:29px;
text-align: center;
}
前端 search.html 展示
6.2 后端设计
可详见四/五部分
后端 mysql 数据库
![](https://i-blog.csdnimg.cn/blog_migrate/6185dbab8b39729e046c10b76e78a9eb.png)
后端逻辑架构
![](https://i-blog.csdnimg.cn/blog_migrate/3ef3b561c1f30f9fefb7d093d4389ffd.png)
前端根据传入的参数构建SQL语句对后端数据库进行查询,并将查询结果返回,对于时间热度分析,解析出时间与含关键词新闻条数的对应关系,用echarts做图可视化。
6.3 前后端交互设计
根据客户端输入和点击跳转到指定的路由地址,并将输入的 value 作为参数传入,构建sql语句;express构建网站进行sql查询,并将查询结果以json格式返回给前端,再以表格形式展现给用户。
以 author search为例
前端 search.html 关于author search 部分如下
<br><input class="text-input" type="text" name="author_text" id="author_text" placeholder="Author">
<input class="form-submit" type="button" value="查询" id="author_button">
当用户点击 button 时,跳转到/process_get_author路由,根据传入的参数request.query.author构建sql语句,进行后端数据库查询,并将得到的json结果作为response返回给前端,展示出来。
router.get('/process_get_author', function(request, response) {
//sql字符串和参数
var fetchSql = "select url,source_name,title,author,publish_date " +
"from china where author like '%" + request.query.author + "%'";
mysql.query(fetchSql, function(err, result, fields) {
response.writeHead(200, {
"Content-Type": "application/json"
});
response.write(JSON.stringify(result));
response.end();
});
});
search.html 在div中以table形式展示出搜索结果
<div class="cardLayout" style="margin: 10px 0px">
<table width="100%" id="record2" style="margin-left:auto; margin-right:auto;"></table>
</div>
七. 总结
借助本次中期实践完成了 web 全栈开发,学习了 node.js 和 express 框架,亲手实践了爬虫,数据库查询,可视化等功能。
Node.js是一个让JavaScript运行在服务器端的开发平台,在解决服务器高性能瓶颈问题上具有一定优势。Node.js不为每个客户连接创建一个新的线程,而仅仅使用一个线程。当有用户连接了,就触发一个内部事件,通过非阻塞I/O、事件驱动机制,让Node.js程序宏观上也是并行的。Node.js自身哲学,是花最小的硬件成本,追求更高的并发,更高的处理性能。
Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性创建各种 Web 应用和丰富的 HTTP 工具。简单点说express就是一个封装了很多功能的包,而你只需要用简单的express的专属的一些代码便可解决本来正常较为复杂的代码,方便使用。
通过理论课的学习和实验课的实践对web编程整体体系有了更进一阶的认识,同时借此机会,我的代码能力也有所提升。感谢老师和助教学长学姐的悉心教导和耐心帮助!