准备工作
首先下载Node.js以及VSCode这两个软件
找到电脑里的cmd命令行
然后还需要下载一些安装包,如request,cheerio,mysql等等
接下来就可以开始快乐地 爬虫了
初次接触
刚开始因为完全不懂,就从万能地hello world入手,让自己对Node.js有所了解
var foo="helloworld"
console.log("foo的值是:",foo)
爬虫任务
在正式开始之前,有一个开胃菜
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"))
})
有一说一,搞得还有模有样。。
但接下来有我崩溃的了,呜~~
require
通过require引入各种包,以方便功能地实现
request:发送http请求;cheerio:获取html内容;
iconv-lite:编码转换;date-utils
(ps.此处还发生了一个小插曲,最后快要好的时候,死活找不到哪儿有问题,最后发现自己把date写成了data了,傻不啦叽的~~)
var fs=require('fs')
var myRequest=require('request') // 发http请求
var myCheerio=require('cheerio') // 获取html的内容
var myIconv=require('iconv-lite') // 编码转换GB2312到UTF-8
require('date-utils') // 为了解析日期的方便
获取URL
URL,全称uniform resource locator,是因特网的万维网服务程序上用于指定信息位置的表示方法;
其格式一般为“协议名 + 主机名/IP地址及其端口 + 文件路径”
所以我们需要获取网址(小声bb:我也不知道为什么要定义两个。。)
var source_name='中国新闻网'
var domain='http://www.chinanews.com/'
var myEncoding='utf-8'
var seedURL='http://www.chinanews.com/'
定义新闻元素的读取方式
var seedURL_format="$('a')" //把所有链接取出来
var keywords_format="$('meta[name=\"ketwords\"]').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}日)/
正则表达式
正则表达式描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。
一些常见的例子
. 可以表示所有字符,但不包括回车换行;
[ ] 方括号里面是‘或’的关系;表示任意小写字母;[A-Z]表示任意大写字母;[A-z]表示任意字母;[A-z0-9]表示所有的字母和数字
+:表示一个及其以上相当于{1,};*:表示0个及以上相当于{0,};?:表示0或1个相当于{0,1};
如一个手机号的正则表达式:var phoneReg=/^1[3-9][0-9]{9}$/;
防止网站屏蔽我的爬虫
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链接
try{}catch(err){}
try:检查是否又错误的代码块;catch:如果try语句发生错误执行的代码块它执行。
读取种子页面
request(seedURL,function(err,res,body){
if (err||res.statusCode!==200){
console.log("种子页面爬取失败")
return
}
try{
var html=myIconv.decode(body,myEncoding) //用iconv转换编码
// console.log(html)
var $=myCheerio.load(html,{decodeEntities: true}) //准备用cheerio解析html
}
catch(e){
console.log('读取种子页面并转码出错:'+e)
}
var seedurl_news
try{
seedurl_news=eval(seedURL_format)
}
catch(e) {
console.log('url列表所处的html快识别出错:'+e)
}
遍历种子页面里所有的链接
seedurl_news.each(function(i,e){
var myURL=""
try{
// 得到具体新闻url
var href=""
href=$(e).attr("href")
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的正则表达式
// 类似continue
}
// console,length(myURL)
newsGet(myURL) //读取新闻页面
})
})
** 读取具体的新闻页面,构造一个空的fetch对象用于存储数据**
function newsGet(myURL) { //读取新闻页面
var options={
url:myURL,
encoding:null,
timeout:10000
}
myRequest(options,function(err,res,body){ //读取新闻页面
try{
var html_news=myIconv.decode(body,myEncoding) //用iconv转换编码
// console.log(html_news)
var $=myCheerio.load(html_news,{decodeEntities: true}) //准备用Cheerio解析html_news
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)
}
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 //作者
}
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","")
}
// 将fetch对象保存在文件中
var filename=source_name+"_"+(new Date()).toFormat("YYYY-MM-DD")+"_"+myURL.substr(myURL.lastIndexOf('/')+1)+".json"
// 存储json
fs.writeFileSync(filename,JSON.stringify(fetch))
})
}
**小菜鸟遇到的一下憨憨问题 **
看着老师的视频,照着敲了一遍代码,一运行,瞬间傻眼了,这都是些啥,
所以接下来开始了我的暴风修改计划~~
噔噔噔!!!第一次修改结束,报错倒是没了,它直接不给我输出了。。。晕~
靠我自己这辈子别想改好了,悄咪咪得到询问了自己的大佬同学,得知代码还是出现了小问题,自己的request的函数都没有用到options。。。当当当当,新鲜代码出炉
尽管还是有点小问题没有解决,但至少没有出现乱码了,可喜可贺啊 ,唉~😔
这样一个爬虫实验的代码就结束了,接下来我们来尝试升级一下
未完待续~~