百度网页关键字抓取(编程的“回调地狱”,async/下方的新闻数据)
优采云 发布时间: 2021-09-21 14:04百度网页关键字抓取(编程的“回调地狱”,async/下方的新闻数据)
1、首先,我们来分析一下百度新闻首页的页面信息
百度新闻首页一般分为“热点新闻”、“本地新闻”、“国内新闻”、“国际新闻”。。。等等,这一次,我们试着捕捉一下左边的“热门新闻”和下面的“本地新闻”的新闻数据
F12打开chrome控制台并查看页面元素。查看左侧“热门新闻”信息的DOM结构后,我们发现所有“热门新闻”信息(包括新闻标题和新闻页面链接)都位于DOM中,ID为#pane news
&燃气轮机;在下面的标签中。jQuery的选择器表示为:#pane news UL Li a
2、为了抓取新闻数据,首先我们需要使用supergenerate请求目标页面并获取整个新闻主页的信息
// 引入所需要的第三方包
const superagent= require('superagent');
let hotNews = []; // 热点新闻
let localNews = []; // 本地新闻
/**
* index.js
* [description] - 使用superagent.get()方法来访问百度新闻首页
*/
superagent.get('http://news.baidu.com/').end((err, res) => {
if (err) {
// 如果访问失败或者出错,会这行这里
console.log(`热点新闻抓取失败 - ${err}`)
} else {
// 访问成功,请求http://news.baidu.com/页面所返回的数据会包含在res
// 抓取热点新闻数据
hotNews = getHotNews(res)
}
});
3、获取页面信息后,让我们定义一个函数gethotnews()来获取页面中的“热门新闻”数据
/**
* index.js
* [description] - 抓取热点新闻页面
*/
// 引入所需要的第三方包
const cheerio = require('cheerio');
let getHotNews = (res) => {
let hotNews = [];
// 访问成功,请求http://news.baidu.com/页面所返回的数据会包含在res.text中。
/* 使用cheerio模块的cherrio.load()方法,将HTMLdocument作为参数传入函数
以后就可以使用类似jQuery的$(selectior)的方式来获取页面元素
*/
let $ = cheerio.load(res.text);
// 找到目标数据所在的页面元素,获取数据
$('div#pane-news ul li a').each((idx, ele) => {
// cherrio中$('selector').each()用来遍历所有匹配到的DOM元素
// 参数idx是当前遍历的元素的索引,ele就是当前便利的DOM元素
let news = {
title: $(ele).text(), // 获取新闻标题
href: $(ele).attr('href') // 获取新闻网页链接
};
hotNews.push(news) // 存入最终结果数组
});
return hotNews
};
这里还有几点:
异步/等待被认为是异步编程的最终解决方案。它允许我们以同步的思维方式异步编程。Promise解决了异步编程的“回调地狱”。Async/await还使异步进程控制变得友好和清晰。有兴趣的学生可以了解它。它真的很容易使用。超级模块提供了许多方法,如get、post、delete等,可以轻松地执行Ajax请求操作。执行。请求结束后的End()回调函数End()将函数作为参数,它有两个参数error和res。当请求失败时,error将收录返回的错误信息。请求成功,错误值为null,返回的数据将收录在res参数中。这个cherio模块的Load()方法将HTML文档作为参数传递给函数。稍后,您可以使用类似于jQuery$(选择器)的方法来获取页面元素。同时,您可以使用。每个()类似于jQuery,用于遍历元素。此外,还有很多方法。你可以自己谷歌/百度
4、将捕获的数据返回前端浏览器
在前面,const app=express();实例化了一个express对象应用程序
应用程序。Get(“”,async()=>;{})接受两个参数。第一个参数接受字符串类型的路由路径,表示Ajax请求路径。第二个参数接受一个函数。当请求此路径时,将执行此函数中的代码
/**
* [description] - 跟路由
*/
// 当一个get请求 http://localhost:3000时,就会后面的async函数
app.get('/', async (req, res, next) => {
res.send(hotNews);
});
在DOS中执行项目根目录百度新闻下的node index.js,使项目运行。之后,打开浏览器并访问:3000,您将发现捕获的数据返回到首页。运行代码后,浏览器将显示以下返回信息:
注意:由于my Chrome安装了jsonview扩展,返回的数据将在页面显示期间自动格式化为结构化JSON格式,以便于查看
好的!!这样,一个简单的百度“热点新闻”爬虫就完成了
总之,步骤很简单:
Express启动一个简单的HTTP服务,分析目标页面的DOM结构,找到要捕获信息的相关DOM元素,使用super请求目标页面,使用cherio获取页面元素,获取目标数据,并将数据返回到前端浏览器
现在,继续我们捕获“本地新闻”数据的目标(我们将在编码过程中遇到一些有趣的问题)
在过去的基础上,我们自然而然地想到对“地方新闻”数据
使用同样的方法。
1、分析页面中“本地新闻”的DOM结构,如下图:
F12打开控制台,查看“本地新闻”的DOM元素。我们发现,“本地新闻”分为两个主要部分,“左侧新闻”和“右侧新闻信息”。所有目标数据都位于Div中的#local u; News的ID中。“left News”数据位于ID#localnews focus的UL标记下Li标记下的a标记中,包括新闻标题和页面链接。“本地信息”数据也位于div下UL标签下的a标签中,ID#localnews Zixun,以及div下的Li标签中,包括新闻标题和页面链接
2、OK!本文分析DOM结构并确定数据的位置。接下来,像抓取“热门新闻”一样,逐步定义getlocalnews()函数来抓取这些数据
/**
* [description] - 抓取本地新闻页面
*/
let getLocalNews = (res) => {
let localNews = [];
let $ = cheerio.load(res);
// 本地新闻
$('ul#localnews-focus li a').each((idx, ele) => {
let news = {
title: $(ele).text(),
href: $(ele).attr('href'),
};
localNews.push(news)
});
// 本地资讯
$('div#localnews-zixun ul li a').each((index, item) => {
let news = {
title: $(item).text(),
href: $(item).attr('href')
};
localNews.push(news);
});
return localNews
};
相应地,在超级请求页面后。Get(),我们需要调用getlocalnews()函数来抓取本地新闻数据
超级。Get()函数修改为:
superagent.get('http://news.baidu.com/').end((err, res) => {
if (err) {
// 如果访问失败或者出错,会这行这里
console.log(`热点新闻抓取失败 - ${err}`)
} else {
// 访问成功,请求http://news.baidu.com/页面所返回的数据会包含在res
// 抓取热点新闻数据
hotNews = getHotNews(res)
localNews = getLocalNews(res)
}
});
同时,我们还应该将数据返回到应用程序中的前端浏览器。获取()路径。应用程序。Get()路由代码修改为:
/**
* [description] - 跟路由
*/
// 当一个get请求 http://localhost:3000时,就会后面的async函数
app.get('/', async (req, res, next) => {
res.send({
hotNews: hotNews,
localNews: localNews
});
});
编码完成了,兴奋!!让项目在DOS下运行,使用浏览器访问:3000
发生了令人尴尬的事!!返回的数据仅为热门新闻,而本地新闻返回空数组[]。检查代码并发现没有问题,但是为什么总是返回空数组呢
找到原因后,我们可以找出问题所在
一个有趣的问题。为了找出原因,首先,让我们看看在回调函数中从第二个参数RES中得到的。End()。结束((err,RES)=>;{})请求百度新闻主页
// 新定义一个全局变量 pageRes
let pageRes = {}; // supergaent页面返回值
// superagent.get()中将res存入pageRes
superagent.get('http://news.baidu.com/').end((err, res) => {
if (err) {
// 如果访问失败或者出错,会这行这里
console.log(`热点新闻抓取失败 - ${err}`)
} else {
// 访问成功,请求http://news.baidu.com/页面所返回的数据会包含在res
// 抓取热点新闻数据
// hotNews = getHotNews(res)
// localNews = getLocalNews(res)
pageRes = res
}
});
// 将pageRes返回给前端浏览器,便于查看
app.get('/', async (req, res, next) => {
res.send({
// {}hotNews: hotNews,
// localNews: localNews,
pageRes: pageRes
});
});
访问浏览器:3000,页面显示以下内容:
如您所见,返回值中的文本字段应该是整个页面HTML代码的字符串格式。为了便于观察,我们可以直接将文本字段值返回到前端浏览器,以便可以清楚地看到浏览器呈现的页面
修改前端浏览器的返回值
app.get('/', async (req, res, next) => {
res.send(pageRes.text)
}
访问浏览器:3000,页面显示以下内容:
在查看元素之后,我们发现我们获取的目标数据所在的DOM元素是空的,其中没有数据
这里,一切都出来了!当我们使用超级。Get()访问百度新闻首页,获取的res中收录的页面内容中没有生成我们想要的“本地新闻”数据,并且DOM node元素为空,所以出现前面的情况!获取后返回的数据始终是空数组[]
在控制台的网络中,我们发现页面曾经请求过这样的接口:
:3000/widget?id=LocalNews&;ajax=json&;T=17,接口状态404
这应该是百度新闻获取“本地新闻”的接口。我明白这里的一切!“本地新闻”是在页面加载后通过动态请求上面的接口获取的,所以当我们使用super请求的页面时。Get()若要再次请求此接口,接口URL的主机名部分将成为本地IP地址,但本地计算机上没有此类接口,因此404无法请求数据
找出原因,让我们找到解决这个问题的方法
直接使用super访问正确合法的百度“本地新闻”界面,获取数据并返回前端浏览器。使用第三方NPM包通过模拟浏览器访问百度新闻主页。在此模拟浏览器中,成功加载“本地新闻”后,抓取数据并将其返回到前端浏览器
以上所有方法均可使用。让我们试试第二种更有趣的方法
使用nightcare自动测试工具electron,您可以使用纯JavaScript调用Chrome丰富的本机界面来创建桌面应用程序。您可以将其视为node的变体。专注于桌面应用程序而非web服务器的JS。它基于浏览器的应用程序可以轻松地进行各种响应式交互
Nightcare是一个用于web自动测试和爬虫程序的基于电子的框架,因为它与plantomjs具有相同的自动测试功能。它可以模拟用户在页面上的行为,并触发一些异步数据加载。它还可以直接访问URL来抓取请求库之类的数据,并设置页面的延迟时间,因此,无论是手动触发脚本还是行为触发脚本,都很容易
安装依赖项
// 安装nightmare
yarn add nightmare
要获取本地新闻,请继续编码
将以下代码添加到index.js:
const Nightmare = require('nightmare'); // 自动化测试包,处理动态页面
const nightmare = Nightmare({ show: true }); // show:true 显示内置模拟浏览器
/**
* [description] - 抓取本地新闻页面
* [nremark] - 百度本地新闻在访问页面后加载js定位IP位置后获取对应新闻,
* 所以抓取本地新闻需要使用 nightmare 一类的自动化测试工具,
* 模拟浏览器环境访问页面,使js运行,生成动态页面再抓取
*/
// 抓取本地新闻页面
nightmare
.goto('http://news.baidu.com/')
.wait("div#local_news")
.evaluate(() => document.querySelector("div#local_news").innerHTML)
.then(htmlStr => {
// 获取本地新闻数据
localNews = getLocalNews(htmlStr)
})
.catch(error => {
console.log(`本地新闻抓取失败 - ${error}`);
})
将getlocalnews()函数修改为:
/**
* [description]- 获取本地新闻数据
*/
let getLocalNews = (htmlStr) => {
let localNews = [];
let $ = cheerio.load(htmlStr);
// 本地新闻
$('ul#localnews-focus li a').each((idx, ele) => {
let news = {
title: $(ele).text(),
href: $(ele).attr('href'),
};
localNews.push(news)
});
// 本地资讯
$('div#localnews-zixun ul li a').each((index, item) => {
let news = {
title: $(item).text(),
href: $(item).attr('href')
};
localNews.push(news);
});
return localNews
}
将app.get('/')路由修改为:
/**
* [description] - 跟路由
*/
// 当一个get请求 http://localhost:3000时,就会后面的async函数
app.get('/', async (req, res, next) => {
res.send({
hotNews: hotNews,
localNews: localNews
})
});
此时,DOS命令行再次启动项目。浏览器访问:3000以查看页面上显示的信息以及是否捕获了“本地新闻”数据
到目前为止,一个简单而完整的抓取百度新闻页面“热门新闻”和“本地新闻”的爬虫程序已经完成
最后,总体思路如下:
Express启动一个简单的HTTP服务,分析目标页面的DOM结构,找到要捕获信息的相关DOM元素,并使用super请求目标页面的动态页面(加载页面后需要运行JS或请求接口的页面)。您可以使用nightcare模拟浏览器访问,并使用cherio获取页面元素,获取目标数据完整代码爬虫完整代码GitHub地址:完整代码
稍后,我们应该做一些高级工作来抓取一些网站好看的图片(手动搞笑),这将涉及一些并发控制和反爬虫策略。然后使用爬虫获取一些网站,需要登录并输入验证码。欢迎关注和正确沟通
我想再次感谢你们的赞扬、关注和评论,感谢你们的支持,谢谢!我想我是一个半文学的程序员,喜欢文字、音乐和编码。我一直想写技术和其他文学方面的东西文章. 虽然我的基础不是很好,