期中作业要求
基础概念引入
前后端
前台:呈现给用户的视觉和基本的操作。
后台:用户浏览网页时,我们看不见的后台数据跑动。后台包括前端、后端。
前端:对应html、css、javascript 等网页语言作用在前端网页。
后端:对应jsp、javaBean、dao层、action层和service层的业务逻辑代码。(包括数据库)
Web
定义:Web 是一种基于超文本和HTTP协议的、全球性的、动态交互的、跨平台的分布式图形信息系统,是建立在Internet上的一种网络服务。
组成部分:
结构标准语言(html):存放网页元素。决定网页结构和内容(决定看到什么),相当于人的身体(描述类)
表现标准语言(css):对元素的样式进行设置,例如宽度、高度、位置、字体。决定网页呈现给用户的模样(决定好不好看),相当于化妆(描述类)
行为标准语言(javascript):对元素取值,动态的改变。实现业务逻辑和页面控制(决定功能),相当于人的各种动作(编程类)
Node.js
Node.js 就是运行在服务端的 JavaScript。
Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google Chrome的V8引擎,V8引擎执行JavaScript的速度非常快,性能非常好。
node.js属于异步编程:发送初始请求,没有得到响应的时候可以继续发送请求,如果第一个发送的请求得到响应了,就返回。即同时执行多个。
而如php,java是同步,必须得到响应之后才能继续发送请求
检查和源码的区别
1.查看源代码:他人服务器发送到浏览器的原封不动的代码,也就是最原始的代码。在源代码中找不到的代码,是在浏览器执行js动态生成的。
2.检查(F12):最终的html代码。即:源代码 + 网页js渲染 。
前期准备工作
安装node.js
安装数据库
以使用最广泛的开源数据库MySQL为例
mysql下载地址
数据库详细的配置可以参考我的博客
安装Navicat Premium 15
学姐推荐下载这个软件,可以使爬取的数据显示更为清晰明了
Navicat是一套快速、可靠的数据库管理工具。
Navicat下载地址
正则表达式学习网站
regular expressions 101
菜鸟教程正则表达式在线测试
课堂示例演示
示例一(显示在终端中
代码
var myRequest=require('request')
var myCheerio=require('cheerio')
var myURL='http://www.ecnu.edu.cn/e5/bc/c1950a255420/page.htm'
function request(url,callback){
var options={
url:url,
encoding:null,
headers:null
}
myRequest(options,callback)
}
request(myURL,function(err,res,body){
var html=body
var $=myCheerio.load(html,{
decodeEntities:false})
console.log($.html())
// html页面title的信息
console.log('title:'+$('title').text())
// html页面的摘要的信息
// console.log('description:'+$('meta[name="description"]').eq(0).attr("content"))
})
运行结果
实际现在这个网站不可访问,无法输出下列结果,但可以正常输出网页结构,即运行成功
示例二(保存在本地文件
流程
1、读取种子页面
2、分析出种子页面里的所有新闻链接
3、爬取所有新闻链接的内容
4、分析新闻页面内容,解析出结构化数据
5、将结构化数据保存到本地文件
代码
require引入模块。保存在本地文件的关键模块是fs。故在后续存入数据库的代码中,fs模块是可以省略的。
var fs = require('fs');//通过require引入各种包,以方便功能地实现request,fs功能是使爬取内容保存在本地,对系统文件和目录读取
var myRequest = require('request')//发送http请求
var myCheerio = require('cheerio')//获取html内容,使用jquery的方式
var myIconv = require('iconv-lite')//用于各字符集间高效的转码,编码转码gb2312到utf-8
require('date-utils');//为了解析日期的方便
获取URL(即获取要访问的网址
Uniform Resource Locator,统一资源定位器。它是万维网(www)的统一资源定位标志,即指网络地址。
URL格式一般如下:
协议类型://服务器地址[:端口号]/路径/文件名[参数=值]
var source_name = "中国新闻网";//获取url 其格式一般为“协议名+主机名/IP地址及其端口+文件路径”
var myEncoding = "utf-8";//字符编码
var seedURL = 'http://www.chinanews.com/';//目标网址中国新闻网
定义新闻元素的读取方式,即规定哪些url可有作为新闻页面,最后两行使用的是正则表达式,用于检查网页是否符合规范
正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。
正则表达式语法教程
var seedURL_format = "$('a')";
var keywords_format = " $('meta[name=\"keywords\"]').eq(0).attr(\"content\")";
var title_format = "$('title').text()";
var date_format = "$('#pubtime_baidu').text()";
var author_format = "$('#editor_baidu').text()";
var content_format = "$('.left_zw').text()";
var desc_format = " $('meta[name=\"description\"]').eq(0).attr(\"content\")";
var source_format = "$('#source_baidu').text()";
var url_reg = /\/(\d{4})\/(\d{2})-(\d{2})\/(\d{7}).shtml/;
var regExp = /((\d{4}|\d{2})(\-|\/|\.)\d{1,2}\3\d{1,2})|(\d{4}年\d{1,2}月\d{1,2}日)/
构造一个模仿浏览器的request
//防止网站屏蔽爬虫
//如果爬取过于频繁,可能会被网站屏蔽,这段代码的作用是伪装成浏览器,防止被屏蔽
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模块异步fetch url
function request(url, callback) {
var options = {
url: url,
encoding: null,
headers: headers,
timeout: 10000
}
myRequest(options, callback)
}
读取种子页面,解析出种子页面里所有的a herf链接
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) };
遍历种子页面里所有的a herf链接
规整化所有链接,如果满足新闻url的正则表达式就爬取
seedurl_news.each(function(i, e) {
var myURL = "";
try {
//得到具体新闻url
var href = "";
href = $(e).attr("href");
if (typeof(href) == "undefined") {
// 有些网页地址undefined
return true;
}
if (href.toLowerCase().indexOf('http://') >= 0 || href.toLowerCase().indexOf('https://') >= 0) myURL = href; //http://开头的或者https://开头
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);
newsGet(myURL); //读取新闻页面
});
});
读取具体的新闻页面,构造一个空的fetch对象用于存储数据
function newsGet(myURL) {
//读取新闻页面
request(myURL, function(err, res, body) {
//读取具体的新闻页面,构造一个空的fetch对象用于存储数据
//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();
读取新闻页面中的元素并保存到fetch对象里
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);
console.log(myURL);
fetch.publish_date = regExp.exec(fetch.publish_date)[0];
fetch.publish_date = fetch.publish_date.replace('年',