提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、必要工具安装
- 二、爬取前操作
- 三、爬取新闻数据
- 四、建立个人网页展示爬取内容
- 总结
前言
网络爬虫是按照一定规则自动获取网络信息数据的一种程序
本次爬虫实验项目目标是选取新闻网站建立爬虫,对新闻页面进行分析,爬取出编码、标题、作者、时间、关键词、摘要、内容、来源等结构化信息并存储在数据库中,再建立个人网页提供对爬取内容的分项全文搜索。
一、必要工具安装
1.Node.js安装
Node.js是一个让 JavaScript 运行在服务端的开发平台,本次要使用Node.js实现网络爬虫。
选择合适的版本下载,然后不断next到选定地址安装。
判断安装成功:打开cmd,输入node -v与npm -v,若输出版本号即表示安装成功。
2.安装vscode插件
打开vscode应用商城下载node.js相关插件
3.安装数据库
这里我下载了phpstudy,使用里面自带的数据库进行后续操作
二、爬取前操作
为了使后端代码更加规范,这里使用了express框架
创建reptile文件夹存放代码模块等,用cmd运行express reptile来创建后端express项目
然后进入reptile目录下运行npm install,安装依赖包,生成package.json与package-lock.json。
打开package.json文件,修改node为nodemon,使得代码每次修改后自动重新启动服务器,之后安装nodemon模块并启动服务器。
成功创建express项目:
之后需创建爬虫路由,先创建爬虫代码(newsList.js)并在app.js中添加代码
var newsListRouter = require('./routes/newsList');
app.use('/newsList', newsListRouter)
页面内容如下:
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('reptile List');
});
module.exports = router;
添加爬虫需要的模块,通过npm install… -s 安装
其中cheerio是模块库,可从html的片断中构建DOM结构,然后提供像jquery一样的css选择器查询,快速、灵活、实施的jQuery核心实现。
request也是一个node.js的模块库,用于获取网页内容,即页面数据请求模块。
let cheerio = require('cheerio'); //爬虫模块
var request = require('request') //页面数据请求模块
const uk = require("unique-keygen")//生成唯一id,作为数据库id
接下来创建数据库连接,将之后爬取的新闻数据保存到数据库中
const mysql = require('mysql');
const pool = mysql.createPool({
host: "localhost",//数据库主机
user: "root",//用户名
password: "root",//密码
database: "news"//数据库名
});
function query(sql, params, callback) {
pool.getConnection(function(err, connection) {
if (err) {
console.log('数据库连接出错:' + err)
return
}
connection.query(sql, params, function(err, result) {
if (err) {
console.log('数据库查询出错:' + err)
return
}
callback(err, result);
})
connection.release(); //释放
})
}
exports.query = query;
三.爬取新闻数据
现在要正式开始爬取新闻数据了,先安装需要的包
可通过npm install...进行安装
若之后运行显示Error: Cannot find module 'XXX'时,说明没有安装
本项目选取了网易新闻、腾讯新闻与新浪新闻三个网站作为爬取对象,爬取方式近乎相同,这里以新浪新闻为例:
打开网页源代码分析页面结构
然后使用cheerio与request模块获取内容,爬取内容的同时将数据存至mysql,数据库表:
爬虫代码如下:
//新浪新闻
router.get('/xinlang_new', (req, res) => {
let url = 'https://culture.sina.cn/'
request({
method:'GET',
url:url,
},function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
var $ = cheerio.load(body);
var newsList = $('.card_module section')
var sql = `insert into news(id,title,detailLink,img,keyWords,type,source,publishTime) values(?,?,?,?,?,?,?,?)`
new Promise((resolve, reject) => {
newsList.each((i, ele) => {
var $ = cheerio.load(ele)
let id = uk(8)
let title = $('.m_f_con h2').text()
let source = $('.m_f_con_add').text()
let time = moment().format('YYYY-MM-DD HH:mm:ss')
let describe = ''
let img = $('section a img').attr('data-src') == undefined ? '' : $('section a img').attr('data-src')
let link = $('section a:first').attr('href')
db.query(sql, [id, title, link, img, describe, source, source, time], (err, row) => {
if (err) {
console.log(err)
return
}
console.log(row.affectedRows)
})
console.log(link)
})
resolve()
}).then(() => {
res.send({
flag: true,
msg: "爬取完毕"
})
})
}else{
console.log("error");
}
});
})
此外为了防止新闻网站屏蔽爬虫,需要进行伪装
function request(url, callback) {
var options = {
url: url,
encoding: null,
headers: headers,
timeout: 10000
}
myRequest(options, callback)
}
在页面执行:http://localhost:8880/newsList/xinlang_new,就开始自动爬取信息并保存到数据库中
四.建立个人网页展示爬取内容
如此爬取完网易腾讯新浪三个新闻网站后,需要建立个人网站展示爬取内容。
创建reptitle目录,并引入bootstrap文件和jquery,然后创建index.html
静态页面代码:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>XXX的爬虫网页</title>
</head>
<link rel="stylesheet" href="css/bootstrap.min.css">
<script src="js/jquery-3.5.0.js"></script>
<script src="js/bootstrap.min.js"></script>
<body>
<div class="container" id="container">
<div class="jumbotron py-5">
<h1>XXX的爬虫网页</h1>
</div>
<!--搜索-->
<div style="padding: 30px 0px;">
<form class="bs-example bs-example-form" role="form">
<div class="row">
<div class="col-lg-6">
<div class="input-group">
<input type="text" id="keyWord" class="form-control" placeholder="请输入搜索关键词">
<span class="input-group-btn">
<button class="btn btn-success" type="button" onclick="search()">搜索</button>
</span>
<span class="input-group-btn px-3">
<button class="btn btn-success" type="button" onclick="update()">刷新</button>
</span>
</div>
</div>
</div>
</form>
</div>
<div class="w-100">
<div id="list" class="w-75 card px-2">
<h2 class="fw-bolder py-3">新闻列表</h3>
<ul>
<!-- <a id="link" href="https://culture.sina.cn/2021-03-22/detail-ikkntiam6259503.d.html?vt=4">
<div class="new_list">
<div class="item-img">
<img id="item-img" src="https://k.sinaimg.cn/n/news/transform/56/w550h306/20210331/e901-knaqvqn7373581.png/w160h120l1t12b3.jpg" alt="">
</div>
<div class="item-right">
<div class="title">
<p>学的不仅是技术,更是梦想学的不仅是技术,更是梦想!学的不仅是技术,更是梦想!!</p>
</div>
<div class="item-foot">
<p class="">来源:腾讯新闻</p>
<p class="">2021-03-30 22:48:21</p>
</div>
</div>
</div>
</a> -->
</ul>
</div>
</div>
</div>
</body>
</html>
再使用ajax向后台异步请求数据,然后使用jquery动态把内容显示到页面
var page = 1 //页码
function getNews() {
$.ajax({
url: "http://localhost:8880/client_news/get_news?page=" + page,
type: "get",
success: function(obj) {
//把数据放到数组中
var data = obj.data
console.log(obj)
const list = document.getElementById('list')
for (let i = 0; i < data.length; i++) {
//添加结点
const a = document.createElement('a');
const new_list = document.createElement('div');
const item_img = document.createElement('div');
const ul = document.createElement('ul')
const img = document.createElement('img');
const item_right = document.createElement('div');
const title = document.createElement('div');
const item_foot = document.createElement('div');
const source = document.createElement('p');
const time = document.createElement('p');
const hr = document.createElement('hr');
//给结点添加内容
a.href = data[i].detailLink
img.src = data[i].img
title.innerHTML = data[i].title
source.innerHTML = data[i].source
time.innerHTML = crtTimeFtt(data[i].publishTime)
a.appendChild(new_list)
new_list.appendChild(item_img)
new_list.appendChild(item_right)
item_img.appendChild(img)
item_right.appendChild(title)
item_right.appendChild(item_foot)
item_foot.appendChild(time)
item_foot.appendChild(source)
item_right.appendChild(item_foot)
ul.appendChild(a)
ul.appendChild(hr)
list.appendChild(ul)
//给结点添加样式
addClass(new_list, 'new_list')
addClass(item_img, 'item-img')
addClass(item_foot, 'item-foot')
addClass(item_right, 'item-right')
addClass(title, 'title')
}
},
error: function(obj) {
console.log(obj)
}
});
}
//添加样式
function addClass(ele, str) {
if (ele.className) {
var oriName = ele.className;
var newClass = oriName + "" + str;
ele.className = newClass;
} else {
ele.className = str;
}
}
类似于创建newsList路由,现创建client_news.js来使后台服务器获取数据并返回前端
const { query } = require('express');
var express = require('express');
var router = express.Router();
var db = require('../db/db')
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
router.get('/get_news', (req, res) => {
let page = req.query.page
if (page == 1) {
var pageIndex = 0
} else {
var pageIndex = (page - 1) * 10 + 1
}
let sql = `select * from news order by publishTime DESC limit ${pageIndex},10`
db.query(sql, (err, data) => {
if (err) {
console.log(err)
return
} else if (data.length > 0) {
res.send({
flag: true,
data: data
})
} else {
res.send({
flag: false,
data: '没有更多数据'
})
}
})
})
module.exports = router;
页面打开后执行getNews函数,最后呈现的页面效果:
接下来要实现网页搜索功能
//搜索
router.post('/searchNews', (req, res) => {
let { keyWords } = req.body
console.log(req.body)
keyWords = "%" + keyWords + "%"
let sql = `select * from news where title like '${keyWords}' or keyWords like '${keyWords}'`
db.query(sql, (err, data) => {
if (err) {
console.log(err)
return
} else if (data.length > 0) {
console.log(data)
res.send({
flag: true,
data: data
})
} else {
res.send({
flag: false,
msg: "没有更多数据"
})
}
})
})
搜索后台代码:
//搜索
router.post('/searchNews', (req, res) => {
let { keyWords } = req.body
console.log(req.body)
keyWords = "%" + keyWords + "%"
let sql = `select * from news where title like '${keyWords}' or keyWords like '${keyWords}'`
db.query(sql, (err, data) => {
if (err) {
console.log(err)
return
} else if (data.length > 0) {
console.log(data)
res.send({
flag: true,
data: data
})
} else {
res.send({
flag: false,
msg: "没有更多数据"
})
}
})
})
还要实现页面滚动到底加载更多数据
//滚动到底部加载更多信息
$(window).scroll(function() {
var scrollTop = $(this).scrollTop();
var scrollHeight = $(document).height();
var windowHeight = $(this).height();
if (scrollTop + windowHeight+50>scrollHeight) {
page += 1
if (!isSearch) {
getNews()
}
}
console.log("lower")
});
效果如下
最后要实现能够刷新页面
//刷新
var isSearch = false //是否有进行搜索操作
function update() {
isSearch = false
getNews()
}
如此就实现了可搜索、刷新的新闻页面,项目基本完成
总结
基本了解了实现网络爬虫的步骤及部分原理,但对代码的具体编写仍有所欠缺,部分代码参考了老师以及网上的内容。css方面知识尤其欠缺使得网页略单调,并且使用了express框架(要求中尽量不使用框架)。感觉爬取数据本身难度不是很大,主要是后续难度较大且过程繁琐,从网上查阅了很多爬虫案例和代码才得以实现,日后仍需加强对这部分的练习理解。