百度网页关键字抓取(编程的“回调地狱”,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地址:完整代码

  稍后,我们应该做一些高级工作来抓取一些网站好看的图片(手动搞笑),这将涉及一些并发控制和反爬虫策略。然后使用爬虫获取一些网站,需要登录并输入验证码。欢迎关注和正确沟通

  我想再次感谢你们的赞扬、关注和评论,感谢你们的支持,谢谢!我想我是一个半文学的程序员,喜欢文字、音乐和编码。我一直想写技术和其他文学方面的东西文章. 虽然我的基础不是很好,

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线