2021-04-27

CJY de 实验

目录

实验成果展示

准备工作

实验过程

step 01--调包

step 02--通过request工具包访问欲爬取的网页并储存

web1.新浪财经网

web2.网易体育网

step03--前端

step04--后端

实验收获!


本来的实验项目是这样的,然后要求又继续跟进ing!

真的是很Alexander啊!!But!作为一个一直跃跃欲试又蠢蠢欲动的新手小白,说罢就开工!!

然后最终实验成果就是这样的。。。。。。

实验成果展示

准备工作

1.安装vscode

2.安装Node.js

3.在控制台安装一些需要de工具包

npm install request     //默认get请求
npm install cheerio     //筛选网页信息
npm install mysql       //存储信息
npm install fs          //读取
npm install iconv-lite  //能用来解决乱码问题

实验过程

因为真的是零基础小白,就先研究了老师给出的爬取中国新闻网的代码,发现里面真的是好错综复杂,看了好久愣是没看懂,于是就请教了一位学姐,她在基础上给我讲解了各语句的作用,磨了大概有四五天之后,终于有点懂了!!然后我就动手重新开了一个模板,毕竟自己写的代码思路更容易看懂啦!!!!(虽然老师写的看起来更高级wuwuwu)

step 01--调包

就像C++一样,打上#include<iostream>

所以我们----不管内容先调包!!

'use strict';
var cheerio = require('cheerio');
var request = require('request');
var fs = require('fs');
var database = require('./mysql.js');

//这里就用到了上面在控制台上安装的工具包啦!!

step 02--通过request工具包访问欲爬取的网页并储存

web1.新浪财经网

爬虫部分

确实,贫穷的学生党--我!最近在买基金做理财,虽然上个月踏了青,but!我还是想先爬一个财经网,以便不时关注一下,然后我就在芸芸财经网里面选到了新浪!

其实主要也是因为新浪比较好爬,适合我这种初来乍到的萌新。

(在网页里面右击“检查”即可看代码)

新浪财经网我选择从<ul class="m-list">开始爬,因为前面几则的url和后面的实在是太不一样了,所以权衡之下果断放弃!

request({ url: "https://finance.sina.com.cn/", encoding: null, headers: null }, function (err, res, body) {   //这里的url即欲爬的网站地址
	if (err || res.statusCode != 200) {    //从这里开始
		console.error(err);                //
		console.error(res.statusCode);     //
		return;                            //
	}                                      //到这里结束是判断网站的可爬性,若不可爬直接就返回五内容啦~
	let $ = cheerio.load(body);          //若可爬那就把网站的内容都暂时存到$符号里面去,就是moneymoneymoney~

从这里开始正式进入网站!

所有的let都跟C++的局部变量差不多,而var则跟全局变量类似

    let cnt = 0;                                   //在这里铺垫第一个循环,用cnt来记录循环次数
    let ulArr = $("ul.m-list").eq(cnt);             //将一开始选定的ul class="m-list"存储到ulArr中去
	while (ulArr.text()) {                         //第一个循环,若该内容有东西可爬,则进入循环
		let cnt1 = 0;                               //在这里铺垫第二个循环,用cnt1来记录循环次数
		let liArr = ulArr.children("li").eq(cnt1); //找到ul下面的子标签li,用children("li")
		while (liArr.text()) {                      //第二个循环,若该内容有东西可爬,则进入循环
			let cnt2 = 0;                          //从这里就开始为二级网站的循环做铺垫
			let aArr = liArr.children("a").eq(cnt2);//从这里就开始进入了二级网站

这里出现的循环是因为每个标签下面有很多一样的子标签可爬,这样经过几次循环就可以把全部的内容给爬下来。

进入了二级网站以后直接去找想爬的内容,网页的源代码实在是太复杂了wuwuwu,所以我就找了标题,来源,时间和正文,而且更令人窒息的是,新浪财经的来源居然是在正文里的,而且跟在时间后面的来源是个超链接,哎呀这就难倒我了,我就去网上查资料看怎么把来源分离出来,愣是没怎么看懂wuwuwu,所以爬的第一个网站的来源就这样开了天窗。

标题在<h1 class="main-title">里面

时间在<span class="date">里面

正文在<div class="article">里面

由上面几张图就可以爬到二级网站的标题,时间和正文啦~

res.request.uri.href,               //URL
$("h1.main-title").text(),           //标题
$("a.source ent-source").text(),    //来源(爬不出)
$("span.date").text(),               //时间
$("div.article").text(),            //正文

最后把结尾的循环给补上~

                cnt2++;                                 //二级网站的循环
				aArr = liArr.children("a").eq(cnt2);
				urlstr = aArr.attr("href");
				title = aArr.text();
			}                                           //第二个循环
			cnt1++;
			liArr = ulArr.children("li").eq(cnt1);
		}                                               //第一个循环
		cnt++;
		ulArr = $("ul.m-list").eq(cnt);
	}
});

