小型新闻爬虫查询网站

小型新闻爬虫查询网站

作业要求

核心需求:

1、选取3-5个代表性的新闻网站(比如新浪新闻、网易新闻等,或者某个垂直领域权威性的网站比如经济领域的雪球财经、东方财富等,或者体育领域的腾讯体育、虎扑体育等等)建立爬虫,针对不同网站的新闻页面进行分析,爬取出编码、标题、作者、时间、关键词、摘要、内容、来源等结构化信息,存储在数据库中。

2、建立网站提供对爬取内容的分项全文搜索,给出所查关词的时间热度分析。

技术要求:

1、必须采用Node.JS实现网络爬虫

2、必须采用Node.JS实现查询网站后端,HTML+JS实现前端(尽量不要使用任何前后端框架)

实例分析

建立SQL

CREATE TABLE `fetches` (
  `id_fetches` int(11)  NOT NULL AUTO_INCREMENT,
  `url` varchar(200) DEFAULT NULL,
  `source_name` varchar(200) DEFAULT NULL,
  `source_encoding` varchar(45) DEFAULT NULL,
  `title` varchar(200) DEFAULT NULL,
  `keywords` varchar(200) DEFAULT NULL,
  `author` varchar(200) DEFAULT NULL,
  `publish_date` date DEFAULT NULL,
  `crawltime` datetime DEFAULT NULL,
  `content` longtext,
  `createtime` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id_fetches`),
  UNIQUE KEY `id_fetches_UNIQUE` (`id_fetches`),
  UNIQUE KEY `url_UNIQUE` (`url`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

注意到charset定义为utf-8,很多网站(例如网易,就不能直接使用这套table了,如果使用的话需要进行转换)

SQL DML 和 DDL

可以把 SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL)。

SQL (结构化查询语言)是用于执行查询的语法。但是 SQL 语言也包含用于更新、插入和删除记录的语法。

查询和更新指令构成了 SQL 的 DML 部分:

  • SELECT - 从数据库表中获取数据
  • UPDATE - 更新数据库表中的数据
  • DELETE - 从数据库表中删除数据
  • INSERT INTO - 向数据库表中插入数据

SQL 的数据定义语言 (DDL) 部分使我们有能力创建或删除表格。我们也可以定义索引(键),规定表之间的链接,以及施加表间的约束。

SQL 中最重要的 DDL 语句:

  • CREATE DATABASE - 创建新数据库
  • ALTER DATABASE - 修改数据库
  • CREATE TABLE - 创建新表
  • ALTER TABLE - 变更(改变)数据库表
  • DROP TABLE - 删除表
  • CREATE INDEX - 创建索引(搜索键)
  • DROP INDEX - 删除索引

mysql.js

目的:使用 Node.js 来连接 MySQL,并对数据库进行操作。

var mysql = require("mysql");
var pool = mysql.createPool({
    host: '127.0.0.1',
    user: 'root',
    password: 'root',
    database: 'crawl'
});
var query = function(sql, sqlparam, callback) {
    pool.getConnection(function(err, conn) {
        if (err) {
            callback(err, null, null);
        } else {
            conn.query(sql, sqlparam, function(qerr, vals, fields) {
                conn.release(); //释放连接 
                callback(qerr, vals, fields); //事件驱动回调 
            });
        }
    });
};
var query_noparam = function(sql, callback) {
    pool.getConnection(function(err, conn) {
        if (err) {
            callback(err, null, null);
        } else {
            conn.query(sql, function(qerr, vals, fields) {
                conn.release(); //释放连接 
                callback(qerr, vals, fields); //事件驱动回调 
            });
        }
    });
};
exports.query = query;
exports.query_noparam = query_noparam;

crawler.js

目的:采集中国新闻网的数据并保存在mysql中

这里的重点是cheerio选择器和简单的正则表达式(之后每个网站都需要重新修改)。

这里梳理一下大致内容

jQuery DOM 选择器:和cheerio很类似,可以先学这个

var myElement = $("#id01"); //返回 id="intro" 的元素
var myElements = $("p"); //返回所有 <p> 元素
var myElements = $(".intro"); //返回 class="intro" 的所有元素
var myElements = $("p.intro"); //返回包含 class="intro" 的所有 <p> 元素的列表。

myElement.text("Hello China!"); //设置 HTML 元素的内部文本
var myText = myElement.text();//获取 HTML 元素的内部文本
var myElement.html("<p>Hello World</p>");//设置元素的 HTML 内容
var content = myElement.html();//获取元素的 HTML 内容

$("body").find("div").eq(2).addClass("blue");//通过为index为2的 div 添加适当的类,将其变为蓝色

cheerio的选择器

//example
<ul id="fruits">
  <li class="apple">Apple</li>
  <li class="orange">Orange</li>
  <li class="pear">Pear</li>
</ul>

$('.apple', '#fruits').text()
//=> Apple

$('ul .pear').attr('class')
//=> pear

$('li[class=orange]').html()
//=> <li class = "orange">Orange</li>

//.attr(name, value) 获取和更改属性
$('ul').attr('id')
//=> fruits

$('.apple').attr('id', 'favorite').html()
//=> <li class = "apple" id = "favorite">Apple</li>

$('.pear').removeAttr('class').html() //移除名为name的属性
//=> <li>Pear</li> 

$('.pear').hasClass('pear')//检查元素是否含有此类名
//=> true

$('apple').hasClass('fruit')
//=> false

$('li').hasClass('pear')
//=> true

$('.pear').addClass('fruit').html()//添加类名到所有的匹配元素,可以用函数作为参数
//=> <li class = "pear fruit">Pear</li>

$('.apple').addClass('fruit red').html()
//=> <li class = "apple fruit red">Apple</li>

//移除一个或者多个(空格分隔)的类名,如果className为空,则所有的类名都会被移除,可以传递函数作为参数
$('.pear').removeClass('pear').html()
//=> <li class = "">Pear</li>

$('.apple').addClass('red').removeClass().html()
//=> <li class = "">Apple</li>

正则表达式: 形如 /pattern/modifiers; 的表达式

//search() 返回位置,无返回-1
var str = "Visit W3School!";
var n = str.search("W3School"); // n=6

var str = "Visit W3School";
var n = str.search(/w3school/i); // n=6

//replace() 返回模式被替换处修改后的字符串
var str = "Visit Microsoft!";
var res = str.replace("Microsoft", "W3School");  //Visit W3School!

var str = "Visit Microsoft!";
var res = str.replace(/microsoft/i, "W3School");  //Visit W3School!

//test() 通过模式来搜索字符串,然后根据结果返回 true 或 false
var patt = /e/;
patt.test("The best things in life are free!"); // true
/e/.test("The best things in life are free!"); // true

//exec() 通过指定的模式(pattern)搜索字符串,并返回已找到的文本
/e/.exec("The best things in life are free!"); // e
修饰符 描述
i 执行对大小写不敏感的匹配。
g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m 执行多行匹配。
表达式 描述
[abc] 查找方括号之间的任何字符。
[0-9] 查找任何从 0 至 9 的数字。
(x|y) 查找由 | 分隔的任何选项。
元字符 描述
\d 查找数字。
\s 查找空白字符。
\b 匹配单词边界。
\uxxxx 查找以十六进制数 xxxx 规定的 Unicode 字符。
量词 描述
n+ 匹配任何包含至少一个 n 的字符串。
n* 匹配任何包含零个或多个 n 的字符串。
n? 匹配任何包含零个或一个 n 的字符串。

一些简单的转义字符:\. and / ,因为那两个符号都有其他用处。

网页编码

接着,一个蛮重要的问题是网页的编码。有些是GBK,有些是UTF-8。一个简单的方法就是直接在google的console中输入

document.charset

返回值就是网页的编码,这样就不需要在源码中找关于charset的信息。

一个失败的例子:网易新闻(新闻内容使用GBK显示的)

var source_name = "网易新闻";
var domain = 'https://news.163.com/';
var myEncoding = "GBK";
var seedURL = 'https://news.163.com/';

var seedURL_format = "$('a')";
var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var title_format = " $('meta[property=\"og:title\"]').eq(0).attr(\"content\")";
var date_format = "('.post_time_source').text()"|"(#ptime).text()"|"('meta[property=\"article:published_time\"]').eq(0).attr(\"content\")";
var author_format =" $('meta[name=\"author\"]').eq(0).attr(\"content\")";
var content_format = "$('#endText').text()"|"$('.post_body').text()";
var desc_format = " $('meta[name=\"description\"]').eq(0).attr(\"content\")";
var source_format =" $('meta[property=\"og:url\"]').eq(0).attr(\"content\")";
var url_reg =/\/(\d{2})\/(\d{4})\/(\d{2})\/(\w{10,30}).html/;

var regExp = /((\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2})|(\d{4}年\d{1,2}月\d{1,2}日)|(\d{4}\-\d{2}\-\d{2})/

这样的话关于爬虫中的字符串内容就差不多了!

注意:keywords,date,author等都是从对应新闻中找的

例子如下:

    <meta name="author" content="chinanews" />
    <meta name="copyright" content="www.chinanews.com,版权所有" />
    <meta name="keywords" content="接种,新冠,疫苗接种,华盛顿邮报,纽约时报" />
    <meta name="description" content="综合报道,鉴于美国已报告6例因接种强生疫苗,出现罕见严重血栓病例,美国卫生当局13日发表声明,建议暂停接种强生新冠疫苗。针对有关新冠疫苗供应量的担忧,当地时间13日,美国总统拜登回应称,现在全国的供应量已经足够给“每一个美国人”接种。" />
    
      <div id="BaiduSpider" style="display:none">
          <span id="pubtime_baidu">2021-04-14 08:34:37</span>
          <span id="source_baidu">来源:<a href="http://www.chinanews.com./gj/2021/04-14/9454285.shtml" target="_blank">中国新闻网</a></span>
          <span id="author_baidu">作者:李玉素</span>
          <span id="editor_baidu">责任编辑:李玉素</span>

下面是crawl的代码:

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 source_name = "中国新闻网";
var domain = 'http://www.chinanews.com/';
var myEncoding = "utf-8";
var seedURL = 'http://www.chinanews.com/';

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/;
//因为标准新闻链接长这样:www.chinanews.com/gj/2021/04-12/9452760.shtml


var regExp = /((\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2})|(\d{4}年\d{1,2}月\d{1,2}日)/

//防止网站屏蔽我们的爬虫
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,
        //proxy: 'http://x.x.x.x:8080',
        headers: headers,
        timeout: 10000 //
    }
    myRequest(options, callback)
};

seedget();

function seedget() {
    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) };
        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 fetches 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); //读取新闻页面
            });
        });
    });
};

function newsGet(myURL) { //读取新闻页面
    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();

        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);
        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));

        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写入
    });
}

简单的前后端

<!DOCTYPE html>
<html>

<body>
    <form action="http://127.0.0.1:8080/process_get" method="GET">
        <br> 标题:<input type="text" name="title">
        <input type="submit" value="Submit">
    </form>
    <script>
    </script>
</body>

</html>
var express = require('express');
var mysql = require('./mysql.js')
var app = express();
//app.use(express.static('public'));
app.get('/7.03.html', function(req, res) {
    res.sendFile(__dirname + "/" + "7.03.html");
})
app.get('/7.04.html', function(req, res) {
    res.sendFile(__dirname + "/" + "7.04.html");
})
app.get('/process_get', function(req, res) {
    res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' }); //设置res编码为utf-8
    //sql字符串和参数
    var fetchSql = "select url,source_name,title,author,publish_date from fetches where title like '%" +
        req.query.title + "%'";
    mysql.query(fetchSql, function(err, result, fields) {
        console.log(result);
        res.end(JSON.stringify(result));
    });
})
var server = app.listen(8080, function() {
    console.log("访问地址为 http://127.0.0.1:8080/7.03.html")

})

用express构建网站访问mysql

  1. 拷贝mysql.js以访问数据库

  2. 修改index.html

    var express = require('express');
    var router = express.Router();
    var mysql = require('../mysql.js');
    
    /* GET home page. */
    router.get('/', function(req, res, next) {
        res.render('index', { title: 'Express' });
    });
    
    router.get('/process_get', function(request, response) {
        //sql字符串和参数
        var fetchSql = "select url,source_name,title,author,publish_date " +
            "from fetches where title like '%" + request.query.title + "%'";
        mysql.query(fetchSql, function(err, result, fields) {
            response.writeHead(200, {
                "Content-Type": "application/json"
            });
            response.write(JSON.stringify(result));
            response.end();
        });
    });
    module.exports = router;
    
  3. 增加search.html

    <!DOCTYPE html>
    <html>
    <header>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    </header>
    
    <body>
        <form>
            <br> 标题:<input type="text" name="title_text">
            <input class="form-submit" type="button" value="查询">
        </form>
        <div class="cardLayout" style="margin: 10px 0px">
            <table width="100%" id="record2"></table>
        </div>
        <script>
            $(document).ready(function() {
                $("input:button").click(function() {
                    $.get('/process_get?title=' + $("input:text").val(), function(data) {
                        $("#record2").empty();
                        $("#record2").append('<tr class="cardLayout"><td>url</td><td>source_name</td>' +
                            '<td>title</td><td>author</td><td>publish_date</td></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>');
                        }
                    });
                });
    
            });
        </script>
    </body>
    
    </html>
    

启示和可修改之处

  1. 首先,要加入更多的新闻网站(便于搜索)这里的难点在于每一个对应的网站规则都不一样,因此要为每一个网站设计一套正则表达式来爬取时间标题等内容。正则表达式可以通过https://regex101.com/来进行学习,里面的测试题有一定的难度。

  2. 其次,仅仅表格展示未免有些丑陋,可以通过添加CSS来增加项目的观赏程度。(没有功能上的意义)这里不能套用前端的框架进行美化,因此只要适当进行调参即可。可以进行分页显示。虽然米色有点简陋但蛮好看的(误)

  3. 时间序列分析(其实可以理解成为按照时间或者重要性依次递减)我们可以采用按照时间递减的方式进行,这样比较方便。

项目展示

这里先展示关键代码:

新浪

var source_name = "新闻中心—新浪网";
var domain = 'https://news.sina.com.cn/';
var myEncoding = "utf-8";
// var seedURL = 'https://news.sina.com.cn/';
// var seedURL = 'https://news.sina.com.cn/world/';
var seedURL = 'https://news.sina.com.cn/china/';

var seedURL_format = "$('a')";
var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var title_format = " $('meta[property=\"og:title\"]').eq(0).attr(\"content\")";
var date_format = " $('meta[property=\"article:published_time\"]').eq(0).attr(\"content\")";
var author_format = " $('meta[property=\"article:author\"]').eq(0).attr(\"content\")";
var content_format = "$('.article').text()";
var desc_format = " $('meta[property=\"og:description\"]').eq(0).attr(\"content\")";
var source_format =  " $('meta[property=\"og:url\"]').eq(0).attr(\"content\")";
var url_reg = /news.sina.com.cn\/.\/(\d{4})-(\d{2})-(\d{2})\/doc-[a-z0-9]{10,}.shtml/;

var regExp = /(\d{4})-(\d{2})-(\d{2})/

搜狐

var source_name = "搜狐网新闻";
var domain = 'http://news.sohu.com/';
var myEncoding = "utf-8";
// var seedURL = 'https://learning.sohu.com/?spm=smpc.ch25.search.1.1618716258248gsyC4Ut'; // edu
// var seedURL = 'https://it.sohu.com/?spm=smpc.ch23.header.9.1618716421360hhQRwQj'; // tech
var seedURL = 'https://business.sohu.com/';

var seedURL_format = "$('a')";
var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var title_format = " $('meta[property=\"og:title\"]').eq(0).attr(\"content\")";
var date_format = " $('meta[property=\"og:release_date\"]').eq(0).attr(\"content\")"|" $('meta[itemprop=\"datePublished\"]').eq(0).attr(\"content\")";
var author_format = " $('meta[name=\"mediaid\"]').eq(0).attr(\"content\")";
var content_format = "$('.article').text()";
var desc_format = " $('meta[property=\"og:description\"]').eq(0).attr(\"content\")"|" $('meta[name=\"description\"]').eq(0).attr(\"content\")";
var source_format =  " $('meta[property=\"og:url\"]').eq(0).attr(\"content\")";
var url_reg = /sohu.com\/a\/.{20,}/;

var regExp = /(\d{4})-(\d{2})-(\d{2})/

搜索页面

<!DOCTYPE html>
<html>
<header>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
    <style>
        h1
        {
            color:rosybrown; 
            text-align: center;
        }

        body
        {
            background-color:whitesmoke;
            margin:0px;
        }

        form
        {
            text-align: center;
        }
        form input.search
        {
            width: 400px;
            height: 25px;
        }
        form input.form-submit
        {
            height: 25px;
            font-size: 18px;
            background-color: royalblue;
            color:white;
        }

        table
        {
            border-collapse: collapse;
            margin: 30px 0px 120px;
            
        }

        table td, table th
        {
            border: 1px solid #cad9ea;
            color: #666;
            height: 30px;
        }
        table th
        {
            background-color: #CCE8EB;
            width: 100px;
        }
        table tr:nth-child(odd)
        {
            background: #fff;
        }
        table tr:nth-child(even)
        {
            background: #F5FAFA;
        }

        footer
        {
            background-color:gray;
            color:white;
            height :100px;
            width:100%;
            text-align: center;
            position:sticky;
            bottom:0;
        }

        

    </style>
</header>

<body>
    <h1>小型网页文字爬虫</h1>
    <form>
        <br> <input type="text" name="title_text" class="search">
        <input class="form1" type="checkbox" >title
        <input class="form2" type="checkbox">keywords
        <input class="form3" type="checkbox">content
        <input class="form-submit" type="button" value="search">
    </form>
    <div class="cardLayout" style="margin: 0 auto">
        <table width="100%" id="record2"></table>
    </div>
    <script>
        $(document).ready(function() {
            $("input:button").click(function() {
                var title=$('input.form1').is(":checked");
                var keywords=$('input.form2').is(":checked");
                var content=$('input.form3').is(":checked");
                if (title.valueOf()==false && keywords.valueOf()==false && content.valueOf()==false)
                {
                    alert("请至少选择一项作为搜索范围~");
                    return;
                }
                var words=$('input.search').val();
                $.get('/process_get?title='+ title +'&keywords='+keywords+'&content='+content+  '&words='+words, function(data) {
                    $("#record2").empty();
                    $("#record2").append('<tr class="cardLayout"><td>URL</td><td>数据来源</td>' +
                        '<td>新闻标题</td><td>作者</td><td>发布日期</td></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>');
                    }
                });
            });

        });
    </script>
    <footer class="color-F00"> 

        <br>web编程期中项目展示,若有问题请及时联系我。 thank you for your support :)<br><br>
        <address>联系邮箱:377206338@qq.com / 10195501437@stu.ecnu.edu.cn</address>
          
    </footer> 
</body>

</html>

后端

var express = require('express');
var router = express.Router();
var mysql = require('../mysql.js');

/* GET home page. */
router.get('/', function(req, res, next) {
    res.render('index', { title: 'Express' });
});

router.get('/process_get', function(request, response) {
    //sql字符串和参数
    var fetchSql = "select url,source_name,title,author,publish_date from fetches where";
    var conc=false;
    if (request.query.title=="true"){
        if (!conc){
            conc=true;
        }
        else fetchSql+=" or"
        fetchSql+=" title like '%"+request.query.words + "%'";
    }
    if (request.query.keywords=="true"){
        if (!conc){
            conc=true;
        }
        else fetchSql+=" or"
        fetchSql+=" keywords like '%"+request.query.words + "%'";
    }
    if (request.query.content=="true"){
        if (!conc){
            conc=true;
        }
        else fetchSql+=" or"
        fetchSql+=" content like '%"+request.query.words + "%'";
    }

    fetchSql+=" order by publish_date DESC";
    fetchSql+=";";

    mysql.query(fetchSql, function(err, result, fields) {
        response.writeHead(200, {
            "Content-Type": "application/json"
        });
        response.write(JSON.stringify(result));
        response.end();
    });
});
module.exports = router;

网页展示

搜索界面
小型新闻爬虫查询网站

一些搜索展示
小型新闻爬虫查询网站

项目总结

完成了爬虫的一般内容,并且稍微修改了网站的样貌,使其变得现代一点。

这里的数据是放在SQL里的,因此把项目打包之后需要自己手动爬数据。

有很多内容其实还一知半解,比如node.js里面的各种操作,express框架原理等等,但之后一定会去补坑的。

在项目开始之前仅仅有最基本的html能力,CSS当时还不知道,更别提JS了:),现在至少了解了最基本的网站制作和爬虫。

十分感谢老师的例子和助教的答疑,让基础薄弱的我能够在短短两周写完一个小型的网站。

要忙其他事了,这个项目在之后会有升级(期末大作业),看到这的人应该不多了吧(误)。如果你是其中的一位的话,谢谢你们的支持!

上一篇:使用requests遇到的坑


下一篇:php使用PDO获取结果集的方法