继续用node.js爬虫

    技术2022-07-12  89

    作业要求

    爬虫完善

    雪球网

    var myRequest = require('request'); var myIconv = require('iconv-lite'); var myCheerio = require('cheerio'); var myEncoding = "utf-8"; var mysql = require('./mysql.js'); var source_name = "雪球网"; var domain = 'https://xueqiu.com/'; var myEncoding = "utf-8"; var seedURL = 'https://xueqiu.com/'; function request(url, callback) { var options = { url: url, encoding: null, //proxy: 'http://x.x.x.x:8080', headers: headers, timeout: 10000 // } myRequest(options, callback) }; 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' } seedget(); function seedget() { request('https://xueqiu.com/', function(err, res, body) { var html = myIconv.decode(body, myEncoding); var $ = myCheerio.load(html, { decodeEntities: true }); var seedurl_news; try { seedurl_news = eval($(".AnonymousHome_a__placeholder_3RZ")); } catch (e) { console.log('url列表所处的html块识别出错:' + e) }; seedurl_news.each(function(i, e) { var myURL = ""; var href=""; try { href=$(e).attr("href"); console.log(href); myURL='https://xueqiu.com'+href } catch (e) { console.log('识别种子页面中的新闻链接出错:' + e) } newsget(myURL) }); }); } function newsget(myURL){ request(myURL, function(err, res, body){ var html_news = myIconv.decode(body, myEncoding); var $ = myCheerio.load(html_news, { decodeEntities: true }); myhtml = html_news; console.log("转码读取成功:" + myURL); var fetch = {}; fetch.title = ""; fetch.content = ""; fetch.url = myURL; fetch.title=$("title").text(); fetch.content=$('meta[name="description"]').attr("content"); var fetchadd ='INSERT INTO fetches(url,title,content )VALUES(?,?,?)'; var fetchadd_params=[fetch.url,fetch.title,fetch.content]; mysql.query(fetchadd,fetchadd_params,function(qerr,vals,fields){}); }); }

    查看数据库可以看到新闻信息:

    项目文件

    (都是用的老师的代码,感谢老师和助教) 最终的项目文件夹: public: index.html :登陆页面(index.js是index.html的angular代码); news.html :登陆后跳转的页面(news.js是news.html的angular代码); search.html:查询新闻的页面,是news.html的一部分,被ng-include引入。 routes: users.js:用户的注册、登陆; news.js:查询、Echarts图操作、转发前端访问后端。

    首先在项目文件的终端中npm install,将所有依赖的node modules安装好。 这里出了点小问题,nodejieba安装失败: 卡住… 幸好有同学遇到同样问题,助教老师给出了解决方法: 可以了。

    然后在数据库中创建两张表,分别用来记录用户信息和用户操作(登陆、查询)。 建立连接MySQL配置文件:

    module.exports = { mysql: { host: 'localhost', user: 'root', password: 'root', database:'crawl', // 最大连接数,默认为10 connectionLimit: 10 } };

    实现用户可注册登陆网站,非注册用户不可登陆查看数据:

    登陆&注册页 (public/index.html)登陆&注册页路由(routes/users.js): var express = require('express'); var router = express.Router(); var userDAO = require('../dao/userDAO'); router.post('/login', function(req, res) { var username = req.body.username; var password = req.body.password; // var sess = req.session; userDAO.getByUsername(username, function (user) { if(user.length==0){ res.json({msg:'用户不存在!请检查后输入'}); }else { if(password===user[0].password){ req.session['username'] = username; res.cookie('username', username); res.json({msg: 'ok'}); // res.json({msg:'ok'}); }else{ res.json({msg:'用户名或密码错误!请检查后输入'}); } } }); }); /* add users */ router.post('/register', function (req, res) { var add_user = req.body; // 先检查用户是否存在 userDAO.getByUsername(add_user.username, function (user) { if (user.length != 0) { // res.render('index', {msg:'用户不存在!'}); res.json({msg: '用户已存在!'}); }else { userDAO.add(add_user, function (success) { res.json({msg: '成功注册!请登录'}); }) } }); }); // 退出登录 router.get('/logout', function(req, res, next){ // 备注:这里用的 session-file-store 在destroy 方法里,并没有销毁cookie // 所以客户端的 cookie 还是存在,导致的问题 --> 退出登陆后,服务端检测到cookie // 然后去查找对应的 session 文件,报错 // session-file-store 本身的bug req.session.destroy(function(err) { if(err){ res.json('退出登录失败'); return; } // req.session.loginUser = null; res.clearCookie('username'); res.json({result:'/index.html'}); }); }); module.exports = router; session信息(app.js): //设置session app.use(session({ secret: 'sessiontest',//与cookieParser中的一致 resave: true, saveUninitialized: false, // 是否保存未初始化的会话 cookie : { maxAge : 1000 * 60 * 60, // 设置 session 的有效时间,单位毫秒 }, }));

    实现查询词支持布尔表达式:

    查询页(public/search.html)查询页路由(routes/news.js): var newsDAO = require('../dao/newsDAO'); var express = require('express'); var router = express.Router(); var mywordcutModule = require('./wordcut.js'); var myfreqchangeModule = require('./freqchange.js'); router.get('/search', function(request, response) { console.log(request.session['username']); //sql字符串和参数 if (request.session['username']===undefined) { // response.redirect('/index.html') response.json({message:'url',result:'/index.html'}); }else { var param = request.query; newsDAO.search(param,function (err, result, fields) { response.json({message:'data',result:result}); }) } }); router.get('/histogram', function(request, response) { //sql字符串和参数 console.log(request.session['username']); //sql字符串和参数 if (request.session['username']===undefined) { // response.redirect('/index.html') response.json({message:'url',result:'/index.html'}); }else { var fetchSql = "select publish_date as x,count(publish_date) as y from fetches group by publish_date order by publish_date;"; newsDAO.query_noparam(fetchSql, function (err, result, fields) { response.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": 0 }); response.write(JSON.stringify({message:'data',result:result})); response.end(); }); } }); router.get('/pie', function(request, response) { //sql字符串和参数 console.log(request.session['username']); //sql字符串和参数 if (request.session['username']===undefined) { // response.redirect('/index.html') response.json({message:'url',result:'/index.html'}); }else { var fetchSql = "select author as x,count(author) as y from fetches group by author;"; newsDAO.query_noparam(fetchSql, function (err, result, fields) { response.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": 0 }); response.write(JSON.stringify({message:'data',result:result})); response.end(); }); } }); router.get('/line', function(request, response) { //sql字符串和参数 console.log(request.session['username']); //sql字符串和参数 if (request.session['username']===undefined) { // response.redirect('/index.html') response.json({message:'url',result:'/index.html'}); }else { var keyword = '疫情'; //也可以改进,接受前端提交传入的搜索词 var fetchSql = "select content,publish_date from fetches where content like'%" + keyword + "%' order by publish_date;"; newsDAO.query_noparam(fetchSql, function (err, result, fields) { response.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": 0 }); response.write(JSON.stringify({message:'data',result:myfreqchangeModule.freqchange(result, keyword)})); response.end(); }); } }); router.get('/wordcloud', function(request, response) { //sql字符串和参数 console.log(request.session['username']); //sql字符串和参数 if (request.session['username']===undefined) { // response.redirect('/index.html') response.json({message:'url',result:'/index.html'}); }else { var fetchSql = "select content from fetches;"; newsDAO.query_noparam(fetchSql, function (err, result, fields) { response.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": 0 }); response.write(JSON.stringify({message:'data',result:mywordcutModule.wordcut(result)}));//返回处理过的数据 response.end(); }); } }); module.exports = router; 查询结果的展示(public/search.html)

    实现爬虫数据查询结果列表支持分页: 当页面列表数据过多时,我们经常会收到将列表内容分页的需求,列表内容分页一般会有两种做法: 1、不需要后台配合,前台一次性拿完所有数据,然后进行分页展示;这种方式只是为了界面上对用户更友好,并没有实际提升页面的效率(数据量过大时页面加载压力比较大) 2、需要后台配合,后台对改数据做分页处理,页面每次只请求需要展示的该页面的数据,换页时需要二次请求,这种方式是比较推荐的 示例使用了AngularJs分页的实现,所以是采用第一种做法,不需要后台做数据分页上的支持 分页的代码都是用angular写的( public/javascripts/news.js):

    用Echarts实现4个数据分析图表:

    前端angular.js代码(public/javascripts/news.js): 路由代码(routes/news.js): 实现用户注册、登陆、查询等操作记入数据库中的日志:

    项目运行

    阿巴阿巴,看一下项目成果吧。

    先在终端中启动项目: 然后用Chrome访问地址,页面如下: 注册后登录: 查询功能(支持布尔表达式&&查询结果分页+排序): 图表(饼图显示不出来emmm,还没有弄明白): 此时可以看到数据库中的用户信息和操作日志有记录了(密码已打码):

    总结

    这次的大作业主要是在上次简陋的新闻数据展示页面上进行一些功能扩展,使我对项目的建立有了一些初步的认识。对于老师提供的js代码,我读起来还是挺困难的,要是自己写肯定直接gg了… 这学期因为疫情原因,没能在校与老师和助教面对面上课,感觉非常遗憾。网课期间,对web编程的态度也不够认真,经常在ddl前疯狂赶作业。这门课本身有趣又有用,希望自己能继续探索叭。 再次感谢老师和助教,虽然我没有问过问题ಠ_ಠ太害羞了,但老师和助教每次在群里提供的帮助都非常精准扶贫(我)🙏🏻

    Processed: 0.009, SQL: 9