新闻爬虫以及爬取结果查询网站搭建(二)
摘要
本章节将介绍爬虫的主要流程和代码实现。
爬虫流程如下:
- 读取种子页面
- 提取种子页面中包含的的新闻链接
- 爬取新闻链接的内容(标题,发表时间,内容等等)
- 将爬取后的内容整合成结构化的数据并存储到数据库中
在爬虫中首先构造模仿浏览器的request,通过header来防止网站屏蔽爬虫代码。request函数能够访问指定的url并且设置回调函数来处理得到的html页面。
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,
headers: headers,
timeout: 10000
}
myRequest(options, callback)
}
准备工作
数据库建表与连接
建表语句(news、keywords两张表):
CREATE TABLE news (
id_news serial UNIQUE,
url text DEFAULT NULL UNIQUE,
source_name varchar(50) DEFAULT NULL,
source_encoding varchar(45) DEFAULT NULL,
title varchar(100) DEFAULT NULL,
publish_date date DEFAULT CURRENT_TIMESTAMP,
content text,
PRIMARY KEY (id_news)
);
CREATE TABLE keywords (
id_word serial UNIQUE,
id_news int,
word varchar(50) DEFAULT NULL
);
连接并配置数据库
本项目采用连接池的方法连接数据库。
建立数据库连接池的好处:
- 节约资源
- 用户访问高效
- 首先安装数据库连接模版pg
npm install pg
- 创建连接池
var pg = require('pg');
var config = {
host:"127.0.0.1",
user:"root",
database:"spider",
password:"syz",
port:5432,
max:20, // 连接池最大连接数
idleTimeoutMillis:3000, // 连接最大空闲时间 3s
}
var pool = new pg.Pool(config);
爬取网页(网易新闻、中新网财经频道、雪球网)
以网易新闻网为例详细展开说明:网易新闻网
分析种子页面
从上图我们可以看出,中间新闻的url均在"div class="main_center_news"下面,在该种子页面中,需要爬取每一个新闻的url。具体代码如下:
var source_name = "网易新闻";
var myEncoding = "utf-8";
var seedURL = 'https://news.163.com/';
var seedURL_format = "$('a')";
var title_format = "$('title').text()";
var date_format = "$('html#ne_wrap').attr(\"data\-publishtime\")";//
var pgsql = require('../pg.js');
var Iconv = require('iconv-lite');
var myRequest = require('request');
var myCheerio = require('cheerio');
var url_reg = /\/(\d{2})\/(\d{4})\/(\d{2})\/([A-Z0-9]{16}).html/;
var regExp = /((\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2})|(\d{4}年\d{1,2}月\d{1,2}日)/
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) {//request module fetching url
var options = {
url: url,
encoding: null,
headers: headers,
timeout: 10000
}
myRequest(options, callback)
}
request(seedURL, function (err, res, data) {
var buf = Iconv.encode(data, 'utf-8');
var html = Iconv.decode(buf, myEncoding);
var $ = myCheerio.load(html, { decodeEntities: true });
try {
seedurl_news = eval(seedURL_format);
} catch (e) { };
seedurl_news.each(function(){
var myURL = "";
try {
var href = "";
href = $(this).attr("href");
if (href == undefined) return;
if (href.toLowerCase().indexOf('https://') >= 0 || href.toLowerCase().indexOf('http://') >= 0) myURL = href;
else if (href.startsWith('//')) myURL = 'http:' + href;
else myURL = seedURL.substr(0, seedURL.lastIndexOf('/') + 1) + href;
}catch (e) {
}
if (!url_reg.test(myURL)) return;
var news = {};
news.url = myURL;
news.source_name = source_name;
news.source_encoding = myEncoding;
var news_url_Sql = 'select url from news where url= $1';
var news_url_Sql_Params = [myURL];
pgsql.query(news_url_Sql, news_url_Sql_Params, function(err, result) {
if (err) {
console.log(err)
} else {
Detail(news, myURL);
}
});
});
});
分析新闻页面
获取新闻页面后,调用Detail函数来爬取新闻标题、作者、发布时间以及具体内容。
function Detail(news, url) {
request(url, function(err, res, data) {
var $ = myCheerio.load(data, { decodeEntities: true});
news.title = "";
news.content = "";
news.publish_date = new Date().toLocaleDateString().split('/').join('-');
if (title_format == "")
news.title = ""
else news.title = eval(title_format);
if(!isChinese(news.title[0]) && !isNumber(news.title[0]))
return ;
if (date_format == "") news.publish_date = "";
else news.publish_date = eval(date_format);
if (news.publish_date) {
news.publish_date = regExp.exec(news.publish_date)[0];
news.publish_date = news.publish_date.replace('年', '-');
news.publish_date = news.publish_date.replace('月', '-');
news.publish_date = news.publish_date.replace('日', '');
}
遇到的问题与解决方案
在爬取网页内容的过程中,遇到了title为乱码的情况。
解决方法如下:
- 通过isChinese和isNumber函数判断title字符串首字符是否为中文字符或者是数字,若都不是则直接返回。
function isChinese(temp){
var re=/[^\u4E00-\u9FA5]/;
if (re.test(temp)) return false ;
return true ;
}
//验证字符串是否是数字
function isNumber(theObj) {
var reg = /^[0-9]+.?[0-9]*$/;
if (reg.test(theObj)) {
return true;
}
return false;
}
存储结果
如下图所示: