Node.js网站爬虫(三)网络爬虫

文章目录

  • 1 对目标网站进行分析:
    • 1.1 分析网页html结构
      • 1.1.1 主页:
      • 1.1.2 新闻详情页:
    • 1.2 其他方式:
  • 2 爬取结构与工具包:
    • 2.1 爬取整体结构:
    • 2.2 工具包
      • 2.2.1 模块下载和导入模块:
      • 2.2.2 工具包(重要模块):
  • 3 爬取流程:
    • 3.1 定义format:
      • 3.1.1 根据html结构使用CSS选择器:
      • 3.1.2 设置正则表达式
    • 3.2 配置异步执行和定时执行等:
    • 3.3 读取种子页面:
    • 3.4 读取新闻页面:
      • 3.4.1 转码并获取内容:
      • 3.4.2 动态执行format字符串,构建json对象:
      • 3.4.3 将数据存入mysql数据库中。
      • 3.4.4 此部分代码:
  • 4 爬取其他网址需更改处和爬取结果:

相关链接汇总:
Node.js网站爬虫(一)项目简介与完成情况
Node.js网站爬虫(二)mysql配置与数据库设计
Node.js网站爬虫(三)网络爬虫
Node.js网站爬虫(四)Express框架构建网站执行搜索功能
Node.js网站爬虫(五)扩展:热点词时间热度动态可视化展示
Node.js网站爬虫(六)易错点合集和感想

1 对目标网站进行分析:

这里以环球网为例子:

1.1 分析网页html结构

通过控制台分析网页html结构

1.1.1 主页:

​ 使用谷歌浏览器进入环球网官网:https://www.huanqiu.com/

​ 右键—>检查,进入控制台。(或通过F12 / CTRL+F12进入)

​ 点击控制台最左上角的箭头后,点击页面的一条新闻,可看到控制台定位到一个标签中,内含该条新闻的跳转url。

Node.js网站爬虫(三)网络爬虫_第1张图片

1.1.2 新闻详情页:

​ 进入一条新闻详情页并进入控制台:https://tech.huanqiu.com/article/42ujmzmNHGV

标题:

发现源代码中有几个标签里都能找到标题

方式1:< title >的文本中 (后续爬虫时采用了此方式)

Node.js网站爬虫(三)网络爬虫_第2张图片

方式2:< h3 >的文本中

Node.js网站爬虫(三)网络爬虫_第3张图片

时间:

Node.js网站爬虫(三)网络爬虫_第4张图片

作者、来源等标签同理可得,不再赘述。

1.2 其他方式:

也可右键->网页源代码,直接用搜索功能定位标题(或时间等)所在位置,查看所在标签。但这种方式不适用于不知道要查询内容的情况。

Node.js网站爬虫(三)网络爬虫_第5张图片

2 爬取结构与工具包:

2.1 爬取整体结构:

  • 导入模块
  • 设置网站、format、正则表达式
  • 配置:异步获取url、定时执行
  • 读取种子页面
  • 读取新闻页面

2.2 工具包

2.2.1 模块下载和导入模块:

​ 运行.js文件出现找不到’xxx’module时,通过npm install xxx 进行下载。

​ 使用require导入模块,例如:var myRequest = require(‘request’);

2.2.2 工具包(重要模块):

request:用于发送GET和POST请求。

cheerio:本实验主要用于提取html文件指定内容。

node-schedule:定时功能。

iconv-lite:转码。

mysql:执行sql语句,将数据存入mysql。

3 爬取流程:

3.1 定义format:

3.1.1 根据html结构使用CSS选择器:

//通过元素
var seedurl_format="$('a')";
var title_format = "$('title').text()";
//通过属性(meta属性)
var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var desc_format = " $('meta[name=\"description\"]').eq(0).attr(\"content\")";
//通过class
var date_format = "$('.time').text()";
var author_format = "$('.author').text()";
var source_format = "$('.source data-source').eq(0).attr(\"data-source\")";
//通过id
var content_format = "$('#ContentBody').text()";

