node.js爬虫实验

node.js爬虫实验


实验要求

1.爬取种子网页
以人民网为例

待爬取网站的url及编码方式:

var source_name = "人民网";
var domain = 'http://www.people.com.cn/';
var myEncoding = "GBK";
var seedURL = 'http://www.people.com.cn/';

需要爬站的信息:关键词、标题、作者、时间等

var seedURL_format = "$('a')";
var keywords_format = "$('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var title_format = "$('title').text()";
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[name=\"description\"]').eq(0).attr(\"content\")";
var source_format =" $('meta[name=\"mediaid\"]').eq(0).attr(\"content\")";
var url_reg = /\/(\d{4})-(\d{2})-(\d{2})\/(\w{3})-(\w{15}).shtml/;
var regExp = /((\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2})|(\d{4}年\d{1,2}月\d{1,2}日)/

引入需要的包:

var fs = require('fs');
var myRequest = require('request')
var myCheerio = require('cheerio')
var myIconv = require('iconv-lite')
require('date-utils');

防止网站屏蔽我们的爬虫,伪装成一个浏览器

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

function request(url, callback) {
    var options = {
            url: url,
            encoding: null,
            //proxy: 'http://x.x.x.x:8080',
            headers: headers,        
            timeout: 10000 //
            }    
            myRequest(options, callback)
 }

读取种子界面的链接

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);
        //console.log(seedurl_news);    
        } 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); //读取新闻页面   
     });
 });

读取新闻页面中的元素并保存到fetch对象里
将fetch对象保存在文件中

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

在这里插入图片描述
在这里插入图片描述

2.将爬取的内容存入mysql数据库
下载mysql并初始化:
在官网(https://dev.mysql.com/downloads/mysql/)下载mysql到c盘
以管理员身份打开命令行工具
打开bin文件夹:

cd C:\mysql\bin

安装mysql服务:

mysqld -install

初始化mysql :

mysqld --initialize --console

启动mysql :

net start mysql

进入mysql :

mysql -u root -p

修改密码

alter user 'root'@'localhost'identified by 'root' 

给root用户授权从客户端访问

alter user 'root'@'localhost' identified with mysql_native_password by 'root'
flush privileges

创建数据库crawl和表fetches:

create database crawl;
use crawl;
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;

修改爬虫代码
加mysql模块

var mysql=require('./mysql.js'

在newsGet函数中修改

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

运行结果:在这里插入图片描述
3功能拓展
(1)爬取之前查询mysql数据库,是否已爬取过:

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

(2)定时工作
引入第三方包node-schedule :npm install node-schedule

var schedule=require('node-schedule');
//!定时执行
var rule = new schedule.RecurrenceRule();
var times = [0, 12]; //每天2次自动执行
var times2 = 5; //定义在第几分钟执行
rule.hour = times;rule.minute = times2;
//定时执行httpGet()函数
schedule.scheduleJob(rule, function() { 
   seedget()
   ;});

4.用express构建网站访问mysql中的数据并实现搜索功能

js代码

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")
})

在nodejss文件夹下运行命令行,输入express -e search_site,创建一个search_site的文件夹,把之前写的mysql.js拷贝进去。然后在文件夹下运行命令行,输入npm install mysql -save和npm install来安装包和依赖项。
安装完毕以后用vscode打开search_site文件夹,找到index.js文件进行修改。

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;

在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
用chrome浏览器打开http://127.0.0.1/search.html

运行结果:
在这里插入图片描述

5.总结
实验刚开始时,我一点nodejs,hlmt相关的知识都不会,便去找相关的教程和书看,但是完全不能给我带来思路。后来从老师的代码入手,从看懂代码到修改代码,最终完成了实验。实验中遇到的问题还并没有全部解决,之前尝试爬取的新浪新闻不同的种子界面中date的表达不一样,导致经常爬取几个新闻就结束了,最终选择了换一个网站爬取成功了。
第一次实验给我最大的体会是要从实验本身的要求入手,不能盲目地去学相关的基础知识,要在实验中将知识掌握,否则看了很多知识点实验还是会无从下手。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值