其实一开始我是把这些都存到本地的response文件夹里面了(这个要和爬虫代码所在的文件夹建在同一级)方便核对和修改,代码如下:

setTimeout(function () {
	for (let i in Url) {
		fs.writeFileSync("response/" + i + ".txt", `URL: ${Url[i]}\nTitle: ${Title[i]}\nSource: ${Source[i]}\nContent: ${Content[i]}\nTime: ${Time[i]}`);
	}
}, 10000);

然后第一个网站就这样爬完啦,完整代码如下:

'use strict';
var cheerio = require('cheerio');
var request = require('request');
var fs = require('fs');
var database = require('./mysql.js');

request({ url: "https://finance.sina.com.cn/", encoding: null, headers: null }, function (err, res, body) {
	if (err || res.statusCode != 200) {
		console.error(err);
		console.error(res.statusCode);
		return;
	}
	let $ = cheerio.load(body);

	let cnt = 0;
	let ulArr = $("ul.m-list").eq(cnt);
	while (ulArr.text()) {
		let cnt1 = 0;
		let liArr = ulArr.children("li").eq(cnt1);
		while (liArr.text()) {
			let cnt2 = 0;
			let aArr = liArr.children("a").eq(cnt2);
			while (aArr.text()) {
				let urlstr = aArr.attr("href");
				let title = aArr.text();
				if (urlstr && title) {
					console.log(`爬取${title}`);
					request({ url: urlstr, encoding: null, headers: null }, function (err, res, body) {
						if (err || res.statusCode != 200) {
							console.error(err);
							console.error(res.statusCode);
							return;
						}
						let $ = cheerio.load(body);

						database.query('INSERT INTO xinlang (URL, Title, Source, Time, Content) VALUES(?, ?, ?, ?, ?);', [
							res.request.uri.href,
							$("h1.main-title").text(),
							$("a.source ent-source").text(),
							$("span.date").text(),
							$("div.article").text(),
							], function (err, vals, fields) {
							if (err) {
								console.error(`数据库错误:${err}`);
							}
						});
						console.log(`完成爬取${$("h1.post_title").text()}`);
					});
				}
				cnt2++;
				aArr = liArr.children("a").eq(cnt2);
				urlstr = aArr.attr("href");
				title = aArr.text();
			}
			cnt1++;
			liArr = ulArr.children("li").eq(cnt1);
		}
		cnt++;
		ulArr = $("ul.m-list").eq(cnt);
	}
});

说明一下,我在中间加的  console.log(`爬取${title}`);  和  console.log(`完成爬取${$("h1.post_title").text()}`);  是为了直接看是否进入循环,能爬到哪些东西。

在新浪财经网爬出了一百多条数据。

数据库部分