​ 在其他网站中,还有通过ID进行选择的情况,例如东方财富网:var content_format = “$(’#ContentBody’).text()”;

jQuery 主要选择器:

选择器 实例 选取
* $("*") 有元素
#id $("#lastname") id=“lastname” 的元素
.class $(".intro") 所有 class=“intro” 的元素
element $(“p”) 所有

元素

.class.class $(".intro.demo") 所有 class=“intro” 且 class=“demo” 的元素

​ 更多jQuery 选择器参考网址:https://www.w3school.com.cn/jquery/jquery_ref_selectors.asp

3.1.2 设置正则表达式

​ 分析多个新闻详情页的url,url格式: ‘/artical/’+11个字符(数字或字母)

​ 以一个为例: https://tech.huanqiu.com/article/42ujmzmNHGV

​ 正则表达式:用于读取种子界面时检查是否符合新闻url的正则表达式

var url_reg = /article\/([0-9a-zA-Z]{11})/;

​ 设置时间的正则表达式:检查获取时间是否符合时间的正则表达式

var regExp = /((\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2})|(\d{4}年\d{1,2}月\d{1,2}日)/

3.2 配置异步执行和定时执行等:

防止网站屏蔽我们的爬虫,request模块异步fetch url:

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'
}

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 rule = new schedule.RecurrenceRule();
var times = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]; //每小时
var times2 = 20; //定义在第几分钟执行
rule.hour = times;
rule.minute = times2;

//定时执行httpGet()函数
schedule.scheduleJob(rule, function() {
    seedget();
});

3.3 读取种子页面:

​ 读取种子页面,遍历种子页面里所有的a链接,得到具体新闻url。

function seedget() {
    request(seedURL, function(err, res, body) { //读取种子页面
        // try {
        //用iconv转换编码
        var html = myIconv.decode(body, myEncoding);
        
        //准备用cheerio解析html
        var $ = myCheerio.load(html, { decodeEntities: true });
        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 fetches where url=?';
            var fetch_url_Sql_Params = [myURL];
            mysql.query(fetch_url_Sql, fetch_url_Sql_Params, function(qerr, vals, fields) {
                if(!vals){
                    console.log('no URL')  //vals: null / undefined / NaN ...
                }
                else if (vals.length > 0) {
                    console.log('URL duplicate!')
                } else newsGet(myURL); //读取新闻页面
            });
        });
    });
};

3.4 读取新闻页面:

3.4.1 转码并获取内容:

​ iconv转换编码,cheerio提取html文件内容。

3.4.2 动态执行format字符串,构建json对象:

​ 从提取内容中获得title、publish_date、source、author、content等信息,一定注意判断获取到的是否为空值(null/undefined)。

3.4.3 将数据存入mysql数据库中。

​ 如果成功获取到content,则执行sql语句,将数据存入mysql数据库中。

​ 因为数据库中fetch表里的url属性是unique的,所以不会把重复的url内容写入数据库。

3.4.4 此部分代码:

function newsGet(myURL) { //读取新闻页面
    request(myURL, function(err, res, body) { //读取新闻页面
        //try {
        var html_news = myIconv.decode(Buffer.from(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); //标题
        
        // info_format得到:时间 来源
        if(info_format != "" && info_format!= null){
            var info_all = eval(info_format);
            //时间
            var date_inform = date_reg.exec(info_all);
            if(date_inform){
                fetch.publish_date = date_inform[0];
                fetch.publish_date = new Date(fetch.publish_date).toFormat("YYYY-MM-DD");
            }else fetch.publish_date = "no publish date";
            
            //来源
            var source_inform = source_reg.exec(info_all);
            if(source_inform)fetch.source = source_inform[0];
            else fetch.source = fetch.source_name;
        };
        //作者
        if (author_format == "") fetch.author = source_name;   
        else fetch.author = eval(author_format);
        //内容
        if (content_format == "") fetch.content = "";
        else fetch.content = eval(content_format).replace("\r\n" + fetch.author, ""); //内容,是否要去掉作者信息自行决定

        //摘要  
        if (desc_format == "" && desc_format == undefined){
            fetch.desc = fetch.title;   //没有摘要,则title就是摘要
        }
        else {
            var descstring = eval(desc_format);
            if (descstring != null)fetch.desc = eval(desc_format).replace("\r\n", "");
        
        } 

        //写进数据库
        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);
                return;
            }
        }); //mysql写入
    });
}

4 爬取其他网址需更改处和爬取结果:

根据具体情况修改以下部分即可。
Node.js网站爬虫(三)网络爬虫_第6张图片
最终爬取了三个网站(环球、澎湃、东方财富)共620条信息。
Node.js网站爬虫(三)网络爬虫_第7张图片

你可能感兴趣的:(web编程项目,node.js)