前端设计与开发实验项目1:nodejs新闻爬虫及爬取结果的查询网站
项目要求

第一部分:爬虫部分
对三个新闻网站进行爬取:新浪财经、东方财富网、人民网。
爬虫步骤可大致分为两步:1.从网页获取需要的内容 2.对获取的内容进行处理
1.第一步:从网页获取需要的内容
这个部分中,各个网页不同,有一个便捷的方法:保存爬取到的body部分,直接搜索对应的关键词,比如:keyword,title,author等等,就可以快速定位;也可以打开开发者工具慢慢找。
- 新浪财经
从新浪财经的网页中寻找规律,提取所需字段
。
var source_name = "新浪财经";
var domain = 'https://finance.sina.com.cn/';
var myEncoding = "utf-8";
var seedURL = 'https://finance.sina.com.cn/';
var seedURL_format = "$('a')";
var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var title_format = "$('.main-title').text()";
var date_format = "$('.date').text()";
var author_format = " $('meta[name=\"author\"]').attr(\"content\")";
var content_format = "$('.article').text()";
var desc_format = " $('meta[name=\"description\"]').eq(0).attr(\"content\")";
var source_format = "$('.source').text()";
- 东方财富网
从东方财富网的网页中寻找规律,提取所需字段
var source_name = "东方财富";
var domain = 'https://www.eastmoney.com/';
var myEncoding = "utf-8";
var seedURL = 'https://www.eastmoney.com/';
var seedURL_format = "$('a')";
var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var title_format = "$('h1').text()";
var date_format = "$('.time').text()";
var author_format = "$('p.res-edit').text()";
var content_format = "$('.post_text p').text()";
var desc_format = "$('.b-review').text()";
var source_format = "$('p.em_media').text()";
- 人民网
从人民网的网页中寻找规律,提取所需字段
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 = "$('h1').text()";
var date_format = " $('meta[name=\"publishdate\"]').eq(0).attr(\"content\")";
var author_format = "$('p.author').text()";
var content_format = "$('.box_con p').text()";
var desc_format = "$('meta[name=\"description\"]').eq(0).attr(\"content\")";
var source_format = " $('meta[name=\"source\"]').eq(0).attr(\"content\")";
2.正则化
- 获取文章链接
从新闻网站中获得的很多链接可能不是站内文章链接,会引发错误,因此要使用正则化筛选。每个网站的文章链接命名规则不同,要从中找出规律。据观察:
新浪财经:文章链接都包括“doc-”
东方财富网:文章链接都包括“a/2020”
人民网:文章链接都包括“2020” - 通用正则表达式
观察到有一些爬取到的内容会插入数据库失败,为了找出失败的原因,需要将爬取到的内容或sql语句写入文档存档。
我采取的方式是以source_name+日期+title的方式作为文件名,存储爬取到的内容。当title里有空格、问号等符号时,会出错,因此进行正则化处理。
另外,content如果有大量换行符、空格、制表符会显得很混乱,因此进行一些正则化处理。
var url_reg = /\/(\d{4})\/(\d{2})-(\d{2})\/.+\.shtml/;
var regExp = /((\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2})|(\d{4}年\d{1,2}月\d{1,2}日)/
var reg = new RegExp(`,`,"g");
var reg1 = new RegExp('?',"g");
var reg2 = new RegExp('\n',"g");
var reg3 = new RegExp(' ',"g");
var reg4 = new RegExp('|',"g");
var reg5 = new RegExp(`“`,"g");
var reg6 = new RegExp(`”`,"g");
var reg7 = new RegExp('\t',"g");
var regf = /[\u4e00-\u9fa5]/g;
3.处理爬取到的字段
- 处理标题
标题只保留汉字,实测这样存文件名时才不会报错。
实测标题不太会是undefined,但有可能为空,需要判断一下。
match后的形式是list,用tostring再转回字符串
if (title_format == "") fetch.title = " "
else fetch.title = eval(title_format); //标题
strtitle=fetch.title;
if (strtitle != '') fetch.title=strtitle.match(regf).toString().replace(reg,'');
2.处理日期
这样的replace一次只替换一个,把日期转换成类似“2020-04-27”的格式
if (date_format != "") fetch.publish_date = eval(date_format); //刊登日期
if (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");
}
3.处理内容
去掉换行符和制表符和空格,不去除的话很可能影响插入数据库。
或许有比我更高级的replace方法吧。
if (content_format == "") fetch.content = fetch.desc;
else fetch.content = eval(content_format).replace(`'`, '');
if (fetch.content == "") fetch.content = fetch.desc;
if (fetch.content != '' && fetch.content != undefined) {
fetch.content=fetch.content.replace(reg2,'');
fetch.content=fetch.content.replace(reg3,'');
fetch.content=fetch.content.replace(reg5,'');
fetch.content=fetch.content.replace(reg6,'');
fetch.content=fetch.content.replace(reg7,'');
}
4.写入数据库
插入成功的就保存fetch,插入失败的就保存quertstr,之后尝试手动插入数据库看看失败原因。
尝试的过程中有些手动插入可以成功,后来发现原因是引号的问题。因此替换掉title和content中的引号可以提高成功率。汉字以外的字符都有可能导致失败。
基本上插入失败的原因都是,网站上有多个链接对应同一篇文章,因此重复插入会导致失败。
var filename = source_name + "_" + (new Date()).toFormat("YYYY-MM-DD") +
"_" + fetch.title + ".json";
var filepath = 'people-result/'+filename
if (strtitle != ''){
const mysql=require('mysql')
const db=mysql.createPool({host:'localhost',port:'3306',user:'root',password:'123456',database:'crawler_data'});
str=`'`+fetch.source_encoding+`'`+','+`'`+fetch.title+`'`+','+`'`+fetch.source_name+`'`+','+`'`+fetch.publish_date+`'`+','+`'`+fetch.keywords+`'`+','+`'`+fetch.desc+`'`+','+`'`+fetch.content+`'`
querystr='INSERT INTO news1 (source_encoding,title,author,publish_date,keywords,description,content) VALUE('+str+');'
//console.log(querystr)
db.query(querystr,function(err,data){
if(err)
{
var errorname = source_name + "_" + (new Date()).toFormat("YYYY-MM-DD") +"_" + fetch.title + ".json";
var errorpath = 'people-result/error/'+filename
fs.writeFileSync(errorpath, JSON.stringify(querystr));
}else{
fs.writeFileSync(filepath, JSON.stringify(fetch));
}
})
第二部分:网站部分
1.根据自己的数据库配置
let connection = mysql.createConnection({
host:'localhost',
port:3306,
user:'root',
password:'xxxxxx',
database:'crawler_data'
});
connection.connect();
2.搜索并跳转显示结果
搜索框效果如下
跳转后结果如下,这里只截取了搜索结果的前五条:
3.搜索框的代码实现
index.html
<html>
<body>
<form action="http://127.0.0.1:8081/process_get" method="GET">
<h1>输入单词并搜索:</h1>
<input type="text" name="keyword">
<input type="submit" value="搜索">
</form>
</body>
</html>
4.搜索结果的显示-代码
先从数据库里搜索,再逐条在网页上显示
js文件,运行时通过cmd里node加文件名
app.get('/process_get', function (req, res) {
var response = {
"search":req.query.keyword,
};
var searchword = req.query.keyword
console.log(user_sql)
var user_sql = "SELECT * FROM news WHERE title like '%" + searchword + "%' Order By publish_date desc;";
console.log(user_sql)
connection.query(user_sql,function(err,result){
if(err){
console.log('[query]-:'+err);
res.send("没有搜索到结果")
}else{
result = "<h1>搜索结果为:</h1>"
for (var i=0;i<result.length;i++)
{result += "<p>"
result += result[i].title
result += " "
result += result[i].publish_date
result += "</p>"
}
res.send(result)
}
})
})
5.时间热度分析效果
从数据库中统计每天包括该关键词的新闻数量
sql:
var user_sql = "SELECT publish_date,count(*) as Num FROM news WHERE title like '%" + searchword + "%' group by publish_date order by publish_date desc;"
第三部分:总结
爬虫总是有各种各样的意外情况,插入数据库也是,特别是引号的问题!!
因此在写爬虫代码的过程中需要加入很多判断,去除undefined和null的情况,还有很多异常处理,否则就会没爬几条就因为各种各样的原因异常退出
新闻网站相对来说没遇到什么反爬虫机制,爬取起来还是比较方便的。