一开始对怎么把爬下来的数据存到数据库里我是真的没有一点头绪,问了同学说都是用老师提供的代码的,而我的代码就不能直接应用老师的存入数据库的方法了。期间有想过尝试改一改老师储存的代码,但是还是看不怎么懂,我就再次问了学姐,学姐推荐了Navicat for MySQL,但是鉴于这个软件要付费,在一个博主的博客里面寻找使用方法。

原博在这里:Navicat for Mysql 使用方法_xiaoshisande的博客-CSDN博客

但是我一直卡在建立连接那里,说是mysql的权限太高了无法连接,网上说要修改my.ini,但是我怎么都没找到这个文件,试了大概两个多小时,果断放弃了。

然后我就换了一个方法,想直接在vscode里面连接mysql,也不需要通过其它软件,步骤简单了不少。

这是直接连接参考的博客:(3条消息) vscode 下使用mysql_antRain的博客-CSDN博客

而从爬虫代码连接到mysql的代码如下:

database.query('INSERT INTO xinlang (URL, Title, Source, Time, Content) VALUES(?, ?, ?, ?, ?);', [
							res.request.uri.href,
							$("h1.main-title").text(),
							$("a.source ent-source").text(),
							$("span.date").text(),
							$("div.article").text(),
							], function (err, vals, fields) {
							if (err) {
								console.error(`数据库错误:${err}`);
							}

这里的xinlang是我储存数据的表名,存在叫pachong_sister的数据库里面。括号里面的五个变量URL, Title, Source, Time, Content就是表里的五列内容头,在VALUES()里面的?代表五个变量的意思,下面的五行分别和上面的五个变量相对应,千万不能写岔了!

建立数据库和表的步骤在命令提示符里面实行:

这里的‘xln’改成‘xinlang’(第一次创建表的时候没有截屏,所以就截了后来新建叫‘xln’的表的内容)

而建立表的代码如下:

CREATE TABLE `xinlang` (
  `URL` varchar(200) DEFAULT NULL,      //
  `Title` varchar(200) DEFAULT NULL,    //
  `Source` varchar(200) DEFAULT NULL,   //
  `Time` text DEFAULT NULL,             //
  `content` longtext,                   //在表里面开辟了五列内容,分别是URL,Title,Source,Time,content
  UNIQUE KEY `URL_UNIQUE` (`URL`)       //防止有重复的网址
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

(注释都在代码后面啦~,直接在mysql的命令提示符里黏贴就好)

最后运行出来的结果如下:

差不多就是这样,因为表格太大截不完。

后来在navicat破解版里面的数据库是这样的:

然后就结束啦~~

web2.网易体育网

爬虫部分

完整代码如下:

'use strict';
var cheerio = require('cheerio');
var request = require('request');
var fs = require('fs');
var iconv = require('iconv-lite');
var database = require('./mysql.js');

request({ url: "https://sports.163.com/", encoding: null , headers: null }, function (err, res, body) {
	if (err || res.statusCode != 200) {
		console.error(err);
		console.error(res.statusCode);
		return;
	}
	let $ = cheerio.load(iconv.decode(body, "gbk"));   

	let cnt = 0;
	let ulArr = $("ul.topnews_nlist").eq(cnt);
	while (ulArr.text()) {
		let cnt1 = 0;
		let liArr = ulArr.children("li").eq(cnt1);
		while (liArr.text()) {
			let cnt2 = 0;
			let aArr = liArr.children("h3").children("a");
			if(!aArr.text()){
				let aArr = liArr.children("h2").children("a");
			}
			while (aArr.text()) {
				let urlstr = aArr.attr("href");
				let title = aArr.text();
				if (urlstr && title && urlstr.split("/dy/").length < 2) {
					console.log(`爬取${title}`);
					request({ url: urlstr, encoding: null, headers: null }, function (err, res, body) {
						if (err || res.statusCode != 200) {
							console.error(err);
							console.error(res.statusCode);
							return;
						}
						let $ = cheerio.load(body);

						database.query('INSERT INTO pe (URL, Title, Source, Time, Content) VALUES(?, ?, ?, ?, ?);', [
							res.request.uri.href,
							$("h1.post_title").text(),
							$("div.post_info").text().toString().split("来源:")[1],
							$("div.post_info").text().toString().split("来源:")[0],
							$("div.post_body").text().toString().replace(/ /g, "")
							], function (err, vals, fields) {
							if (err) {
								console.error(`数据库错误:${err}`);
							}
						});
						console.log(`完成爬取${$("h1.post_title").text()}`);
					});
				}
				cnt2++;
				aArr = liArr.children("a").eq(cnt2);
				urlstr = aArr.attr("href");
				title = aArr.text();
			}
			cnt1++;
			liArr = ulArr.children("li").eq(cnt1);
		}
		cnt++;
		ulArr = $("ul.topnews_nlist").eq(cnt);
	}
});

我在这里修改了几个地方,下面我来列出:

改动1:

	if (err || res.statusCode != 200) {
		console.error(err);
		console.error(res.statusCode);
		return;
	}
	let $ = cheerio.load(iconv.decode(body, "gbk"));

最后一行我增加了用gbk(汉字内码扩展规范)来解码网易体育的一级网站,至于为什么呢?

因为我发现网易体育网不同于平常的用“utf-8”,而是用“gbk”,所以需要特殊修改。发现方法如下:

在 <meta http-equiv="Content-Type" content="text/html; charset=gbk"> 行发现它的charset(字符集)是用“gbk”来实现的。

改动2:

let cnt = 0;
	let ulArr = $("ul.topnews_nlist").eq(cnt);
	while (ulArr.text()) {
		let cnt1 = 0;
		let liArr = ulArr.children("li").eq(cnt1);
		while (liArr.text()) {
			let cnt2 = 0;
			let aArr = liArr.children("h3").children("a");
			if(!aArr.text()){
				let aArr = liArr.children("h2").children("a");
			}

在第二个循环里我做了改动,我发现:

在中间那一块我发现li的子标签分为 <h3> 和 <h2> 两个,而  <a> 就是在 <h3> 和 <h2> 的子标签内的,所以我们要分类进入二级网站。因而我让 aArr 先取 <h3> ,若发现里面没有任何东西,再让 aArr 取 <h2> ,当 aArr 取了 <h3> 或 <h2> 后,再进入二级循环,否则只能爬到一半。

改动3:

database.query('INSERT INTO pe (URL, Title, Source, Time, Content) VALUES(?, ?, ?, ?, ?);', [
							res.request.uri.href,
							$("h1.post_title").text(),
							$("div.post_info").text().toString().split("来源:")[1],
							$("div.post_info").text().toString().split("来源:")[0],
							$("div.post_body").text().toString().replace(/ /g, "")
							], function (err, vals, fields) {
							if (err) {
								console.error(`数据库错误:${err}`);
							}
						});

在这里的改动是爬取来源,时间和正文的时候。

看中间一列,我发现时间和来源都在 <div class="post_info"> 里面,就只能通过 split() 来切割。split("来源:")[1] 表示取 “来源:” 前面的内容,split("来源:")[0] 表示取 “来源:” 后面的内容。

分析得正文在 <div class="post_body"> 里面, 但是爬出来我发现正文内容里面有很多 " " ,所以我就通过正则表达式,将里面的 " " 全部替换成了 "" ,看起来更赏心悦目啦~

数据库部分

储存在vscode里的效果如下:

在Navicat for MySQL效果如下:

step03--前端

代码如下:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=1900">
    <title>Hannah's First Searching Web</title>
</head>

<body>
    <form action="/" method="GET">
        <h2>Let's find something!</h2>
        <input name="kw" type="text" style="width: 640px" placeholder="Please input your keywords..."/><input type="submit" value="Search" /></p>
    </form>
    <table border="1" id="xinlang">
        <caption style="font-size: 24px;">xinlang</caption>
        <tr>
            <th>URL</th>
            <th>Title</th>
            <th>Time</th>
            <th>content</th>
            <th>Source</th>
        </tr>
    </table>
    <table border="1" id="pe">
        <caption style="font-size: 24px;">pe</caption>
        <tr>
            <th>URL</th>
            <th>Title</th>
            <th>Time</th>
            <th>content</th>
            <th>Source</th>
        </tr>
    </table>
</body>
</html>

这里我是网上搜索了前端制作的方法copy下来,顺便问了学姐,改了一些大致的表格名字和内容做出来的(因为是真的没有html的基础啊!!小白哭泣wuwuwu)

大致做了一个名叫“Hannah's First Searching Web”的网页,并创建了两个表格,一个叫“xinlang”,一个叫“pe”,每个表格各有五列,就是上面爬虫代码中爬下来的五个信息,通过search来搜索需要的内容。

效果如下:

后来觉得太单调了,想加一个背景,但是搜来搜去都没找到怎么直接用vscode给前端编码增加背景,然后我就查到了用HBuilder来把图片存到img里面再增加的方法,方法链接:在html中插入整个页面的背景图_360新知 (so.com)

增加的代码:

<body background="img/kenan.jpg"
      style="background-repeat:no-repeat;
      background-size:100% 100%;
      background-attachment: fixed;">

最后前端效果如下:

step04--后端

代码如下:

'use strict';
var express = require('express');
var cheerio = require('cheerio');
var fs = require('fs');
var database = require('./mysql.js');
var port = process.env.PORT || 1337;

var server = express();
server.use(express.static("public"));

server.get('/', function (req, res) {
	res.writeHead(200, { 'Content-Type': 'text/html' });

	let AdditionSQL = "";

	var html = fs.readFileSync("index.html");
	let $ = cheerio.load(html);

	if (req.query.kw) {
		AdditionSQL = ` WHERE (Title LIKE '%${req.query.kw}%') OR (content LIKE '%${req.query.kw}%') OR (Source LIKE '%${req.query.kw}%')`;
	}
	database.query_noparam('SELECT * FROM xinlang' + AdditionSQL + ';', function(qerr, vals, fields) {                         //从xinlang表里面search
		if (qerr) {
			console.error(qerr);
			return;
		}
		for (let i in vals) {
			$('table#xinlang').append(`<tr>
	<td><a href=${vals[i].URL}>${vals[i].Title}</a></td>  //
	<td>${vals[i].Time}</td>                              //
	<td>${vals[i].content}</td>                           //
	<td>${vals[i].Source}</td>                            //mysql里面的数据
</tr>`);
		}

		database.query_noparam('SELECT * FROM pe' + AdditionSQL + ';', function (qerr, vals, fields) {                //从pe表里面search
			if (qerr) {
				console.error(qerr);
				return;
			}
			for (let i in vals) {
				$('table#pe').append(`<tr>
	<td><a href=${vals[i].URL}>${vals[i].Title}</a></td>  //
	<td>${vals[i].Time}</td>                              //
	<td>${vals[i].content}</td>                           //
	<td>${vals[i].Source}</td>                            //mysql里面的数据
</tr>`);
			}
			res.end($.html());
		});
	});
});

server.listen(port);

实验收获!

因为几乎没有任何html和js的基础(虽然项目前夕有认真猛补过一天),所以实验的过程极其艰难,甚至一开始的时候还不知道怎么下手,面对老师庞大的的爬虫参考代码,完全没有研究的冲动。但是后来也在不断地询问请教地过程中,慢慢完成这个了任务,也形成了一些自己的理解,毕竟,开始就是成功的一半,只要着手去做了,最终还是能到达成功的彼岸!

真的很感谢那些一直在帮助我答疑的学长学姐,助教和老师!!!!也很感谢勇敢去做的自己,同时也能成功帮助室友们完成任务,答疑解惑,因为能教会别人的知识点才是自己真正弄懂的知识啊!!

千万不要因为“不会”,因为“烦”而不去写!!实践才是学习~~~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值