Web编程第一个实验项目

Web编程第一个实验项目

题目要求:

在这里插入图片描述

项目完成步骤

使用Node.js和模板库Cheerio,request做一个爬虫代码,爬取新浪新闻网易新闻中国新闻网中的各个部分。
下面以新浪新闻为例,分析页面:

1. 新闻页面分析

1.1 对某一篇新闻页面元素进行分析:
  • 标题 main-title
    在这里插入图片描述

  • 作者 show_author
    在这里插入图片描述

  • 时间 date
    在这里插入图片描述

  • 关键字:正则化匹配:$('meta[name=\"keywords\"]').eq(0).attr(\"content\")"
    在这里插入图片描述

  • 内容 article
    在这里插入图片描述

  • 来源 source
    在这里插入图片描述

1.2 对新闻网页的URL进行分析
https://news.sina.com.cn/c/2021-04-23/doc-ikmxzfmk8553784.shtml
https://news.sina.com.cn/c/2021-04-23/doc-ikmyaawc1428218.shtml
https://news.sina.com.cn/o/2021-04-22/doc-ikmxzfmk8371786.shtml

从上述三个网址可知,每个网页都是由

  • https://news.sina.com.cn 网页链接
  • /o/ 一个字母
  • 2021-04-22 年月日,由四个数字-两个数字-两个数字组成
  • doc-ikmxzfmk8371786.shtml doc-15个字符.shtml
    因此可以写出匹配网页的正则化为:/\/(\d{4})-(\d{2})-(\d{2})\/doc-(\w{15}).shtml/

2. 数据库设计和代码实现

2.1 设计数据库
  • 在mysql中创建数据库crawlnews,然后在该数据库里创建表fetches:
    在这里插入图片描述
  • 根据第1步分析的网页元素,设置fetches表的结构,url设置的属性为UNIQUE,因此插入数据之前需要判断是否已经爬取过相同的网页。
    在这里插入图片描述
2.2 数据库连接和查询代码
  • 在js爬虫代码中使用mysql,需要先引用mysql文件。
var mysql = require('./mysql.js');
  • 其中在mysql.js连接数据库,并且完成了带参数和不带参数的sql语句查询。(password需要写自己mysql的密码
    在这里插入图片描述

3. 爬虫具体代码实现

根据上述新闻分析,实现一个爬虫代码:

3.1 网页解析代码实现
  • 首先定义我们想要爬取的网页名称和网页URL,和该网页的编码方式(编码方式可以在网页的header中找到)
    在这里插入图片描述
var source_name = "新浪新闻";
var myEncoding = "utf-8"; //转码成utf-8
var seedURL = 'https://news.sina.com.cn/china/';
  • 在代码中定义我们需要爬取的网页的格式:带有a标签,标题从class = main-title中获取,向keywords这种从headers中读取的,就使用正则化进行匹配,每个选择的内容都在第1.1部分进行了说明。
    在这里插入图片描述

  • 另外,还需要对新浪新闻中的网页进行分析,在第1.2部分中进行了解释
    在这里插入图片描述
    同理:网易新闻的页面解析代码为:
    在这里插入图片描述
    (已提供)中国新闻网的解析代码为:
    在这里插入图片描述

3.2 爬虫代码实现
  • 需要的模块介绍
    需要在代码开始导入fs,request,cheerio,iconv-lite和date-utils五个模块,其中fs为node.js内置的。下面为每个模块的含义和示例:

fs模块:Node.js 文件系统,提供一组类似 UNIX(POSIX)标准的文件操作API。Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()。
异步的方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。建议使用异步方法,比起同步,异步方法性能更高,速度更快,而且没有阻塞。

var fs = require("fs");
// 异步读取
fs.readFile('input.txt', function (err, data) {
   if (err) {
       return console.error(err);
   }
   console.log("异步读取: " + data.toString());
});
// 同步读取
var data = fs.readFileSync('input.txt');
console.log("同步读取: " + data.toString());
console.log("程序执行完毕。");

request:属于http模块,使用前需要引入http模块,主要作为客户端向HTTP服务器发起请求。

// 发送Get请求
// 第一个参数:请求的完整URL,包括参数
// 第二个参数:请求结果回调函数,会传入3个参数,第一个错误,第二个响应对象,第三个请求数据
request(url,function (error, response, data) {
    console.log(data) 
});

cheerio:cheerio是jquery核心功能的一个快速灵活而又简洁的实现,主要是为了用在服务器端需要对DOM进行操作的地方。

//把HTML告诉你的服务器.
const cheerio = require('cheerio');
const $ = cheerio.load('<h2 class="title">Hello world</h2>');

$('h2.title').text('Hello there!');
$('h2').addClass('welcome');

$.html();
//=> <html><head></head><body><h2 class="title welcome">Hello there!</h2></body></html>

iconv-lite:使用纯 javascript 转化字符编码。

var iconv = require('iconv-lite');

// Convert from an encoded buffer to js string. 
str = iconv.decode(new Buffer([0x68, 0x65, 0x6c, 0x6c, 0x6f]), 'win1251');

// Convert from js string to an encoded buffer. 
buf = iconv.encode("Sample input string", 'win1251');

// Check if encoding is supported 
iconv.encodingExists("us-ascii")

date-utils:nodejs日期时间插件。

require('date-utils');
var dt = new Date();
console.log(dt.toFormat("YYYY-MM-DD HH24:MI:SS"));
  • 在代码中导入需要的模块
var fs = require('fs');
var myRequest = require('request')
var myCheerio = require('cheerio')
var myIconv = require('iconv-lite')
require('date-utils');
  • 设置headers,防止网页屏蔽该爬虫,下面代码为设置的request函数。
    在这里插入图片描述

  • 实现爬取新闻主页中的所有新闻网页。seedURL为我们需要爬取的新闻主页,因此对新闻主页爬取后对里面所有的链接进行判断,判断是否是一个合理的新闻网页。使用之前找到的新闻网址的规律,进行正则化项匹配,匹配成功后需要调用mysql中的查询函数。

  • 因为需要判断是否爬取过相同的网页,因此实现查询(select)的sql语句:fetch_url_Sql和需要传入的参数(这次处理的网页URL):fetch_url_Sql_Params,调用mysql的query函数对数据库进行查询,通过有无查询结果来判断之前是否已经爬取。

  • 如果不存在,再对其网页进行爬取,即调用newsGet(myURL)函数。
    在这里插入图片描述

  • 爬取新闻网页。用cheerio解析网页,定义一个新的变量fetch,存储我们需要的标题,作者,内容等等之类的。使用前面定义的title_formatdate_format等之类的标准化格式,获取爬取网页中的各个元素。

  • 函数最后需要将得到的网页元素插入(insert)到fetches表中。实现insert语句的sql代码fetchAddSql和需要插入的网页元素值fetchAddSql_Params,调用mysql中的query函数,对表进行插入操作。

function newsGet(myURL) { //读取新闻页面
    request(myURL, function(err, res, body) { //读取新闻页面
        //try {
        console.log(myURL)
        if (body == undefined) return;
        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用于存储数据
        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();

        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);
        console.log(myURL);
        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", ""); //摘要    

        // var filename = source_name + "_" + (new Date()).toFormat("YYYY-MM-DD") +
        //     "_" + myURL.substr(myURL.lastIndexOf('/') + 1) + ".json";
        // 存储json
        // fs.writeFileSync(filename, JSON.stringify(fetch));

        //sql插入语句
        var fetchAddSql = 'INSERT INTO fetches(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
    });
  • 任意一篇新闻爬取结果如下:
{
    "title": "河海大学图书借阅排行榜第一的书居然是?|【世界读书日】",
    "content": "\n\t\t\t\t  原标题:河海大学图书借阅排行榜第一的书居然是?|【世界读书日】  来源:河海大学  世  界  读  书  日",
    "publish_date": "2021-04-23",
    "url": "https://news.sina.com.cn/c/2021-04-23/doc-ikmyaawc1423692.shtml",
    "source_name": "新浪新闻",
    "source_encoding": "utf-8",
    "crawltime": "2021-04-23T11:56:04.698Z",
    "keywords": "河海大学图书借阅排行榜第一的书居然是?|【世界读书日】",
    "author": "",
    "source": "河海大学",
    "desc": "原标题:河海大学图书借阅排行榜第一的书居然是?|【世界读书日】来源:河海大学"
}
  • 存储到数据库crawlnews中结果如下:
    在这里插入图片描述

4. 使用Express框架搭建web服务器实现新闻查询和时间热度分析

4.1 安装express

Express是一个简单且灵活的node.js Web应用框架,可以快速搭建一个完整功能的网站。

  • 可以设置中间件响应HTTP请求
  • 定义路由表执行不同的HTTP请求
  • 通过向模板传递参数来动态渲染HTML页面

使用Express脚手架初始化一个规范化项目。

虽然当项目的路由过多时会使app.js文件很臃肿,但是当前项目没有很多路由,因此使用express脚手架是合理的。

  1. 使用express脚手架创建一个网站框架
express -e search_site
  1. 在该网页中需要查询mysql中fetches中的数据,因此需要安装mysql
npm install mysql -save
  1. 安装其他需要的模块
npm install
  1. 在cmd中输入node bin/www查看网页127.0.0.1:3000已搭建成功。
    在这里插入图片描述
4.2 新闻查询
  • 在search.html网页中实现可以针对标题,内容,关键词进行检索。相应的SQL查询语句为:
SELECT * FROM crawlnews 
WHERE title like con1 AND keywords like con2 AND content like con3;
  • 在search.html中有一个可以输入标题,内容和关键字的表单,用户可以输入想要查询的内容。
    在这里插入图片描述
  • 当后端收到html页面发送的GET请求时,对请求进行解析,使用request.query分别得到标题,内容和关键字的查询内容,然后构造SQL语句,对数据库进行查询。将查询结果作为response返回给网页。
  • 其中的publish_date.toLocaleDateString()语句是将标准时间的publish_date转变为年月日的格式,更加符合观看者的习惯。
2020-01-13T16:00:00.000Z  ->   2020/1/14

在这里插入图片描述

  • 然后在前端中对response进行解析,在id=record2的地方构造一个table,显示查询后符合的新闻。
    在这里插入图片描述
    (虽然有实现分页代码,但因为不能正常显示新闻在网页上,等待之后完善)
4.3 时间热度分析
  • 在wordhot.html中实现对于一个查询词显示每一天在新闻中出现的次数。相应的SQL查询语句为:
SELECT publish_date,count(*) AS num 
FROM fetches WHERE title like con1 GROUP BY publish_date ORDER BY publish_date desc;
需要对publish_date进行分组,计算每个组中新闻的条数。
还可以使用ORDER BY publish_date desc对新闻日期进行降序排序。
  • 首先在wordhot.html中构建一个输入标题的表单。使得用户可以输入想要查询的标题内容。在这里插入图片描述
  • 然后在后端中获取该request,使用request.query.title解析出标题,针对这个标题,利用前面写的SQL语法进行查询,并且将查询值返回。
    在这里插入图片描述
  • 最初,我使用表格显示查询到的数据。在表格中显示日期和总计新闻数。该表格构造的js代码和前一个search.html中的代码类似。
    在这里插入图片描述
    显示结果如下:
    在这里插入图片描述
  • 因为表格的显示不够直观,因此改为折线图展示。导入Echart的js文件,定义一个chart并且使用echarts.init()初始化一个echart图形。在option中填入需要的title,x值,y值,并且设定图形类型为line
    在这里插入图片描述

5. 网页展示

完成第4步后,又对网页进行了:

  1. 主页设计:实现了两个功能页面的跳转。
  2. CSS设计:使得页面更加美观
  3. 导航栏设计:使得页面的跳转更加方便。
    在这里插入图片描述
    因此最终网页展示如下:
  • 主页(localhost:3000):
    可以跳转至:
    - 关键词查询网页(search.html)
    - 时间热度分析网页(wordhot.html)
    在这里插入图片描述

  • 关键词查询网页(search.html):
    当输入想要查询的词后,可以以列表的形式显示查询结果:

    • 仅查询title
      在这里插入图片描述
    • 查询titlekeywords:
      在这里插入图片描述
    • 查询titlecontentkeywords:
      在这里插入图片描述
  • 时间热度分析网页(wordhot.html)

输入想要查询的标题,会显示数据库中的新闻中包含该词的数量,并且按照日期绘制折线图。使得时间热度分析更加直观。
在这里插入图片描述
在这里插入图片描述

总结
  • 在本次项目中,理解js代码是如何运行的,该怎么设计一个网站的框架,如何使用代码对网页和服务器进行通信,是比较重要也是我比较薄弱的方面。
  • 实践了使用js进行爬虫和构建网页,学习到了前后端如何通过request和response进行交互,怎么在网页中插入图表,对网页设置CSS,完成了作业的要求。
  • 但因为对js和html代码仍然不太熟悉,因此没有完成分页功能,希望在之后对代码进行改进和优化。
  • 通过本次项目发现,只有真实的完成过任务,才能真正掌握一门语言。希望之后也能主动使用js。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值