Web 编程期中大作业

Web 编程期中大作业:

node.js进行网页爬虫

前言

在做期中作业之前,我们已经学习了HTML,CSS,Node.js,javascript的相关知识,但这个项目还是令我感到一头雾水,不知从何下手,好在有老师给出的爬虫代码示例,我就先从示例开始尝试

初次尝试

1、准备工作
在准备的时候发生了一些小插曲,走了很多弯路
按照老师的要求配置node.js,并准备vscode中JavaScript的调试文件,以便后续调试代码。接着使用命令:npm install (module name)下载各种模块,然后发现下载完成后总是提示:缺少request模块。
于是请教老师,发现是没有转换到node所在的盘符操作,用cd 命令可以转换盘符
但是令人崩溃的是转换盘符后操作仍然没有成功,几经挣扎后我选择了卸载后重新安装Node。重新安装后终于成功了。

2、简单爬虫
按照老师PPT里给的简单爬虫示例进行华东师大网站源代码的爬取

var myRequest = require('request') 
var myCheerio = require('cheerio')
var myURL = "https://www.ecnu.edu.cn"
function request(url, callback) {
    var options = {
        url: url,  encoding: null, headers: null
    }
    myRequest(options, callback)
}
request(myURL, function (err, res, body) {
    var html = body;  
    var $ = myCheerio.load(html, { decodeEntities: false });
    console.log($.html());
    console.log("title: " + $('title').text());     
})   

上面的代码运行成功会将华东师大官网的源代码全部爬取下来。

3、正式开始爬虫
爬虫任务:
1、读取种子页面
2、分析出种子页面里的所有新闻链接
3、爬取所有新闻链接的内容
4、分析新闻页面内容,解析出结构化数据
5、将结构化数据保存到数据库

配置数据库
1、下载MySql, 在命令行中cd c:\mysql\bin
2、安装mysql的服务:mysqld –install
3、初始化mysql:mysqld --initialize --console,初始化会产生一个随机密码
4、启动mysql:net start mysql
5、命令行执行 mysql -u root –p 进入mysql,按提示输入刚才得到的随机密码
6、进入mysql后可将密码修改为自己的密码:
alter user ‘root’@‘localhost’ identified by ‘root’;
Quit退出后再用mysql -u root –p 进入mysql,密码为root
7、给root用户授权从客户端访问:
alter user ‘root’@‘localhost’ identified with mysql_native_password by ‘root’;
flush privileges;
8、在node中配置mysql:npm install mysql;
到此数据库配置完毕

创建数据库:
1、创建数据库:create database crawl;
2、查看数据库:show databases;
在这里插入图片描述

3、使用数据库:use crawl;
4、创建表格:

CREATE TABLE `fetch1` (
  `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;

5、显示表格:show tables;
就能看到下面已经创建的表格(此处我创建了三个)
在这里插入图片描述
爬虫开始:
首先调用各种模块

var fs = require('fs'); //调用fs模块
var myRequest = require('request'); //调用request模块
var myCheerio = require('cheerio');  //调用cheerio模块
var myIconv = require('iconv-lite'); 
require('date-utils');
var mysql = require('./mysql.js'); //调用mysql

这边选择中国新闻网作为示例进行爬取

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

日期的正则表达式匹配

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,
        headers: headers,
        timeout: 10000 //
    }
    myRequest(options, callback)
};

读取种子界面并解析出种子页面里所有的a href链接(后面会发现并不是所有的a链接都是有用的新闻链接,很多事无效的)

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 fetch1 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); //读取新闻页面
            });
        });
    });
};

遍历种子页面里所有的a链接
规整化所有链接,如果符合新闻URL的正则表达式就爬取
并存储在数据库中

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 fetchAddSql = 'INSERT INTO fetch1(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);
            }
            console.log(vals);
        }); //mysql写入
    });
}

运行成功
在这里插入图片描述
下面进入数据库中查看表格数据
使用命令select url,title from fetch1;
在这里插入图片描述

在这里插入图片描述
就可以在表格中查看到爬取的新闻的url和title.
正式爬虫的示例就结束了。

自己设计爬虫项目

用老师给的示例调试运行成功后整个爬虫作业只成功了一半,下面我要根据自己爬取网页的特点修改代码来获取我想要的数据
准备工作:
首先要知道对于每个网页不同标签的选择需要用到JQuery选择器,在此之前我只在学习HTML和CSS的时候学过CSS的选择器,而在JQuery选择器这方面的语法是欠缺的,于是我开始补充学习JQuery语法:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(来源W3 school jQuery在线教程 : https://www.w3school.com.cn/jquery/index.asp)
现在jQuery选择器语法基本补全了,开始正式分析网页结构
分析网页:
因为我本人比较热爱运动,所以我选择了腾讯体育作为爬虫的网站
在这里插入图片描述
1、打开腾讯体育官网,空白处右键进行代码检查
这边先从head部分开始分析
在这里插入图片描述
2、注意到keywords 和 description两个标签很重要,于是源代码这两部分保持不变
在这里插入图片描述
3、把编码文字改成GBK
在这里插入图片描述
在这里插入图片描述
4、title 标题标签必然需要,代码不变(选择器语法:.text()函数选择title的内容)
在这里插入图片描述
在这里插入图片描述
5、改动内容格式
在这里插入图片描述
在这里插入图片描述
6、改变时间格式
在这里插入图片描述
在这里插入图片描述
7、改变来源格式
在这里插入图片描述
在这里插入图片描述
8、改变新闻页面内部内容格式
在这里插入图片描述
在这里插入图片描述
9、正则表达式
鉴于该部分即为重要,我现在网上将正则表达式的所有知识点系统地学习了一下,大体了解了各个符号的含义以及如何书写正则表达式
而爬虫代码里的正则表达式有两处
(1)是通过匹配新闻链接中的共同字符来达到筛选链接的作用
在这里插入图片描述
我们通过分析发现,新闻链接中都有/omn/这样的字符串表达式,因此,在写正则表达式的时候只需要匹配这样的字符串即可。
注意到 \ 在正则表达式中用 \ /表示,因此我们的正则表达式即为:
在这里插入图片描述
(2)另一处是时间的正则表达式
在这里插入图片描述
分析发现腾讯新闻的时间是“月/日/年”的形式,那么我们修改时间正则表达式为:
在这里插入图片描述
使用预定义类正则表达式:\ d代表0-9的数字,{1,2}代表一个或两个数字 ,后面类似
至此正则表达式修改完毕

10、mysql导入部分修改
注意到腾讯体育文章是没有注明作者的,因此将fetch部分原本的author注释掉
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
并且在用问号去匹配value值的时候问号数量要进行删减,否则会出错

11、seedget函数
有一些空链接或者无效链接不是我们想要爬的数据
因此加一个先导判断:
在这里插入图片描述
这样遇到未定义的链接就自动跳过

12、时间链接修改
有一些奇怪的时间我们是不需要的,因此在读取新闻链接的时候我们要对无效时间进行删除
在这里插入图片描述
好的,现在主体部分基本修改完毕,我们运行程序
在这里插入图片描述
爬取成功

接着我们登陆数据库
在这里插入图片描述
在这里插入图片描述
可喜的看到爬取的体育新闻标题已经存储入数据库

继续实操

爬取完体育领域,我来对新闻领域进行爬虫
我选择了网易新闻进行试验

var source_name = "网易新闻";
var domain = "https://news.163.com/";
var myEncoding = "utf-8";
var seedURL = "https://news.163.com/";

var seedURL_format = "$('a')";
var keywords_format = ' $(\'meta[name="keywords"]\').eq(0).attr("content")';
var desc_format = ' $(\'meta[name="description"]\').eq(0).attr("content")';
var title_format = "$('title').text()";
var content_format = "$('.post_body').text()";
var date_format = "$('.post_info').text()";
var source_format = "$('.post_info').text()";
var content_format = "$('.post_body').text()";
var url_reg = /article/;

var regExp2 = /(\d{4}(\-|\/|\.)\d{1,2}\2\d{1,2})/;

将jQuery以及正则表达式同样依据网页进行简单的修改以后,我很快就再次成功了!
在这里插入图片描述
上面腾讯体育以及网易新闻的爬取至此就结束了

用MySql简单命令提取关键信息

对于在数据库表格中繁杂的信息,我们可以使用mysql的语句进行关键字的选择
语法:(like关键字的使用)

select title from (表格) where title like "%关键字%"

这样我们就可以在找到我们要的关键字了
例如:我们需要查找带有“中国”关键字的标题,则:
在这里插入图片描述
数目明显减少了
到这里后端的数据爬取功能基本完成

前端网页制作

简单尝试

首先创建一个简单的网页,里面放上表单控件

<!DOCTYPE html>
<html>

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

</html>

效果如下
在这里插入图片描述
接着写后端代码用来作为前端网页查询的接收器

var http = require('http');
var fs = require('fs');
var url = require('url');
var mysql = require('./mysql.js');
http.createServer(function(request, response) {
    var pathname = url.parse(request.url).pathname;
    var params = url.parse(request.url, true).query;
    fs.readFile(pathname.substr(1), function(err, data) {
        response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
        if ((params.title === undefined) && (data !== undefined))
            response.write(data.toString());
        else {
            response.write(JSON.stringify(params));
            var select_Sql = "select title,author,publish_date from fetch1 where title like '%" +
                params.title + "%'";
            mysql.query(select_Sql, function(qerr, vals, fields) {
                console.log(vals);
            });
        }
        response.end();
    });
}).listen(8080);
console.log('Server running at http://127.0.0.1:8080/');

我们运行js文件并打开html网页输入查询关键词“中国”
在这里插入图片描述
在这里插入图片描述
就会看到查询结果显示在vscode终端中

第一次改进

上面的代码要么是在数据库中,要么是在IDE终端中显示爬取信息的功能,这样的功能显然我们是不满意的,我们下面一部要实现在网页中查找信息直接显示出结果的功能。

我们先将表单的action属性进行修改

<form action="http://127.0.0.1:8080/process_get" method="GET">

然后相应地在后端的js文件中进行修改

var express = require('express');
var mysql = require('./mysql.js')
var app = express();
app.get('/search.html', function(req, res) {
    res.sendFile(__dirname + "/" + "search.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 fetch1 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/search.html")

})

运行后打开网页输入关键字查询,就会显示下面爬取的结果
在这里插入图片描述

第二次改进

显然这样密密麻麻杂乱无章的结果不是我们想要的,我们想要实现用表格显示信息的功能
接着我按照老师ppt中的方法一步步操作:

1、我们先用express脚手架来创建一个网站框架
express –e search_site
-e的参数表明我们用ejs作为缺省view引擎,而非采用jade。

2、然后生成出一个search_site的文件夹
由于我们需要使用mysql,因此将mysql.js拷贝进这个文件夹

3、mysql.js拷贝后还需要在search_site文件夹内cmd运行:
npm install mysql –save
–save表示将mysql包安装到该项目中,并且将依赖项保存进package.json里

4、在search_site文件夹内cmd运行npm install
将package.json中列举的依赖项全部安装,完成网站搭建。
在这里插入图片描述
在这里插入图片描述
5、用vscode打开文件夹search_site
在route文件夹中修改index

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

6、在search_site/public/下创建一个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>

在search_site文件夹下运行cmd,输入 node bin/www
在浏览器里输入http://localhost:3000/search.html(注意要加端口号!)
或者127.0.0.1:3000/search.html也可以
在这里插入图片描述
输入关键字查询带有“中国”的信息
就会欣喜的发现网站url , 来源,标题,时间都按照表格的形式整齐地显示了,明显比原来的信息更整洁有序。
在这里插入图片描述
到这里我们前端网页查询爬虫信息的任务基本完成了
但是这样有点过于单调的网页我们是不想看到的,下一步我们考虑怎么用已经学过的html和css的知识把网页的页面布局进行修改润色,让它变得更美观

网页布局修改

因为不好直接对表单进行大量位置的修改,所以我们把表单的所有控件放到一个div盒子中操作

<div class="center">
(表单控件代码)
</div>

然后对center类直接修改,用CSS的选择器选中这个类

.center {
      position: fixed;
      top: 0px;
      right: 450px;
      width: 500px;
      height: 500px;
    }

这样就能把这个盒子移到页面上中部

接着对表单控件进行修改:
文本框
原来的文本框太小了,改变文本框宽度和高度使其变大,并且文本框里面显示 “请输入关键字”进行输入引导

 <input
          type="text"
          name="title_text"
          value="请输入关键字"
          style="width: 250px; height: 50px"
        />

实现效果:
在这里插入图片描述
按钮
首先我先加了一个“清空”按钮用来删除输入的内容
在这里插入图片描述
然后这两个按钮同样需要改变大小,为了美观我添加了不同的颜色

代码如下:

<input
          class="form-submit"
          type="button"
          value="查询"
          style="
            width: 100px;
            height: 50px;
            background-color: blueviolet;
            color: white;
            font-size: 20px;
            border-radius: 10px;
          "
        />
        <input
          type="reset"
          value="清空"
          style="
            width: 100px;
            height: 50px;
            background-color: blue;
            color: white;
            font-size: 20px;
            border-radius: 10px;
          "
        />

其中小细节:用border-radius添加圆角边框进行艺术性美化

实现效果如下:
在这里插入图片描述
明显美观了很多

小标题:
再添加小标题:
网易新闻查询
在这里插入图片描述
并修改字体属性

h1 {
      position:fixed;
      top: 0px;
      left: 350px;
      font-family: "华文中宋";
      font-size: 35px;
    }

效果如下:
在这里插入图片描述
这样字体相对来说更加美观大气

背景
最后,再给我的网页加上一个漂亮的背景

只需要给body部分加上style类型就可以添加背景

<body style="background: url(pexels-photo-457881.jpeg) ; background-size:100% 100% ; background-attachment: fixed">

最后所有效果如下图:
在这里插入图片描述

查询后结果如下:

在这里插入图片描述
但这样的显示格式我还是不太满意的,最好在信息周围加上表格
在csdn上进行搜索后我了解到加表格的方法:

table,table tr th, table tr td { border:3px solid #9900ff; }
        table { width: 1300px; min-height: 25px; line-height: 55px; 
        text-align: center; border-collapse: collapse;}   

这样就可给爬取的信息加上表格了
在这里插入图片描述
爬取的结果就是漂亮的表格了
简单但是我还是很有成就感滴!

后记:

至此本次作业就彻底完成了,当然其他一些效果我也没有能力去自己实现了,写完这篇博客突然想记录一下我的感受,看看过几年再看会有什么想法吧
这次web编程期中的大作业是我第一次做大作业,虽然有老师写好的代码示例作为参考,但是我还是费了无数周折才做出这样的效果。(可能是我实力还不够吧)在这里要感谢两位助教耐心对我代码的指导与修改。
其实想到后面还有期末大作业、听说下学期还有csapp的许多难度很大的大作业,我就很慌了,但是凡事总要去尝试、去探索才能有不同的感悟吧,这或许就是一个工科专业的魅力所在,也是大学学习的魅力所在。探索的过程虽然痛苦,但有成果总是令人欣慰的,每过一段时间回头看看之前的付出,总会感慨道这些都是有意义有价值的。
未来的路还很长,还有更多知识等着我去探索,继续努力吧!
(完)

  • 1
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值