nodejs抓取动态网页

nodejs抓取动态网页

nodejs抓取动态网页(如何利用Webkit从JS渲染网页中实现浏览器非常有趣)

网站优化优采云 发表了文章 • 0 个评论 • 71 次浏览 • 2021-10-30 02:14 • 来自相关话题

  nodejs抓取动态网页(如何利用Webkit从JS渲染网页中实现浏览器非常有趣)
  当我们抓取网页时,我们会使用一定的规则从返回的 HTML 数据中提取有效信息。但是如果网页中收录Java代码,就必须经过渲染处理才能得到原创数据。此时,如果我们仍然使用常规方法从中抓取数据,那么我们将一无所获。浏览器知道如何处理这些代码并显示出来,但是我们的程序应该如何处理这些代码呢?接下来介绍一个简单粗暴的抓取收录Java代码的网页信息的方法。
  大多数人使用 lxml 和 BeautifulSoup 两个包来提取数据。在本文中,我不会介绍任何爬虫框架内容,因为我只使用最基础的 lxml 包来处理数据。也许你很好奇我为什么更喜欢 lxml。那是因为 lxml 使用元素遍历来处理数据,而不是像 BeautifulSoup 那样使用正则表达式来提取数据。在这篇文章中,我将介绍一个非常有趣的案例——我突然发现我的文章出现在最近的Pycoders周刊第147期,所以我想每周抓取Pycoders中所有文件的链接。
  
  很明显,这是一个带有Java渲染的网页。我想抓取网页中的所有文件信息和相应的链接信息。所以我该怎么做?首先,我们无法使用 HTTP 方法获取任何信息。
  进口请求
  从 lxml 导入 html
  # 存储响应
  response = requests.get('')
  # 从响应体创建 lxml 树
  树 = html.fromstring(response.text)
  # 在响应中查找所有锚标记
  print tree.xpath('//div[@class="campaign"]/a/@href')
  当我们运行上面的代码时,我们无法获得任何信息。这怎么可能?网页上有很多文件。接下来我们需要考虑如何解决这个问题?
  如何获取内容信息?
  接下来,我将介绍如何使用 Web kit 从 JS 渲染网页中获取数据。什么是网络套件?Web kit 可以实现浏览器可以处理的任何内容。对于某些浏览器,Web kit 是底层的网页渲染工具。Web kit 是 QT 库的一部分,所以如果你已经安装了 QT 和 PyQT4 库,那么你可以直接运行它。
  您可以使用命令行安装软件库:
  须藤 apt-get 安装 python-qt4
  现在所有的准备工作已经完成,我们将使用一种全新的方法来提取信息。
  解决方案
  我们首先通过Web kit发送请求信息,然后等待网页完全加载并赋值给一个变量。接下来,我们使用 lxml 从 HTML 数据中提取有效信息。这个过程需要一段时间,但你会惊讶地发现整个网页都被完全加载了。
  导入系统
  从 PyQt4.QtGui 导入 *
  从 PyQt4.Qtcore 导入 *
  从 PyQt4.QtWebKit 导入 *
  类渲染(QWebPage):
  def __init__(self, url):
  self.app = QApplication(sys.argv)
  QWebPage.__init__(self)
  self.loadFinished.connect(self._loadFinished)
  self.mainFrame().load(QUrl(url))
  self.app.exec_()
  def _loadFinished(self, result):
  self.frame = self.mainFrame()
  self.app.quit()
  Render 类可用于呈现网页。当我们创建一个新的Render类时,它可以加载url中的所有信息并将其存储在一个新的框架中。 查看全部

  nodejs抓取动态网页(如何利用Webkit从JS渲染网页中实现浏览器非常有趣)
  当我们抓取网页时,我们会使用一定的规则从返回的 HTML 数据中提取有效信息。但是如果网页中收录Java代码,就必须经过渲染处理才能得到原创数据。此时,如果我们仍然使用常规方法从中抓取数据,那么我们将一无所获。浏览器知道如何处理这些代码并显示出来,但是我们的程序应该如何处理这些代码呢?接下来介绍一个简单粗暴的抓取收录Java代码的网页信息的方法。
  大多数人使用 lxml 和 BeautifulSoup 两个包来提取数据。在本文中,我不会介绍任何爬虫框架内容,因为我只使用最基础的 lxml 包来处理数据。也许你很好奇我为什么更喜欢 lxml。那是因为 lxml 使用元素遍历来处理数据,而不是像 BeautifulSoup 那样使用正则表达式来提取数据。在这篇文章中,我将介绍一个非常有趣的案例——我突然发现我的文章出现在最近的Pycoders周刊第147期,所以我想每周抓取Pycoders中所有文件的链接。
  
  很明显,这是一个带有Java渲染的网页。我想抓取网页中的所有文件信息和相应的链接信息。所以我该怎么做?首先,我们无法使用 HTTP 方法获取任何信息。
  进口请求
  从 lxml 导入 html
  # 存储响应
  response = requests.get('')
  # 从响应体创建 lxml 树
  树 = html.fromstring(response.text)
  # 在响应中查找所有锚标记
  print tree.xpath('//div[@class="campaign"]/a/@href')
  当我们运行上面的代码时,我们无法获得任何信息。这怎么可能?网页上有很多文件。接下来我们需要考虑如何解决这个问题?
  如何获取内容信息?
  接下来,我将介绍如何使用 Web kit 从 JS 渲染网页中获取数据。什么是网络套件?Web kit 可以实现浏览器可以处理的任何内容。对于某些浏览器,Web kit 是底层的网页渲染工具。Web kit 是 QT 库的一部分,所以如果你已经安装了 QT 和 PyQT4 库,那么你可以直接运行它。
  您可以使用命令行安装软件库:
  须藤 apt-get 安装 python-qt4
  现在所有的准备工作已经完成,我们将使用一种全新的方法来提取信息。
  解决方案
  我们首先通过Web kit发送请求信息,然后等待网页完全加载并赋值给一个变量。接下来,我们使用 lxml 从 HTML 数据中提取有效信息。这个过程需要一段时间,但你会惊讶地发现整个网页都被完全加载了。
  导入系统
  从 PyQt4.QtGui 导入 *
  从 PyQt4.Qtcore 导入 *
  从 PyQt4.QtWebKit 导入 *
  类渲染(QWebPage):
  def __init__(self, url):
  self.app = QApplication(sys.argv)
  QWebPage.__init__(self)
  self.loadFinished.connect(self._loadFinished)
  self.mainFrame().load(QUrl(url))
  self.app.exec_()
  def _loadFinished(self, result):
  self.frame = self.mainFrame()
  self.app.quit()
  Render 类可用于呈现网页。当我们创建一个新的Render类时,它可以加载url中的所有信息并将其存储在一个新的框架中。

nodejs抓取动态网页(使用node开发一个小工具,扫描分子反应动力(图) )

网站优化优采云 发表了文章 • 0 个评论 • 57 次浏览 • 2021-10-27 12:00 • 来自相关话题

  nodejs抓取动态网页(使用node开发一个小工具,扫描分子反应动力(图)
)
  使用node开发一个小工具,扫描分子反应动力学国家重点实验室新闻列表页面的前三页(地址如下:)。需要打印新闻名称、链接地址和发布时间。
  比如在控制台打印
  韩克力入选2016年度“中国科学院特聘研究员”2016-06-14
  我室金属表面解离和吸附动力学理论研究取得新进展2016-06-12
  张大宇讲座第21期:加州理工学院William A. Goddard III教授2016-05-04
<p><br /><br /><br /><br />/**
*设计
*第一种:抓取一页打印一页。
*第二种:把三页全部抓取完后,存到数组中,统一打印。
*第一种方式较高效。使用第一种方式。
*/
/**
*思路:
*1.抓取网站html内容。
*2.获取抓取的html的必要内容。
*3.把获取内容存到数组。
*4.把数组内容输到控制台。
*/
var http = require('http'); //引入nodejs的http模块,该模块用于构建http服务和作为HttpClient使用。
var cheerio = require('cheerio'); //可以理解为服务端的Jquery。使用方法和客户端一样。
//var promise = require('promise'); //对异步编程进行流程控制,更加符合后端程序员的编程习惯。
//var url = 'http://www.sklmr.dicp.ac.cn/list.php?tid=1'; //要抓取的网址,后面有拼接。
//抓取每一个节点的信息
function filterChapters(html){
var $ = cheerio.load(html); //把HTML内容解析成DOM对象 并且可以像jquery css选择器查询那样对这个DOM进行筛选
var articleList = $('td.text').find('tr');
var articleArr = [];
articleList.each(function() {
var curEle = $(this);
var title = curEle.find('a.title10').text().replace(/\s*\r\n\s*/g,""); //获取文章标题
var time = curEle.find('td.title11').text().replace(/\s*\r\n\s*/g,""); //获取文章时间
var href = "http://www.sklmr.dicp.ac.cn/"+curEle.find('a.title10').attr('href'); //获取文章链接
if( title!=null&&title!="") //有点小困难。因为DOM数据和直线是同一个等级,并且直线只有属性没有id。所以必须去除直线里面的tr空数据,否则会打印一部分空数据和错误信息。
articleArr.push({
title:title,
time:time,
href:href
});
})
return articleArr;
}
//在控制台打印信息
function printCourseInfo(courseData){
courseData.forEach(function(item){
var chapterTitle = item.title;
var chaptertime = item.time;
var chapterhref = item.href;
console.log(chapterTitle+"\t"+chaptertime+"\t"+chapterhref+"\n");
});
}
//可以异步下载任意的URL (通过 HTTP GET方法),在完成下载的时候,它会调用回调函数并把下载的内容当做参数传进去,并将其内容输出到控制台。
function getPageList(url){
http.get(url, function(res) {
var html = ''
res.on('data', function(data) {
res.setEncoding('utf8'); //设置buffer字符集
html += data; //拼接buffer
})
res.on('end', function() {
// 将抓取的内容进行处理
var courseData= filterChapters(html);
printCourseInfo(courseData);
})
}).on('error', function(err) {
console.log('错误信息:' + err)
})
}
//或者请求前3页的数据。
list = ['http://www.sklmr.dicp.ac.cn/list.php?tid=1','http://www.sklmr.dicp.ac.cn/list.php?tid=1&page=20','http://www.sklmr.dicp.ac.cn/list.php?tid=1&page=40'];
for(var i=0;i 查看全部

  nodejs抓取动态网页(使用node开发一个小工具,扫描分子反应动力(图)
)
  使用node开发一个小工具,扫描分子反应动力学国家重点实验室新闻列表页面的前三页(地址如下:)。需要打印新闻名称、链接地址和发布时间。
  比如在控制台打印
  韩克力入选2016年度“中国科学院特聘研究员”2016-06-14
  我室金属表面解离和吸附动力学理论研究取得新进展2016-06-12
  张大宇讲座第21期:加州理工学院William A. Goddard III教授2016-05-04
<p><br /><br /><br /><br />/**
*设计
*第一种:抓取一页打印一页。
*第二种:把三页全部抓取完后,存到数组中,统一打印。
*第一种方式较高效。使用第一种方式。
*/
/**
*思路:
*1.抓取网站html内容。
*2.获取抓取的html的必要内容。
*3.把获取内容存到数组。
*4.把数组内容输到控制台。
*/
var http = require('http'); //引入nodejs的http模块,该模块用于构建http服务和作为HttpClient使用。
var cheerio = require('cheerio'); //可以理解为服务端的Jquery。使用方法和客户端一样。
//var promise = require('promise'); //对异步编程进行流程控制,更加符合后端程序员的编程习惯。
//var url = 'http://www.sklmr.dicp.ac.cn/list.php?tid=1'; //要抓取的网址,后面有拼接。
//抓取每一个节点的信息
function filterChapters(html){
var $ = cheerio.load(html); //把HTML内容解析成DOM对象 并且可以像jquery css选择器查询那样对这个DOM进行筛选
var articleList = $('td.text').find('tr');
var articleArr = [];
articleList.each(function() {
var curEle = $(this);
var title = curEle.find('a.title10').text().replace(/\s*\r\n\s*/g,""); //获取文章标题
var time = curEle.find('td.title11').text().replace(/\s*\r\n\s*/g,""); //获取文章时间
var href = "http://www.sklmr.dicp.ac.cn/"+curEle.find('a.title10').attr('href'); //获取文章链接
if( title!=null&&title!="") //有点小困难。因为DOM数据和直线是同一个等级,并且直线只有属性没有id。所以必须去除直线里面的tr空数据,否则会打印一部分空数据和错误信息。
articleArr.push({
title:title,
time:time,
href:href
});
})
return articleArr;
}
//在控制台打印信息
function printCourseInfo(courseData){
courseData.forEach(function(item){
var chapterTitle = item.title;
var chaptertime = item.time;
var chapterhref = item.href;
console.log(chapterTitle+"\t"+chaptertime+"\t"+chapterhref+"\n");
});
}
//可以异步下载任意的URL (通过 HTTP GET方法),在完成下载的时候,它会调用回调函数并把下载的内容当做参数传进去,并将其内容输出到控制台。
function getPageList(url){
http.get(url, function(res) {
var html = ''
res.on('data', function(data) {
res.setEncoding('utf8'); //设置buffer字符集
html += data; //拼接buffer
})
res.on('end', function() {
// 将抓取的内容进行处理
var courseData= filterChapters(html);
printCourseInfo(courseData);
})
}).on('error', function(err) {
console.log('错误信息:' + err)
})
}
//或者请求前3页的数据。
list = ['http://www.sklmr.dicp.ac.cn/list.php?tid=1','http://www.sklmr.dicp.ac.cn/list.php?tid=1&page=20','http://www.sklmr.dicp.ac.cn/list.php?tid=1&page=40'];
for(var i=0;i

nodejs抓取动态网页(爬虫爬取的流程和最终如何展示数据的地址?)

网站优化优采云 发表了文章 • 0 个评论 • 83 次浏览 • 2021-10-26 05:01 • 来自相关话题

  nodejs抓取动态网页(爬虫爬取的流程和最终如何展示数据的地址?)
  其实我之前做过利马财经的销售统计,不过是前端js写的。需要在首页的控制台调试面板中粘贴一段代码才能执行,点击这里。主要目的是通过定时爬取异步接口来获取数据。然后通过一定的去重算法得到最终的数据。但这有以下缺点:
  代码只能在浏览器窗口中运行。如果浏览器关闭或电脑无效,则只能抓取一个页面的数据,无法整合其他页面的数据。爬取的数据无法存储在本地。异步接口数据将被部分过滤。导致我们的去重算法失败
  由于最近了解了节点爬虫,所以可以在后台模拟请求,爬取页面数据。而且我开通了阿里云服务器,我可以把代码放到云端运行。这样1、2、3都可以解决。4因为我们不知道这个ajax界面每三分钟更新一次,这样我们就可以以此为依据对权重进行排序,保证数据不会重复。说到爬虫,大家想到的更多的是python。确实,python有Scrapy等成熟的框架,可以实现非常强大的爬虫功能。但是node也有自己的优势。凭借其强大的异步特性,可以轻松实现高效的异步并发请求,节省CPU开销。其实节点爬虫还是比较简单的,让'
  在线地址
  一、爬虫进程
  我们的最终目标是爬取利马财经每天的销售额,知道哪些产品在售,哪些用户在什么时间点购买了每种产品。首先介绍一下爬取的主要步骤:
  1. 结构分析
  我们要抓取页面的数据。第一步当然是分析页面结构,抓取哪些页面,页面结构是什么,不需要登录;有没有ajax接口,返回什么样的数据等
  2. 数据采集
  要分析清楚要爬取哪些页面和ajax,就需要爬取数据。现在网页的数据大致分为同步页面和ajax接口。同步页面数据的爬取需要我们首先分析网页的结构。Python爬取数据一般是通过正则表达式匹配获取需要的数据;node有一个cheerio工具,可以将获取到的页面内容转换成jquery对象。然后就可以使用jquery强大的dom API来获取节点相关的数据了。其实看源码,这些API本质上都是正则匹配。ajax接口数据一般都是json格式,处理起来比较简单。
  3. 数据存储
  捕获数据后,它会做一个简单的筛选,然后保存需要的数据,以便后续的分析和处理。当然我们可以使用MySQL、Mongodb等数据库来存储数据。在这里,为了方便,我们直接使用文件存储。
  4. 数据分析
  因为我们最终要展示数据,所以需要按照一定的维度对原创数据进行处理和分析,然后返回给客户端。这个过程可以在存储时进行处理,也可以在显示时,前端发送请求,后台取出存储的数据进行处理。这取决于我们希望如何显示数据。
  5. 结果显示
  做了这么多功课,一点显示输出都没有,怎么不甘心?这又回到我们的老本行了,前端展示页面大家应该都很熟悉了。将数据展示更直观,方便我们进行统计分析。
  二、常见爬虫库介绍1. Superagent
  Superagent 是一个轻量级的 http 库。是nodejs中一个非常方便的客户端请求代理模块。当我们需要进行get、post、head等网络请求时,试试吧。
  2. 啦啦队
  Cheerio可以理解为jquery的一个Node.js版本,用于通过css选择器从网页中获取数据,用法和jquery完全一样。
  3. 异步
  async 是一个过程控制工具包,提供了直接强大的异步函数mapLimit(arr,limit,iterator,callback),我们主要使用这个方法,可以去官网看API。
  4. arr-del
  arr-del 是我自己写的一个删除数组元素的工具。通过传入由要删除的数组元素的索引组成的数组,可以立即删除它。
  5. arr-sort
  arr-sort 是我自己写的一个数组排序方法工具。可以根据一个或多个属性进行排序,支持嵌套属性。而且可以在每个条件中指定排序方向,传入比较函数。
  三、页面结构分析
  让我们重复爬行的想法。利马理财网上的产品主要是普通的和利马金库(新推出的光大银行理财产品,手续繁琐,初期投资额高,基本没人买,所以我们不买)不要在这里计算它们)。定期我们可以爬取财富管理页面的ajax界面:。(更新:最近经常断货,可能看不到数据) 数据如下图:
  
  这包括所有在线销售的常规产品。Ajax 数据只有产品本身的信息,如产品 id、募集金额、当前销售额、年化收益率、投资天数等,没有关于谁购买了产品的信息。. 所以我们需要去它的商品详情页面用id参数进行爬取,比如Lima Jucai-December HLB01239511。详情页有一栏投资记录,里面有我们需要的信息,如下图:
  
  但是,详情页只有在我们登录后才能查看,这就需要我们访问cookies,cookies是有有效期的。如何让我们的 cookie 保持登录状态?请稍后再看。
  其实Lima Vault也有类似的ajax接口:但是里面的相关数据是硬编码的,没有意义。而且金库的详情页也没有投资记录信息。这就需要我们爬取我们开头所说的主页的ajax界面:。但是后来我发现这个界面每三分钟更新一次,也就是说后台每三分钟向服务器请求一次数据。一次有10条数据,所以如果三分钟内购买记录超过10条,数据就会有遗漏。没办法,所以直接金库的统计数据会比真实的少。
  四、爬虫代码分析1. 获取登录cookie
  因为商品详情页需要登录,所以我们需要先获取登录cookie。getCookie 方法如下:
  function getCookie() {
superagent.post('https://www.lmlc.com/user/s/web/logon')
.type('form')
.send({
phone: phone,
password: password,
productCode: "LMLC",
origin: "PC"
})
.end(function(err, res) {
if (err) {
handleErr(err.message);
return;
}
cookie = res.header['set-cookie']; //从response中得到cookie
emitter.emit("setCookeie");
})
}
  手机号和密码参数是从命令行传入的,就是用你的手机号登录的账号和密码。我们使用superagent来模拟请求即时理财登录界面:。传入相应的参数。在回调中,我们获取头部的 set-cookie 信息,并发送一个 setCookeie 事件。因为我们设置了监听事件:emitter.on("setCookie", requestData),一旦拿到cookie,就会执行requestData方法。
  2. 财富管理页面ajax爬取
  requestData 方法的代码如下:
<p>function requestData() {
superagent.get('https://www.lmlc.com/web/product/product_list?pageSize=100&pageNo=1&type=0')
.end(function(err,pres){
// 常规的错误处理
if (err) {
handleErr(err.message);
return;
}
// 在这里清空数据,避免一个文件被同时写入
if(clearProd){
fs.writeFileSync('data/prod.json', JSON.stringify([]));
clearProd = false;
}
let addData = JSON.parse(pres.text).data;
let formatedAddData = formatData(addData.result);
let pageUrls = [];
if(addData.totalPage > 1){
handleErr('产品个数超过100个!');
return;
}
for(let i=0,len=addData.result.length; i 查看全部

  nodejs抓取动态网页(爬虫爬取的流程和最终如何展示数据的地址?)
  其实我之前做过利马财经的销售统计,不过是前端js写的。需要在首页的控制台调试面板中粘贴一段代码才能执行,点击这里。主要目的是通过定时爬取异步接口来获取数据。然后通过一定的去重算法得到最终的数据。但这有以下缺点:
  代码只能在浏览器窗口中运行。如果浏览器关闭或电脑无效,则只能抓取一个页面的数据,无法整合其他页面的数据。爬取的数据无法存储在本地。异步接口数据将被部分过滤。导致我们的去重算法失败
  由于最近了解了节点爬虫,所以可以在后台模拟请求,爬取页面数据。而且我开通了阿里云服务器,我可以把代码放到云端运行。这样1、2、3都可以解决。4因为我们不知道这个ajax界面每三分钟更新一次,这样我们就可以以此为依据对权重进行排序,保证数据不会重复。说到爬虫,大家想到的更多的是python。确实,python有Scrapy等成熟的框架,可以实现非常强大的爬虫功能。但是node也有自己的优势。凭借其强大的异步特性,可以轻松实现高效的异步并发请求,节省CPU开销。其实节点爬虫还是比较简单的,让'
  在线地址
  一、爬虫进程
  我们的最终目标是爬取利马财经每天的销售额,知道哪些产品在售,哪些用户在什么时间点购买了每种产品。首先介绍一下爬取的主要步骤:
  1. 结构分析
  我们要抓取页面的数据。第一步当然是分析页面结构,抓取哪些页面,页面结构是什么,不需要登录;有没有ajax接口,返回什么样的数据等
  2. 数据采集
  要分析清楚要爬取哪些页面和ajax,就需要爬取数据。现在网页的数据大致分为同步页面和ajax接口。同步页面数据的爬取需要我们首先分析网页的结构。Python爬取数据一般是通过正则表达式匹配获取需要的数据;node有一个cheerio工具,可以将获取到的页面内容转换成jquery对象。然后就可以使用jquery强大的dom API来获取节点相关的数据了。其实看源码,这些API本质上都是正则匹配。ajax接口数据一般都是json格式,处理起来比较简单。
  3. 数据存储
  捕获数据后,它会做一个简单的筛选,然后保存需要的数据,以便后续的分析和处理。当然我们可以使用MySQL、Mongodb等数据库来存储数据。在这里,为了方便,我们直接使用文件存储。
  4. 数据分析
  因为我们最终要展示数据,所以需要按照一定的维度对原创数据进行处理和分析,然后返回给客户端。这个过程可以在存储时进行处理,也可以在显示时,前端发送请求,后台取出存储的数据进行处理。这取决于我们希望如何显示数据。
  5. 结果显示
  做了这么多功课,一点显示输出都没有,怎么不甘心?这又回到我们的老本行了,前端展示页面大家应该都很熟悉了。将数据展示更直观,方便我们进行统计分析。
  二、常见爬虫库介绍1. Superagent
  Superagent 是一个轻量级的 http 库。是nodejs中一个非常方便的客户端请求代理模块。当我们需要进行get、post、head等网络请求时,试试吧。
  2. 啦啦队
  Cheerio可以理解为jquery的一个Node.js版本,用于通过css选择器从网页中获取数据,用法和jquery完全一样。
  3. 异步
  async 是一个过程控制工具包,提供了直接强大的异步函数mapLimit(arr,limit,iterator,callback),我们主要使用这个方法,可以去官网看API。
  4. arr-del
  arr-del 是我自己写的一个删除数组元素的工具。通过传入由要删除的数组元素的索引组成的数组,可以立即删除它。
  5. arr-sort
  arr-sort 是我自己写的一个数组排序方法工具。可以根据一个或多个属性进行排序,支持嵌套属性。而且可以在每个条件中指定排序方向,传入比较函数。
  三、页面结构分析
  让我们重复爬行的想法。利马理财网上的产品主要是普通的和利马金库(新推出的光大银行理财产品,手续繁琐,初期投资额高,基本没人买,所以我们不买)不要在这里计算它们)。定期我们可以爬取财富管理页面的ajax界面:。(更新:最近经常断货,可能看不到数据) 数据如下图:
  
  这包括所有在线销售的常规产品。Ajax 数据只有产品本身的信息,如产品 id、募集金额、当前销售额、年化收益率、投资天数等,没有关于谁购买了产品的信息。. 所以我们需要去它的商品详情页面用id参数进行爬取,比如Lima Jucai-December HLB01239511。详情页有一栏投资记录,里面有我们需要的信息,如下图:
  
  但是,详情页只有在我们登录后才能查看,这就需要我们访问cookies,cookies是有有效期的。如何让我们的 cookie 保持登录状态?请稍后再看。
  其实Lima Vault也有类似的ajax接口:但是里面的相关数据是硬编码的,没有意义。而且金库的详情页也没有投资记录信息。这就需要我们爬取我们开头所说的主页的ajax界面:。但是后来我发现这个界面每三分钟更新一次,也就是说后台每三分钟向服务器请求一次数据。一次有10条数据,所以如果三分钟内购买记录超过10条,数据就会有遗漏。没办法,所以直接金库的统计数据会比真实的少。
  四、爬虫代码分析1. 获取登录cookie
  因为商品详情页需要登录,所以我们需要先获取登录cookie。getCookie 方法如下:
  function getCookie() {
superagent.post('https://www.lmlc.com/user/s/web/logon')
.type('form')
.send({
phone: phone,
password: password,
productCode: "LMLC",
origin: "PC"
})
.end(function(err, res) {
if (err) {
handleErr(err.message);
return;
}
cookie = res.header['set-cookie']; //从response中得到cookie
emitter.emit("setCookeie");
})
}
  手机号和密码参数是从命令行传入的,就是用你的手机号登录的账号和密码。我们使用superagent来模拟请求即时理财登录界面:。传入相应的参数。在回调中,我们获取头部的 set-cookie 信息,并发送一个 setCookeie 事件。因为我们设置了监听事件:emitter.on("setCookie", requestData),一旦拿到cookie,就会执行requestData方法。
  2. 财富管理页面ajax爬取
  requestData 方法的代码如下:
<p>function requestData() {
superagent.get('https://www.lmlc.com/web/product/product_list?pageSize=100&pageNo=1&type=0')
.end(function(err,pres){
// 常规的错误处理
if (err) {
handleErr(err.message);
return;
}
// 在这里清空数据,避免一个文件被同时写入
if(clearProd){
fs.writeFileSync('data/prod.json', JSON.stringify([]));
clearProd = false;
}
let addData = JSON.parse(pres.text).data;
let formatedAddData = formatData(addData.result);
let pageUrls = [];
if(addData.totalPage > 1){
handleErr('产品个数超过100个!');
return;
}
for(let i=0,len=addData.result.length; i

nodejs抓取动态网页(【微信群直播记录】为什么需要一个前端监控系统)

网站优化优采云 发表了文章 • 0 个评论 • 65 次浏览 • 2021-10-25 00:23 • 来自相关话题

  nodejs抓取动态网页(【微信群直播记录】为什么需要一个前端监控系统)
  本文首发于infoQ和“前端之巅”微信公众号(微信群直播记录)。感谢infoQ Top of Frontend的同学对文章的整理和校对,以及微信群直播的组织策划
  为什么需要前端监控系统
  通常大型Web项目中的监控有很多,比如后端服务API监控,接口存活、调用、延迟等监控,这些一般都是用来监控后端接口数据层面的信息的。而对于一个大型的网站系统来说,从后端服务到前端展示会有很多层:内网VIP、CDN等,但是这些监控并不能准确反映前端的状态用户看到的页面,如:页面第三方系统数据调用失败、模块加载异常、数据不正确、天窗空白等。这时候就需要从前端DOM展示的角度分析采集用户真正看到的东西,从而检测页面是否有异常问题
  监控系统需要解决的问题
  一般当页面出现以下问题时,需要通过邮件和短信通知相关人员解决问题
  必须有触发报警时的场景快照,才能重现问题
  技术选型
  监控和回归测试的含义本质上是一样的。两者都对上线功能进行回归测试,但不同的是监控需要长期、可持续、循环的回归测试,上线后只需进行一次测试。返回
  由于监控和测试的本质是一样的,我们可以将测试作为一个监控系统。在自动化测试技术遍地开花的时代,有很多有用的自动化工具。我们只需要集成这些自动化工具供我们使用。
  节点
  NodeJS 是一个 JavaScript 运行环境,非阻塞 I/O 和异步,事件驱动,这些点对于我们基于 DOM 元素构建监控非常重要
  PhantomJS
  PhantomJS 是一个基于 webkit 的浏览器引擎,可以使用 JavaScript API 来模拟浏览器操作。它使用 QtWebKit 作为浏览器核心,使用 webkit 来编译、解释和执行 JavaScript 代码。换句话说,你可以在 webkit 浏览器中做的任何事情,它都能做
  它不仅是一个隐形浏览器,还提供CSS选择器,支持Web标准、DOM操作、JSON、HTML5、Canvas、SVG等,还提供处理文件I/O的操作。PhantomJS 用途广泛,如网络监控、网页截图、无浏览器网页测试、页面访问自动化等。
  为什么不是硒
  做自动化测试的同学一定知道Selenium。可以使用Selenium在浏览器中执行测试用例,Selenium对各种平台和常用浏览器的支持比较好,但是Selenium上手稍微有点难度,使用Selenium需要在服务器端安装浏览器
  考虑到监控的主要任务是监控而不是测试。系统不需要过多考虑兼容性,监控功能比较单一,主要是针对页面的功能回归测试,所以选择了PhantomJS
  架构设计架构概述
  
  架构简介
  对于DOM监控服务,在应用层面垂直划分:
  应用层面的垂直划分,实现了应用的分布式部署,提升了处理能力。也方便后期的性能优化、系统改造和扩展
  解决方案前端规则入口
  这是一个独立的Web系统,系统主要用于采集用户输入的页面信息,页面对应的规则,显示错误信息。通过调用后端页面爬取服务完成页面检测任务,系统可以创建三种类型的检测页面:定期监控、高级监控、可用性监控
  常规监测
  输入一个页面地址和几个检测规则。注意这里的检测规则,我们将一些常用的检测点抽象成一个类似于测试用例的语句。每条规则用于匹配页面上的一个DOM元素,DOM元素的属性用于匹配期望。如果匹配失败,系统会产生错误信息,由报警系统处理。
  一般有几种匹配类型:长度、文本、HTML、属性
  处理器类似于编程语言中的运算符:大于、大于或等于、小于、小于或等于、等于、正则
  这样做的重点是,进入规则的人只需要了解一点 DOM 选择器的知识即可上手。在我们内部,通常是交给测试工程师来完成规则的录入。
  
  高级监控
  主要用于提供高级页面测试功能。通常,有经验的工程师会编写测试用例。这个测试用例写起来会有一定的学习成本,但是可以模拟网页操作,比如点击、鼠标移动等事件来准确捕获页面信息
  
  可用性监控
  可用性监控侧重于实时监控更严重的问题,例如页面可访问性和内容正确性。通常我们只需要在程序中启动一个Worker就可以获取页面的HTML来检查匹配结果,所以我们选择NodeJS来做异步页面爬取队列来高效快速的完成这种网络密度。任务
  
  主动报错页面脚本执行错误监控
  页面引入监控脚本,采集页面产生的错误事件返回的错误信息,并自动上报给后端服务。在系统中,可以汇总所有错误信息,以及对应的客户端浏览器版本、操作系统、IP地址等。
  主动举报页面
  该功能需要相应的前端工程师调用代码中的报错API主动提交错误信息。使用的主要场景有:页面异步服务延迟无响应、模块降级主动通知等,监控脚本提供了几个简单的API来完成这个任务
  // error 方法调用后立即上报错误信息并发出邮件、短信通知
errorTracker.error('错误描述')
// info 方法调用后立即上报信息,并在单位时间内仅产生一条邮件、短信通知
errorTracker.info('信息描述')
// log 方法调用后由报错检测是否达到设置阀值,最终确认是否报错
errorTracker.log('日志信息')
  后端页面抓取服务
  因为京东的很多页面都是异步加载的,所以首页、单品等系统有很多第三方异步接口调用。后端程序抓取的页面数据是同步的,无法检索到动态JavaScript渲染内容。所以你必须使用像 PhantomJS 这样可以模拟浏览器的工具
  对于日常监控,我们使用PhantomJS模拟浏览器打开页面进行抓取,然后将监控规则解析成JavaScript代码片段执行并采集结果
  高级监控 我们使用 PhantomJS 打开页面,将 jasmine、mocha 等前端 JavaScript 测试框架注入页面,然后在页面上执行相应的输入测试用例并返回结果
  常规队列生成器
  规则队列生成器会将采集的规则转换成消息队列,然后交给长期连续处理器处理一次
  为什么使用类似的消息队列处理方式?
  这与 PhantomJS 的性能是分不开的。从很多实践中发现,PhantomJS 不能很好地进行并发处理。当并发过多时,会导致CPU过载,导致机器宕机。
  本地环境下虚拟机并发测试,数据不理想,限制基本在ab -n 100 -c 50左右。 所以为了防止并发问题,我们选择使用消息队列避免高并发导致服务不可用
  类消息队列的实现
  这里我们通过调用内部的分布式缓存系统来生成消息队列。其实队列的产生可以参考数据结构-queue。最基本的模式是在缓存中创建一个KEY,然后按照队列数据结构的模式插入和读取数据
  当然,类消息队列的中间介质可以根据自己的实际情况选择,也可以使用原生内存来实现。这可能会导致应用程序和类似消息的队列争夺内存
  长时处理器
  长期连续处理器是消费规则队列生成器生成的消息队列。
  长期连续加工实现
  在长期持久化处理器的具体实现中,我们使用了 JavaScript 的 setInterval 方法不断获取累了的消息队列的内容发送给规则转换器,再转发给负载均衡调度器。然后对返回的结果进行统一处理,如邮件或短信报警
  应用程序接口
  PhantomJS服务可以作为公共API提供给客户端处理测试需求,通过HTTP调用API。在 API 处理中,需要提供 HTTP 数据到规则和 PhantomJS 的转换。从而将 HTTP 数据演化为规则转换器
  PhantomJS 服务
  PhantomJS 服务是指将 PhantomJS 与 HTTP 服务和子流程结合起来的服务处理 查看全部

  nodejs抓取动态网页(【微信群直播记录】为什么需要一个前端监控系统)
  本文首发于infoQ和“前端之巅”微信公众号(微信群直播记录)。感谢infoQ Top of Frontend的同学对文章的整理和校对,以及微信群直播的组织策划
  为什么需要前端监控系统
  通常大型Web项目中的监控有很多,比如后端服务API监控,接口存活、调用、延迟等监控,这些一般都是用来监控后端接口数据层面的信息的。而对于一个大型的网站系统来说,从后端服务到前端展示会有很多层:内网VIP、CDN等,但是这些监控并不能准确反映前端的状态用户看到的页面,如:页面第三方系统数据调用失败、模块加载异常、数据不正确、天窗空白等。这时候就需要从前端DOM展示的角度分析采集用户真正看到的东西,从而检测页面是否有异常问题
  监控系统需要解决的问题
  一般当页面出现以下问题时,需要通过邮件和短信通知相关人员解决问题
  必须有触发报警时的场景快照,才能重现问题
  技术选型
  监控和回归测试的含义本质上是一样的。两者都对上线功能进行回归测试,但不同的是监控需要长期、可持续、循环的回归测试,上线后只需进行一次测试。返回
  由于监控和测试的本质是一样的,我们可以将测试作为一个监控系统。在自动化测试技术遍地开花的时代,有很多有用的自动化工具。我们只需要集成这些自动化工具供我们使用。
  节点
  NodeJS 是一个 JavaScript 运行环境,非阻塞 I/O 和异步,事件驱动,这些点对于我们基于 DOM 元素构建监控非常重要
  PhantomJS
  PhantomJS 是一个基于 webkit 的浏览器引擎,可以使用 JavaScript API 来模拟浏览器操作。它使用 QtWebKit 作为浏览器核心,使用 webkit 来编译、解释和执行 JavaScript 代码。换句话说,你可以在 webkit 浏览器中做的任何事情,它都能做
  它不仅是一个隐形浏览器,还提供CSS选择器,支持Web标准、DOM操作、JSON、HTML5、Canvas、SVG等,还提供处理文件I/O的操作。PhantomJS 用途广泛,如网络监控、网页截图、无浏览器网页测试、页面访问自动化等。
  为什么不是硒
  做自动化测试的同学一定知道Selenium。可以使用Selenium在浏览器中执行测试用例,Selenium对各种平台和常用浏览器的支持比较好,但是Selenium上手稍微有点难度,使用Selenium需要在服务器端安装浏览器
  考虑到监控的主要任务是监控而不是测试。系统不需要过多考虑兼容性,监控功能比较单一,主要是针对页面的功能回归测试,所以选择了PhantomJS
  架构设计架构概述
  
  架构简介
  对于DOM监控服务,在应用层面垂直划分:
  应用层面的垂直划分,实现了应用的分布式部署,提升了处理能力。也方便后期的性能优化、系统改造和扩展
  解决方案前端规则入口
  这是一个独立的Web系统,系统主要用于采集用户输入的页面信息,页面对应的规则,显示错误信息。通过调用后端页面爬取服务完成页面检测任务,系统可以创建三种类型的检测页面:定期监控、高级监控、可用性监控
  常规监测
  输入一个页面地址和几个检测规则。注意这里的检测规则,我们将一些常用的检测点抽象成一个类似于测试用例的语句。每条规则用于匹配页面上的一个DOM元素,DOM元素的属性用于匹配期望。如果匹配失败,系统会产生错误信息,由报警系统处理。
  一般有几种匹配类型:长度、文本、HTML、属性
  处理器类似于编程语言中的运算符:大于、大于或等于、小于、小于或等于、等于、正则
  这样做的重点是,进入规则的人只需要了解一点 DOM 选择器的知识即可上手。在我们内部,通常是交给测试工程师来完成规则的录入。
  
  高级监控
  主要用于提供高级页面测试功能。通常,有经验的工程师会编写测试用例。这个测试用例写起来会有一定的学习成本,但是可以模拟网页操作,比如点击、鼠标移动等事件来准确捕获页面信息
  
  可用性监控
  可用性监控侧重于实时监控更严重的问题,例如页面可访问性和内容正确性。通常我们只需要在程序中启动一个Worker就可以获取页面的HTML来检查匹配结果,所以我们选择NodeJS来做异步页面爬取队列来高效快速的完成这种网络密度。任务
  
  主动报错页面脚本执行错误监控
  页面引入监控脚本,采集页面产生的错误事件返回的错误信息,并自动上报给后端服务。在系统中,可以汇总所有错误信息,以及对应的客户端浏览器版本、操作系统、IP地址等。
  主动举报页面
  该功能需要相应的前端工程师调用代码中的报错API主动提交错误信息。使用的主要场景有:页面异步服务延迟无响应、模块降级主动通知等,监控脚本提供了几个简单的API来完成这个任务
  // error 方法调用后立即上报错误信息并发出邮件、短信通知
errorTracker.error('错误描述')
// info 方法调用后立即上报信息,并在单位时间内仅产生一条邮件、短信通知
errorTracker.info('信息描述')
// log 方法调用后由报错检测是否达到设置阀值,最终确认是否报错
errorTracker.log('日志信息')
  后端页面抓取服务
  因为京东的很多页面都是异步加载的,所以首页、单品等系统有很多第三方异步接口调用。后端程序抓取的页面数据是同步的,无法检索到动态JavaScript渲染内容。所以你必须使用像 PhantomJS 这样可以模拟浏览器的工具
  对于日常监控,我们使用PhantomJS模拟浏览器打开页面进行抓取,然后将监控规则解析成JavaScript代码片段执行并采集结果
  高级监控 我们使用 PhantomJS 打开页面,将 jasmine、mocha 等前端 JavaScript 测试框架注入页面,然后在页面上执行相应的输入测试用例并返回结果
  常规队列生成器
  规则队列生成器会将采集的规则转换成消息队列,然后交给长期连续处理器处理一次
  为什么使用类似的消息队列处理方式?
  这与 PhantomJS 的性能是分不开的。从很多实践中发现,PhantomJS 不能很好地进行并发处理。当并发过多时,会导致CPU过载,导致机器宕机。
  本地环境下虚拟机并发测试,数据不理想,限制基本在ab -n 100 -c 50左右。 所以为了防止并发问题,我们选择使用消息队列避免高并发导致服务不可用
  类消息队列的实现
  这里我们通过调用内部的分布式缓存系统来生成消息队列。其实队列的产生可以参考数据结构-queue。最基本的模式是在缓存中创建一个KEY,然后按照队列数据结构的模式插入和读取数据
  当然,类消息队列的中间介质可以根据自己的实际情况选择,也可以使用原生内存来实现。这可能会导致应用程序和类似消息的队列争夺内存
  长时处理器
  长期连续处理器是消费规则队列生成器生成的消息队列。
  长期连续加工实现
  在长期持久化处理器的具体实现中,我们使用了 JavaScript 的 setInterval 方法不断获取累了的消息队列的内容发送给规则转换器,再转发给负载均衡调度器。然后对返回的结果进行统一处理,如邮件或短信报警
  应用程序接口
  PhantomJS服务可以作为公共API提供给客户端处理测试需求,通过HTTP调用API。在 API 处理中,需要提供 HTTP 数据到规则和 PhantomJS 的转换。从而将 HTTP 数据演化为规则转换器
  PhantomJS 服务
  PhantomJS 服务是指将 PhantomJS 与 HTTP 服务和子流程结合起来的服务处理

nodejs抓取动态网页(爬虫爬取的流程和最终如何展示数据的地址?)

网站优化优采云 发表了文章 • 0 个评论 • 70 次浏览 • 2021-10-25 00:21 • 来自相关话题

  nodejs抓取动态网页(爬虫爬取的流程和最终如何展示数据的地址?)
  其实我之前做过利马财经的销售统计,不过是前端js写的。需要在首页的控制台调试面板中粘贴一段代码才能执行,点击这里。主要目的是通过定时爬取异步接口来获取数据。然后通过一定的去重算法得到最终的数据。但这有以下缺点:
  1. 代码只能在浏览器窗口运行,关闭浏览器或关闭电脑都会失效
  2. 只能抓取一个页面的数据,不能整合其他页面的数据
  3. 爬取的数据无法本地存储
  4. 以上异步接口数据会被部分过滤,导致我们的去重算法失败
  由于最近了解了节点爬虫,所以可以在后台模拟请求,爬取页面数据。而且我开通了阿里云服务器,我可以把代码放到云端运行。这样1、2、3都可以解决。4因为我们不知道这个ajax界面每三分钟更新一次,这样我们就可以以此为依据对权重进行排序,保证数据不会重复。说到爬虫,大家想到的更多的是python。确实,python有Scrapy等成熟的框架,可以实现非常强大的爬虫功能。但是node也有自己的优势。凭借其强大的异步特性,可以轻松实现高效的异步并发请求,节省CPU开销。其实节点爬虫还是比较简单的,让'
  在线地址
  一、爬虫进程
  我们的最终目标是爬取利马财经每天的销售额,知道哪些产品在售,哪些用户在什么时间点购买了每种产品。首先介绍一下爬取的主要步骤:
  1. 结构分析
  我们要抓取页面的数据。第一步当然是分析页面结构,抓取哪些页面,页面的结构是什么,不需要登录;有没有ajax接口,返回什么样的数据等
  2. 数据采集
  要分析清楚要爬取哪些页面和ajax,就需要爬取数据。现在网页的数据大致分为同步页面和ajax接口。同步页面数据的爬取需要我们首先分析网页的结构。Python爬取数据一般是通过正则表达式匹配获取需要的数据;node有一个cheerio工具,可以将获取到的页面内容转换成jquery对象。然后就可以使用jquery强大的dom API来获取节点相关的数据了。其实看源码,这些API本质上都是正则匹配。ajax接口数据一般都是json格式,处理起来比较简单。
  3. 数据存储
  捕获数据后,它会做一个简单的筛选,然后保存需要的数据,以便后续的分析和处理。当然我们可以使用MySQL、Mongodb等数据库来存储数据。这里,为了方便,我们直接使用文件存储。
  4. 数据分析
  因为我们最终要展示数据,所以需要按照一定的维度对原创数据进行处理和分析,然后返回给客户端。这个过程可以在存储时进行处理,也可以在显示时,前端发送请求,后台取出存储的数据进行处理。这取决于我们希望如何显示数据。
  5. 结果显示
  做了这么多功课,一点显示输出都没有,怎么不甘心?这又回到我们的老本行了,前端展示页面大家应该都很熟悉了。将数据展示更直观,方便我们进行统计分析。
  二、常见爬虫库介绍1. Superagent
  Superagent 是一个轻量级的 http 库。是nodejs中一个非常方便的客户端请求代理模块。当我们需要进行get、post、head等网络请求时,试试吧。
  2. 啦啦队
  Cheerio可以理解为jquery的一个Node.js版本,用于通过css选择器从网页中获取数据,用法和jquery完全一样。
  3. 异步
  async 是一个过程控制工具包,提供了直接强大的异步函数mapLimit(arr,limit,iterator,callback),我们主要使用这个方法,可以去官网看API。
  4. arr-del
  arr-del 是我自己写的一个删除数组元素的工具。通过传入由要删除的数组元素的索引组成的数组,可以立即删除它。
  5. arr-sort
  arr-sort 是我自己写的一个数组排序方法工具。可以根据一个或多个属性进行排序,支持嵌套属性。而且可以在每个条件中指定排序方向,传入比较函数。
  三、页面结构分析
  让我们重复爬行的想法。利马理财网上的产品主要是普通的和利马金库(新推出的光大银行理财产品,手续繁琐,初期投资额高,基本没人买,所以我们不买)不要在这里计算它们)。定期我们可以爬取财富管理页面的ajax界面:。(更新:近期定期断货,可能看不到数据,1月19日前可以看到数据) 数据如下图所示:
  
  这包括所有在线销售的常规产品。Ajax 数据只有产品本身的信息,如产品 id、募集金额、当前销售额、年化收益率、投资天数等,没有关于谁购买了产品的信息。. 所以我们需要去它的商品详情页面用id参数进行爬取,比如Lima Jucai-December HLB01239511。详情页有一栏投资记录,里面有我们需要的信息,如下图:
  
  但是,详情页只有在我们登录后才能查看,这就需要我们访问cookies,cookies是有有效期的。如何让我们的 cookie 保持登录状态?请稍后再看。
  其实Lima Vault也有类似的ajax接口:,但是里面的相关数据是硬编码的,没有意义。而且金库的详情页也没有投资记录信息。这就需要我们爬取我们开头所说的主页的ajax界面:。但是后来我发现这个界面每三分钟更新一次,也就是说后台每三分钟向服务器请求一次数据。一次有10条数据,所以如果三分钟内购买记录超过10条,数据就会有遗漏。没办法,所以直接金库的统计数据会比真实的少。
  四、爬虫代码分析1. 获取登录cookie
  因为商品详情页需要登录,所以我们需要先获取登录cookie。getCookie 方法如下:
  function getCookie() {
superagent.post(&#39;https://www.lmlc.com/user/s/web/logon&#39;)
.type(&#39;form&#39;)
.send({
phone: phone,
password: password,
productCode: "LMLC",
origin: "PC"
})
.end(function(err, res) {
if (err) {
handleErr(err.message);
return;
}
cookie = res.header[&#39;set-cookie&#39;]; //从response中得到cookie
emitter.emit("setCookeie");
})
}
  手机号和密码参数是从命令行传入的,就是用你的手机号登录的账号和密码。我们使用superagent来模拟请求即时理财登录界面:。传入相应的参数。在回调中,我们获取头部的 set-cookie 信息,并发送一个 setCookeie 事件。因为我们设置了监听事件:emitter.on("setCookie", requestData),一旦拿到cookie,就会执行requestData方法。
  2. 财富管理页面ajax爬取
  requestData 方法的代码如下:
<p>function requestData() {
superagent.get(&#39;https://www.lmlc.com/web/produ ... %2339;)
.end(function(err,pres){
// 常规的错误处理
if (err) {
handleErr(err.message);
return;
}
// 在这里清空数据,避免一个文件被同时写入
if(clearProd){
fs.writeFileSync(&#39;data/prod.json&#39;, JSON.stringify([]));
clearProd = false;
}
let addData = JSON.parse(pres.text).data;
let formatedAddData = formatData(addData.result);
let pageUrls = [];
if(addData.totalPage > 1){
handleErr(&#39;产品个数超过100个!&#39;);
return;
}
for(let i=0,len=addData.result.length; i 查看全部

  nodejs抓取动态网页(爬虫爬取的流程和最终如何展示数据的地址?)
  其实我之前做过利马财经的销售统计,不过是前端js写的。需要在首页的控制台调试面板中粘贴一段代码才能执行,点击这里。主要目的是通过定时爬取异步接口来获取数据。然后通过一定的去重算法得到最终的数据。但这有以下缺点:
  1. 代码只能在浏览器窗口运行,关闭浏览器或关闭电脑都会失效
  2. 只能抓取一个页面的数据,不能整合其他页面的数据
  3. 爬取的数据无法本地存储
  4. 以上异步接口数据会被部分过滤,导致我们的去重算法失败
  由于最近了解了节点爬虫,所以可以在后台模拟请求,爬取页面数据。而且我开通了阿里云服务器,我可以把代码放到云端运行。这样1、2、3都可以解决。4因为我们不知道这个ajax界面每三分钟更新一次,这样我们就可以以此为依据对权重进行排序,保证数据不会重复。说到爬虫,大家想到的更多的是python。确实,python有Scrapy等成熟的框架,可以实现非常强大的爬虫功能。但是node也有自己的优势。凭借其强大的异步特性,可以轻松实现高效的异步并发请求,节省CPU开销。其实节点爬虫还是比较简单的,让'
  在线地址
  一、爬虫进程
  我们的最终目标是爬取利马财经每天的销售额,知道哪些产品在售,哪些用户在什么时间点购买了每种产品。首先介绍一下爬取的主要步骤:
  1. 结构分析
  我们要抓取页面的数据。第一步当然是分析页面结构,抓取哪些页面,页面的结构是什么,不需要登录;有没有ajax接口,返回什么样的数据等
  2. 数据采集
  要分析清楚要爬取哪些页面和ajax,就需要爬取数据。现在网页的数据大致分为同步页面和ajax接口。同步页面数据的爬取需要我们首先分析网页的结构。Python爬取数据一般是通过正则表达式匹配获取需要的数据;node有一个cheerio工具,可以将获取到的页面内容转换成jquery对象。然后就可以使用jquery强大的dom API来获取节点相关的数据了。其实看源码,这些API本质上都是正则匹配。ajax接口数据一般都是json格式,处理起来比较简单。
  3. 数据存储
  捕获数据后,它会做一个简单的筛选,然后保存需要的数据,以便后续的分析和处理。当然我们可以使用MySQL、Mongodb等数据库来存储数据。这里,为了方便,我们直接使用文件存储。
  4. 数据分析
  因为我们最终要展示数据,所以需要按照一定的维度对原创数据进行处理和分析,然后返回给客户端。这个过程可以在存储时进行处理,也可以在显示时,前端发送请求,后台取出存储的数据进行处理。这取决于我们希望如何显示数据。
  5. 结果显示
  做了这么多功课,一点显示输出都没有,怎么不甘心?这又回到我们的老本行了,前端展示页面大家应该都很熟悉了。将数据展示更直观,方便我们进行统计分析。
  二、常见爬虫库介绍1. Superagent
  Superagent 是一个轻量级的 http 库。是nodejs中一个非常方便的客户端请求代理模块。当我们需要进行get、post、head等网络请求时,试试吧。
  2. 啦啦队
  Cheerio可以理解为jquery的一个Node.js版本,用于通过css选择器从网页中获取数据,用法和jquery完全一样。
  3. 异步
  async 是一个过程控制工具包,提供了直接强大的异步函数mapLimit(arr,limit,iterator,callback),我们主要使用这个方法,可以去官网看API。
  4. arr-del
  arr-del 是我自己写的一个删除数组元素的工具。通过传入由要删除的数组元素的索引组成的数组,可以立即删除它。
  5. arr-sort
  arr-sort 是我自己写的一个数组排序方法工具。可以根据一个或多个属性进行排序,支持嵌套属性。而且可以在每个条件中指定排序方向,传入比较函数。
  三、页面结构分析
  让我们重复爬行的想法。利马理财网上的产品主要是普通的和利马金库(新推出的光大银行理财产品,手续繁琐,初期投资额高,基本没人买,所以我们不买)不要在这里计算它们)。定期我们可以爬取财富管理页面的ajax界面:。(更新:近期定期断货,可能看不到数据,1月19日前可以看到数据) 数据如下图所示:
  
  这包括所有在线销售的常规产品。Ajax 数据只有产品本身的信息,如产品 id、募集金额、当前销售额、年化收益率、投资天数等,没有关于谁购买了产品的信息。. 所以我们需要去它的商品详情页面用id参数进行爬取,比如Lima Jucai-December HLB01239511。详情页有一栏投资记录,里面有我们需要的信息,如下图:
  
  但是,详情页只有在我们登录后才能查看,这就需要我们访问cookies,cookies是有有效期的。如何让我们的 cookie 保持登录状态?请稍后再看。
  其实Lima Vault也有类似的ajax接口:,但是里面的相关数据是硬编码的,没有意义。而且金库的详情页也没有投资记录信息。这就需要我们爬取我们开头所说的主页的ajax界面:。但是后来我发现这个界面每三分钟更新一次,也就是说后台每三分钟向服务器请求一次数据。一次有10条数据,所以如果三分钟内购买记录超过10条,数据就会有遗漏。没办法,所以直接金库的统计数据会比真实的少。
  四、爬虫代码分析1. 获取登录cookie
  因为商品详情页需要登录,所以我们需要先获取登录cookie。getCookie 方法如下:
  function getCookie() {
superagent.post(&#39;https://www.lmlc.com/user/s/web/logon&#39;)
.type(&#39;form&#39;)
.send({
phone: phone,
password: password,
productCode: "LMLC",
origin: "PC"
})
.end(function(err, res) {
if (err) {
handleErr(err.message);
return;
}
cookie = res.header[&#39;set-cookie&#39;]; //从response中得到cookie
emitter.emit("setCookeie");
})
}
  手机号和密码参数是从命令行传入的,就是用你的手机号登录的账号和密码。我们使用superagent来模拟请求即时理财登录界面:。传入相应的参数。在回调中,我们获取头部的 set-cookie 信息,并发送一个 setCookeie 事件。因为我们设置了监听事件:emitter.on("setCookie", requestData),一旦拿到cookie,就会执行requestData方法。
  2. 财富管理页面ajax爬取
  requestData 方法的代码如下:
<p>function requestData() {
superagent.get(&#39;https://www.lmlc.com/web/produ ... %2339;)
.end(function(err,pres){
// 常规的错误处理
if (err) {
handleErr(err.message);
return;
}
// 在这里清空数据,避免一个文件被同时写入
if(clearProd){
fs.writeFileSync(&#39;data/prod.json&#39;, JSON.stringify([]));
clearProd = false;
}
let addData = JSON.parse(pres.text).data;
let formatedAddData = formatData(addData.result);
let pageUrls = [];
if(addData.totalPage > 1){
handleErr(&#39;产品个数超过100个!&#39;);
return;
}
for(let i=0,len=addData.result.length; i

nodejs抓取动态网页(让百度下我的动态填充vue单页面ssr解决问题)

网站优化优采云 发表了文章 • 0 个评论 • 65 次浏览 • 2021-10-22 11:03 • 来自相关话题

  nodejs抓取动态网页(让百度下我的动态填充vue单页面ssr解决问题)
  遇到的问题:
  最近在写个人博客的时候,遇到了一个大家可能会遇到的问题。
  vue单页SEO很弱,尤其是百度不会抓取动态脚本
  Vue-router配合前后端分离,防止蜘蛛爬行时动态填充meta标签
  Vue单页是大势所趋,写起来不仅很酷,当然也可以选择多页
  但是即使面对文章和文档有多个页面,也不能说每个文章生成一个vue页面。
  ssr当然可以解决这个问题,但是仔细想想,ssr不是和之前的.php页面一样吗?
  所有数据都是提前拉取然后填充返回浏览器,消耗服务器资源较多,配置麻烦
  当然预渲染不能解决这个问题
  所以问题来了,我只想百度抓取我的动态文章,但是配置一个繁琐的ssr并不是最好的选择
  我的解决方案:
  由于我只是想让百度抓取我的动态文章,其实蜘蛛抓取我的静态页面的时候,html meta标签就已经填好了
  那么就很简单了,我们只需要实现一个很简单的ssr阉割版即可。
  ps:好久没在百度找到相关的文章,不知道是不是我百度姿势不对
  具体思路:
  因为我的服务器后端语言是php,服务是nginx,所以我这里展示的所有后端代码都是基于php的。当然你可以选择nodejs或者其他语言来实现。这是一个简单的想法
  如上所述,我们将实现一个阉割版的 ssr。实际上,当服务器有请求时,在静态html元标记上填写数据,然后将其返回给请求者。
  这里的实现是基于搭建的vue单页,所以请先搭建一个vue单页
  先改造建dist下的index.html
  获取最上面的变量,因为接口都是现成的,所以偷懒直接获取接口
  将获取到的数据输出到meta标签
<p> 查看全部

  nodejs抓取动态网页(让百度下我的动态填充vue单页面ssr解决问题)
  遇到的问题:
  最近在写个人博客的时候,遇到了一个大家可能会遇到的问题。
  vue单页SEO很弱,尤其是百度不会抓取动态脚本
  Vue-router配合前后端分离,防止蜘蛛爬行时动态填充meta标签
  Vue单页是大势所趋,写起来不仅很酷,当然也可以选择多页
  但是即使面对文章和文档有多个页面,也不能说每个文章生成一个vue页面。
  ssr当然可以解决这个问题,但是仔细想想,ssr不是和之前的.php页面一样吗?
  所有数据都是提前拉取然后填充返回浏览器,消耗服务器资源较多,配置麻烦
  当然预渲染不能解决这个问题
  所以问题来了,我只想百度抓取我的动态文章,但是配置一个繁琐的ssr并不是最好的选择
  我的解决方案:
  由于我只是想让百度抓取我的动态文章,其实蜘蛛抓取我的静态页面的时候,html meta标签就已经填好了
  那么就很简单了,我们只需要实现一个很简单的ssr阉割版即可。
  ps:好久没在百度找到相关的文章,不知道是不是我百度姿势不对
  具体思路:
  因为我的服务器后端语言是php,服务是nginx,所以我这里展示的所有后端代码都是基于php的。当然你可以选择nodejs或者其他语言来实现。这是一个简单的想法
  如上所述,我们将实现一个阉割版的 ssr。实际上,当服务器有请求时,在静态html元标记上填写数据,然后将其返回给请求者。
  这里的实现是基于搭建的vue单页,所以请先搭建一个vue单页
  先改造建dist下的index.html
  获取最上面的变量,因为接口都是现成的,所以偷懒直接获取接口
  将获取到的数据输出到meta标签
<p>

nodejs抓取动态网页(服务器请求响应不同的文件路由:根据不同文件请求)

网站优化优采云 发表了文章 • 0 个评论 • 85 次浏览 • 2021-10-21 23:10 • 来自相关话题

  nodejs抓取动态网页(服务器请求响应不同的文件路由:根据不同文件请求)
  昨天创建的服务器只是在浏览器请求时做出响应,而今天要创建的服务器可以根据不同的URL请求响应不同的文件,这就是所谓的文件路由:不同的文件请求响应不同的“路” .
  第一步:创建文件Luyou.js,在其中声明引用模块的变量和要响应的文件路径
  //获取http模块
var http = require("http");
//文件模块
var fs = require(&#39;fs&#39;);
//主页路由模块,file文件夹里的index.js文件
var index = require(&#39;./file/index&#39;);
//错误处理文件路径
var error = "./file/error404.html";
//春晓页面路径
var cx = "./file/cunxiao.html";
  需要提供路由的文件主要包括三个文件。第一个是index模块,负责首页的响应;第二个是404错误页面的响应;三是春晓诗的回复页。文件目录如下:
  
  步骤二:实现文件响应功能和404错误响应功能
  //函数Response,将HTML、css、js等文件响应给客户端
var Response = function(res,filePath){
//读取文件,读取完成后给客户端响应
fs.readFile(filePath,function(err,data){
if(err){ //如果失败,就返回错误文件
if(filePath != error) //如果失败的不是错误文件,才返回错误文件
Response(res,error);
}else{
res.writeHead(200,{ //响应客户端,将文件内容发回去
&#39;Content-type&#39;:"text/html"});
res.end(data);
}
});
};
//404错误响应文件
var error404 = function(res){
Response(res,error);
};
  该函数使用了fs文件模块,用于提取文件内容,提取的内容(或错误)会在回调函数中传回,这是node.js非的思想- 阻塞 I/O 事件编程表现。 查看全部

  nodejs抓取动态网页(服务器请求响应不同的文件路由:根据不同文件请求)
  昨天创建的服务器只是在浏览器请求时做出响应,而今天要创建的服务器可以根据不同的URL请求响应不同的文件,这就是所谓的文件路由:不同的文件请求响应不同的“路” .
  第一步:创建文件Luyou.js,在其中声明引用模块的变量和要响应的文件路径
  //获取http模块
var http = require("http");
//文件模块
var fs = require(&#39;fs&#39;);
//主页路由模块,file文件夹里的index.js文件
var index = require(&#39;./file/index&#39;);
//错误处理文件路径
var error = "./file/error404.html";
//春晓页面路径
var cx = "./file/cunxiao.html";
  需要提供路由的文件主要包括三个文件。第一个是index模块,负责首页的响应;第二个是404错误页面的响应;三是春晓诗的回复页。文件目录如下:
  
  步骤二:实现文件响应功能和404错误响应功能
  //函数Response,将HTML、css、js等文件响应给客户端
var Response = function(res,filePath){
//读取文件,读取完成后给客户端响应
fs.readFile(filePath,function(err,data){
if(err){ //如果失败,就返回错误文件
if(filePath != error) //如果失败的不是错误文件,才返回错误文件
Response(res,error);
}else{
res.writeHead(200,{ //响应客户端,将文件内容发回去
&#39;Content-type&#39;:"text/html"});
res.end(data);
}
});
};
//404错误响应文件
var error404 = function(res){
Response(res,error);
};
  该函数使用了fs文件模块,用于提取文件内容,提取的内容(或错误)会在回调函数中传回,这是node.js非的思想- 阻塞 I/O 事件编程表现。

nodejs抓取动态网页(Web浏览器呈现的HTML,您可以开始网页并JavaScript吗?)

网站优化优采云 发表了文章 • 0 个评论 • 64 次浏览 • 2021-10-21 07:03 • 来自相关话题

  nodejs抓取动态网页(Web浏览器呈现的HTML,您可以开始网页并JavaScript吗?)
  Puppeteer 是一个流行的 Headless Chrome NodeJS API 爬虫库,由 Google 构建。 Puppeteer Sharp 是用 C# 编写的,由 Dario Kondratiuque 于 2017 年发布,为 .NET 开发人员提供相同的功能。
  
  傀儡师标志
  Puppeteer Sharp 使 .NET 开发人员能够以编程方式控制开源 Google Chrome。 Puppeteer API 的便利之处在于能够使用浏览器的 headless 特性而无需显示浏览器来提高性能。
  为什么要使用 Puppeteer Sharp?
  如果您是 .NET 开发人员,可以通过 Nuget 包将其安装到项目中:
  在现代 Web 中,Web 应用程序通常依赖 JavaScript 来加载 UI。如果您使用爬虫加载 Bing 地图,您可能会失望地收到:
  
  Bing 地图为空
  Puppeteer Sharp 除了检索 JavaScript 渲染的 HTML 之外,还可以注入 HTML 进行导航网站;与 UI 元素交互;截取屏幕截图或创建 PDF,现在流行的 Google NodeJS API 中收录更多功能。
  入门
  在新的或现有的 .NET 项目中使用 Puppeteer Sharp。安装最新版本的 Nuget 包“PuppeteeSharp”。
  
  图像.png
  首先我们需要将Chrome浏览器下载到本地。这是 Puppeteer Sharp 将用于与 网站 交互的浏览器。
  幸运的是,我们可以使用 C# 下载默认修订版或开发者指定的修订版。仅当本地计算机上不存在修订版时才会下载。
  // Download the Chromium revision if it does not already exist
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
  如果下载成功,您会在项目目录中看到在操作系统上运行所需的浏览器版本:
  
  图像.png
  加载网页
  现在您已将浏览器下载到本地计算机,您可以开始加载网页并检索 JavaScript 呈现的 HTML。
  首先,我们将启动一个无头 Web 浏览器的实例,加载一个新选项卡并转到“地图”:
  // Create an instance of the browser and configure launch options
Browser browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true
});
// Create a new page and go to Bing Maps
Page page = await browser.NewPageAsync();
await page.GoToAsync("https://www.bing.com/maps");
  
  图像.png
  在无头浏览器中成功加载网页后,让我们通过搜索当地旅游景点与网页进行交互:
  // Search for a local tourist attraction on Bing Maps
await page.WaitForSelectorAsync(".searchbox input");
await page.FocusAsync(".searchbox input");
await page.Keyboard.TypeAsync("CN Tower, Toronto, Ontario, Canada");
await page.ClickAsync(".searchIcon");
await page.WaitForNavigationAsync();
  我们可以使用Puppeteer Sharp与JavaScript渲染的Bing地图HTML进行交互,搜索“加拿大安大略省多伦多CN塔”!
  如果您想存储 HTML 以分析地址或描述等信息,您可以轻松地将 HTML 存储在变量中:
  // Store the HTML of the current page
string content = await page.GetContentAsync();
  完成后关闭浏览器释放资源:
  // Close the browser
await browser.CloseAsync();
  屏幕截图和 PDF 文档
  Puppeteer Sharp 的好处之一是能够生成当前页面的屏幕截图和 PDF 文档。这对于调试、自动化测试或以特定分辨率捕获网页特别有用。
  如果你想截取当前页面的截图:
  await page.ScreenshotAsync("C:\\Files\\screenshot.png");
  
  人偶截图
  或者,生成当前页面的PDF文档:
  await page.PdfAsync("C:\\Files\\document.pdf");
  
  图像.png
  更改页面大小
  如果您需要测试特定显示尺寸的网页(例如,查看页面在手机上的显示方式),您可以使用 Puppeter Sharp 更改当前页面上的网页尺寸:
  // Change the size of the view port to simulate the iPhone X
await page.SetViewportAsync(new ViewPortOptions
{
Width = 1125,
Height = 2436
});
  
  图像.png
  跟踪日志
  除了上述功能外,Puppeteer Sharp 还可用于监控和检测与网络用户界面相关的问题。 .NET 开发人员可以使用 Puppeteer Sharp 检查任何网络性能问题。
  为此,我们可以启动和停止跟踪日志:
  await page.Tracing.StartAsync(new TracingOptions { Path = "C:\\Files\\trace.json" });
...
await page.Tracing.StopAsync();
  
  图像.png
  如果跟踪日志没有捕获调试会话所需的详细信息,您可以启用 Chrome DevTools 进行进一步分析:
  Browser browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Devtools = true
});
  如果在 Puppeteer Sharp 中启用 Chrome DevTools,headless 配置将自动禁用,您将可以查看浏览器,并且 DevTools 将显示查看 Web 应用程序的 JavaScript 渲染代码的选项,以及诸如查看网络活动。
  
  图像.png
  连接到远程浏览器
  Puppeteer Sharp 的最后一个功能是能够连接到远程浏览器。如果您的服务器上无法安装浏览器(例如 Linux),此功能可能会很有用。
  比如老外的这个browserless.io:可以用不花钱的童鞋
  
  图像.png
  var connectOptions = new ConnectOptions()
{
BrowserWSEndpoint = "$wss://chrome.browserless.io/"
};
using (var browser = await Puppeteer.ConnectAsync(connectOptions))
{
...
}
  项目捐赠
  项目官网。 查看全部

  nodejs抓取动态网页(Web浏览器呈现的HTML,您可以开始网页并JavaScript吗?)
  Puppeteer 是一个流行的 Headless Chrome NodeJS API 爬虫库,由 Google 构建。 Puppeteer Sharp 是用 C# 编写的,由 Dario Kondratiuque 于 2017 年发布,为 .NET 开发人员提供相同的功能。
  
  傀儡师标志
  Puppeteer Sharp 使 .NET 开发人员能够以编程方式控制开源 Google Chrome。 Puppeteer API 的便利之处在于能够使用浏览器的 headless 特性而无需显示浏览器来提高性能。
  为什么要使用 Puppeteer Sharp?
  如果您是 .NET 开发人员,可以通过 Nuget 包将其安装到项目中:
  在现代 Web 中,Web 应用程序通常依赖 JavaScript 来加载 UI。如果您使用爬虫加载 Bing 地图,您可能会失望地收到:
  
  Bing 地图为空
  Puppeteer Sharp 除了检索 JavaScript 渲染的 HTML 之外,还可以注入 HTML 进行导航网站;与 UI 元素交互;截取屏幕截图或创建 PDF,现在流行的 Google NodeJS API 中收录更多功能。
  入门
  在新的或现有的 .NET 项目中使用 Puppeteer Sharp。安装最新版本的 Nuget 包“PuppeteeSharp”。
  
  图像.png
  首先我们需要将Chrome浏览器下载到本地。这是 Puppeteer Sharp 将用于与 网站 交互的浏览器。
  幸运的是,我们可以使用 C# 下载默认修订版或开发者指定的修订版。仅当本地计算机上不存在修订版时才会下载。
  // Download the Chromium revision if it does not already exist
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
  如果下载成功,您会在项目目录中看到在操作系统上运行所需的浏览器版本:
  
  图像.png
  加载网页
  现在您已将浏览器下载到本地计算机,您可以开始加载网页并检索 JavaScript 呈现的 HTML。
  首先,我们将启动一个无头 Web 浏览器的实例,加载一个新选项卡并转到“地图”:
  // Create an instance of the browser and configure launch options
Browser browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true
});
// Create a new page and go to Bing Maps
Page page = await browser.NewPageAsync();
await page.GoToAsync("https://www.bing.com/maps";);
  
  图像.png
  在无头浏览器中成功加载网页后,让我们通过搜索当地旅游景点与网页进行交互:
  // Search for a local tourist attraction on Bing Maps
await page.WaitForSelectorAsync(".searchbox input");
await page.FocusAsync(".searchbox input");
await page.Keyboard.TypeAsync("CN Tower, Toronto, Ontario, Canada");
await page.ClickAsync(".searchIcon");
await page.WaitForNavigationAsync();
  我们可以使用Puppeteer Sharp与JavaScript渲染的Bing地图HTML进行交互,搜索“加拿大安大略省多伦多CN塔”!
  如果您想存储 HTML 以分析地址或描述等信息,您可以轻松地将 HTML 存储在变量中:
  // Store the HTML of the current page
string content = await page.GetContentAsync();
  完成后关闭浏览器释放资源:
  // Close the browser
await browser.CloseAsync();
  屏幕截图和 PDF 文档
  Puppeteer Sharp 的好处之一是能够生成当前页面的屏幕截图和 PDF 文档。这对于调试、自动化测试或以特定分辨率捕获网页特别有用。
  如果你想截取当前页面的截图:
  await page.ScreenshotAsync("C:\\Files\\screenshot.png");
  
  人偶截图
  或者,生成当前页面的PDF文档:
  await page.PdfAsync("C:\\Files\\document.pdf");
  
  图像.png
  更改页面大小
  如果您需要测试特定显示尺寸的网页(例如,查看页面在手机上的显示方式),您可以使用 Puppeter Sharp 更改当前页面上的网页尺寸:
  // Change the size of the view port to simulate the iPhone X
await page.SetViewportAsync(new ViewPortOptions
{
Width = 1125,
Height = 2436
});
  
  图像.png
  跟踪日志
  除了上述功能外,Puppeteer Sharp 还可用于监控和检测与网络用户界面相关的问题。 .NET 开发人员可以使用 Puppeteer Sharp 检查任何网络性能问题。
  为此,我们可以启动和停止跟踪日志:
  await page.Tracing.StartAsync(new TracingOptions { Path = "C:\\Files\\trace.json" });
...
await page.Tracing.StopAsync();
  
  图像.png
  如果跟踪日志没有捕获调试会话所需的详细信息,您可以启用 Chrome DevTools 进行进一步分析:
  Browser browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Devtools = true
});
  如果在 Puppeteer Sharp 中启用 Chrome DevTools,headless 配置将自动禁用,您将可以查看浏览器,并且 DevTools 将显示查看 Web 应用程序的 JavaScript 渲染代码的选项,以及诸如查看网络活动。
  
  图像.png
  连接到远程浏览器
  Puppeteer Sharp 的最后一个功能是能够连接到远程浏览器。如果您的服务器上无法安装浏览器(例如 Linux),此功能可能会很有用。
  比如老外的这个browserless.io:可以用不花钱的童鞋
  
  图像.png
  var connectOptions = new ConnectOptions()
{
BrowserWSEndpoint = "$wss://chrome.browserless.io/"
};
using (var browser = await Puppeteer.ConnectAsync(connectOptions))
{
...
}
  项目捐赠
  项目官网。

nodejs抓取动态网页(异步特性()(2)_知乎点开(图))

网站优化优采云 发表了文章 • 0 个评论 • 63 次浏览 • 2021-10-21 07:02 • 来自相关话题

  nodejs抓取动态网页(异步特性()(2)_知乎点开(图))
  写了个小爬虫,貌似很不完善。很多地方都没有处理。例如,当在 知乎 中打开一个问题时,并不是所有的答案都被加载。当你拉到答案的结尾时,点击加载更多来加载一部分答案,所以如果你直接发送一个问题的请求链接,你得到的页面是不完整的。还有就是我们通过访问链接下载图片的时候,是一张一张的下载的。如果图片太多,真的会下载到你睡着了。
  这个爬虫是上一个的升级版。爬虫代码可以在我的github=&gt;NodeSpider上找到。
  整个爬虫的思路是这样的:一开始我们通过请求问题的链接来抓取部分页面数据,然后我们在代码中模拟ajax请求拦截剩余页面的数据,当然,这里也可以通过异步来实现并发。对于小规模的异步过程控制,可以使用这个module=&gt;eventproxy,但是我这里没有用!我们从获取到的页面中截取所有图片的链接,然后通过异步并发实现这些图片的批量下载。
  抓取页面的初始数据很简单,这里就不解释了。
   1 /*获取首屏所有图片链接*/
2 var getInitUrlList=function(){
3 request.get("https://www.zhihu.com/question/34937418")
4 .end(function(err,res){
5 if(err){
6 console.log(err);
7 }else{
8 var $=cheerio.load(res.text);
9 var answerList=$(".zm-item-answer");
10 answerList.map(function(i,answer){
11 var images=$(answer).find('.zm-item-rich-text img');
12 images.map(function(i,image){
13 photos.push($(image).attr("src"));
14 });
15 });
16 console.log("已成功抓取"+photos.length+"张图片的链接");
17 getIAjaxUrlList(20);
18 }
19 });
20 }
  模拟ajax请求获取完整页面
  下一步是如何模拟点击加载更多时发送的ajax请求。去知乎看看吧!
  
  
  
  有了这些信息,您可以模拟发送相同的请求来获取数据。
   1 /*每隔100毫秒模拟发送ajax请求,并获取请求结果中所有的图片链接*/
2 var getIAjaxUrlList=function(offset){
3 request.post("https://www.zhihu.com/node/Que ... 6quot;)
4 .set(config)
5 .send("method=next&params=%7B%22url_token%22%3A34937418%2C%22pagesize%22%3A20%2C%22offset%22%3A" +offset+ "%7D&_xsrf=98360a2df02783902146dee374772e51")
6 .end(function(err,res){
7 if(err){
8 console.log(err);
9 }else{
10 var response=JSON.parse(res.text);/*想用json的话对json序列化即可,提交json的话需要对json进行反序列化*/
11 if(response.msg&&response.msg.length){
12 var $=cheerio.load(response.msg.join(""));/*把所有的数组元素拼接在一起,以空白符分隔,不要这样join(),它会默认数组元素以逗号分隔*/
13 var answerList=$(".zm-item-answer");
14 answerList.map(function(i,answer){
15 var images=$(answer).find('.zm-item-rich-text img');
16 images.map(function(i,image){
17 photos.push($(image).attr("src"));
18 });
19 });
20 setTimeout(function(){
21 offset+=20;
22 console.log("已成功抓取"+photos.length+"张图片的链接");
23 getIAjaxUrlList(offset);
24 },100);
25 }else{
26 console.log("图片链接全部获取完毕,一共有"+photos.length+"条图片链接");
27 // console.log(photos);
28 return downloadImg(50);
29 }
30 }
31 });
32 }
  在代码中post这条请求https://www.zhihu.com/node/QuestionAnswerListV2,把原请求头和请求参数复制下来,作为我们的请求头和请求参数,superagent的set方法可用来设置请求头,send方法可以用来发送请求参数。我们把请求参数中的offset初始为20,每隔一定时间offset再加20,再重新发送请求,这样就相当于我们每隔一定时间发送了一条ajax请求,获取到最新的20条数据,每获取到了数据,我们再对这些数据进行一定的处理,让它们变成一整段的html,便于后面的提取链接处理。
  
异步并发控制下载图片
再获取完了所有的图片链接之后,即判定response.msg为空时,我们就要对这些图片进行下载了,不可能一条一条下对不对,因为如你所看到的,我们的图片足足有

  没错,2万多,还好nodejs有神奇的单线程异步功能,我们可以同时下载这些图片。但是这个时候问题就来了。听说如果同时发送太多请求,会被网站拦截!所以我们绝对不能同时下载超过 20,000 张图片。这时候就需要控制异步并发的数量。
  这里使用了一个神奇的模块=&gt; async,它不仅可以帮助我们询问难以维护的回调金字塔恶魔,还可以轻松帮助我们管理异步进程。具体看文档,这里只使用了一个强大的 async.mapLimit 方法。
<p> 1 var requestAndwrite=function(url,callback){
2 request.get(url).end(function(err,res){
3 if(err){
4 console.log(err);
5 console.log("有一张图片请求失败啦...");
6 }else{
7 var fileName=path.basename(url);
8 fs.writeFile("./img1/"+fileName,res.body,function(err){
9 if(err){
10 console.log(err);
11 console.log("有一张图片写入失败啦...");
12 }else{
13 console.log("图片下载成功啦");
14 callback(null,"successful !");
15 /*callback貌似必须调用,第二个参数将传给下一个回调函数的result,result是一个数组*/
16 }
17 });
18 }
19 });
20 }
21
22 var downloadImg=function(asyncNum){
23 /*有一些图片链接地址不完整没有“http:”头部,帮它们拼接完整*/
24 for(var i=0;i 查看全部

  nodejs抓取动态网页(异步特性()(2)_知乎点开(图))
  写了个小爬虫,貌似很不完善。很多地方都没有处理。例如,当在 知乎 中打开一个问题时,并不是所有的答案都被加载。当你拉到答案的结尾时,点击加载更多来加载一部分答案,所以如果你直接发送一个问题的请求链接,你得到的页面是不完整的。还有就是我们通过访问链接下载图片的时候,是一张一张的下载的。如果图片太多,真的会下载到你睡着了。
  这个爬虫是上一个的升级版。爬虫代码可以在我的github=&gt;NodeSpider上找到。
  整个爬虫的思路是这样的:一开始我们通过请求问题的链接来抓取部分页面数据,然后我们在代码中模拟ajax请求拦截剩余页面的数据,当然,这里也可以通过异步来实现并发。对于小规模的异步过程控制,可以使用这个module=&gt;eventproxy,但是我这里没有用!我们从获取到的页面中截取所有图片的链接,然后通过异步并发实现这些图片的批量下载。
  抓取页面的初始数据很简单,这里就不解释了。
   1 /*获取首屏所有图片链接*/
2 var getInitUrlList=function(){
3 request.get("https://www.zhihu.com/question/34937418";)
4 .end(function(err,res){
5 if(err){
6 console.log(err);
7 }else{
8 var $=cheerio.load(res.text);
9 var answerList=$(".zm-item-answer");
10 answerList.map(function(i,answer){
11 var images=$(answer).find('.zm-item-rich-text img');
12 images.map(function(i,image){
13 photos.push($(image).attr("src"));
14 });
15 });
16 console.log("已成功抓取"+photos.length+"张图片的链接");
17 getIAjaxUrlList(20);
18 }
19 });
20 }
  模拟ajax请求获取完整页面
  下一步是如何模拟点击加载更多时发送的ajax请求。去知乎看看吧!
  
  
  
  有了这些信息,您可以模拟发送相同的请求来获取数据。
   1 /*每隔100毫秒模拟发送ajax请求,并获取请求结果中所有的图片链接*/
2 var getIAjaxUrlList=function(offset){
3 request.post("https://www.zhihu.com/node/Que ... 6quot;)
4 .set(config)
5 .send("method=next&params=%7B%22url_token%22%3A34937418%2C%22pagesize%22%3A20%2C%22offset%22%3A" +offset+ "%7D&_xsrf=98360a2df02783902146dee374772e51")
6 .end(function(err,res){
7 if(err){
8 console.log(err);
9 }else{
10 var response=JSON.parse(res.text);/*想用json的话对json序列化即可,提交json的话需要对json进行反序列化*/
11 if(response.msg&&response.msg.length){
12 var $=cheerio.load(response.msg.join(""));/*把所有的数组元素拼接在一起,以空白符分隔,不要这样join(),它会默认数组元素以逗号分隔*/
13 var answerList=$(".zm-item-answer");
14 answerList.map(function(i,answer){
15 var images=$(answer).find('.zm-item-rich-text img');
16 images.map(function(i,image){
17 photos.push($(image).attr("src"));
18 });
19 });
20 setTimeout(function(){
21 offset+=20;
22 console.log("已成功抓取"+photos.length+"张图片的链接");
23 getIAjaxUrlList(offset);
24 },100);
25 }else{
26 console.log("图片链接全部获取完毕,一共有"+photos.length+"条图片链接");
27 // console.log(photos);
28 return downloadImg(50);
29 }
30 }
31 });
32 }
  在代码中post这条请求https://www.zhihu.com/node/QuestionAnswerListV2,把原请求头和请求参数复制下来,作为我们的请求头和请求参数,superagent的set方法可用来设置请求头,send方法可以用来发送请求参数。我们把请求参数中的offset初始为20,每隔一定时间offset再加20,再重新发送请求,这样就相当于我们每隔一定时间发送了一条ajax请求,获取到最新的20条数据,每获取到了数据,我们再对这些数据进行一定的处理,让它们变成一整段的html,便于后面的提取链接处理。
  
异步并发控制下载图片
再获取完了所有的图片链接之后,即判定response.msg为空时,我们就要对这些图片进行下载了,不可能一条一条下对不对,因为如你所看到的,我们的图片足足有

  没错,2万多,还好nodejs有神奇的单线程异步功能,我们可以同时下载这些图片。但是这个时候问题就来了。听说如果同时发送太多请求,会被网站拦截!所以我们绝对不能同时下载超过 20,000 张图片。这时候就需要控制异步并发的数量。
  这里使用了一个神奇的模块=&gt; async,它不仅可以帮助我们询问难以维护的回调金字塔恶魔,还可以轻松帮助我们管理异步进程。具体看文档,这里只使用了一个强大的 async.mapLimit 方法。
<p> 1 var requestAndwrite=function(url,callback){
2 request.get(url).end(function(err,res){
3 if(err){
4 console.log(err);
5 console.log("有一张图片请求失败啦...");
6 }else{
7 var fileName=path.basename(url);
8 fs.writeFile("./img1/"+fileName,res.body,function(err){
9 if(err){
10 console.log(err);
11 console.log("有一张图片写入失败啦...");
12 }else{
13 console.log("图片下载成功啦");
14 callback(null,"successful !");
15 /*callback貌似必须调用,第二个参数将传给下一个回调函数的result,result是一个数组*/
16 }
17 });
18 }
19 });
20 }
21
22 var downloadImg=function(asyncNum){
23 /*有一些图片链接地址不完整没有“http:”头部,帮它们拼接完整*/
24 for(var i=0;i

nodejs抓取动态网页(登录页面与主页面的判断,下有路由独享钩子就像说的一样 )

网站优化优采云 发表了文章 • 0 个评论 • 63 次浏览 • 2021-10-21 07:01 • 来自相关话题

  nodejs抓取动态网页(登录页面与主页面的判断,下有路由独享钩子就像说的一样
)
  说明:在开发过程中,我们经常会遇到进入登录页面和主页面的判断。通常后台会发回一个session来判断。现在考虑有多少种方法可以达到这种效果;
  1. 之前使用的方法是在app.vue入口文件中直接判断是跳转到登录页面还是主页面。
  优点:简单明了,直接根据是否有session来判断入口文件是登录还是主页面;
  缺点:体验不好,每次判断前都会有登录页面,然后跳转到主页面,
  2. 使用动态路由判断用户登录是跳转到登录页面还是主页面,判断管理员权限,判断页面是否存在,没有跳转到404页面。
  优点:可以应用于多个时钟,体验好,
  添加 main.js 或 router.js
  router.beforeEach((to, from, next) => {
console.log(store.state.token)
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
const route = [&#39;index&#39;, &#39;list&#39;];
let isLogin = store.state.token; // 是否登录
// 未登录状态;当路由到route指定页时,跳转至login
if (route.indexOf(to.name) >= 0) {
if (isLogin == null) {
router.push({ path: &#39;/login&#39;, });
}
}
// 已登录状态;当路由到login时,跳转至home
console.log(to.name)
localStorage.setItem(&#39;routerName&#39;, to.name)
if (to.name === &#39;login&#39;) {
if (isLogin != null) {
router.push({ path: &#39;/HomeMain&#39;, });;
}
}
next();
});
  下面是路由的钩子函数:
  路由钩子主要是为用户定义路由变化时进行一些特殊处理
  一般来说,vue 中提供了三种类型的钩子
  1、全局钩子
  2、一个路由专用的钩子
  3、组件内钩子
  1.全局钩子
  主要包括beforeEach和aftrEach,
  beforeEach 函数有三个参数:
  to:路由器即将进入的路由对象
  from:当前导航即将离开的路线
  next: 函数,管道中的一个钩子。如果执行完成,则确认导航状态;否则为假,导航终止。
  afterEach 函数不需要传递 next() 函数
  这种钩子主要是全局使用的,一般用来判断权限和页面丢失时需要执行的操作,比如上面的
  router.beforeEach((to, from, next) => {
console.log(store.state.token)
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
const route = [&#39;index&#39;, &#39;list&#39;];
let isLogin = store.state.token; // 是否登录
// 未登录状态;当路由到route指定页时,跳转至login
if (route.indexOf(to.name) >= 0) {
if (isLogin == null) {
router.push({ path: &#39;/login&#39;, });
}
}
// 已登录状态;当路由到login时,跳转至home
console.log(to.name)
localStorage.setItem(&#39;routerName&#39;, to.name)
if (to.name === &#39;login&#39;) {
if (isLogin != null) {
router.push({ path: &#39;/HomeMain&#39;, });;
}
}
next();
});
  2.一个路由专属钩子
  如上所述,某个路由的单次使用与后续组件中的钩子本质上是相同的。都是指特定的路线。不同的是,这里的一般定义是在路由器中,而不是在组件中。如下
   {
path: &#39;/dashboard&#39;,
component: resolve => require([&#39;../components/page/Dashboard.vue&#39;], resolve),
meta: { title: &#39;系统首页&#39; },
beforeEnter: (to, from, next) => {

},
beforeLeave: (to, from, next) => {

}

},
  3.组件路由
  主要包括beforeRouteEnter和beforeRouteUpdate,beforeRouteLeave。这些钩子写在组件中,也可以传递三个参数(to、from、next),它们的功能和前面的类似。
  beforeRouteLeave(to, from, next) {
// ....
next()
},
beforeRouteEnter(to, from, next) {
// ....
next()
},
beforeRouteUpdate(to, from, next) {
// ....
next()
},
computed: {},
method: {}
  最后看官网介绍
  to:Route:即将进入的目标路由对象
  from:Route:当前导航即将离开的路线
  next: 功能:必须调用这个方法来解析这个钩子。执行效果取决于next方法的调用参数。
  next():转到管道中的下一个钩子。如果所有钩子都执行完毕,则确认导航的状态。
  next(false):中断当前导航。如果浏览器的 URL 发生变化(可能是用户手动或浏览器后退按钮),URL 地址将重置为 from 路由对应的地址。
  next('/') 或 next({ path:'/' }):跳转到不同的地址。当前导航被中断,然后执行新的导航。
  最后一点,关于页面不存在,跳转到404页面
  由于路由器本身是从上到下匹配的,如果在前面找到匹配的路由,就会跳转。所以可以直接在最后加上404路由,如下
  let routerConfig = [{
path: &#39;/pages&#39;,
component: App,
children: [{
path: &#39;demo/step1/list&#39;,
component: coupon,
name: &#39;coupon-list&#39;,
meta: {
title: &#39;红包&#39;
}
}]
}, {
path: &#39;*&#39;,
component: NotFound,
name: &#39;notfound&#39;,
meta: {
title: &#39;404-页面丢了&#39;
}
}] 查看全部

  nodejs抓取动态网页(登录页面与主页面的判断,下有路由独享钩子就像说的一样
)
  说明:在开发过程中,我们经常会遇到进入登录页面和主页面的判断。通常后台会发回一个session来判断。现在考虑有多少种方法可以达到这种效果;
  1. 之前使用的方法是在app.vue入口文件中直接判断是跳转到登录页面还是主页面。
  优点:简单明了,直接根据是否有session来判断入口文件是登录还是主页面;
  缺点:体验不好,每次判断前都会有登录页面,然后跳转到主页面,
  2. 使用动态路由判断用户登录是跳转到登录页面还是主页面,判断管理员权限,判断页面是否存在,没有跳转到404页面。
  优点:可以应用于多个时钟,体验好,
  添加 main.js 或 router.js
  router.beforeEach((to, from, next) => {
console.log(store.state.token)
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
const route = [&#39;index&#39;, &#39;list&#39;];
let isLogin = store.state.token; // 是否登录
// 未登录状态;当路由到route指定页时,跳转至login
if (route.indexOf(to.name) >= 0) {
if (isLogin == null) {
router.push({ path: &#39;/login&#39;, });
}
}
// 已登录状态;当路由到login时,跳转至home
console.log(to.name)
localStorage.setItem(&#39;routerName&#39;, to.name)
if (to.name === &#39;login&#39;) {
if (isLogin != null) {
router.push({ path: &#39;/HomeMain&#39;, });;
}
}
next();
});
  下面是路由的钩子函数:
  路由钩子主要是为用户定义路由变化时进行一些特殊处理
  一般来说,vue 中提供了三种类型的钩子
  1、全局钩子
  2、一个路由专用的钩子
  3、组件内钩子
  1.全局钩子
  主要包括beforeEach和aftrEach,
  beforeEach 函数有三个参数:
  to:路由器即将进入的路由对象
  from:当前导航即将离开的路线
  next: 函数,管道中的一个钩子。如果执行完成,则确认导航状态;否则为假,导航终止。
  afterEach 函数不需要传递 next() 函数
  这种钩子主要是全局使用的,一般用来判断权限和页面丢失时需要执行的操作,比如上面的
  router.beforeEach((to, from, next) => {
console.log(store.state.token)
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
const route = [&#39;index&#39;, &#39;list&#39;];
let isLogin = store.state.token; // 是否登录
// 未登录状态;当路由到route指定页时,跳转至login
if (route.indexOf(to.name) >= 0) {
if (isLogin == null) {
router.push({ path: &#39;/login&#39;, });
}
}
// 已登录状态;当路由到login时,跳转至home
console.log(to.name)
localStorage.setItem(&#39;routerName&#39;, to.name)
if (to.name === &#39;login&#39;) {
if (isLogin != null) {
router.push({ path: &#39;/HomeMain&#39;, });;
}
}
next();
});
  2.一个路由专属钩子
  如上所述,某个路由的单次使用与后续组件中的钩子本质上是相同的。都是指特定的路线。不同的是,这里的一般定义是在路由器中,而不是在组件中。如下
   {
path: &#39;/dashboard&#39;,
component: resolve => require([&#39;../components/page/Dashboard.vue&#39;], resolve),
meta: { title: &#39;系统首页&#39; },
beforeEnter: (to, from, next) => {

},
beforeLeave: (to, from, next) => {

}

},
  3.组件路由
  主要包括beforeRouteEnter和beforeRouteUpdate,beforeRouteLeave。这些钩子写在组件中,也可以传递三个参数(to、from、next),它们的功能和前面的类似。
  beforeRouteLeave(to, from, next) {
// ....
next()
},
beforeRouteEnter(to, from, next) {
// ....
next()
},
beforeRouteUpdate(to, from, next) {
// ....
next()
},
computed: {},
method: {}
  最后看官网介绍
  to:Route:即将进入的目标路由对象
  from:Route:当前导航即将离开的路线
  next: 功能:必须调用这个方法来解析这个钩子。执行效果取决于next方法的调用参数。
  next():转到管道中的下一个钩子。如果所有钩子都执行完毕,则确认导航的状态。
  next(false):中断当前导航。如果浏览器的 URL 发生变化(可能是用户手动或浏览器后退按钮),URL 地址将重置为 from 路由对应的地址。
  next('/') 或 next({ path:'/' }):跳转到不同的地址。当前导航被中断,然后执行新的导航。
  最后一点,关于页面不存在,跳转到404页面
  由于路由器本身是从上到下匹配的,如果在前面找到匹配的路由,就会跳转。所以可以直接在最后加上404路由,如下
  let routerConfig = [{
path: &#39;/pages&#39;,
component: App,
children: [{
path: &#39;demo/step1/list&#39;,
component: coupon,
name: &#39;coupon-list&#39;,
meta: {
title: &#39;红包&#39;
}
}]
}, {
path: &#39;*&#39;,
component: NotFound,
name: &#39;notfound&#39;,
meta: {
title: &#39;404-页面丢了&#39;
}
}]

nodejs抓取动态网页(需要的jar包代码如下:链接不支持xpath解析)

网站优化优采云 发表了文章 • 0 个评论 • 167 次浏览 • 2021-10-19 13:08 • 来自相关话题

  nodejs抓取动态网页(需要的jar包代码如下:链接不支持xpath解析)
  需要的jar包:
  1
2 org.jsoup
3 jsoup
4 1.10.3
5
  代码如下:
   1 // 请求超时时间,30秒
2 public static final int TIME_OUT = 30*1000;
3 // 模拟浏览器请求头信息
4 public static Map headers = new HashMap();
5 static{
6 headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0");
7 headers.put("Accept", "text/html");
8 headers.put("Accept-Language", "zh-CN,zh");
9 }
10
11 //根据url获取html文档
12 protected Document getDoc(String url) throws IOException{
13 if(logger.isDebugEnabled())
14 logger.debug(url);
15 //新建一个连接
16 Connection conn = Jsoup.connect(url).timeout(TIME_OUT);
17 conn = conn.headers(headers);
18 conn = conn.proxy(Proxy.NO_PROXY);
19 Document doc = conn.get();
20
21 if(logger.isTraceEnabled()){
22 logger.trace("["+url+"]\n"+doc);
23 }
24 return doc;
25 }
   1 public static final String CHINAZ_ICP_URL = "http://icp.chinaz.com/?type=host&s=%s";
2 public List doHandler(String domain) {
3 List results = new ArrayList();
4 String url = String.format(CHINAZ_ICP_URL, domain);
5 Document doc;
6 try {
7 doc = this.getDoc(url);
8 // 获取当前页ICP信息所在标签
9 Elements eles = doc.select("ul.IcpMain01>li:lt(7)>p");
10
11 if(null == eles || eles.isEmpty()){
12 return results;
13 }
14 //获取ICP信息
15 for (Element element : eles) {
16 //当前元素为认证信息时,跳过
17 if("safe".equals(element.attr("id"))){
18 continue;
19 }
20 Node firstNode = element.childNode(0);
21 if(firstNode.childNodeSize() > 0){
22 results.add(element.child(0).text());
23 }else{
24 results.add(((TextNode)firstNode).text());
25 }
26 }
27 } catch (IOException e) {
28 logger.error("get Chinaz ICP message error :",e);
29 }
30 doc = null;
31 return results;
32 }
  参考 Jsoup 的文档:链接
  Jsoup不支持xpath解析,这个很痛苦,不过总得有人做个支持xpath的东西---JsoupXpath,链接,有兴趣的网友可以自己试试!
  三、htmlunit
  支持Xpath分析,可以模拟浏览器动作,比如点击下一页、加载更多等等。文档链接:
  需要的jar包
  1
2 net.sourceforge.htmlunit
3 htmlunit
4 2.18
5
  代码如下:
   1 import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
2 import com.gargoylesoftware.htmlunit.TopLevelWindow;
3 import com.gargoylesoftware.htmlunit.WebClient;
4 import com.gargoylesoftware.htmlunit.html.HtmlPage;
5 import com.gargoylesoftware.htmlunit.html.HtmlTableRow;
6
7 import java.io.IOException;
8 import java.util.ArrayList;
9 import java.util.List;
10
11
12 public class UrlTest {
13
14 public static void main(String[] args) {
15 BaseCollector baseCollector = new BaseCollector();
16 WebClient webClient = baseCollector.getWebClient();
17 String url="http://htmlunit.sourceforge.net/";
18 HtmlPage homePage= null;
19 try {
20 homePage = webClient.getPage(url);
21 if (homePage != null && homePage instanceof HtmlPage) {
22 homePage.getEnclosingWindow().setName("IpHomePage");
23 System.out.println("打开 IPHomePage ");
24 System.out.println("内容是: "+homePage.getBody().getTextContent());
25
26 List htmlTableRows = (List) homePage.getByXPath("/html/body/pre");
27 if (htmlTableRows != null && htmlTableRows.size() > 0) {
28 for (int i = 0; i < htmlTableRows.size(); i++) {
29 HtmlTableRow htmlTableRow = htmlTableRows.get(i);
30 //日期
31 String firstTime = htmlTableRow.getCell(0).getTextContent().trim();
32 System.out.println(firstTime);
33 }
34
35 }
36 closeWindowByName(webClient, "IPHomePage");
37 System.out.println("关闭 IPHomePage ");
38 }
39 webClient.close();
40
41 } catch (IOException e) {
42 System.out.println(e.getMessage()+" ===="+e);
43 }catch (FailingHttpStatusCodeException e){
44 System.out.println(e.getMessage()+" ===="+e);
45 }
46 System.out.println("内容是: "+homePage.getBody().getTextContent());
47 }
48
49 public static void closeWindowByName(WebClient webClient, String name){
50 List list = webClient.getTopLevelWindows();
51 List windowNames = new ArrayList();
52 for (int j = 0; j < list.size(); j++) {
53 if(list.get(j).getName().equals(name)){
54 list.get(j).close();
55 }
56 windowNames.add(list.get(j).getName());
57 }
58 System.out.println("当前窗口 : {}"+list.toString());
59 }
60 }
61
62
  四、HeadlessChrome1,HeadlessChrome 和 PhantomJS 对比
  在 Chrome 未提供原生 Headless 模式之前,Web 开发者可以使用 PhantomJS 等第三方 Headless 浏览器。既然官方已经准备好提供 Headless,PhantomJS 维护者 Vitaly Slobodin 立即在邮件列表中宣布辞职。另一个流行的浏览器 Firefox 也准备提供 Headless 模式。
  2、什么是 HeadlessChrome
  HeadlessChrome 是 Chrome 浏览器的非接口形式。您可以使用 Chrome 支持的所有功能来运行您的程序,而无需打开浏览器。与现代浏览器相比,HeadlessChrome 可以更方便地测试 Web 应用程序、获取 网站 的截图、做爬虫抓取信息等。
  3、环境配置
  首先需要下载chrome-driver,不同版本的Chrome对应不同的Chrome-driver,可以通过这个链接下载对应的Chrome-driver
  支持各种元素的获取,List elements = driver.findElements(By.xpath("///*[@id=\"body\"]/ul[2]/li"));
  可以模拟浏览器的各种动作,driver.findElement(By.linkText("Next page")).click();
  用Python搞HeadlessChrome更方便简单,简直太神奇了。 . . . 链接:
  可以参考
  需要的jar包:
  1
2 org.seleniumhq.selenium
3 selenium-chrome-driver
4 3.11.0
5
  代码如下:
<p> 1 import org.jsoup.Jsoup;
2 import org.jsoup.nodes.Document;
3 import org.openqa.selenium.By;
4 import org.openqa.selenium.WebDriver;
5 import org.openqa.selenium.WebElement;
6 import org.openqa.selenium.chrome.ChromeDriver;
7 import org.openqa.selenium.chrome.ChromeOptions;
8
9 import java.util.List;
10 import java.util.concurrent.TimeUnit;
11
12 /**
13 * Created by sqy on 2018/5/2.
14 */
15 public class HeadlessChromeTest {
16
17 public static void main(String args[]) {
18
19
20
21 //G:\chromedriver
22 System.setProperty("webdriver.chrome.driver","G:\\chromedriver\\chromedriver.exe");
23 ChromeOptions chromeOptions = new ChromeOptions();
24 // 设置为 headless 模式 (必须)
25 chromeOptions.addArguments("--headless");
26 // 设置浏览器窗口打开大小 (非必须)
27 chromeOptions.addArguments("--window-size=1920,1080");
28 WebDriver driver = new ChromeDriver(chromeOptions);
29 driver.get("https://lvyou.baidu.com/scene/s-feb/");
30
31 System.out.println("url: "+driver.getCurrentUrl());
32
33 for(int i=0;i 查看全部

  nodejs抓取动态网页(需要的jar包代码如下:链接不支持xpath解析)
  需要的jar包:
  1
2 org.jsoup
3 jsoup
4 1.10.3
5
  代码如下:
   1 // 请求超时时间,30秒
2 public static final int TIME_OUT = 30*1000;
3 // 模拟浏览器请求头信息
4 public static Map headers = new HashMap();
5 static{
6 headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0");
7 headers.put("Accept", "text/html");
8 headers.put("Accept-Language", "zh-CN,zh");
9 }
10
11 //根据url获取html文档
12 protected Document getDoc(String url) throws IOException{
13 if(logger.isDebugEnabled())
14 logger.debug(url);
15 //新建一个连接
16 Connection conn = Jsoup.connect(url).timeout(TIME_OUT);
17 conn = conn.headers(headers);
18 conn = conn.proxy(Proxy.NO_PROXY);
19 Document doc = conn.get();
20
21 if(logger.isTraceEnabled()){
22 logger.trace("["+url+"]\n"+doc);
23 }
24 return doc;
25 }
   1 public static final String CHINAZ_ICP_URL = "http://icp.chinaz.com/?type=host&s=%s";
2 public List doHandler(String domain) {
3 List results = new ArrayList();
4 String url = String.format(CHINAZ_ICP_URL, domain);
5 Document doc;
6 try {
7 doc = this.getDoc(url);
8 // 获取当前页ICP信息所在标签
9 Elements eles = doc.select("ul.IcpMain01>li:lt(7)>p");
10
11 if(null == eles || eles.isEmpty()){
12 return results;
13 }
14 //获取ICP信息
15 for (Element element : eles) {
16 //当前元素为认证信息时,跳过
17 if("safe".equals(element.attr("id"))){
18 continue;
19 }
20 Node firstNode = element.childNode(0);
21 if(firstNode.childNodeSize() > 0){
22 results.add(element.child(0).text());
23 }else{
24 results.add(((TextNode)firstNode).text());
25 }
26 }
27 } catch (IOException e) {
28 logger.error("get Chinaz ICP message error :",e);
29 }
30 doc = null;
31 return results;
32 }
  参考 Jsoup 的文档:链接
  Jsoup不支持xpath解析,这个很痛苦,不过总得有人做个支持xpath的东西---JsoupXpath,链接,有兴趣的网友可以自己试试!
  三、htmlunit
  支持Xpath分析,可以模拟浏览器动作,比如点击下一页、加载更多等等。文档链接:
  需要的jar包
  1
2 net.sourceforge.htmlunit
3 htmlunit
4 2.18
5
  代码如下:
   1 import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
2 import com.gargoylesoftware.htmlunit.TopLevelWindow;
3 import com.gargoylesoftware.htmlunit.WebClient;
4 import com.gargoylesoftware.htmlunit.html.HtmlPage;
5 import com.gargoylesoftware.htmlunit.html.HtmlTableRow;
6
7 import java.io.IOException;
8 import java.util.ArrayList;
9 import java.util.List;
10
11
12 public class UrlTest {
13
14 public static void main(String[] args) {
15 BaseCollector baseCollector = new BaseCollector();
16 WebClient webClient = baseCollector.getWebClient();
17 String url="http://htmlunit.sourceforge.net/";
18 HtmlPage homePage= null;
19 try {
20 homePage = webClient.getPage(url);
21 if (homePage != null && homePage instanceof HtmlPage) {
22 homePage.getEnclosingWindow().setName("IpHomePage");
23 System.out.println("打开 IPHomePage ");
24 System.out.println("内容是: "+homePage.getBody().getTextContent());
25
26 List htmlTableRows = (List) homePage.getByXPath("/html/body/pre");
27 if (htmlTableRows != null && htmlTableRows.size() > 0) {
28 for (int i = 0; i < htmlTableRows.size(); i++) {
29 HtmlTableRow htmlTableRow = htmlTableRows.get(i);
30 //日期
31 String firstTime = htmlTableRow.getCell(0).getTextContent().trim();
32 System.out.println(firstTime);
33 }
34
35 }
36 closeWindowByName(webClient, "IPHomePage");
37 System.out.println("关闭 IPHomePage ");
38 }
39 webClient.close();
40
41 } catch (IOException e) {
42 System.out.println(e.getMessage()+" ===="+e);
43 }catch (FailingHttpStatusCodeException e){
44 System.out.println(e.getMessage()+" ===="+e);
45 }
46 System.out.println("内容是: "+homePage.getBody().getTextContent());
47 }
48
49 public static void closeWindowByName(WebClient webClient, String name){
50 List list = webClient.getTopLevelWindows();
51 List windowNames = new ArrayList();
52 for (int j = 0; j < list.size(); j++) {
53 if(list.get(j).getName().equals(name)){
54 list.get(j).close();
55 }
56 windowNames.add(list.get(j).getName());
57 }
58 System.out.println("当前窗口 : {}"+list.toString());
59 }
60 }
61
62
  四、HeadlessChrome1,HeadlessChrome 和 PhantomJS 对比
  在 Chrome 未提供原生 Headless 模式之前,Web 开发者可以使用 PhantomJS 等第三方 Headless 浏览器。既然官方已经准备好提供 Headless,PhantomJS 维护者 Vitaly Slobodin 立即在邮件列表中宣布辞职。另一个流行的浏览器 Firefox 也准备提供 Headless 模式。
  2、什么是 HeadlessChrome
  HeadlessChrome 是 Chrome 浏览器的非接口形式。您可以使用 Chrome 支持的所有功能来运行您的程序,而无需打开浏览器。与现代浏览器相比,HeadlessChrome 可以更方便地测试 Web 应用程序、获取 网站 的截图、做爬虫抓取信息等。
  3、环境配置
  首先需要下载chrome-driver,不同版本的Chrome对应不同的Chrome-driver,可以通过这个链接下载对应的Chrome-driver
  支持各种元素的获取,List elements = driver.findElements(By.xpath("///*[@id=\"body\"]/ul[2]/li"));
  可以模拟浏览器的各种动作,driver.findElement(By.linkText("Next page")).click();
  用Python搞HeadlessChrome更方便简单,简直太神奇了。 . . . 链接:
  可以参考
  需要的jar包:
  1
2 org.seleniumhq.selenium
3 selenium-chrome-driver
4 3.11.0
5
  代码如下:
<p> 1 import org.jsoup.Jsoup;
2 import org.jsoup.nodes.Document;
3 import org.openqa.selenium.By;
4 import org.openqa.selenium.WebDriver;
5 import org.openqa.selenium.WebElement;
6 import org.openqa.selenium.chrome.ChromeDriver;
7 import org.openqa.selenium.chrome.ChromeOptions;
8
9 import java.util.List;
10 import java.util.concurrent.TimeUnit;
11
12 /**
13 * Created by sqy on 2018/5/2.
14 */
15 public class HeadlessChromeTest {
16
17 public static void main(String args[]) {
18
19
20
21 //G:\chromedriver
22 System.setProperty("webdriver.chrome.driver","G:\\chromedriver\\chromedriver.exe");
23 ChromeOptions chromeOptions = new ChromeOptions();
24 // 设置为 headless 模式 (必须)
25 chromeOptions.addArguments("--headless");
26 // 设置浏览器窗口打开大小 (非必须)
27 chromeOptions.addArguments("--window-size=1920,1080");
28 WebDriver driver = new ChromeDriver(chromeOptions);
29 driver.get("https://lvyou.baidu.com/scene/s-feb/";);
30
31 System.out.println("url: "+driver.getCurrentUrl());
32
33 for(int i=0;i

nodejs抓取动态网页(puppeteer和浏览器的区别,安装注意先安装nodejs.eachSeries)

网站优化优采云 发表了文章 • 0 个评论 • 258 次浏览 • 2021-10-18 02:00 • 来自相关话题

  nodejs抓取动态网页(puppeteer和浏览器的区别,安装注意先安装nodejs.eachSeries)
  傀儡师
  google chrome 团队制作的 puppeteer 是一个自动化测试库,依赖于 nodejs 和chromium。它最大的优点是可以处理网页中的动态内容,比如JavaScript,可以更好的模拟用户。
  一些网站反爬虫方法在某些javascript/ajax请求中隐藏了部分内容,使得直接获取a标签的方法不起作用。甚至有些网站会设置隐藏元素“陷阱”,用户不可见,脚本触发器被认为是机器。在这种情况下,Puppeteer 的优势就凸显出来了。
  它可以实现以下功能:
  生成页面的屏幕截图和 PDF。获取 SPA 并生成预渲染内容(即“SSR”)。自动表单提交、UI 测试、键盘输入等。创建最新的自动化测试环境。使用最新的 JavaScript 和浏览器功能直接在最新版本的 Chrome 中运行测试。捕获并跟踪您的时间线 网站 以帮助诊断性能问题。
  开源地址:[][1]
  安装
  npm i puppeteer
  注意先安装nodejs,在nodejs文件的根目录下执行(npm文件同级)。
  安装过程中会下载Chromium,大约120M。
  花了两天时间(约10小时)探索绕过了相当多的异步坑。作者对puppeteer和nodejs有一定的掌握。
  一张长图,抢博客文章列表:
  
  抢博客文章
  以csdn博客为例,文章的内容需要点击“阅读全文”才能获取,导致只能读取dom的脚本失败。
  /**
* load blog.csdn.net article to local files
**/
const puppeteer = require('puppeteer');
//emulate iphone
const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1';
const workPath = './contents';
const fs = require("fs");
if (!fs.existsSync(workPath)) {
fs.mkdirSync(workPath)
}
//base url
const rootUrl = 'https://blog.csdn.net/';
//max wait milliseconds
const maxWait = 100;
//max loop scroll times
const makLoop = 10;
(async () => {
let url;
let countUrl=0;
const browser = await puppeteer.launch({headless: false});//set headless: true will hide chromium UI
const page = await browser.newPage();
await page.setUserAgent(userAgent);
await page.setViewport({width:414, height:736});
await page.setRequestInterception(true);
//filter to block images
page.on('request', request => {
if (request.resourceType() === 'image')
request.abort();
else
request.continue();
});
await page.goto(rootUrl);

for(let i= 0; iwindow.scrollTo(0, document.body.scrollHeight));
await page.waitForNavigation({timeout:maxWait,waitUntil: ['networkidle0']});
}catch(err){
console.log('scroll to bottom and then wait '+maxWait+'ms.');
}
}
await page.screenshot({path: workPath+'/screenshot.png',fullPage: true, quality :100, type :'jpeg'});
//#feedlist_id li[data-type="blog"] a
const sel = '#feedlist_id li[data-type="blog"] h2 a';
const hrefs = await page.evaluate((sel) => {
let elements = Array.from(document.querySelectorAll(sel));
let links = elements.map(element => {
return element.href
})
return links;
}, sel);
console.log('total links: '+hrefs.length);
process();
async function process(){
if(countUrl {
if (request.resourceType() === 'image')
request.abort();
else
request.continue();
});
await tab.goto(url);
//execute tap request
try{
await tab.tap('.read_more_btn');
}catch(err){
console.log('there\'s none read more button. No need to TAP');
}
let title = await tab.evaluate(() => document.querySelector('#article .article_title').innerText);
let contents = await tab.evaluate(() => document.querySelector('#article .article_content').innerText);
contents = 'TITLE: '+title+'\nURL: '+url+'\nCONTENTS: \n'+contents;
const fs = require("fs");
fs.writeFileSync(workPath+'/'+tab.url().substring(tab.url().lastIndexOf('/'),tab.url().length)+'.txt',contents);
console.log(title + " has been downloaded to local.");
await tab.close();
}catch(err){
console.log('url: '+tab.url()+' \n'+err.toString());
}finally{
process();
}

}
})();
  实施过程
  录屏可以在我的公众号查看。下面是一个屏幕截图:
  
  结果
  文章内容列表:
  
  文章内容:
  
  结束语
  我以为由于nodejs使用JavaScript脚本语言,它肯定可以处理网页的JavaScript内容,但我还没有找到合适/高效的库。直到找到木偶师,我才下定决心试水。
  话虽如此,nodejs的异步性确实让人头疼。我已经在 10 个小时内抛出了大约数百行代码。
  您可以扩展代码中的 process() 方法以使用 async.eachSeries。我使用的递归方法不是最佳解决方案。
  其实一一处理是没有效率的。本来我写了一个异步的方法来关闭浏览器:
<p>let tryCloseBrowser = setInterval(function(){
console.log("check if any process running...")
if(countDown 查看全部

  nodejs抓取动态网页(puppeteer和浏览器的区别,安装注意先安装nodejs.eachSeries)
  傀儡师
  google chrome 团队制作的 puppeteer 是一个自动化测试库,依赖于 nodejs 和chromium。它最大的优点是可以处理网页中的动态内容,比如JavaScript,可以更好的模拟用户。
  一些网站反爬虫方法在某些javascript/ajax请求中隐藏了部分内容,使得直接获取a标签的方法不起作用。甚至有些网站会设置隐藏元素“陷阱”,用户不可见,脚本触发器被认为是机器。在这种情况下,Puppeteer 的优势就凸显出来了。
  它可以实现以下功能:
  生成页面的屏幕截图和 PDF。获取 SPA 并生成预渲染内容(即“SSR”)。自动表单提交、UI 测试、键盘输入等。创建最新的自动化测试环境。使用最新的 JavaScript 和浏览器功能直接在最新版本的 Chrome 中运行测试。捕获并跟踪您的时间线 网站 以帮助诊断性能问题。
  开源地址:[][1]
  安装
  npm i puppeteer
  注意先安装nodejs,在nodejs文件的根目录下执行(npm文件同级)。
  安装过程中会下载Chromium,大约120M。
  花了两天时间(约10小时)探索绕过了相当多的异步坑。作者对puppeteer和nodejs有一定的掌握。
  一张长图,抢博客文章列表:
  
  抢博客文章
  以csdn博客为例,文章的内容需要点击“阅读全文”才能获取,导致只能读取dom的脚本失败。
  /**
* load blog.csdn.net article to local files
**/
const puppeteer = require('puppeteer');
//emulate iphone
const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1';
const workPath = './contents';
const fs = require("fs");
if (!fs.existsSync(workPath)) {
fs.mkdirSync(workPath)
}
//base url
const rootUrl = 'https://blog.csdn.net/';
//max wait milliseconds
const maxWait = 100;
//max loop scroll times
const makLoop = 10;
(async () => {
let url;
let countUrl=0;
const browser = await puppeteer.launch({headless: false});//set headless: true will hide chromium UI
const page = await browser.newPage();
await page.setUserAgent(userAgent);
await page.setViewport({width:414, height:736});
await page.setRequestInterception(true);
//filter to block images
page.on('request', request => {
if (request.resourceType() === 'image')
request.abort();
else
request.continue();
});
await page.goto(rootUrl);

for(let i= 0; iwindow.scrollTo(0, document.body.scrollHeight));
await page.waitForNavigation({timeout:maxWait,waitUntil: ['networkidle0']});
}catch(err){
console.log('scroll to bottom and then wait '+maxWait+'ms.');
}
}
await page.screenshot({path: workPath+'/screenshot.png',fullPage: true, quality :100, type :'jpeg'});
//#feedlist_id li[data-type="blog"] a
const sel = '#feedlist_id li[data-type="blog"] h2 a';
const hrefs = await page.evaluate((sel) => {
let elements = Array.from(document.querySelectorAll(sel));
let links = elements.map(element => {
return element.href
})
return links;
}, sel);
console.log('total links: '+hrefs.length);
process();
async function process(){
if(countUrl {
if (request.resourceType() === 'image')
request.abort();
else
request.continue();
});
await tab.goto(url);
//execute tap request
try{
await tab.tap('.read_more_btn');
}catch(err){
console.log('there\'s none read more button. No need to TAP');
}
let title = await tab.evaluate(() => document.querySelector('#article .article_title').innerText);
let contents = await tab.evaluate(() => document.querySelector('#article .article_content').innerText);
contents = 'TITLE: '+title+'\nURL: '+url+'\nCONTENTS: \n'+contents;
const fs = require("fs");
fs.writeFileSync(workPath+'/'+tab.url().substring(tab.url().lastIndexOf('/'),tab.url().length)+'.txt',contents);
console.log(title + " has been downloaded to local.");
await tab.close();
}catch(err){
console.log('url: '+tab.url()+' \n'+err.toString());
}finally{
process();
}

}
})();
  实施过程
  录屏可以在我的公众号查看。下面是一个屏幕截图:
  
  结果
  文章内容列表:
  
  文章内容:
  
  结束语
  我以为由于nodejs使用JavaScript脚本语言,它肯定可以处理网页的JavaScript内容,但我还没有找到合适/高效的库。直到找到木偶师,我才下定决心试水。
  话虽如此,nodejs的异步性确实让人头疼。我已经在 10 个小时内抛出了大约数百行代码。
  您可以扩展代码中的 process() 方法以使用 async.eachSeries。我使用的递归方法不是最佳解决方案。
  其实一一处理是没有效率的。本来我写了一个异步的方法来关闭浏览器:
<p>let tryCloseBrowser = setInterval(function(){
console.log("check if any process running...")
if(countDown

nodejs抓取动态网页(.jscvsresovlercvsresovler2 )

网站优化优采云 发表了文章 • 0 个评论 • 89 次浏览 • 2021-10-17 05:10 • 来自相关话题

  nodejs抓取动态网页(.jscvsresovlercvsresovler2
)
  列:·
  介绍本文文章主要介绍Nodejs中的puppeteer抓取浏览器HAR数据(示例代码)及相关经验技巧,文章约4481字,浏览量157,点赞数2,值得一看参考!
  有这样的要求。首先从csv文件中读取要解析的url数据,然后使用puppeteer和puppeteer-har获取浏览器的HAR数据。在调试的过程中,发现for循环中如何操作是异步的,终于找到了解决办法,也记录在这里。
  har.js
  const puppeteer = require(‘puppeteer‘);
const PuppeteerHar = require(‘puppeteer-har‘);
/*
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const har = new PuppeteerHar(page);
await har.start({ path: ‘results.har‘ });
await page.goto(‘http://example.com‘);
await har.stop();
await browser.close();
})();
*/
async function saveHarlog(url,filename){
//启动浏览器实例 [puppeteer.createBrowserFetcher([options])]
let browser = await puppeteer.launch({
// 若是手动下载的chromium需要指定chromium地址, 默认引用地址为 /项目目录/node_modules/puppeteer/.local-chromium/
//executablePath: ‘/Users/huqiyang/Documents/project/z/chromium/Chromium.app/Contents/MacOS/Chromium‘,
//设置超时时间
timeout: 15000,
//如果是访问https页面 此属性会忽略https错误
ignoreHTTPSErrors: true,
// 关闭headless模式, 不会打开浏览器
headless: false,
args:["--disk-cache-size=0","--disable-cache",‘--disable-infobars‘],
//是否为每个选项卡自动打开DevTools面板。 如果此选项为true,则headless选项将设置为false。
devtools:false
});
//创建一个新页面
let page = await browser.newPage();
//Puppeteer 初始化的屏幕大小默认为 800px x 600px。但是这个尺寸可以通过 Page.setViewport() 设置。
await page.setViewport({
width: 800,
height: 600
});
let har = new PuppeteerHar(page);
await har.start({ path: (filename +‘.har‘) });
await page.goto(url);
// Get the "viewport" of the page, as reported by the page.
let dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
});
await har.stop();
await browser.close();
}
exports.saveHarlog = saveHarlog;
  cvsresovler.js
  const fs = require("fs");
const path = require("path");
const csv =require(‘csv‘);
const parse = require(‘csv-parse/lib/sync‘)
const iconv = require(‘iconv-lite‘);
/*
npm install iconv-lite
*/
function readUrlRecord(csvpath){
console.log(‘开始解析文件:‘ + csvpath) ;
//读取文件
const input = fs.readFileSync(csvpath,‘utf8‘) ;
/*
解析文件,生成JSON格式
{ ‘ ‘: ‘142‘,
AREA_NAME: ‘湖北‘,
SITE_LINK: ‘www.banggo.com‘,
BEARING_MODE: ‘移动接入‘,
SITE_NAME: ‘邦购‘,
MENU_TYPE: ‘二级‘ }
*/
const records = parse(input, {
columns: true,
skip_empty_lines: true,
delimiter: ‘,‘,
}) ;
return records ;
}
//readUrlRecord(‘../top300.csv‘) ;
exports.readUrlRecord = readUrlRecord;
  main.js
  const fs = require("fs");
const path = require("path");
const moment = require("moment");
const schedule = require(‘node-schedule‘);
const cvsresovler=require("./module/cvsresovler");
const mhar=require("./module/har");
/*
cnpm install --save moment
cnpm install --save csv
cnpm install --save node-schedule
cnpm install --save puppeteer
cnpm install --save puppeteer-har
cnpm install --save iconv-lite
*/
function init(){
console.log(‘初始化调度器‘) ;
//每分钟的第30秒定时执行一次:
schedule.scheduleJob(‘0 21 * * * *‘,()=>{
let ftime = moment().format(‘YYYYMMDDHHmm‘);
console.log(‘当前调度时间为:‘ + ftime) ;
let dirPath = path.join(__dirname,‘harlogs‘,ftime) ;
console.log("创建目录:" + dirPath) ;
let isExist = false ;
if(fs.existsSync(dirPath)){
//创建文件夹
let stat = fs.lstatSync(dirPath);
if(stats.isDirectory()){
isExist = false ;
}
}
if(!isExist){
//创建文件夹
console.log("创建文件夹" + ftime) ;
fs.mkdirSync(dirPath);
}
//开始解析需要处理的URL
let dataArr = cvsresovler.readUrlRecord(path.join(__dirname,‘top300.csv‘)) ;
console.log("解析出URL共计" + dataArr.length + "条") ;
/*
开始抓取HAR数据【同步的方式执行】。
注意:如果这里直接通过for循环遍历dataArr并调用saveHarlog方法,那么这将是一个异步的过程。
*/
(async function iterator(i){
let data = dataArr[i]
let url = data[‘SITE_LINK‘] ;
console.log("开始处理:" + url ) ;
await mhar.saveHarlog(‘http://‘ + url,path.join(dirPath,url.replace(‘/‘,"-"))) ;
if(i + 1 < dataArr.length){
iterator(i+1) ;
}
})(0) ;
});
console.log(‘应用程序启动完成‘) ;
}
//执行
init(); 查看全部

  nodejs抓取动态网页(.jscvsresovlercvsresovler2
)
  列:·
  介绍本文文章主要介绍Nodejs中的puppeteer抓取浏览器HAR数据(示例代码)及相关经验技巧,文章约4481字,浏览量157,点赞数2,值得一看参考!
  有这样的要求。首先从csv文件中读取要解析的url数据,然后使用puppeteer和puppeteer-har获取浏览器的HAR数据。在调试的过程中,发现for循环中如何操作是异步的,终于找到了解决办法,也记录在这里。
  har.js
  const puppeteer = require(‘puppeteer‘);
const PuppeteerHar = require(‘puppeteer-har‘);
/*
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const har = new PuppeteerHar(page);
await har.start({ path: ‘results.har‘ });
await page.goto(‘http://example.com‘);
await har.stop();
await browser.close();
})();
*/
async function saveHarlog(url,filename){
//启动浏览器实例 [puppeteer.createBrowserFetcher([options])]
let browser = await puppeteer.launch({
// 若是手动下载的chromium需要指定chromium地址, 默认引用地址为 /项目目录/node_modules/puppeteer/.local-chromium/
//executablePath: ‘/Users/huqiyang/Documents/project/z/chromium/Chromium.app/Contents/MacOS/Chromium‘,
//设置超时时间
timeout: 15000,
//如果是访问https页面 此属性会忽略https错误
ignoreHTTPSErrors: true,
// 关闭headless模式, 不会打开浏览器
headless: false,
args:["--disk-cache-size=0","--disable-cache",‘--disable-infobars‘],
//是否为每个选项卡自动打开DevTools面板。 如果此选项为true,则headless选项将设置为false。
devtools:false
});
//创建一个新页面
let page = await browser.newPage();
//Puppeteer 初始化的屏幕大小默认为 800px x 600px。但是这个尺寸可以通过 Page.setViewport() 设置。
await page.setViewport({
width: 800,
height: 600
});
let har = new PuppeteerHar(page);
await har.start({ path: (filename +‘.har‘) });
await page.goto(url);
// Get the "viewport" of the page, as reported by the page.
let dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
});
await har.stop();
await browser.close();
}
exports.saveHarlog = saveHarlog;
  cvsresovler.js
  const fs = require("fs");
const path = require("path");
const csv =require(‘csv‘);
const parse = require(‘csv-parse/lib/sync‘)
const iconv = require(‘iconv-lite‘);
/*
npm install iconv-lite
*/
function readUrlRecord(csvpath){
console.log(‘开始解析文件:‘ + csvpath) ;
//读取文件
const input = fs.readFileSync(csvpath,‘utf8‘) ;
/*
解析文件,生成JSON格式
{ ‘ ‘: ‘142‘,
AREA_NAME: ‘湖北‘,
SITE_LINK: ‘www.banggo.com‘,
BEARING_MODE: ‘移动接入‘,
SITE_NAME: ‘邦购‘,
MENU_TYPE: ‘二级‘ }
*/
const records = parse(input, {
columns: true,
skip_empty_lines: true,
delimiter: ‘,‘,
}) ;
return records ;
}
//readUrlRecord(‘../top300.csv‘) ;
exports.readUrlRecord = readUrlRecord;
  main.js
  const fs = require("fs");
const path = require("path");
const moment = require("moment");
const schedule = require(‘node-schedule‘);
const cvsresovler=require("./module/cvsresovler");
const mhar=require("./module/har");
/*
cnpm install --save moment
cnpm install --save csv
cnpm install --save node-schedule
cnpm install --save puppeteer
cnpm install --save puppeteer-har
cnpm install --save iconv-lite
*/
function init(){
console.log(‘初始化调度器‘) ;
//每分钟的第30秒定时执行一次:
schedule.scheduleJob(‘0 21 * * * *‘,()=>{
let ftime = moment().format(‘YYYYMMDDHHmm‘);
console.log(‘当前调度时间为:‘ + ftime) ;
let dirPath = path.join(__dirname,‘harlogs‘,ftime) ;
console.log("创建目录:" + dirPath) ;
let isExist = false ;
if(fs.existsSync(dirPath)){
//创建文件夹
let stat = fs.lstatSync(dirPath);
if(stats.isDirectory()){
isExist = false ;
}
}
if(!isExist){
//创建文件夹
console.log("创建文件夹" + ftime) ;
fs.mkdirSync(dirPath);
}
//开始解析需要处理的URL
let dataArr = cvsresovler.readUrlRecord(path.join(__dirname,‘top300.csv‘)) ;
console.log("解析出URL共计" + dataArr.length + "条") ;
/*
开始抓取HAR数据【同步的方式执行】。
注意:如果这里直接通过for循环遍历dataArr并调用saveHarlog方法,那么这将是一个异步的过程。
*/
(async function iterator(i){
let data = dataArr[i]
let url = data[‘SITE_LINK‘] ;
console.log("开始处理:" + url ) ;
await mhar.saveHarlog(‘http://‘ + url,path.join(dirPath,url.replace(‘/‘,"-"))) ;
if(i + 1 < dataArr.length){
iterator(i+1) ;
}
})(0) ;
});
console.log(‘应用程序启动完成‘) ;
}
//执行
init();

nodejs抓取动态网页(Rocket.Chat聊天程序的开发版本安装部署(图))

网站优化优采云 发表了文章 • 0 个评论 • 96 次浏览 • 2021-10-17 04:22 • 来自相关话题

  nodejs抓取动态网页(Rocket.Chat聊天程序的开发版本安装部署(图))
  Rocket.Chat聊天程序开发版安装部署,Rocket.Chat开发版安装部署
  你可以在 Linux 机器或 VM 上运行 Rocket.Chat 进行开发。以下说明已在新的 Ubuntu 18.04 LTS 安装上进行了测试。尝试查找并使用未安装其他不必要软件(而不是“桌面”或“客户端”)的新 Ubuntu 服务器安装。
  不要使用安装了 nodeJS 的系统以避免出现问题。
  在构建过程中,内存使用量将接近 8G,这是为开发工作站推荐的最低 RAM 级别。(如果你不做任何开发,只部署Rocket.Chat服务器——所需的RAM可能低至1G。)
  重要信息:请注意,基本操作系统上无需安装 mongo、nodejs 或 npm。如果您安装了其中任何一个;重新开始,或使用另一个 CLEAN 系统。
  一、玩转过程
  node的单线程只是js层面的单线程。它是基于 V8 引擎的单线程。因为V8,前后端js执行模型基本类似,但是node的核心机制还是通过libuv调用epoll或者epoll。IOCP 的多线程机制。换句话说,严格意义上的node并不是真正的单线程架构。节点内核本身有一定的IO线程和IO线程池。通过libuv调度,直接使用操作系统层面的多线程。Node开发者可以通过扩展c/c++模块直接操作多线程来提高效率。但是单线程的好处是程序状态单一,不存在锁、线程同步、线程上下文切换等问题。但是单线程程序并不完美。目前很多服务器都是多cpu和多cpu核的。一个节点实例只能使用一个 CPU 核。那么其他的cpu核就浪费了?而且,单线程的容错能力也很弱。一旦抛出未捕获的异常,必然会导致整个程序崩溃。这样的程序一定非常脆弱。这样的服务器端语言有什么价值?
  13 个中的 12 个
  nodejs模块——jsdom中文文档
  由 织梦 先生于 2019 年 12 月 13 日发布
  jsdom 是一系列完全由 javascript 实现的 web 标准,特别是 WHATWG 组织开发的用于 nodejs 的 DOM 和 HTML 标准。一般来说,该项目的目标是模拟足够多的 Web 浏览器子集,用于测试和挖掘真实世界的 Web 应用程序。
  最新版本的 jsdom 运行环境需要 node.js v6 或更高版本。(jsdom v10以下的版本在nodejs v4以下仍然可用,但我们不再支持维护)
  jsdom 的 v10 版本有一个全新的 API(如下所述)。
  发表于 NodeJS | Tagged jsdom, nodejs 模块, 中文文档
  十二个 12
  【转】Node.js初探及项目结构分析
  由 织梦 先生于 2018 年 12 月 12 日发布
  一个偶然的机会,我有幸跨越浏览器的鸿沟,以真实的方式体验 Node.js。
  首先我想说:“非常荣幸,经过两个月的努力,第一个Node.js项目落地了。” 整个工程顺利完成。
  事情很简单:Node.js 负责访问层。
  有一个原因
  前端技术创新日新月异,Node.js离不开前端工程。现在大多数项目使用前后端分离的架构。后端提供接口,前端通过接口数据进行数据渲染。但是现在前端代码逻辑越来越复杂,场景也越来越多。这种架构是否适合所有应用场景值得考虑。大前端的出现只是一种尝试。尝试通过 Node.js 访问来访问各种应用场景。
  发表于 NodeJS | 标记 angular, angular.js, gulp, jenkins, koa2, node, nodejs, webpack, 架构分析, 项目架构
  PDF.js 是一种使用 HTML5 构建的便携式文档格式 (PDF) 浏览器。
  PDF.js 是由 Mozilla 实验室驱动和支持的社区。目标是创建一个通用的、基于 Web 标准的平台来解析和呈现 pdf。
  下面的方法是从github复制过来的。我这次的项目是对织梦的二次开发,也就是说网站php环境不是nodejs。客户的要求是word文档上传后可以直接在浏览器中查看,所以我可以把这些文档转成PDF格式,然后用PDF.js在浏览器中查看。虽然我全局安装了 gulp,但是我没有使用 gulp server 命令。测试时访问了域名/pdf.js/web/viewer.html。viewer.html没有做任何改动,加载了很多js文件,速度很慢。访问域名/pdf.js/examples/components/simpleviewer .html只加载必要的js,访问速度还可以。
  因此,在使用中,您还需要结合您的实际需要进行考虑和测试。
  关于
  本书致力于教你如何使用 Node.js 开发应用程序,并会教你在这个过程中需要的所有“高级”JavaScript 知识。本书绝不是“Hello World”教程。
  状态
  您正在阅读的是本书的最终版本。因此,只有在纠正错误并对新版本的 Node.js 中的更改进行相应更正时才会更新。
  本书中的代码示例已经在Node.js 0.6.11 版本中测试,可以正常工作。
  观众
  本书最适合与我有类似技术背景的读者:至少有一些面向对象语言的经验,如Ruby、Python、PHP或Java;刚开始使用 JavaScript,完全是 Node.js 的新手。
  这是指对其他编程语言有一定经验的开发者,意味着本书不会介绍数据类型、变量、控制结构等非常基础的概念。为了理解本书,我假设你已经理解了这些基本概念.
  但是,本书仍然会详细介绍 JavaScript 中的函数和对象,因为它们与其他类似编程语言中的函数和对象有很大的不同。
  书籍结构
  阅读本书后,您将完成一个完整的 Web 应用程序,该应用程序允许用户浏览页面和上传文件。
  当然,应用程序本身并没有什么了不起的。相比为实现这个功能而编写的代码本身,我们更关心如何创建一个框架来干净地剥离我们应用程序的不同模块。是不是很神秘?以后你会明白的。
  本书首先介绍了 Node.js 环境下的 JavaScript 开发和浏览器环境下的 JavaScript 开发的区别。
  然后,我将带领大家完成一个最传统的“Hello World”应用,这也是最基本的 Node.js 应用。
  最后,我将与您讨论如何设计一个“真正”完整的应用程序,分析完成应用程序需要实现的不同模块,并逐步介绍如何实现这些模块。
  可以保证的是,在这个过程中,每个人都会学习到一些 JavaScript 中的高级概念,如何使用它们,以及为什么使用这些概念可以实现,而其他编程语言中的类似概念是无法实现的。 查看全部

  nodejs抓取动态网页(Rocket.Chat聊天程序的开发版本安装部署(图))
  Rocket.Chat聊天程序开发版安装部署,Rocket.Chat开发版安装部署
  你可以在 Linux 机器或 VM 上运行 Rocket.Chat 进行开发。以下说明已在新的 Ubuntu 18.04 LTS 安装上进行了测试。尝试查找并使用未安装其他不必要软件(而不是“桌面”或“客户端”)的新 Ubuntu 服务器安装。
  不要使用安装了 nodeJS 的系统以避免出现问题。
  在构建过程中,内存使用量将接近 8G,这是为开发工作站推荐的最低 RAM 级别。(如果你不做任何开发,只部署Rocket.Chat服务器——所需的RAM可能低至1G。)
  重要信息:请注意,基本操作系统上无需安装 mongo、nodejs 或 npm。如果您安装了其中任何一个;重新开始,或使用另一个 CLEAN 系统。
  一、玩转过程
  node的单线程只是js层面的单线程。它是基于 V8 引擎的单线程。因为V8,前后端js执行模型基本类似,但是node的核心机制还是通过libuv调用epoll或者epoll。IOCP 的多线程机制。换句话说,严格意义上的node并不是真正的单线程架构。节点内核本身有一定的IO线程和IO线程池。通过libuv调度,直接使用操作系统层面的多线程。Node开发者可以通过扩展c/c++模块直接操作多线程来提高效率。但是单线程的好处是程序状态单一,不存在锁、线程同步、线程上下文切换等问题。但是单线程程序并不完美。目前很多服务器都是多cpu和多cpu核的。一个节点实例只能使用一个 CPU 核。那么其他的cpu核就浪费了?而且,单线程的容错能力也很弱。一旦抛出未捕获的异常,必然会导致整个程序崩溃。这样的程序一定非常脆弱。这样的服务器端语言有什么价值?
  13 个中的 12 个
  nodejs模块——jsdom中文文档
  由 织梦 先生于 2019 年 12 月 13 日发布
  jsdom 是一系列完全由 javascript 实现的 web 标准,特别是 WHATWG 组织开发的用于 nodejs 的 DOM 和 HTML 标准。一般来说,该项目的目标是模拟足够多的 Web 浏览器子集,用于测试和挖掘真实世界的 Web 应用程序。
  最新版本的 jsdom 运行环境需要 node.js v6 或更高版本。(jsdom v10以下的版本在nodejs v4以下仍然可用,但我们不再支持维护)
  jsdom 的 v10 版本有一个全新的 API(如下所述)。
  发表于 NodeJS | Tagged jsdom, nodejs 模块, 中文文档
  十二个 12
  【转】Node.js初探及项目结构分析
  由 织梦 先生于 2018 年 12 月 12 日发布
  一个偶然的机会,我有幸跨越浏览器的鸿沟,以真实的方式体验 Node.js。
  首先我想说:“非常荣幸,经过两个月的努力,第一个Node.js项目落地了。” 整个工程顺利完成。
  事情很简单:Node.js 负责访问层。
  有一个原因
  前端技术创新日新月异,Node.js离不开前端工程。现在大多数项目使用前后端分离的架构。后端提供接口,前端通过接口数据进行数据渲染。但是现在前端代码逻辑越来越复杂,场景也越来越多。这种架构是否适合所有应用场景值得考虑。大前端的出现只是一种尝试。尝试通过 Node.js 访问来访问各种应用场景。
  发表于 NodeJS | 标记 angular, angular.js, gulp, jenkins, koa2, node, nodejs, webpack, 架构分析, 项目架构
  PDF.js 是一种使用 HTML5 构建的便携式文档格式 (PDF) 浏览器。
  PDF.js 是由 Mozilla 实验室驱动和支持的社区。目标是创建一个通用的、基于 Web 标准的平台来解析和呈现 pdf。
  下面的方法是从github复制过来的。我这次的项目是对织梦的二次开发,也就是说网站php环境不是nodejs。客户的要求是word文档上传后可以直接在浏览器中查看,所以我可以把这些文档转成PDF格式,然后用PDF.js在浏览器中查看。虽然我全局安装了 gulp,但是我没有使用 gulp server 命令。测试时访问了域名/pdf.js/web/viewer.html。viewer.html没有做任何改动,加载了很多js文件,速度很慢。访问域名/pdf.js/examples/components/simpleviewer .html只加载必要的js,访问速度还可以。
  因此,在使用中,您还需要结合您的实际需要进行考虑和测试。
  关于
  本书致力于教你如何使用 Node.js 开发应用程序,并会教你在这个过程中需要的所有“高级”JavaScript 知识。本书绝不是“Hello World”教程。
  状态
  您正在阅读的是本书的最终版本。因此,只有在纠正错误并对新版本的 Node.js 中的更改进行相应更正时才会更新。
  本书中的代码示例已经在Node.js 0.6.11 版本中测试,可以正常工作。
  观众
  本书最适合与我有类似技术背景的读者:至少有一些面向对象语言的经验,如Ruby、Python、PHP或Java;刚开始使用 JavaScript,完全是 Node.js 的新手。
  这是指对其他编程语言有一定经验的开发者,意味着本书不会介绍数据类型、变量、控制结构等非常基础的概念。为了理解本书,我假设你已经理解了这些基本概念.
  但是,本书仍然会详细介绍 JavaScript 中的函数和对象,因为它们与其他类似编程语言中的函数和对象有很大的不同。
  书籍结构
  阅读本书后,您将完成一个完整的 Web 应用程序,该应用程序允许用户浏览页面和上传文件。
  当然,应用程序本身并没有什么了不起的。相比为实现这个功能而编写的代码本身,我们更关心如何创建一个框架来干净地剥离我们应用程序的不同模块。是不是很神秘?以后你会明白的。
  本书首先介绍了 Node.js 环境下的 JavaScript 开发和浏览器环境下的 JavaScript 开发的区别。
  然后,我将带领大家完成一个最传统的“Hello World”应用,这也是最基本的 Node.js 应用。
  最后,我将与您讨论如何设计一个“真正”完整的应用程序,分析完成应用程序需要实现的不同模块,并逐步介绍如何实现这些模块。
  可以保证的是,在这个过程中,每个人都会学习到一些 JavaScript 中的高级概念,如何使用它们,以及为什么使用这些概念可以实现,而其他编程语言中的类似概念是无法实现的。

nodejs抓取动态网页(nodejs抓取动态网页想实现就点击分享按钮把网页发给你的朋友)

网站优化优采云 发表了文章 • 0 个评论 • 83 次浏览 • 2021-10-16 07:01 • 来自相关话题

  nodejs抓取动态网页(nodejs抓取动态网页想实现就点击分享按钮把网页发给你的朋友)
  nodejs抓取动态网页
  想实现就点击分享按钮把网页发给你的朋友,你的朋友再点击分享按钮把网页发给他们的朋友。让全网的人都看见,同时要满足你的需求,动态更新的。
  针对于flash来说,目前可以通过以下方式实现:设置网页不允许从电子邮件自动下载flash页面。如果主动提交动态链接,失败后才会发送二进制下载链接,然后读取二进制下载链接来解析从其他下载。网站是否开启iscroll?例如,如果当前链接显示无效,就是开启了,那么动态下载失败则是需要发送二进制下载链接。通过cookie(ietester之类的)登录使用ns传递到其他网站通过httppost登录,使用httpauthorization:动态登录等方式来解析从别的网站下载flash然后传递给网站下载。
  直接从mainflash登录不需要进行其他操作。在不通过httppost的情况下,一般来说,我们采用httppost就可以实现动态的下载了。
  一般的做法是用新浪的api,直接把链接提交就可以得到对应的动态。
  后台封装json或xml直接调用。
  flashwrite
  使用文本编辑器,把链接分享出去
  最简单的:iframemeta=xxxx然后在除了点击发送,别的时候都用同样的按钮不就行了。 查看全部

  nodejs抓取动态网页(nodejs抓取动态网页想实现就点击分享按钮把网页发给你的朋友)
  nodejs抓取动态网页
  想实现就点击分享按钮把网页发给你的朋友,你的朋友再点击分享按钮把网页发给他们的朋友。让全网的人都看见,同时要满足你的需求,动态更新的。
  针对于flash来说,目前可以通过以下方式实现:设置网页不允许从电子邮件自动下载flash页面。如果主动提交动态链接,失败后才会发送二进制下载链接,然后读取二进制下载链接来解析从其他下载。网站是否开启iscroll?例如,如果当前链接显示无效,就是开启了,那么动态下载失败则是需要发送二进制下载链接。通过cookie(ietester之类的)登录使用ns传递到其他网站通过httppost登录,使用httpauthorization:动态登录等方式来解析从别的网站下载flash然后传递给网站下载。
  直接从mainflash登录不需要进行其他操作。在不通过httppost的情况下,一般来说,我们采用httppost就可以实现动态的下载了。
  一般的做法是用新浪的api,直接把链接提交就可以得到对应的动态。
  后台封装json或xml直接调用。
  flashwrite
  使用文本编辑器,把链接分享出去
  最简单的:iframemeta=xxxx然后在除了点击发送,别的时候都用同样的按钮不就行了。

nodejs抓取动态网页(一个网络爬虫的开发过程及实现过程原理目标分析)

网站优化优采云 发表了文章 • 0 个评论 • 142 次浏览 • 2021-10-14 22:14 • 来自相关话题

  nodejs抓取动态网页(一个网络爬虫的开发过程及实现过程原理目标分析)
  Nodejs 将前端开发语言移植到了服务端。如今,前端开发者可以轻松地使用 Nodejs 实现网络爬虫,这在以前是不可想象的。本文介绍了一个简单的Nodejs爬虫开发过程,只想看代码拉到最后。
  爬行原理目标分析
  这次爬取的目标选择是观察cnBeta的新闻详情页收录到相邻页面的链接,但是通过查看源码发现这个链接是由Js生成的:
  
  这是一种常见的反爬虫措施。关联的页面链接通过异步请求获取,然后由js动态生成。检查网络面板,您可以看到该页面确实发送了一个异步请求。结果具有我们想要的关联页面 ID:
  
  接下来分析这个请求,可以发现有两个参数,这两个参数都可以在HTML中找到:
  
  第一个参数_csrf很容易直接在源码中搜索:
  
  第二个参数全文搜索找不到:
  
  观察这个参数的结构,发现数据被两个逗号分隔成三段,所以猜测数据是由于多部分拼接造成的,单独搜索真的找到了:
  
  但是只找到了最后两段数据,开头是1,不知道是哪来的。由于只有一个字符,检索起来比较困难,观察到这个请求在很多页面中都是以1开头的,所以这里干脆写死了。. .
  至此,对目标的分析结束,下面将执行爬虫。
  实施过程程序结构
  大体思路是从起始页开始爬取,异步获取上一个新闻页面的链接继续爬取,并设置最大爬取次数,防止陷入死循环。伪代码如下:
  1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  let fetchLimit = 50; //最大抓取条数
let fetched = 0; //计数器
let getNext = function(_csrf, op){ //获取下一篇文章ID
return Promise(function(resolve,reject){
let nextID;
...
resolve(nextID);
})
}
let fetchPage = function(ID){ //抓取程序
let _csrf = ...;
let op = ...;
save(ID); //保存内容
fetched++; //计数器累加
getNext(_csrf, op).then(function(nextID) {
fetchPage(nextID); //获取下一篇ID并进入循环
});
}
fetchPage('STARTID'); //开始抓取
  功能点
  关键是保存内容。首先,获取页面的 HTML 代码。主要使用http模块,如下:
  1
2
3
4
5
6
7
8
9
10
11
  const http = require('http');
http.get(pageUrl, function(res){
let html='';
res.setEncoding('utf8');
res.on('data', (chunk) => {
html += chunk;
});
res.on('end', () => {
console.log(html); //这里得到完整的HTML字符串
});
})
  要从 HTML 获取信息,您可以使用常规匹配,或使用cheerio。Cheerio 可以说实现了一个 Nodejs 端的 jQuery。它和jQuery的区别在于它需要先生成一个实例,然后像jQuery一样使用它:
  1
2
3
  const cheerio = require('cheerio');
const $ = cheerio.load(html);
let news_title = $('.cnbeta-article .title h1').text().trim().replace(/\//g, '-');
  fs模块主要用于保存文件,如下:
  1
2
3
4
5
6
  const fs = require('fs');
fs.writeFile(FilePath, FileContent, 'utf-8', function(err) {
if (err) {
console.log(err);
}
});
  这里有个坑。我们希望将文章的文本保存为与标题同名的txt文本,但标题可能收录斜线(/)。保存这样的文件时,程序会误认为标题斜线之前的部分。把它看成是路径,报错,所以需要替换标题中的斜线。
  保存图片与保存文本大致相同。主要区别在于写入格式,需要以二进制方式写入:
  1
2
3
4
5
6
7
8
9
10
11
12
13
  http.get(img_src, function(res) {
let imgData = "";
res.setEncoding("binary"); //注意格式
res.on("data", function(chunk) {
imgData += chunk;
});
res.on("end", function() {
fs.writeFile(imgSavePath, imgData, "binary", function(err) { //注意格式
if (err) {
console.log(err);
}
});
});
  程序的结构和主要功能基本是这样的。
  后记
  实现爬虫说起来容易,但是健壮性真的很难保证。在爬cnBeta的过程中,又发现了一个301跳坑。URL跳转时,程序抓取的HTML为空,无法获取。因此,请求得到响应后,需要判断响应头是否为301,如果是,则需要从响应信息中找到重定向的URL,重新发起请求。好在cnBeta是不需要用户登录的,如果是必须登录才能访问的网站,爬虫会很麻烦。
  本项目完整代码见Nodejs爬虫,感谢cnBeta^^。
  前路原创技术文章,转载请注明出处:爬虫练习笔记/
  
  不甘平庸的你,快来和我一起充电,关注风景,获取更多精彩内容。 查看全部

  nodejs抓取动态网页(一个网络爬虫的开发过程及实现过程原理目标分析)
  Nodejs 将前端开发语言移植到了服务端。如今,前端开发者可以轻松地使用 Nodejs 实现网络爬虫,这在以前是不可想象的。本文介绍了一个简单的Nodejs爬虫开发过程,只想看代码拉到最后。
  爬行原理目标分析
  这次爬取的目标选择是观察cnBeta的新闻详情页收录到相邻页面的链接,但是通过查看源码发现这个链接是由Js生成的:
  
  这是一种常见的反爬虫措施。关联的页面链接通过异步请求获取,然后由js动态生成。检查网络面板,您可以看到该页面确实发送了一个异步请求。结果具有我们想要的关联页面 ID:
  
  接下来分析这个请求,可以发现有两个参数,这两个参数都可以在HTML中找到:
  
  第一个参数_csrf很容易直接在源码中搜索:
  
  第二个参数全文搜索找不到:
  
  观察这个参数的结构,发现数据被两个逗号分隔成三段,所以猜测数据是由于多部分拼接造成的,单独搜索真的找到了:
  
  但是只找到了最后两段数据,开头是1,不知道是哪来的。由于只有一个字符,检索起来比较困难,观察到这个请求在很多页面中都是以1开头的,所以这里干脆写死了。. .
  至此,对目标的分析结束,下面将执行爬虫。
  实施过程程序结构
  大体思路是从起始页开始爬取,异步获取上一个新闻页面的链接继续爬取,并设置最大爬取次数,防止陷入死循环。伪代码如下:
  1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  let fetchLimit = 50; //最大抓取条数
let fetched = 0; //计数器
let getNext = function(_csrf, op){ //获取下一篇文章ID
return Promise(function(resolve,reject){
let nextID;
...
resolve(nextID);
})
}
let fetchPage = function(ID){ //抓取程序
let _csrf = ...;
let op = ...;
save(ID); //保存内容
fetched++; //计数器累加
getNext(_csrf, op).then(function(nextID) {
fetchPage(nextID); //获取下一篇ID并进入循环
});
}
fetchPage('STARTID'); //开始抓取
  功能点
  关键是保存内容。首先,获取页面的 HTML 代码。主要使用http模块,如下:
  1
2
3
4
5
6
7
8
9
10
11
  const http = require('http');
http.get(pageUrl, function(res){
let html='';
res.setEncoding('utf8');
res.on('data', (chunk) => {
html += chunk;
});
res.on('end', () => {
console.log(html); //这里得到完整的HTML字符串
});
})
  要从 HTML 获取信息,您可以使用常规匹配,或使用cheerio。Cheerio 可以说实现了一个 Nodejs 端的 jQuery。它和jQuery的区别在于它需要先生成一个实例,然后像jQuery一样使用它:
  1
2
3
  const cheerio = require('cheerio');
const $ = cheerio.load(html);
let news_title = $('.cnbeta-article .title h1').text().trim().replace(/\//g, '-');
  fs模块主要用于保存文件,如下:
  1
2
3
4
5
6
  const fs = require('fs');
fs.writeFile(FilePath, FileContent, 'utf-8', function(err) {
if (err) {
console.log(err);
}
});
  这里有个坑。我们希望将文章的文本保存为与标题同名的txt文本,但标题可能收录斜线(/)。保存这样的文件时,程序会误认为标题斜线之前的部分。把它看成是路径,报错,所以需要替换标题中的斜线。
  保存图片与保存文本大致相同。主要区别在于写入格式,需要以二进制方式写入:
  1
2
3
4
5
6
7
8
9
10
11
12
13
  http.get(img_src, function(res) {
let imgData = "";
res.setEncoding("binary"); //注意格式
res.on("data", function(chunk) {
imgData += chunk;
});
res.on("end", function() {
fs.writeFile(imgSavePath, imgData, "binary", function(err) { //注意格式
if (err) {
console.log(err);
}
});
});
  程序的结构和主要功能基本是这样的。
  后记
  实现爬虫说起来容易,但是健壮性真的很难保证。在爬cnBeta的过程中,又发现了一个301跳坑。URL跳转时,程序抓取的HTML为空,无法获取。因此,请求得到响应后,需要判断响应头是否为301,如果是,则需要从响应信息中找到重定向的URL,重新发起请求。好在cnBeta是不需要用户登录的,如果是必须登录才能访问的网站,爬虫会很麻烦。
  本项目完整代码见Nodejs爬虫,感谢cnBeta^^。
  前路原创技术文章,转载请注明出处:爬虫练习笔记/
  
  不甘平庸的你,快来和我一起充电,关注风景,获取更多精彩内容。

nodejs抓取动态网页(索性-spider项目页面(/spider))

网站优化优采云 发表了文章 • 0 个评论 • 73 次浏览 • 2021-10-14 22:05 • 来自相关话题

  nodejs抓取动态网页(索性-spider项目页面(/spider))
  之前研究过数据,写了一些数据爬虫的爬虫,不过写的比较随意。现在看来不合理的地方很多。这段时间比较闲,本来想重构一下之前的项目。
  后来利用这个周末,干脆重新写了一个项目,这个项目guwen-spider。目前这个爬虫还是比较简单的类型。它直接抓取页面,然后从页面中提取数据,并将数据保存到数据库中。
  对比我之前写的,我觉得难点在于整个程序的健壮性和相应的容错机制。昨天写代码的过程中,其实反映了真正的主代码其实写的很快,花了大部分时间
  做稳定性调试,寻求更合理的方式处理数据与过程控制的关系。
  背景
  该项目的背景是抓取一个一级页面,它是一个内容列表。单击目录是章节和长度的列表。点击章节或长度是进入具体内容页面。
  概述
  本项目github地址:[guwen-spider](yangfan0095/guwen-spider)(PS:最后还有彩蛋~~逃跑
  项目技术细节
  项目使用了大量的ES7 async函数,更直观的反映了程序的流程。为方便起见,在数据遍历的过程中直接使用了众所周知的async库,所以不可避免地会用到回调promise。因为数据的处理发生在回调函数中,难免会遇到一些数据传输的问题。其实你也可以直接用ES7的async await写一个方法来实现同样的功能。其实这里最好的一点就是使用Class的静态方法来封装数据库的操作。顾名思义,静态方法与原型相同,不占用额外空间。
  该项目主要用于
  * 1 ES7 的 async await 协程做异步逻辑处理。
  * 2 使用 npm 的 async 库做循环遍历和并发请求操作。
  * 3 使用log4js进行日志处理
  * 4 使用cheerio处理dom操作。
  * 5 使用mongoose连接mongoDB进行数据存储和操作。
  目录结构
  ├── bin//入口
  │ ├── booklist.js// 抢书逻辑
  │ ├── Chapterlist.js// 抓取章节逻辑
  │ ├── content.js// 抓取内容逻辑
  │ └── index.js// 程序入口
  ├── config//配置文件
  ├── dbhelper// 数据库操作方法目录
  ├── logs// 项目日志目录
  ├── model// mongoDB 集合操作示例
  ├── node_modules
  ├── utils// 工具函数
  ├── package.json
  项目实现计划分析
  该项目是典型的多级爬取案例,目前只有三个层次,分别是书单、书目对应的章节列表、章节链接对应的内容。有两种方法可以捕获这样的结构。一种是直接从外层抓到内层,抓到内层后再执行下一个外层,另一种是先将外层保存到数据库中。,然后根据外层抓取到所有内层章节的链接,再次保存,然后从数据库中查询对应的链接单元来抓取内容。这两种方案各有利弊。其实这两种方法我都试过了。后者有一个优势,因为三个层次是分别捕获的,以便更方便,尽可能保存到相关章节。数据。试想一下,如果按照正常逻辑采用前者
  遍历一级目录抓取对应的二级章节列表,再遍历章节列表抓取内容。当三级内容单元被捕获并需要保存时,如果你需要大量一级目录信息,你需要在这些分层数据之间进行数据传输,这实际上应该是一个比较复杂的考虑。因此,单独保存数据在一定程度上避免了不必要和复杂的数据传输。
  目前,我们认为我们要捕捉的古籍数量并不多,涵盖各种历史的古籍大约只有180本左右。它和章节内容本身是一小块数据,即一个集合中有180个文档记录。这180本书的所有章节共有16000章,对应爬取相应内容需要访问的16000页。所以选择第二个应该是合理的。
  项目实现
  主程序有bookListInit、chapterListInit、contentListInit三个方法,分别是抓取图书目录、章节列表、图书内容的初始化方法。通过async,可以控制这三种方法的运行过程。图书目录抓取完成后,将数据保存到数据库中,然后将执行结果返回给主程序。如果主程序运行成功,会根据书单抓取章节列表。,同样抢书的内容。
  项目主入口
  /**
* 爬虫抓取主入口
*/
const start = async() => {
let booklistRes = await bookListInit();
if (!booklistRes) {
logger.warn(&#39;书籍列表抓取出错,程序终止...&#39;);
return;
}
logger.info(&#39;书籍列表抓取成功,现在进行书籍章节抓取...&#39;);
let chapterlistRes = await chapterListInit();
if (!chapterlistRes) {
logger.warn(&#39;书籍章节列表抓取出错,程序终止...&#39;);
return;
}
logger.info(&#39;书籍章节列表抓取成功,现在进行书籍内容抓取...&#39;);
let contentListRes = await contentListInit();
if (!contentListRes) {
logger.warn(&#39;书籍章节内容抓取出错,程序终止...&#39;);
return;
}
logger.info(&#39;书籍内容抓取成功&#39;);
}
// 开始入口
if (typeof bookListInit === &#39;function&#39; && typeof chapterListInit === &#39;function&#39;) {
// 开始抓取
start();
}
  介绍bookListInit、chapterListInit、contentListInit,三个方法
  书单.js
  /**
* 初始化方法 返回抓取结果 true 抓取成果 false 抓取失败
*/
const bookListInit = async() => {
logger.info(&#39;抓取书籍列表开始...&#39;);
const pageUrlList = getPageUrlList(totalListPage, baseUrl);
let res = await getBookList(pageUrlList);
return res;
}
  章节列表.js
  /**
* 初始化入口
*/
const chapterListInit = async() => {
const list = await bookHelper.getBookList(bookListModel);
if (!list) {
logger.error(&#39;初始化查询书籍目录失败&#39;);
}
logger.info(&#39;开始抓取书籍章节列表,书籍目录共:&#39; + list.length + &#39;条&#39;);
let res = await asyncGetChapter(list);
return res;
};
  内容.js
  /**
* 初始化入口
*/
const contentListInit = async() => {
//获取书籍列表
const list = await bookHelper.getBookLi(bookListModel);
if (!list) {
logger.error(&#39;初始化查询书籍目录失败&#39;);
return;
}
const res = await mapBookList(list);
if (!res) {
logger.error(&#39;抓取章节信息,调用 getCurBookSectionList() 进行串行遍历操作,执行完成回调出错,错误信息已打印,请查看日志!&#39;);
return;
}
return res;
}
  关于内容抓取的想法
  图书目录爬取的逻辑其实很简单。你只需要使用 async.mapLimit 做一次遍历保存数据,但是我们保存内容时的简化逻辑其实就是遍历章节列表去抓取链接中的内容。但实际情况是链接数多达数万。从内存使用的角度来看,我们无法将它们全部保存到一个数组中然后遍历它们,因此我们需要将内容捕获进行单元化。
  常见的遍历方法是每次查询一定数量进行爬取。缺点是只用一定数量进行分类,数据之间没有相关性,插入是分批进行的。如果出现错误,容错方面会出现一些小问题,我们认为将一本书作为采集会遇到问题。因此,我们使用第二种方法以书为单位捕获和保存内容。
  这里使用了方法`async.mapLimit(list, 1, (series, callback) =&gt; {})` 来遍历。回调是不可避免的使用,感觉很恶心。async.mapLimit() 的第二个参数可以设置同时请求的数量。
  /*
* 内容抓取步骤:
* 第一步得到书籍列表, 通过书籍列表查到一条书籍记录下 对应的所有章节列表,
* 第二步 对章节列表进行遍历获取内容保存到数据库中
* 第三步 保存完数据后 回到第一步 进行下一步书籍的内容抓取和保存
*/
/**
* 初始化入口
*/
const contentListInit = async() => {
//获取书籍列表
const list = await bookHelper.getBookList(bookListModel);
if (!list) {
logger.error(&#39;初始化查询书籍目录失败&#39;);
return;
}
const res = await mapBookList(list);
if (!res) {
logger.error(&#39;抓取章节信息,调用 getCurBookSectionList() 进行串行遍历操作,执行完成回调出错,错误信息已打印,请查看日志!&#39;);
return;
}
return res;
}
/**
* 遍历书籍目录下的章节列表
* @param {*} list
*/
const mapBookList = (list) => {
return new Promise((resolve, reject) => {
async.mapLimit(list, 1, (series, callback) => {
let doc = series._doc;
getCurBookSectionList(doc, callback);
}, (err, result) => {
if (err) {
logger.error(&#39;书籍目录抓取异步执行出错!&#39;);
logger.error(err);
reject(false); return;
}
resolve(true);
})
})
}
/**
* 获取单本书籍下章节列表 调用章节列表遍历进行抓取内容
* @param {*} series
* @param {*} callback
*/
const getCurBookSectionList = async(series, callback) => {
let num = Math.random() * 1000 + 1000;
await sleep(num);
let key = series.key;
const res = await bookHelper.querySectionList(chapterListModel, {
key: key
});
if (!res) {
logger.error(&#39;获取当前书籍: &#39; + series.bookName + &#39; 章节内容失败,进入下一部书籍内容抓取!&#39;);
callback(null, null);
return;
}
//判断当前数据是否已经存在
const bookItemModel = getModel(key);
const contentLength = await bookHelper.getCollectionLength(bookItemModel, {});
if (contentLength === res.length) {
logger.info(&#39;当前书籍:&#39; + series.bookName + &#39;数据库已经抓取完成,进入下一条数据任务&#39;);
callback(null, null);
return;
}
await mapSectionList(res);
callback(null, null);
}
  抓包后如何保存数据是个问题
  这里我们使用key对数据进行分类。每次我们拿到link,根据key遍历,这样做的好处是保存的数据是一个整体。现在我们正在考虑数据存储的问题。
  1 可整体插入
  优点:数据库操作快,不浪费时间。
  缺点:有些书可能有几百章,这意味着几百页的内容在插入之前必须保存。这也会消耗内存并可能导致程序运行不稳定。
  2可以以每篇文章文章的形式插入到数据库中。
  优点:页面抓取保存的方式,可以及时保存数据,即使出现后续错误,也无需重新保存之前的章节。
  缺点:明显慢。想爬几万个页面,做几万次*N的数据库操作,想一想。在这里,您还可以创建一个缓冲区来一次保存一定数量的条目。当条目数达到条目数时,再次保存。好的选择。
  /**
* 遍历单条书籍下所有章节 调用内容抓取方法
* @param {*} list
*/
const mapSectionList = (list) => {
return new Promise((resolve, reject) => {
async.mapLimit(list, 1, (series, callback) => {
let doc = series._doc;
getContent(doc, callback)
}, (err, result) => {
if (err) {
logger.error(&#39;书籍目录抓取异步执行出错!&#39;);
logger.error(err);
reject(false);
return;
}
const bookName = list[0].bookName;
const key = list[0].key;
// 以整体为单元进行保存
saveAllContentToDB(result, bookName, key, resolve);
//以每篇文章作为单元进行保存
// logger.info(bookName + &#39;数据抓取完成,进入下一部书籍抓取函数...&#39;);
// resolve(true);
})
})
}
  两者都有其优点和缺点,我们都在这里尝试过。准备了两个错误保存集合,errContentModel 和 error采集Model。插入错误时,信息会保存到相应的集合中。您可以选择两者之一。添加集合保存数据的原因是为了方便一次性查看和后续操作,无需查看日志。
  (PS,其实可以完全使用error采集Model集合,errContentModel集合完全可以保存章节信息)
  //保存出错的数据名称
const errorSpider = mongoose.Schema({
chapter: String,
section: String,
url: String,
key: String,
bookName: String,
author: String,
})
// 保存出错的数据名称 只保留key 和 bookName信息
const errorCollection = mongoose.Schema({
key: String,
bookName: String,
})
  我们把每本书信息的内容放到一个新的集合中,集合以key命名。
  总结
  其实,编写这个项目的主要难点在于程序稳定性的控制,容错机制的设置,以及错误的记录。目前这个项目基本上可以一次直接运行整个流程。但是,程序设计肯定存在很多问题。请指正并交流。
  复活节彩蛋
  写完这个项目,做了一个基于React的前端网站用于页面浏览,一个基于koa2.x的服务器。整体技术栈相当于React+Redux+Koa2,前后端服务分开部署,各自独立可以更好的去除前后端服务之间的耦合。例如,同一组服务器端代码不仅可以为 Web 提供支持,还可以为移动和应用程序提供支持。目前整套还很简陋,但是可以满足基本的查询和浏览功能。希望以后有时间可以充实一下项目。
  本项目地址:[guwen-spider](yangfan0095/guwen-spider)
  对应前端React+Redux+semantic-ui地址:[guwen-react](yangfan0095/guwen-react)
  对应节点koa2.2+猫鼬地址:[guwen-node](yangfan0095/guwen-node)
  项目很简单,但是从前端到服务器端,多了一个学习和研发的环境。
  谢谢阅读!
  以上です 查看全部

  nodejs抓取动态网页(索性-spider项目页面(/spider))
  之前研究过数据,写了一些数据爬虫的爬虫,不过写的比较随意。现在看来不合理的地方很多。这段时间比较闲,本来想重构一下之前的项目。
  后来利用这个周末,干脆重新写了一个项目,这个项目guwen-spider。目前这个爬虫还是比较简单的类型。它直接抓取页面,然后从页面中提取数据,并将数据保存到数据库中。
  对比我之前写的,我觉得难点在于整个程序的健壮性和相应的容错机制。昨天写代码的过程中,其实反映了真正的主代码其实写的很快,花了大部分时间
  做稳定性调试,寻求更合理的方式处理数据与过程控制的关系。
  背景
  该项目的背景是抓取一个一级页面,它是一个内容列表。单击目录是章节和长度的列表。点击章节或长度是进入具体内容页面。
  概述
  本项目github地址:[guwen-spider](yangfan0095/guwen-spider)(PS:最后还有彩蛋~~逃跑
  项目技术细节
  项目使用了大量的ES7 async函数,更直观的反映了程序的流程。为方便起见,在数据遍历的过程中直接使用了众所周知的async库,所以不可避免地会用到回调promise。因为数据的处理发生在回调函数中,难免会遇到一些数据传输的问题。其实你也可以直接用ES7的async await写一个方法来实现同样的功能。其实这里最好的一点就是使用Class的静态方法来封装数据库的操作。顾名思义,静态方法与原型相同,不占用额外空间。
  该项目主要用于
  * 1 ES7 的 async await 协程做异步逻辑处理。
  * 2 使用 npm 的 async 库做循环遍历和并发请求操作。
  * 3 使用log4js进行日志处理
  * 4 使用cheerio处理dom操作。
  * 5 使用mongoose连接mongoDB进行数据存储和操作。
  目录结构
  ├── bin//入口
  │ ├── booklist.js// 抢书逻辑
  │ ├── Chapterlist.js// 抓取章节逻辑
  │ ├── content.js// 抓取内容逻辑
  │ └── index.js// 程序入口
  ├── config//配置文件
  ├── dbhelper// 数据库操作方法目录
  ├── logs// 项目日志目录
  ├── model// mongoDB 集合操作示例
  ├── node_modules
  ├── utils// 工具函数
  ├── package.json
  项目实现计划分析
  该项目是典型的多级爬取案例,目前只有三个层次,分别是书单、书目对应的章节列表、章节链接对应的内容。有两种方法可以捕获这样的结构。一种是直接从外层抓到内层,抓到内层后再执行下一个外层,另一种是先将外层保存到数据库中。,然后根据外层抓取到所有内层章节的链接,再次保存,然后从数据库中查询对应的链接单元来抓取内容。这两种方案各有利弊。其实这两种方法我都试过了。后者有一个优势,因为三个层次是分别捕获的,以便更方便,尽可能保存到相关章节。数据。试想一下,如果按照正常逻辑采用前者
  遍历一级目录抓取对应的二级章节列表,再遍历章节列表抓取内容。当三级内容单元被捕获并需要保存时,如果你需要大量一级目录信息,你需要在这些分层数据之间进行数据传输,这实际上应该是一个比较复杂的考虑。因此,单独保存数据在一定程度上避免了不必要和复杂的数据传输。
  目前,我们认为我们要捕捉的古籍数量并不多,涵盖各种历史的古籍大约只有180本左右。它和章节内容本身是一小块数据,即一个集合中有180个文档记录。这180本书的所有章节共有16000章,对应爬取相应内容需要访问的16000页。所以选择第二个应该是合理的。
  项目实现
  主程序有bookListInit、chapterListInit、contentListInit三个方法,分别是抓取图书目录、章节列表、图书内容的初始化方法。通过async,可以控制这三种方法的运行过程。图书目录抓取完成后,将数据保存到数据库中,然后将执行结果返回给主程序。如果主程序运行成功,会根据书单抓取章节列表。,同样抢书的内容。
  项目主入口
  /**
* 爬虫抓取主入口
*/
const start = async() => {
let booklistRes = await bookListInit();
if (!booklistRes) {
logger.warn(&#39;书籍列表抓取出错,程序终止...&#39;);
return;
}
logger.info(&#39;书籍列表抓取成功,现在进行书籍章节抓取...&#39;);
let chapterlistRes = await chapterListInit();
if (!chapterlistRes) {
logger.warn(&#39;书籍章节列表抓取出错,程序终止...&#39;);
return;
}
logger.info(&#39;书籍章节列表抓取成功,现在进行书籍内容抓取...&#39;);
let contentListRes = await contentListInit();
if (!contentListRes) {
logger.warn(&#39;书籍章节内容抓取出错,程序终止...&#39;);
return;
}
logger.info(&#39;书籍内容抓取成功&#39;);
}
// 开始入口
if (typeof bookListInit === &#39;function&#39; && typeof chapterListInit === &#39;function&#39;) {
// 开始抓取
start();
}
  介绍bookListInit、chapterListInit、contentListInit,三个方法
  书单.js
  /**
* 初始化方法 返回抓取结果 true 抓取成果 false 抓取失败
*/
const bookListInit = async() => {
logger.info(&#39;抓取书籍列表开始...&#39;);
const pageUrlList = getPageUrlList(totalListPage, baseUrl);
let res = await getBookList(pageUrlList);
return res;
}
  章节列表.js
  /**
* 初始化入口
*/
const chapterListInit = async() => {
const list = await bookHelper.getBookList(bookListModel);
if (!list) {
logger.error(&#39;初始化查询书籍目录失败&#39;);
}
logger.info(&#39;开始抓取书籍章节列表,书籍目录共:&#39; + list.length + &#39;条&#39;);
let res = await asyncGetChapter(list);
return res;
};
  内容.js
  /**
* 初始化入口
*/
const contentListInit = async() => {
//获取书籍列表
const list = await bookHelper.getBookLi(bookListModel);
if (!list) {
logger.error(&#39;初始化查询书籍目录失败&#39;);
return;
}
const res = await mapBookList(list);
if (!res) {
logger.error(&#39;抓取章节信息,调用 getCurBookSectionList() 进行串行遍历操作,执行完成回调出错,错误信息已打印,请查看日志!&#39;);
return;
}
return res;
}
  关于内容抓取的想法
  图书目录爬取的逻辑其实很简单。你只需要使用 async.mapLimit 做一次遍历保存数据,但是我们保存内容时的简化逻辑其实就是遍历章节列表去抓取链接中的内容。但实际情况是链接数多达数万。从内存使用的角度来看,我们无法将它们全部保存到一个数组中然后遍历它们,因此我们需要将内容捕获进行单元化。
  常见的遍历方法是每次查询一定数量进行爬取。缺点是只用一定数量进行分类,数据之间没有相关性,插入是分批进行的。如果出现错误,容错方面会出现一些小问题,我们认为将一本书作为采集会遇到问题。因此,我们使用第二种方法以书为单位捕获和保存内容。
  这里使用了方法`async.mapLimit(list, 1, (series, callback) =&gt; {})` 来遍历。回调是不可避免的使用,感觉很恶心。async.mapLimit() 的第二个参数可以设置同时请求的数量。
  /*
* 内容抓取步骤:
* 第一步得到书籍列表, 通过书籍列表查到一条书籍记录下 对应的所有章节列表,
* 第二步 对章节列表进行遍历获取内容保存到数据库中
* 第三步 保存完数据后 回到第一步 进行下一步书籍的内容抓取和保存
*/
/**
* 初始化入口
*/
const contentListInit = async() => {
//获取书籍列表
const list = await bookHelper.getBookList(bookListModel);
if (!list) {
logger.error(&#39;初始化查询书籍目录失败&#39;);
return;
}
const res = await mapBookList(list);
if (!res) {
logger.error(&#39;抓取章节信息,调用 getCurBookSectionList() 进行串行遍历操作,执行完成回调出错,错误信息已打印,请查看日志!&#39;);
return;
}
return res;
}
/**
* 遍历书籍目录下的章节列表
* @param {*} list
*/
const mapBookList = (list) => {
return new Promise((resolve, reject) => {
async.mapLimit(list, 1, (series, callback) => {
let doc = series._doc;
getCurBookSectionList(doc, callback);
}, (err, result) => {
if (err) {
logger.error(&#39;书籍目录抓取异步执行出错!&#39;);
logger.error(err);
reject(false); return;
}
resolve(true);
})
})
}
/**
* 获取单本书籍下章节列表 调用章节列表遍历进行抓取内容
* @param {*} series
* @param {*} callback
*/
const getCurBookSectionList = async(series, callback) => {
let num = Math.random() * 1000 + 1000;
await sleep(num);
let key = series.key;
const res = await bookHelper.querySectionList(chapterListModel, {
key: key
});
if (!res) {
logger.error(&#39;获取当前书籍: &#39; + series.bookName + &#39; 章节内容失败,进入下一部书籍内容抓取!&#39;);
callback(null, null);
return;
}
//判断当前数据是否已经存在
const bookItemModel = getModel(key);
const contentLength = await bookHelper.getCollectionLength(bookItemModel, {});
if (contentLength === res.length) {
logger.info(&#39;当前书籍:&#39; + series.bookName + &#39;数据库已经抓取完成,进入下一条数据任务&#39;);
callback(null, null);
return;
}
await mapSectionList(res);
callback(null, null);
}
  抓包后如何保存数据是个问题
  这里我们使用key对数据进行分类。每次我们拿到link,根据key遍历,这样做的好处是保存的数据是一个整体。现在我们正在考虑数据存储的问题。
  1 可整体插入
  优点:数据库操作快,不浪费时间。
  缺点:有些书可能有几百章,这意味着几百页的内容在插入之前必须保存。这也会消耗内存并可能导致程序运行不稳定。
  2可以以每篇文章文章的形式插入到数据库中。
  优点:页面抓取保存的方式,可以及时保存数据,即使出现后续错误,也无需重新保存之前的章节。
  缺点:明显慢。想爬几万个页面,做几万次*N的数据库操作,想一想。在这里,您还可以创建一个缓冲区来一次保存一定数量的条目。当条目数达到条目数时,再次保存。好的选择。
  /**
* 遍历单条书籍下所有章节 调用内容抓取方法
* @param {*} list
*/
const mapSectionList = (list) => {
return new Promise((resolve, reject) => {
async.mapLimit(list, 1, (series, callback) => {
let doc = series._doc;
getContent(doc, callback)
}, (err, result) => {
if (err) {
logger.error(&#39;书籍目录抓取异步执行出错!&#39;);
logger.error(err);
reject(false);
return;
}
const bookName = list[0].bookName;
const key = list[0].key;
// 以整体为单元进行保存
saveAllContentToDB(result, bookName, key, resolve);
//以每篇文章作为单元进行保存
// logger.info(bookName + &#39;数据抓取完成,进入下一部书籍抓取函数...&#39;);
// resolve(true);
})
})
}
  两者都有其优点和缺点,我们都在这里尝试过。准备了两个错误保存集合,errContentModel 和 error采集Model。插入错误时,信息会保存到相应的集合中。您可以选择两者之一。添加集合保存数据的原因是为了方便一次性查看和后续操作,无需查看日志。
  (PS,其实可以完全使用error采集Model集合,errContentModel集合完全可以保存章节信息)
  //保存出错的数据名称
const errorSpider = mongoose.Schema({
chapter: String,
section: String,
url: String,
key: String,
bookName: String,
author: String,
})
// 保存出错的数据名称 只保留key 和 bookName信息
const errorCollection = mongoose.Schema({
key: String,
bookName: String,
})
  我们把每本书信息的内容放到一个新的集合中,集合以key命名。
  总结
  其实,编写这个项目的主要难点在于程序稳定性的控制,容错机制的设置,以及错误的记录。目前这个项目基本上可以一次直接运行整个流程。但是,程序设计肯定存在很多问题。请指正并交流。
  复活节彩蛋
  写完这个项目,做了一个基于React的前端网站用于页面浏览,一个基于koa2.x的服务器。整体技术栈相当于React+Redux+Koa2,前后端服务分开部署,各自独立可以更好的去除前后端服务之间的耦合。例如,同一组服务器端代码不仅可以为 Web 提供支持,还可以为移动和应用程序提供支持。目前整套还很简陋,但是可以满足基本的查询和浏览功能。希望以后有时间可以充实一下项目。
  本项目地址:[guwen-spider](yangfan0095/guwen-spider)
  对应前端React+Redux+semantic-ui地址:[guwen-react](yangfan0095/guwen-react)
  对应节点koa2.2+猫鼬地址:[guwen-node](yangfan0095/guwen-node)
  项目很简单,但是从前端到服务器端,多了一个学习和研发的环境。
  谢谢阅读!
  以上です

nodejs抓取动态网页(一下动态网页的另一种方法逆向分析啦!!)

网站优化优采云 发表了文章 • 0 个评论 • 87 次浏览 • 2021-10-14 08:20 • 来自相关话题

  nodejs抓取动态网页(一下动态网页的另一种方法逆向分析啦!!)
  首先介绍动态网页,它使用【客户端语言改变页面的HTML和CSS元素】。例如,一个网页使用加载网页将您介绍到另一个页面,但该页面的 URL 连接没有改变,或者当您单击空白时页面会改变颜色。
  然后是客户端脚本语言,它是一种运行在浏览器而不是服务器上的语言。网上通常会遇到两种客户端语言:ActionScript(开发flash应用程序的语言)和JavaScript。
  JavaScript 常用于为网页添加各种动态功能,为用户提供更流畅美观的浏览效果。通常 JavaScript 脚本通过将它们嵌入到 HTML 中来实现它们的功能。
  比如一个下拉式的动态网页,加载后可以在浏览器的开发者工具的网络中的JS选项中看到它刚刚执行的JavaScript,点击就可以看到代码。
  目前已经写了一堆爬虫。对于动态网页,比如图片的下拉加载,在开发者工具/网络/图片中找到图片,分析图片地址的特征,然后在JS中找到刚才的JavaScript文件,如果可以的话可能收录找到比较规范的图片信息,或者网页的跳转关系,可以很方便的写程序,在图片地址下载图片。
  这种方法是逆向分析。
  另一种爬取动态网页的方式是模拟浏览器,通常是Selenium+PhantomJS。
  Selenium 是一个浏览器自动化测试框架,最初是为自动化测试而开发的,现在也用于网络数据采集。框架底层使用JavaScript模拟真实用户操作浏览器。测试脚本执行时,浏览器会根据脚本代码自动进行点击、输入、打开、验证等操作,就像真实用户一样,站在最终用户的角度测试应用。
  Selenium 本身没有浏览器,需要配合第三方浏览器使用。之前用过火狐,每次运行都会打开一个浏览器窗口,程序运行一目了然。PhantomJS 是一个无头浏览器,不显示浏览器窗口。
  Selenium+PhantomJS 可以运行一个非常强大的网络爬虫,它可以处理 cookie、JavaScript、headers 以及您需要做的任何事情。
  2018-03-01 13:50
  不知道有没有朋友喜欢,嘿嘿,,,啾^啾,,
  没有图的答案注定不喜欢吗?
  这是一条更新线,下面的信息是旧新闻。
  反正都是原答案,舍不得删,不喜欢直接跳过
  ╯▂╰
  我是在寒假期间学习的。几天前我想爬一个网站。我在开发者工具里看数据,把它当成静态网页去爬,然后爬下来就找不到数据了。
  然后才知道在开发者工具里看到的网页源码是通过JavaScript渲染的,通过Requests得到的源码没有我想要的数据。
  之前看书的时候讲了selenium模块。考虑到用它来模拟浏览器的时候不容易被逆转,我就用它爬过12306查票。
  我能想到的第一种方法是使用selenium,但是我认为它的速度不能满足我的需求,而且我非常抗拒。(呃(~_~;)
  那我只好百度了,肯定会有其他的解决办法。
  然后CSDN上有一篇很全面的文章。
  套用大佬的总结:
  动态网页可以通过逆向分析尽可能地进行逆向分析,其稳定性和效率是其他解决方案无法比拟的。通常,爬虫有口耳相传的事实。如果点击浏览器F12大法,90%的爬虫问题都可以解决,剩下的10%需要我们动脑筋。这适用于动态页面抓取。如果你能扭转它,试着扭转它。如果无法逆转,请找到折衷的解决方案。在折中方案中,可以尽量使用深度控制JS脚本执行计划(难度稍高),然后是基于标准的浏览器自动化测试框架(即selenium和PhantomJs)爬取。
  如需进一步了解,请直接前往网站:
  /yanbober/article/details/73822475?locationNum=3&amp;fps=1
  时间:2018-02-10 01:23 查看全部

  nodejs抓取动态网页(一下动态网页的另一种方法逆向分析啦!!)
  首先介绍动态网页,它使用【客户端语言改变页面的HTML和CSS元素】。例如,一个网页使用加载网页将您介绍到另一个页面,但该页面的 URL 连接没有改变,或者当您单击空白时页面会改变颜色。
  然后是客户端脚本语言,它是一种运行在浏览器而不是服务器上的语言。网上通常会遇到两种客户端语言:ActionScript(开发flash应用程序的语言)和JavaScript。
  JavaScript 常用于为网页添加各种动态功能,为用户提供更流畅美观的浏览效果。通常 JavaScript 脚本通过将它们嵌入到 HTML 中来实现它们的功能。
  比如一个下拉式的动态网页,加载后可以在浏览器的开发者工具的网络中的JS选项中看到它刚刚执行的JavaScript,点击就可以看到代码。
  目前已经写了一堆爬虫。对于动态网页,比如图片的下拉加载,在开发者工具/网络/图片中找到图片,分析图片地址的特征,然后在JS中找到刚才的JavaScript文件,如果可以的话可能收录找到比较规范的图片信息,或者网页的跳转关系,可以很方便的写程序,在图片地址下载图片。
  这种方法是逆向分析。
  另一种爬取动态网页的方式是模拟浏览器,通常是Selenium+PhantomJS。
  Selenium 是一个浏览器自动化测试框架,最初是为自动化测试而开发的,现在也用于网络数据采集。框架底层使用JavaScript模拟真实用户操作浏览器。测试脚本执行时,浏览器会根据脚本代码自动进行点击、输入、打开、验证等操作,就像真实用户一样,站在最终用户的角度测试应用。
  Selenium 本身没有浏览器,需要配合第三方浏览器使用。之前用过火狐,每次运行都会打开一个浏览器窗口,程序运行一目了然。PhantomJS 是一个无头浏览器,不显示浏览器窗口。
  Selenium+PhantomJS 可以运行一个非常强大的网络爬虫,它可以处理 cookie、JavaScript、headers 以及您需要做的任何事情。
  2018-03-01 13:50
  不知道有没有朋友喜欢,嘿嘿,,,啾^啾,,
  没有图的答案注定不喜欢吗?
  这是一条更新线,下面的信息是旧新闻。
  反正都是原答案,舍不得删,不喜欢直接跳过
  ╯▂╰
  我是在寒假期间学习的。几天前我想爬一个网站。我在开发者工具里看数据,把它当成静态网页去爬,然后爬下来就找不到数据了。
  然后才知道在开发者工具里看到的网页源码是通过JavaScript渲染的,通过Requests得到的源码没有我想要的数据。
  之前看书的时候讲了selenium模块。考虑到用它来模拟浏览器的时候不容易被逆转,我就用它爬过12306查票。
  我能想到的第一种方法是使用selenium,但是我认为它的速度不能满足我的需求,而且我非常抗拒。(呃(~_~;)
  那我只好百度了,肯定会有其他的解决办法。
  然后CSDN上有一篇很全面的文章。
  套用大佬的总结:
  动态网页可以通过逆向分析尽可能地进行逆向分析,其稳定性和效率是其他解决方案无法比拟的。通常,爬虫有口耳相传的事实。如果点击浏览器F12大法,90%的爬虫问题都可以解决,剩下的10%需要我们动脑筋。这适用于动态页面抓取。如果你能扭转它,试着扭转它。如果无法逆转,请找到折衷的解决方案。在折中方案中,可以尽量使用深度控制JS脚本执行计划(难度稍高),然后是基于标准的浏览器自动化测试框架(即selenium和PhantomJs)爬取。
  如需进一步了解,请直接前往网站:
  /yanbober/article/details/73822475?locationNum=3&amp;fps=1
  时间:2018-02-10 01:23

nodejs抓取动态网页(动态页面静态化的方案与方案)

网站优化优采云 发表了文章 • 0 个评论 • 74 次浏览 • 2021-10-09 08:31 • 来自相关话题

  nodejs抓取动态网页(动态页面静态化的方案与方案)
  动态页面静态
  首先我们需要了解两个概念,静态页面和动态页面
  静态页面
  最早的时候,网站的内容是通过在宿主空间放置大量静态网页来实现的
  静态网页的最大缺点是每个人看到的都一样。
  网站对于静态网页最大的难点在于每次都需要更新和重新上传网站的内容。
  动态页面
  动态页面是网页框架和内容本身的抽象分离
  动态页面是通过执行asp、php、jsp、.net等程序访问数据库并生成客户端web代码的网页。
  动态页面通常可以通过网站后台管理系统对网站的内容进行更新和管理
  静止的
  但是为什么将动态网页发布为静态网页呢?
  一个很重要的原因是因为搜索引擎。所谓面向搜索引擎优化,包括访问地址的改写,让动态网页看起来像静态网页,让越来越多的搜索引擎可以收录,从而最大化内容自己有针对性地获得机会。
  另一个重要原因是提高程序性能。很多大网站一进门就看着自己很复杂的页面,但是加载的时间并不长。除了其他必要的原因,我认为静态化也是必须考虑的技术之一。
  她在用户之前获取资源或数据库数据,然后通过静态处理生成静态页面。每个人都访问这个静态页面。静态页面本身的访问速度比动态页面快很多倍,所以程序的性能会降低。有很大的改进。
  总之,页面静态体现为:访问速度加快,用户体验显着提升;在后台具体体现为:访问与数据库分离,减少了数据库访问的压力。
  动态页面静态
  动态页面非常易于管理。但是,在访问网页时,程序需要先对其进行处理,因此访问速度相对较慢。静态页面访问速度快,但不易管理。那么静态动态页面就可以将两种页面的优点结合起来。
  静态页面的解决方法:
  1、使用文件读写功能生成静态页面
  2、 使用nosql从内存中读取内容(其实这个不是静态的而是缓存的),比如redis,虽然没有纯静态快,但是比查询数据库快很多
  静态解决方案需要注意的问题:
  1、静态页面中的动态(即时)数据问题。可以通过ajax解决
  2、静态内容,一旦改变,静态页面需要重新生成。
  伪静态
  伪静态是相对真实的静态。通常,为了增强搜索引擎的友好性,我们会生成带有文章内容的静态页面,但有些朋友会实时显示一些信息。或者你想使用动态脚本来解决一些问题。网站 的内容不能静态显示。
  但这失去了搜索引擎的友好性。如何找到介于两者之间的中间方法?这产生了伪静态技术。
  它以html等静态页面URL的形式显示,但实际上是由ASP等动态脚本处理的。
  综上,在SEO方面,伪静态页面和静态页面的功能是一样的,但是伪静态本质上是动态页面,所以资源消耗和动态页面是一样的,而且因为Rewrite服务器还需要消耗额外的资源 查看全部

  nodejs抓取动态网页(动态页面静态化的方案与方案)
  动态页面静态
  首先我们需要了解两个概念,静态页面和动态页面
  静态页面
  最早的时候,网站的内容是通过在宿主空间放置大量静态网页来实现的
  静态网页的最大缺点是每个人看到的都一样。
  网站对于静态网页最大的难点在于每次都需要更新和重新上传网站的内容。
  动态页面
  动态页面是网页框架和内容本身的抽象分离
  动态页面是通过执行asp、php、jsp、.net等程序访问数据库并生成客户端web代码的网页。
  动态页面通常可以通过网站后台管理系统对网站的内容进行更新和管理
  静止的
  但是为什么将动态网页发布为静态网页呢?
  一个很重要的原因是因为搜索引擎。所谓面向搜索引擎优化,包括访问地址的改写,让动态网页看起来像静态网页,让越来越多的搜索引擎可以收录,从而最大化内容自己有针对性地获得机会。
  另一个重要原因是提高程序性能。很多大网站一进门就看着自己很复杂的页面,但是加载的时间并不长。除了其他必要的原因,我认为静态化也是必须考虑的技术之一。
  她在用户之前获取资源或数据库数据,然后通过静态处理生成静态页面。每个人都访问这个静态页面。静态页面本身的访问速度比动态页面快很多倍,所以程序的性能会降低。有很大的改进。
  总之,页面静态体现为:访问速度加快,用户体验显着提升;在后台具体体现为:访问与数据库分离,减少了数据库访问的压力。
  动态页面静态
  动态页面非常易于管理。但是,在访问网页时,程序需要先对其进行处理,因此访问速度相对较慢。静态页面访问速度快,但不易管理。那么静态动态页面就可以将两种页面的优点结合起来。
  静态页面的解决方法:
  1、使用文件读写功能生成静态页面
  2、 使用nosql从内存中读取内容(其实这个不是静态的而是缓存的),比如redis,虽然没有纯静态快,但是比查询数据库快很多
  静态解决方案需要注意的问题:
  1、静态页面中的动态(即时)数据问题。可以通过ajax解决
  2、静态内容,一旦改变,静态页面需要重新生成。
  伪静态
  伪静态是相对真实的静态。通常,为了增强搜索引擎的友好性,我们会生成带有文章内容的静态页面,但有些朋友会实时显示一些信息。或者你想使用动态脚本来解决一些问题。网站 的内容不能静态显示。
  但这失去了搜索引擎的友好性。如何找到介于两者之间的中间方法?这产生了伪静态技术。
  它以html等静态页面URL的形式显示,但实际上是由ASP等动态脚本处理的。
  综上,在SEO方面,伪静态页面和静态页面的功能是一样的,但是伪静态本质上是动态页面,所以资源消耗和动态页面是一样的,而且因为Rewrite服务器还需要消耗额外的资源

nodejs抓取动态网页(nodejs一个#模块导出的两种方式Puppeteer)

网站优化优采云 发表了文章 • 0 个评论 • 77 次浏览 • 2021-10-08 03:30 • 来自相关话题

  nodejs抓取动态网页(nodejs一个#模块导出的两种方式Puppeteer)
  Intro# 最近需要用nodejs做一个爬虫。 Google 有一个 Puppeteer 项目,可以用来制作爬虫。网上对Puppeteer的介绍很多,这里就不详细介绍了。节点小白,刚开始有点晕,模块导出不行。官方文档说支持*.mjs,但是需要修改文件扩展名。感觉有点怪怪的,没用,主要是基于js-based模块的使用。模块导出的两种方式#因为熟悉C#,所以从我对C#的理解,我把nodejs中的模块导出分为两种形式:1.需要实例化才能调用的模块2. a 一个无需实例化就可以调用的静态类,提供了一些静态方法来导出一个要实例化的类
  module.exports = exports = function (){ };module.exports = exports = function() { this.syncCompanyList = async function(developerName){ await syncCompanyInfo(developerName); }; async function syncCompanyInfo(developerName){ // ... }}
  导出静态类
  exports.funcName = function (){};var getDistrictCode = function (districtName) { if (districtName) { for (let i= 0; i< DistrictInfo.length; i++) { let district = DistrictInfo[i]; if (district["name"] == districtName || district["aliasName"] == districtName) { return district["code"]; } } } return "";};var getNormalDistrictName = function (districtName) { if (districtName) { if (districtName.indexOf("区") > 0) { return districtName; } for (let i= 0; i< DistrictInfo.length; i++) { let district = DistrictInfo[i]; if (district["name"] == districtName || district["aliasName"] == districtName) { return district["name"]; } } } return "";}// 设置导出的方法及属性exports.getDistrictCode = getDistrictCode;exports.getNormalDistrictName = getNormalDistrictName;
  引用模块导出方法#在node中使用require引用模块引用npm包const log4js = require("log4js");参考自己写的模块 const DistrictUtil = require("./utils/districtUtil"); use Exported module#要使用某个模块,需要先引用某个模块,引用的模块可以参考前面的示例类
  const company = require("./company");// ...// 实例化一个 company 对象var comp = new company();// 调用 company 里的 syncCompanyList comp.syncCompanyList ();
  静态类 查看全部

  nodejs抓取动态网页(nodejs一个#模块导出的两种方式Puppeteer)
  Intro# 最近需要用nodejs做一个爬虫。 Google 有一个 Puppeteer 项目,可以用来制作爬虫。网上对Puppeteer的介绍很多,这里就不详细介绍了。节点小白,刚开始有点晕,模块导出不行。官方文档说支持*.mjs,但是需要修改文件扩展名。感觉有点怪怪的,没用,主要是基于js-based模块的使用。模块导出的两种方式#因为熟悉C#,所以从我对C#的理解,我把nodejs中的模块导出分为两种形式:1.需要实例化才能调用的模块2. a 一个无需实例化就可以调用的静态类,提供了一些静态方法来导出一个要实例化的类
  module.exports = exports = function (){ };module.exports = exports = function() { this.syncCompanyList = async function(developerName){ await syncCompanyInfo(developerName); }; async function syncCompanyInfo(developerName){ // ... }}
  导出静态类
  exports.funcName = function (){};var getDistrictCode = function (districtName) { if (districtName) { for (let i= 0; i< DistrictInfo.length; i++) { let district = DistrictInfo[i]; if (district["name"] == districtName || district["aliasName"] == districtName) { return district["code"]; } } } return "";};var getNormalDistrictName = function (districtName) { if (districtName) { if (districtName.indexOf("区") > 0) { return districtName; } for (let i= 0; i< DistrictInfo.length; i++) { let district = DistrictInfo[i]; if (district["name"] == districtName || district["aliasName"] == districtName) { return district["name"]; } } } return "";}// 设置导出的方法及属性exports.getDistrictCode = getDistrictCode;exports.getNormalDistrictName = getNormalDistrictName;
  引用模块导出方法#在node中使用require引用模块引用npm包const log4js = require("log4js");参考自己写的模块 const DistrictUtil = require("./utils/districtUtil"); use Exported module#要使用某个模块,需要先引用某个模块,引用的模块可以参考前面的示例类
  const company = require("./company");// ...// 实例化一个 company 对象var comp = new company();// 调用 company 里的 syncCompanyList comp.syncCompanyList ();
  静态类

nodejs抓取动态网页(如何利用Webkit从JS渲染网页中实现浏览器非常有趣)

网站优化优采云 发表了文章 • 0 个评论 • 71 次浏览 • 2021-10-30 02:14 • 来自相关话题

  nodejs抓取动态网页(如何利用Webkit从JS渲染网页中实现浏览器非常有趣)
  当我们抓取网页时,我们会使用一定的规则从返回的 HTML 数据中提取有效信息。但是如果网页中收录Java代码,就必须经过渲染处理才能得到原创数据。此时,如果我们仍然使用常规方法从中抓取数据,那么我们将一无所获。浏览器知道如何处理这些代码并显示出来,但是我们的程序应该如何处理这些代码呢?接下来介绍一个简单粗暴的抓取收录Java代码的网页信息的方法。
  大多数人使用 lxml 和 BeautifulSoup 两个包来提取数据。在本文中,我不会介绍任何爬虫框架内容,因为我只使用最基础的 lxml 包来处理数据。也许你很好奇我为什么更喜欢 lxml。那是因为 lxml 使用元素遍历来处理数据,而不是像 BeautifulSoup 那样使用正则表达式来提取数据。在这篇文章中,我将介绍一个非常有趣的案例——我突然发现我的文章出现在最近的Pycoders周刊第147期,所以我想每周抓取Pycoders中所有文件的链接。
  
  很明显,这是一个带有Java渲染的网页。我想抓取网页中的所有文件信息和相应的链接信息。所以我该怎么做?首先,我们无法使用 HTTP 方法获取任何信息。
  进口请求
  从 lxml 导入 html
  # 存储响应
  response = requests.get('')
  # 从响应体创建 lxml 树
  树 = html.fromstring(response.text)
  # 在响应中查找所有锚标记
  print tree.xpath('//div[@class="campaign"]/a/@href')
  当我们运行上面的代码时,我们无法获得任何信息。这怎么可能?网页上有很多文件。接下来我们需要考虑如何解决这个问题?
  如何获取内容信息?
  接下来,我将介绍如何使用 Web kit 从 JS 渲染网页中获取数据。什么是网络套件?Web kit 可以实现浏览器可以处理的任何内容。对于某些浏览器,Web kit 是底层的网页渲染工具。Web kit 是 QT 库的一部分,所以如果你已经安装了 QT 和 PyQT4 库,那么你可以直接运行它。
  您可以使用命令行安装软件库:
  须藤 apt-get 安装 python-qt4
  现在所有的准备工作已经完成,我们将使用一种全新的方法来提取信息。
  解决方案
  我们首先通过Web kit发送请求信息,然后等待网页完全加载并赋值给一个变量。接下来,我们使用 lxml 从 HTML 数据中提取有效信息。这个过程需要一段时间,但你会惊讶地发现整个网页都被完全加载了。
  导入系统
  从 PyQt4.QtGui 导入 *
  从 PyQt4.Qtcore 导入 *
  从 PyQt4.QtWebKit 导入 *
  类渲染(QWebPage):
  def __init__(self, url):
  self.app = QApplication(sys.argv)
  QWebPage.__init__(self)
  self.loadFinished.connect(self._loadFinished)
  self.mainFrame().load(QUrl(url))
  self.app.exec_()
  def _loadFinished(self, result):
  self.frame = self.mainFrame()
  self.app.quit()
  Render 类可用于呈现网页。当我们创建一个新的Render类时,它可以加载url中的所有信息并将其存储在一个新的框架中。 查看全部

  nodejs抓取动态网页(如何利用Webkit从JS渲染网页中实现浏览器非常有趣)
  当我们抓取网页时,我们会使用一定的规则从返回的 HTML 数据中提取有效信息。但是如果网页中收录Java代码,就必须经过渲染处理才能得到原创数据。此时,如果我们仍然使用常规方法从中抓取数据,那么我们将一无所获。浏览器知道如何处理这些代码并显示出来,但是我们的程序应该如何处理这些代码呢?接下来介绍一个简单粗暴的抓取收录Java代码的网页信息的方法。
  大多数人使用 lxml 和 BeautifulSoup 两个包来提取数据。在本文中,我不会介绍任何爬虫框架内容,因为我只使用最基础的 lxml 包来处理数据。也许你很好奇我为什么更喜欢 lxml。那是因为 lxml 使用元素遍历来处理数据,而不是像 BeautifulSoup 那样使用正则表达式来提取数据。在这篇文章中,我将介绍一个非常有趣的案例——我突然发现我的文章出现在最近的Pycoders周刊第147期,所以我想每周抓取Pycoders中所有文件的链接。
  
  很明显,这是一个带有Java渲染的网页。我想抓取网页中的所有文件信息和相应的链接信息。所以我该怎么做?首先,我们无法使用 HTTP 方法获取任何信息。
  进口请求
  从 lxml 导入 html
  # 存储响应
  response = requests.get('')
  # 从响应体创建 lxml 树
  树 = html.fromstring(response.text)
  # 在响应中查找所有锚标记
  print tree.xpath('//div[@class="campaign"]/a/@href')
  当我们运行上面的代码时,我们无法获得任何信息。这怎么可能?网页上有很多文件。接下来我们需要考虑如何解决这个问题?
  如何获取内容信息?
  接下来,我将介绍如何使用 Web kit 从 JS 渲染网页中获取数据。什么是网络套件?Web kit 可以实现浏览器可以处理的任何内容。对于某些浏览器,Web kit 是底层的网页渲染工具。Web kit 是 QT 库的一部分,所以如果你已经安装了 QT 和 PyQT4 库,那么你可以直接运行它。
  您可以使用命令行安装软件库:
  须藤 apt-get 安装 python-qt4
  现在所有的准备工作已经完成,我们将使用一种全新的方法来提取信息。
  解决方案
  我们首先通过Web kit发送请求信息,然后等待网页完全加载并赋值给一个变量。接下来,我们使用 lxml 从 HTML 数据中提取有效信息。这个过程需要一段时间,但你会惊讶地发现整个网页都被完全加载了。
  导入系统
  从 PyQt4.QtGui 导入 *
  从 PyQt4.Qtcore 导入 *
  从 PyQt4.QtWebKit 导入 *
  类渲染(QWebPage):
  def __init__(self, url):
  self.app = QApplication(sys.argv)
  QWebPage.__init__(self)
  self.loadFinished.connect(self._loadFinished)
  self.mainFrame().load(QUrl(url))
  self.app.exec_()
  def _loadFinished(self, result):
  self.frame = self.mainFrame()
  self.app.quit()
  Render 类可用于呈现网页。当我们创建一个新的Render类时,它可以加载url中的所有信息并将其存储在一个新的框架中。

nodejs抓取动态网页(使用node开发一个小工具,扫描分子反应动力(图) )

网站优化优采云 发表了文章 • 0 个评论 • 57 次浏览 • 2021-10-27 12:00 • 来自相关话题

  nodejs抓取动态网页(使用node开发一个小工具,扫描分子反应动力(图)
)
  使用node开发一个小工具,扫描分子反应动力学国家重点实验室新闻列表页面的前三页(地址如下:)。需要打印新闻名称、链接地址和发布时间。
  比如在控制台打印
  韩克力入选2016年度“中国科学院特聘研究员”2016-06-14
  我室金属表面解离和吸附动力学理论研究取得新进展2016-06-12
  张大宇讲座第21期:加州理工学院William A. Goddard III教授2016-05-04
<p><br /><br /><br /><br />/**
*设计
*第一种:抓取一页打印一页。
*第二种:把三页全部抓取完后,存到数组中,统一打印。
*第一种方式较高效。使用第一种方式。
*/
/**
*思路:
*1.抓取网站html内容。
*2.获取抓取的html的必要内容。
*3.把获取内容存到数组。
*4.把数组内容输到控制台。
*/
var http = require('http'); //引入nodejs的http模块,该模块用于构建http服务和作为HttpClient使用。
var cheerio = require('cheerio'); //可以理解为服务端的Jquery。使用方法和客户端一样。
//var promise = require('promise'); //对异步编程进行流程控制,更加符合后端程序员的编程习惯。
//var url = 'http://www.sklmr.dicp.ac.cn/list.php?tid=1'; //要抓取的网址,后面有拼接。
//抓取每一个节点的信息
function filterChapters(html){
var $ = cheerio.load(html); //把HTML内容解析成DOM对象 并且可以像jquery css选择器查询那样对这个DOM进行筛选
var articleList = $('td.text').find('tr');
var articleArr = [];
articleList.each(function() {
var curEle = $(this);
var title = curEle.find('a.title10').text().replace(/\s*\r\n\s*/g,""); //获取文章标题
var time = curEle.find('td.title11').text().replace(/\s*\r\n\s*/g,""); //获取文章时间
var href = "http://www.sklmr.dicp.ac.cn/"+curEle.find('a.title10').attr('href'); //获取文章链接
if( title!=null&&title!="") //有点小困难。因为DOM数据和直线是同一个等级,并且直线只有属性没有id。所以必须去除直线里面的tr空数据,否则会打印一部分空数据和错误信息。
articleArr.push({
title:title,
time:time,
href:href
});
})
return articleArr;
}
//在控制台打印信息
function printCourseInfo(courseData){
courseData.forEach(function(item){
var chapterTitle = item.title;
var chaptertime = item.time;
var chapterhref = item.href;
console.log(chapterTitle+"\t"+chaptertime+"\t"+chapterhref+"\n");
});
}
//可以异步下载任意的URL (通过 HTTP GET方法),在完成下载的时候,它会调用回调函数并把下载的内容当做参数传进去,并将其内容输出到控制台。
function getPageList(url){
http.get(url, function(res) {
var html = ''
res.on('data', function(data) {
res.setEncoding('utf8'); //设置buffer字符集
html += data; //拼接buffer
})
res.on('end', function() {
// 将抓取的内容进行处理
var courseData= filterChapters(html);
printCourseInfo(courseData);
})
}).on('error', function(err) {
console.log('错误信息:' + err)
})
}
//或者请求前3页的数据。
list = ['http://www.sklmr.dicp.ac.cn/list.php?tid=1','http://www.sklmr.dicp.ac.cn/list.php?tid=1&page=20','http://www.sklmr.dicp.ac.cn/list.php?tid=1&page=40'];
for(var i=0;i 查看全部

  nodejs抓取动态网页(使用node开发一个小工具,扫描分子反应动力(图)
)
  使用node开发一个小工具,扫描分子反应动力学国家重点实验室新闻列表页面的前三页(地址如下:)。需要打印新闻名称、链接地址和发布时间。
  比如在控制台打印
  韩克力入选2016年度“中国科学院特聘研究员”2016-06-14
  我室金属表面解离和吸附动力学理论研究取得新进展2016-06-12
  张大宇讲座第21期:加州理工学院William A. Goddard III教授2016-05-04
<p><br /><br /><br /><br />/**
*设计
*第一种:抓取一页打印一页。
*第二种:把三页全部抓取完后,存到数组中,统一打印。
*第一种方式较高效。使用第一种方式。
*/
/**
*思路:
*1.抓取网站html内容。
*2.获取抓取的html的必要内容。
*3.把获取内容存到数组。
*4.把数组内容输到控制台。
*/
var http = require('http'); //引入nodejs的http模块,该模块用于构建http服务和作为HttpClient使用。
var cheerio = require('cheerio'); //可以理解为服务端的Jquery。使用方法和客户端一样。
//var promise = require('promise'); //对异步编程进行流程控制,更加符合后端程序员的编程习惯。
//var url = 'http://www.sklmr.dicp.ac.cn/list.php?tid=1'; //要抓取的网址,后面有拼接。
//抓取每一个节点的信息
function filterChapters(html){
var $ = cheerio.load(html); //把HTML内容解析成DOM对象 并且可以像jquery css选择器查询那样对这个DOM进行筛选
var articleList = $('td.text').find('tr');
var articleArr = [];
articleList.each(function() {
var curEle = $(this);
var title = curEle.find('a.title10').text().replace(/\s*\r\n\s*/g,""); //获取文章标题
var time = curEle.find('td.title11').text().replace(/\s*\r\n\s*/g,""); //获取文章时间
var href = "http://www.sklmr.dicp.ac.cn/"+curEle.find('a.title10').attr('href'); //获取文章链接
if( title!=null&&title!="") //有点小困难。因为DOM数据和直线是同一个等级,并且直线只有属性没有id。所以必须去除直线里面的tr空数据,否则会打印一部分空数据和错误信息。
articleArr.push({
title:title,
time:time,
href:href
});
})
return articleArr;
}
//在控制台打印信息
function printCourseInfo(courseData){
courseData.forEach(function(item){
var chapterTitle = item.title;
var chaptertime = item.time;
var chapterhref = item.href;
console.log(chapterTitle+"\t"+chaptertime+"\t"+chapterhref+"\n");
});
}
//可以异步下载任意的URL (通过 HTTP GET方法),在完成下载的时候,它会调用回调函数并把下载的内容当做参数传进去,并将其内容输出到控制台。
function getPageList(url){
http.get(url, function(res) {
var html = ''
res.on('data', function(data) {
res.setEncoding('utf8'); //设置buffer字符集
html += data; //拼接buffer
})
res.on('end', function() {
// 将抓取的内容进行处理
var courseData= filterChapters(html);
printCourseInfo(courseData);
})
}).on('error', function(err) {
console.log('错误信息:' + err)
})
}
//或者请求前3页的数据。
list = ['http://www.sklmr.dicp.ac.cn/list.php?tid=1','http://www.sklmr.dicp.ac.cn/list.php?tid=1&page=20','http://www.sklmr.dicp.ac.cn/list.php?tid=1&page=40'];
for(var i=0;i

nodejs抓取动态网页(爬虫爬取的流程和最终如何展示数据的地址?)

网站优化优采云 发表了文章 • 0 个评论 • 83 次浏览 • 2021-10-26 05:01 • 来自相关话题

  nodejs抓取动态网页(爬虫爬取的流程和最终如何展示数据的地址?)
  其实我之前做过利马财经的销售统计,不过是前端js写的。需要在首页的控制台调试面板中粘贴一段代码才能执行,点击这里。主要目的是通过定时爬取异步接口来获取数据。然后通过一定的去重算法得到最终的数据。但这有以下缺点:
  代码只能在浏览器窗口中运行。如果浏览器关闭或电脑无效,则只能抓取一个页面的数据,无法整合其他页面的数据。爬取的数据无法存储在本地。异步接口数据将被部分过滤。导致我们的去重算法失败
  由于最近了解了节点爬虫,所以可以在后台模拟请求,爬取页面数据。而且我开通了阿里云服务器,我可以把代码放到云端运行。这样1、2、3都可以解决。4因为我们不知道这个ajax界面每三分钟更新一次,这样我们就可以以此为依据对权重进行排序,保证数据不会重复。说到爬虫,大家想到的更多的是python。确实,python有Scrapy等成熟的框架,可以实现非常强大的爬虫功能。但是node也有自己的优势。凭借其强大的异步特性,可以轻松实现高效的异步并发请求,节省CPU开销。其实节点爬虫还是比较简单的,让'
  在线地址
  一、爬虫进程
  我们的最终目标是爬取利马财经每天的销售额,知道哪些产品在售,哪些用户在什么时间点购买了每种产品。首先介绍一下爬取的主要步骤:
  1. 结构分析
  我们要抓取页面的数据。第一步当然是分析页面结构,抓取哪些页面,页面结构是什么,不需要登录;有没有ajax接口,返回什么样的数据等
  2. 数据采集
  要分析清楚要爬取哪些页面和ajax,就需要爬取数据。现在网页的数据大致分为同步页面和ajax接口。同步页面数据的爬取需要我们首先分析网页的结构。Python爬取数据一般是通过正则表达式匹配获取需要的数据;node有一个cheerio工具,可以将获取到的页面内容转换成jquery对象。然后就可以使用jquery强大的dom API来获取节点相关的数据了。其实看源码,这些API本质上都是正则匹配。ajax接口数据一般都是json格式,处理起来比较简单。
  3. 数据存储
  捕获数据后,它会做一个简单的筛选,然后保存需要的数据,以便后续的分析和处理。当然我们可以使用MySQL、Mongodb等数据库来存储数据。在这里,为了方便,我们直接使用文件存储。
  4. 数据分析
  因为我们最终要展示数据,所以需要按照一定的维度对原创数据进行处理和分析,然后返回给客户端。这个过程可以在存储时进行处理,也可以在显示时,前端发送请求,后台取出存储的数据进行处理。这取决于我们希望如何显示数据。
  5. 结果显示
  做了这么多功课,一点显示输出都没有,怎么不甘心?这又回到我们的老本行了,前端展示页面大家应该都很熟悉了。将数据展示更直观,方便我们进行统计分析。
  二、常见爬虫库介绍1. Superagent
  Superagent 是一个轻量级的 http 库。是nodejs中一个非常方便的客户端请求代理模块。当我们需要进行get、post、head等网络请求时,试试吧。
  2. 啦啦队
  Cheerio可以理解为jquery的一个Node.js版本,用于通过css选择器从网页中获取数据,用法和jquery完全一样。
  3. 异步
  async 是一个过程控制工具包,提供了直接强大的异步函数mapLimit(arr,limit,iterator,callback),我们主要使用这个方法,可以去官网看API。
  4. arr-del
  arr-del 是我自己写的一个删除数组元素的工具。通过传入由要删除的数组元素的索引组成的数组,可以立即删除它。
  5. arr-sort
  arr-sort 是我自己写的一个数组排序方法工具。可以根据一个或多个属性进行排序,支持嵌套属性。而且可以在每个条件中指定排序方向,传入比较函数。
  三、页面结构分析
  让我们重复爬行的想法。利马理财网上的产品主要是普通的和利马金库(新推出的光大银行理财产品,手续繁琐,初期投资额高,基本没人买,所以我们不买)不要在这里计算它们)。定期我们可以爬取财富管理页面的ajax界面:。(更新:最近经常断货,可能看不到数据) 数据如下图:
  
  这包括所有在线销售的常规产品。Ajax 数据只有产品本身的信息,如产品 id、募集金额、当前销售额、年化收益率、投资天数等,没有关于谁购买了产品的信息。. 所以我们需要去它的商品详情页面用id参数进行爬取,比如Lima Jucai-December HLB01239511。详情页有一栏投资记录,里面有我们需要的信息,如下图:
  
  但是,详情页只有在我们登录后才能查看,这就需要我们访问cookies,cookies是有有效期的。如何让我们的 cookie 保持登录状态?请稍后再看。
  其实Lima Vault也有类似的ajax接口:但是里面的相关数据是硬编码的,没有意义。而且金库的详情页也没有投资记录信息。这就需要我们爬取我们开头所说的主页的ajax界面:。但是后来我发现这个界面每三分钟更新一次,也就是说后台每三分钟向服务器请求一次数据。一次有10条数据,所以如果三分钟内购买记录超过10条,数据就会有遗漏。没办法,所以直接金库的统计数据会比真实的少。
  四、爬虫代码分析1. 获取登录cookie
  因为商品详情页需要登录,所以我们需要先获取登录cookie。getCookie 方法如下:
  function getCookie() {
superagent.post('https://www.lmlc.com/user/s/web/logon')
.type('form')
.send({
phone: phone,
password: password,
productCode: "LMLC",
origin: "PC"
})
.end(function(err, res) {
if (err) {
handleErr(err.message);
return;
}
cookie = res.header['set-cookie']; //从response中得到cookie
emitter.emit("setCookeie");
})
}
  手机号和密码参数是从命令行传入的,就是用你的手机号登录的账号和密码。我们使用superagent来模拟请求即时理财登录界面:。传入相应的参数。在回调中,我们获取头部的 set-cookie 信息,并发送一个 setCookeie 事件。因为我们设置了监听事件:emitter.on("setCookie", requestData),一旦拿到cookie,就会执行requestData方法。
  2. 财富管理页面ajax爬取
  requestData 方法的代码如下:
<p>function requestData() {
superagent.get('https://www.lmlc.com/web/product/product_list?pageSize=100&pageNo=1&type=0')
.end(function(err,pres){
// 常规的错误处理
if (err) {
handleErr(err.message);
return;
}
// 在这里清空数据,避免一个文件被同时写入
if(clearProd){
fs.writeFileSync('data/prod.json', JSON.stringify([]));
clearProd = false;
}
let addData = JSON.parse(pres.text).data;
let formatedAddData = formatData(addData.result);
let pageUrls = [];
if(addData.totalPage > 1){
handleErr('产品个数超过100个!');
return;
}
for(let i=0,len=addData.result.length; i 查看全部

  nodejs抓取动态网页(爬虫爬取的流程和最终如何展示数据的地址?)
  其实我之前做过利马财经的销售统计,不过是前端js写的。需要在首页的控制台调试面板中粘贴一段代码才能执行,点击这里。主要目的是通过定时爬取异步接口来获取数据。然后通过一定的去重算法得到最终的数据。但这有以下缺点:
  代码只能在浏览器窗口中运行。如果浏览器关闭或电脑无效,则只能抓取一个页面的数据,无法整合其他页面的数据。爬取的数据无法存储在本地。异步接口数据将被部分过滤。导致我们的去重算法失败
  由于最近了解了节点爬虫,所以可以在后台模拟请求,爬取页面数据。而且我开通了阿里云服务器,我可以把代码放到云端运行。这样1、2、3都可以解决。4因为我们不知道这个ajax界面每三分钟更新一次,这样我们就可以以此为依据对权重进行排序,保证数据不会重复。说到爬虫,大家想到的更多的是python。确实,python有Scrapy等成熟的框架,可以实现非常强大的爬虫功能。但是node也有自己的优势。凭借其强大的异步特性,可以轻松实现高效的异步并发请求,节省CPU开销。其实节点爬虫还是比较简单的,让'
  在线地址
  一、爬虫进程
  我们的最终目标是爬取利马财经每天的销售额,知道哪些产品在售,哪些用户在什么时间点购买了每种产品。首先介绍一下爬取的主要步骤:
  1. 结构分析
  我们要抓取页面的数据。第一步当然是分析页面结构,抓取哪些页面,页面结构是什么,不需要登录;有没有ajax接口,返回什么样的数据等
  2. 数据采集
  要分析清楚要爬取哪些页面和ajax,就需要爬取数据。现在网页的数据大致分为同步页面和ajax接口。同步页面数据的爬取需要我们首先分析网页的结构。Python爬取数据一般是通过正则表达式匹配获取需要的数据;node有一个cheerio工具,可以将获取到的页面内容转换成jquery对象。然后就可以使用jquery强大的dom API来获取节点相关的数据了。其实看源码,这些API本质上都是正则匹配。ajax接口数据一般都是json格式,处理起来比较简单。
  3. 数据存储
  捕获数据后,它会做一个简单的筛选,然后保存需要的数据,以便后续的分析和处理。当然我们可以使用MySQL、Mongodb等数据库来存储数据。在这里,为了方便,我们直接使用文件存储。
  4. 数据分析
  因为我们最终要展示数据,所以需要按照一定的维度对原创数据进行处理和分析,然后返回给客户端。这个过程可以在存储时进行处理,也可以在显示时,前端发送请求,后台取出存储的数据进行处理。这取决于我们希望如何显示数据。
  5. 结果显示
  做了这么多功课,一点显示输出都没有,怎么不甘心?这又回到我们的老本行了,前端展示页面大家应该都很熟悉了。将数据展示更直观,方便我们进行统计分析。
  二、常见爬虫库介绍1. Superagent
  Superagent 是一个轻量级的 http 库。是nodejs中一个非常方便的客户端请求代理模块。当我们需要进行get、post、head等网络请求时,试试吧。
  2. 啦啦队
  Cheerio可以理解为jquery的一个Node.js版本,用于通过css选择器从网页中获取数据,用法和jquery完全一样。
  3. 异步
  async 是一个过程控制工具包,提供了直接强大的异步函数mapLimit(arr,limit,iterator,callback),我们主要使用这个方法,可以去官网看API。
  4. arr-del
  arr-del 是我自己写的一个删除数组元素的工具。通过传入由要删除的数组元素的索引组成的数组,可以立即删除它。
  5. arr-sort
  arr-sort 是我自己写的一个数组排序方法工具。可以根据一个或多个属性进行排序,支持嵌套属性。而且可以在每个条件中指定排序方向,传入比较函数。
  三、页面结构分析
  让我们重复爬行的想法。利马理财网上的产品主要是普通的和利马金库(新推出的光大银行理财产品,手续繁琐,初期投资额高,基本没人买,所以我们不买)不要在这里计算它们)。定期我们可以爬取财富管理页面的ajax界面:。(更新:最近经常断货,可能看不到数据) 数据如下图:
  
  这包括所有在线销售的常规产品。Ajax 数据只有产品本身的信息,如产品 id、募集金额、当前销售额、年化收益率、投资天数等,没有关于谁购买了产品的信息。. 所以我们需要去它的商品详情页面用id参数进行爬取,比如Lima Jucai-December HLB01239511。详情页有一栏投资记录,里面有我们需要的信息,如下图:
  
  但是,详情页只有在我们登录后才能查看,这就需要我们访问cookies,cookies是有有效期的。如何让我们的 cookie 保持登录状态?请稍后再看。
  其实Lima Vault也有类似的ajax接口:但是里面的相关数据是硬编码的,没有意义。而且金库的详情页也没有投资记录信息。这就需要我们爬取我们开头所说的主页的ajax界面:。但是后来我发现这个界面每三分钟更新一次,也就是说后台每三分钟向服务器请求一次数据。一次有10条数据,所以如果三分钟内购买记录超过10条,数据就会有遗漏。没办法,所以直接金库的统计数据会比真实的少。
  四、爬虫代码分析1. 获取登录cookie
  因为商品详情页需要登录,所以我们需要先获取登录cookie。getCookie 方法如下:
  function getCookie() {
superagent.post('https://www.lmlc.com/user/s/web/logon')
.type('form')
.send({
phone: phone,
password: password,
productCode: "LMLC",
origin: "PC"
})
.end(function(err, res) {
if (err) {
handleErr(err.message);
return;
}
cookie = res.header['set-cookie']; //从response中得到cookie
emitter.emit("setCookeie");
})
}
  手机号和密码参数是从命令行传入的,就是用你的手机号登录的账号和密码。我们使用superagent来模拟请求即时理财登录界面:。传入相应的参数。在回调中,我们获取头部的 set-cookie 信息,并发送一个 setCookeie 事件。因为我们设置了监听事件:emitter.on("setCookie", requestData),一旦拿到cookie,就会执行requestData方法。
  2. 财富管理页面ajax爬取
  requestData 方法的代码如下:
<p>function requestData() {
superagent.get('https://www.lmlc.com/web/product/product_list?pageSize=100&pageNo=1&type=0')
.end(function(err,pres){
// 常规的错误处理
if (err) {
handleErr(err.message);
return;
}
// 在这里清空数据,避免一个文件被同时写入
if(clearProd){
fs.writeFileSync('data/prod.json', JSON.stringify([]));
clearProd = false;
}
let addData = JSON.parse(pres.text).data;
let formatedAddData = formatData(addData.result);
let pageUrls = [];
if(addData.totalPage > 1){
handleErr('产品个数超过100个!');
return;
}
for(let i=0,len=addData.result.length; i

nodejs抓取动态网页(【微信群直播记录】为什么需要一个前端监控系统)

网站优化优采云 发表了文章 • 0 个评论 • 65 次浏览 • 2021-10-25 00:23 • 来自相关话题

  nodejs抓取动态网页(【微信群直播记录】为什么需要一个前端监控系统)
  本文首发于infoQ和“前端之巅”微信公众号(微信群直播记录)。感谢infoQ Top of Frontend的同学对文章的整理和校对,以及微信群直播的组织策划
  为什么需要前端监控系统
  通常大型Web项目中的监控有很多,比如后端服务API监控,接口存活、调用、延迟等监控,这些一般都是用来监控后端接口数据层面的信息的。而对于一个大型的网站系统来说,从后端服务到前端展示会有很多层:内网VIP、CDN等,但是这些监控并不能准确反映前端的状态用户看到的页面,如:页面第三方系统数据调用失败、模块加载异常、数据不正确、天窗空白等。这时候就需要从前端DOM展示的角度分析采集用户真正看到的东西,从而检测页面是否有异常问题
  监控系统需要解决的问题
  一般当页面出现以下问题时,需要通过邮件和短信通知相关人员解决问题
  必须有触发报警时的场景快照,才能重现问题
  技术选型
  监控和回归测试的含义本质上是一样的。两者都对上线功能进行回归测试,但不同的是监控需要长期、可持续、循环的回归测试,上线后只需进行一次测试。返回
  由于监控和测试的本质是一样的,我们可以将测试作为一个监控系统。在自动化测试技术遍地开花的时代,有很多有用的自动化工具。我们只需要集成这些自动化工具供我们使用。
  节点
  NodeJS 是一个 JavaScript 运行环境,非阻塞 I/O 和异步,事件驱动,这些点对于我们基于 DOM 元素构建监控非常重要
  PhantomJS
  PhantomJS 是一个基于 webkit 的浏览器引擎,可以使用 JavaScript API 来模拟浏览器操作。它使用 QtWebKit 作为浏览器核心,使用 webkit 来编译、解释和执行 JavaScript 代码。换句话说,你可以在 webkit 浏览器中做的任何事情,它都能做
  它不仅是一个隐形浏览器,还提供CSS选择器,支持Web标准、DOM操作、JSON、HTML5、Canvas、SVG等,还提供处理文件I/O的操作。PhantomJS 用途广泛,如网络监控、网页截图、无浏览器网页测试、页面访问自动化等。
  为什么不是硒
  做自动化测试的同学一定知道Selenium。可以使用Selenium在浏览器中执行测试用例,Selenium对各种平台和常用浏览器的支持比较好,但是Selenium上手稍微有点难度,使用Selenium需要在服务器端安装浏览器
  考虑到监控的主要任务是监控而不是测试。系统不需要过多考虑兼容性,监控功能比较单一,主要是针对页面的功能回归测试,所以选择了PhantomJS
  架构设计架构概述
  
  架构简介
  对于DOM监控服务,在应用层面垂直划分:
  应用层面的垂直划分,实现了应用的分布式部署,提升了处理能力。也方便后期的性能优化、系统改造和扩展
  解决方案前端规则入口
  这是一个独立的Web系统,系统主要用于采集用户输入的页面信息,页面对应的规则,显示错误信息。通过调用后端页面爬取服务完成页面检测任务,系统可以创建三种类型的检测页面:定期监控、高级监控、可用性监控
  常规监测
  输入一个页面地址和几个检测规则。注意这里的检测规则,我们将一些常用的检测点抽象成一个类似于测试用例的语句。每条规则用于匹配页面上的一个DOM元素,DOM元素的属性用于匹配期望。如果匹配失败,系统会产生错误信息,由报警系统处理。
  一般有几种匹配类型:长度、文本、HTML、属性
  处理器类似于编程语言中的运算符:大于、大于或等于、小于、小于或等于、等于、正则
  这样做的重点是,进入规则的人只需要了解一点 DOM 选择器的知识即可上手。在我们内部,通常是交给测试工程师来完成规则的录入。
  
  高级监控
  主要用于提供高级页面测试功能。通常,有经验的工程师会编写测试用例。这个测试用例写起来会有一定的学习成本,但是可以模拟网页操作,比如点击、鼠标移动等事件来准确捕获页面信息
  
  可用性监控
  可用性监控侧重于实时监控更严重的问题,例如页面可访问性和内容正确性。通常我们只需要在程序中启动一个Worker就可以获取页面的HTML来检查匹配结果,所以我们选择NodeJS来做异步页面爬取队列来高效快速的完成这种网络密度。任务
  
  主动报错页面脚本执行错误监控
  页面引入监控脚本,采集页面产生的错误事件返回的错误信息,并自动上报给后端服务。在系统中,可以汇总所有错误信息,以及对应的客户端浏览器版本、操作系统、IP地址等。
  主动举报页面
  该功能需要相应的前端工程师调用代码中的报错API主动提交错误信息。使用的主要场景有:页面异步服务延迟无响应、模块降级主动通知等,监控脚本提供了几个简单的API来完成这个任务
  // error 方法调用后立即上报错误信息并发出邮件、短信通知
errorTracker.error('错误描述')
// info 方法调用后立即上报信息,并在单位时间内仅产生一条邮件、短信通知
errorTracker.info('信息描述')
// log 方法调用后由报错检测是否达到设置阀值,最终确认是否报错
errorTracker.log('日志信息')
  后端页面抓取服务
  因为京东的很多页面都是异步加载的,所以首页、单品等系统有很多第三方异步接口调用。后端程序抓取的页面数据是同步的,无法检索到动态JavaScript渲染内容。所以你必须使用像 PhantomJS 这样可以模拟浏览器的工具
  对于日常监控,我们使用PhantomJS模拟浏览器打开页面进行抓取,然后将监控规则解析成JavaScript代码片段执行并采集结果
  高级监控 我们使用 PhantomJS 打开页面,将 jasmine、mocha 等前端 JavaScript 测试框架注入页面,然后在页面上执行相应的输入测试用例并返回结果
  常规队列生成器
  规则队列生成器会将采集的规则转换成消息队列,然后交给长期连续处理器处理一次
  为什么使用类似的消息队列处理方式?
  这与 PhantomJS 的性能是分不开的。从很多实践中发现,PhantomJS 不能很好地进行并发处理。当并发过多时,会导致CPU过载,导致机器宕机。
  本地环境下虚拟机并发测试,数据不理想,限制基本在ab -n 100 -c 50左右。 所以为了防止并发问题,我们选择使用消息队列避免高并发导致服务不可用
  类消息队列的实现
  这里我们通过调用内部的分布式缓存系统来生成消息队列。其实队列的产生可以参考数据结构-queue。最基本的模式是在缓存中创建一个KEY,然后按照队列数据结构的模式插入和读取数据
  当然,类消息队列的中间介质可以根据自己的实际情况选择,也可以使用原生内存来实现。这可能会导致应用程序和类似消息的队列争夺内存
  长时处理器
  长期连续处理器是消费规则队列生成器生成的消息队列。
  长期连续加工实现
  在长期持久化处理器的具体实现中,我们使用了 JavaScript 的 setInterval 方法不断获取累了的消息队列的内容发送给规则转换器,再转发给负载均衡调度器。然后对返回的结果进行统一处理,如邮件或短信报警
  应用程序接口
  PhantomJS服务可以作为公共API提供给客户端处理测试需求,通过HTTP调用API。在 API 处理中,需要提供 HTTP 数据到规则和 PhantomJS 的转换。从而将 HTTP 数据演化为规则转换器
  PhantomJS 服务
  PhantomJS 服务是指将 PhantomJS 与 HTTP 服务和子流程结合起来的服务处理 查看全部

  nodejs抓取动态网页(【微信群直播记录】为什么需要一个前端监控系统)
  本文首发于infoQ和“前端之巅”微信公众号(微信群直播记录)。感谢infoQ Top of Frontend的同学对文章的整理和校对,以及微信群直播的组织策划
  为什么需要前端监控系统
  通常大型Web项目中的监控有很多,比如后端服务API监控,接口存活、调用、延迟等监控,这些一般都是用来监控后端接口数据层面的信息的。而对于一个大型的网站系统来说,从后端服务到前端展示会有很多层:内网VIP、CDN等,但是这些监控并不能准确反映前端的状态用户看到的页面,如:页面第三方系统数据调用失败、模块加载异常、数据不正确、天窗空白等。这时候就需要从前端DOM展示的角度分析采集用户真正看到的东西,从而检测页面是否有异常问题
  监控系统需要解决的问题
  一般当页面出现以下问题时,需要通过邮件和短信通知相关人员解决问题
  必须有触发报警时的场景快照,才能重现问题
  技术选型
  监控和回归测试的含义本质上是一样的。两者都对上线功能进行回归测试,但不同的是监控需要长期、可持续、循环的回归测试,上线后只需进行一次测试。返回
  由于监控和测试的本质是一样的,我们可以将测试作为一个监控系统。在自动化测试技术遍地开花的时代,有很多有用的自动化工具。我们只需要集成这些自动化工具供我们使用。
  节点
  NodeJS 是一个 JavaScript 运行环境,非阻塞 I/O 和异步,事件驱动,这些点对于我们基于 DOM 元素构建监控非常重要
  PhantomJS
  PhantomJS 是一个基于 webkit 的浏览器引擎,可以使用 JavaScript API 来模拟浏览器操作。它使用 QtWebKit 作为浏览器核心,使用 webkit 来编译、解释和执行 JavaScript 代码。换句话说,你可以在 webkit 浏览器中做的任何事情,它都能做
  它不仅是一个隐形浏览器,还提供CSS选择器,支持Web标准、DOM操作、JSON、HTML5、Canvas、SVG等,还提供处理文件I/O的操作。PhantomJS 用途广泛,如网络监控、网页截图、无浏览器网页测试、页面访问自动化等。
  为什么不是硒
  做自动化测试的同学一定知道Selenium。可以使用Selenium在浏览器中执行测试用例,Selenium对各种平台和常用浏览器的支持比较好,但是Selenium上手稍微有点难度,使用Selenium需要在服务器端安装浏览器
  考虑到监控的主要任务是监控而不是测试。系统不需要过多考虑兼容性,监控功能比较单一,主要是针对页面的功能回归测试,所以选择了PhantomJS
  架构设计架构概述
  
  架构简介
  对于DOM监控服务,在应用层面垂直划分:
  应用层面的垂直划分,实现了应用的分布式部署,提升了处理能力。也方便后期的性能优化、系统改造和扩展
  解决方案前端规则入口
  这是一个独立的Web系统,系统主要用于采集用户输入的页面信息,页面对应的规则,显示错误信息。通过调用后端页面爬取服务完成页面检测任务,系统可以创建三种类型的检测页面:定期监控、高级监控、可用性监控
  常规监测
  输入一个页面地址和几个检测规则。注意这里的检测规则,我们将一些常用的检测点抽象成一个类似于测试用例的语句。每条规则用于匹配页面上的一个DOM元素,DOM元素的属性用于匹配期望。如果匹配失败,系统会产生错误信息,由报警系统处理。
  一般有几种匹配类型:长度、文本、HTML、属性
  处理器类似于编程语言中的运算符:大于、大于或等于、小于、小于或等于、等于、正则
  这样做的重点是,进入规则的人只需要了解一点 DOM 选择器的知识即可上手。在我们内部,通常是交给测试工程师来完成规则的录入。
  
  高级监控
  主要用于提供高级页面测试功能。通常,有经验的工程师会编写测试用例。这个测试用例写起来会有一定的学习成本,但是可以模拟网页操作,比如点击、鼠标移动等事件来准确捕获页面信息
  
  可用性监控
  可用性监控侧重于实时监控更严重的问题,例如页面可访问性和内容正确性。通常我们只需要在程序中启动一个Worker就可以获取页面的HTML来检查匹配结果,所以我们选择NodeJS来做异步页面爬取队列来高效快速的完成这种网络密度。任务
  
  主动报错页面脚本执行错误监控
  页面引入监控脚本,采集页面产生的错误事件返回的错误信息,并自动上报给后端服务。在系统中,可以汇总所有错误信息,以及对应的客户端浏览器版本、操作系统、IP地址等。
  主动举报页面
  该功能需要相应的前端工程师调用代码中的报错API主动提交错误信息。使用的主要场景有:页面异步服务延迟无响应、模块降级主动通知等,监控脚本提供了几个简单的API来完成这个任务
  // error 方法调用后立即上报错误信息并发出邮件、短信通知
errorTracker.error('错误描述')
// info 方法调用后立即上报信息,并在单位时间内仅产生一条邮件、短信通知
errorTracker.info('信息描述')
// log 方法调用后由报错检测是否达到设置阀值,最终确认是否报错
errorTracker.log('日志信息')
  后端页面抓取服务
  因为京东的很多页面都是异步加载的,所以首页、单品等系统有很多第三方异步接口调用。后端程序抓取的页面数据是同步的,无法检索到动态JavaScript渲染内容。所以你必须使用像 PhantomJS 这样可以模拟浏览器的工具
  对于日常监控,我们使用PhantomJS模拟浏览器打开页面进行抓取,然后将监控规则解析成JavaScript代码片段执行并采集结果
  高级监控 我们使用 PhantomJS 打开页面,将 jasmine、mocha 等前端 JavaScript 测试框架注入页面,然后在页面上执行相应的输入测试用例并返回结果
  常规队列生成器
  规则队列生成器会将采集的规则转换成消息队列,然后交给长期连续处理器处理一次
  为什么使用类似的消息队列处理方式?
  这与 PhantomJS 的性能是分不开的。从很多实践中发现,PhantomJS 不能很好地进行并发处理。当并发过多时,会导致CPU过载,导致机器宕机。
  本地环境下虚拟机并发测试,数据不理想,限制基本在ab -n 100 -c 50左右。 所以为了防止并发问题,我们选择使用消息队列避免高并发导致服务不可用
  类消息队列的实现
  这里我们通过调用内部的分布式缓存系统来生成消息队列。其实队列的产生可以参考数据结构-queue。最基本的模式是在缓存中创建一个KEY,然后按照队列数据结构的模式插入和读取数据
  当然,类消息队列的中间介质可以根据自己的实际情况选择,也可以使用原生内存来实现。这可能会导致应用程序和类似消息的队列争夺内存
  长时处理器
  长期连续处理器是消费规则队列生成器生成的消息队列。
  长期连续加工实现
  在长期持久化处理器的具体实现中,我们使用了 JavaScript 的 setInterval 方法不断获取累了的消息队列的内容发送给规则转换器,再转发给负载均衡调度器。然后对返回的结果进行统一处理,如邮件或短信报警
  应用程序接口
  PhantomJS服务可以作为公共API提供给客户端处理测试需求,通过HTTP调用API。在 API 处理中,需要提供 HTTP 数据到规则和 PhantomJS 的转换。从而将 HTTP 数据演化为规则转换器
  PhantomJS 服务
  PhantomJS 服务是指将 PhantomJS 与 HTTP 服务和子流程结合起来的服务处理

nodejs抓取动态网页(爬虫爬取的流程和最终如何展示数据的地址?)

网站优化优采云 发表了文章 • 0 个评论 • 70 次浏览 • 2021-10-25 00:21 • 来自相关话题

  nodejs抓取动态网页(爬虫爬取的流程和最终如何展示数据的地址?)
  其实我之前做过利马财经的销售统计,不过是前端js写的。需要在首页的控制台调试面板中粘贴一段代码才能执行,点击这里。主要目的是通过定时爬取异步接口来获取数据。然后通过一定的去重算法得到最终的数据。但这有以下缺点:
  1. 代码只能在浏览器窗口运行,关闭浏览器或关闭电脑都会失效
  2. 只能抓取一个页面的数据,不能整合其他页面的数据
  3. 爬取的数据无法本地存储
  4. 以上异步接口数据会被部分过滤,导致我们的去重算法失败
  由于最近了解了节点爬虫,所以可以在后台模拟请求,爬取页面数据。而且我开通了阿里云服务器,我可以把代码放到云端运行。这样1、2、3都可以解决。4因为我们不知道这个ajax界面每三分钟更新一次,这样我们就可以以此为依据对权重进行排序,保证数据不会重复。说到爬虫,大家想到的更多的是python。确实,python有Scrapy等成熟的框架,可以实现非常强大的爬虫功能。但是node也有自己的优势。凭借其强大的异步特性,可以轻松实现高效的异步并发请求,节省CPU开销。其实节点爬虫还是比较简单的,让'
  在线地址
  一、爬虫进程
  我们的最终目标是爬取利马财经每天的销售额,知道哪些产品在售,哪些用户在什么时间点购买了每种产品。首先介绍一下爬取的主要步骤:
  1. 结构分析
  我们要抓取页面的数据。第一步当然是分析页面结构,抓取哪些页面,页面的结构是什么,不需要登录;有没有ajax接口,返回什么样的数据等
  2. 数据采集
  要分析清楚要爬取哪些页面和ajax,就需要爬取数据。现在网页的数据大致分为同步页面和ajax接口。同步页面数据的爬取需要我们首先分析网页的结构。Python爬取数据一般是通过正则表达式匹配获取需要的数据;node有一个cheerio工具,可以将获取到的页面内容转换成jquery对象。然后就可以使用jquery强大的dom API来获取节点相关的数据了。其实看源码,这些API本质上都是正则匹配。ajax接口数据一般都是json格式,处理起来比较简单。
  3. 数据存储
  捕获数据后,它会做一个简单的筛选,然后保存需要的数据,以便后续的分析和处理。当然我们可以使用MySQL、Mongodb等数据库来存储数据。这里,为了方便,我们直接使用文件存储。
  4. 数据分析
  因为我们最终要展示数据,所以需要按照一定的维度对原创数据进行处理和分析,然后返回给客户端。这个过程可以在存储时进行处理,也可以在显示时,前端发送请求,后台取出存储的数据进行处理。这取决于我们希望如何显示数据。
  5. 结果显示
  做了这么多功课,一点显示输出都没有,怎么不甘心?这又回到我们的老本行了,前端展示页面大家应该都很熟悉了。将数据展示更直观,方便我们进行统计分析。
  二、常见爬虫库介绍1. Superagent
  Superagent 是一个轻量级的 http 库。是nodejs中一个非常方便的客户端请求代理模块。当我们需要进行get、post、head等网络请求时,试试吧。
  2. 啦啦队
  Cheerio可以理解为jquery的一个Node.js版本,用于通过css选择器从网页中获取数据,用法和jquery完全一样。
  3. 异步
  async 是一个过程控制工具包,提供了直接强大的异步函数mapLimit(arr,limit,iterator,callback),我们主要使用这个方法,可以去官网看API。
  4. arr-del
  arr-del 是我自己写的一个删除数组元素的工具。通过传入由要删除的数组元素的索引组成的数组,可以立即删除它。
  5. arr-sort
  arr-sort 是我自己写的一个数组排序方法工具。可以根据一个或多个属性进行排序,支持嵌套属性。而且可以在每个条件中指定排序方向,传入比较函数。
  三、页面结构分析
  让我们重复爬行的想法。利马理财网上的产品主要是普通的和利马金库(新推出的光大银行理财产品,手续繁琐,初期投资额高,基本没人买,所以我们不买)不要在这里计算它们)。定期我们可以爬取财富管理页面的ajax界面:。(更新:近期定期断货,可能看不到数据,1月19日前可以看到数据) 数据如下图所示:
  
  这包括所有在线销售的常规产品。Ajax 数据只有产品本身的信息,如产品 id、募集金额、当前销售额、年化收益率、投资天数等,没有关于谁购买了产品的信息。. 所以我们需要去它的商品详情页面用id参数进行爬取,比如Lima Jucai-December HLB01239511。详情页有一栏投资记录,里面有我们需要的信息,如下图:
  
  但是,详情页只有在我们登录后才能查看,这就需要我们访问cookies,cookies是有有效期的。如何让我们的 cookie 保持登录状态?请稍后再看。
  其实Lima Vault也有类似的ajax接口:,但是里面的相关数据是硬编码的,没有意义。而且金库的详情页也没有投资记录信息。这就需要我们爬取我们开头所说的主页的ajax界面:。但是后来我发现这个界面每三分钟更新一次,也就是说后台每三分钟向服务器请求一次数据。一次有10条数据,所以如果三分钟内购买记录超过10条,数据就会有遗漏。没办法,所以直接金库的统计数据会比真实的少。
  四、爬虫代码分析1. 获取登录cookie
  因为商品详情页需要登录,所以我们需要先获取登录cookie。getCookie 方法如下:
  function getCookie() {
superagent.post(&#39;https://www.lmlc.com/user/s/web/logon&#39;)
.type(&#39;form&#39;)
.send({
phone: phone,
password: password,
productCode: "LMLC",
origin: "PC"
})
.end(function(err, res) {
if (err) {
handleErr(err.message);
return;
}
cookie = res.header[&#39;set-cookie&#39;]; //从response中得到cookie
emitter.emit("setCookeie");
})
}
  手机号和密码参数是从命令行传入的,就是用你的手机号登录的账号和密码。我们使用superagent来模拟请求即时理财登录界面:。传入相应的参数。在回调中,我们获取头部的 set-cookie 信息,并发送一个 setCookeie 事件。因为我们设置了监听事件:emitter.on("setCookie", requestData),一旦拿到cookie,就会执行requestData方法。
  2. 财富管理页面ajax爬取
  requestData 方法的代码如下:
<p>function requestData() {
superagent.get(&#39;https://www.lmlc.com/web/produ ... %2339;)
.end(function(err,pres){
// 常规的错误处理
if (err) {
handleErr(err.message);
return;
}
// 在这里清空数据,避免一个文件被同时写入
if(clearProd){
fs.writeFileSync(&#39;data/prod.json&#39;, JSON.stringify([]));
clearProd = false;
}
let addData = JSON.parse(pres.text).data;
let formatedAddData = formatData(addData.result);
let pageUrls = [];
if(addData.totalPage > 1){
handleErr(&#39;产品个数超过100个!&#39;);
return;
}
for(let i=0,len=addData.result.length; i 查看全部

  nodejs抓取动态网页(爬虫爬取的流程和最终如何展示数据的地址?)
  其实我之前做过利马财经的销售统计,不过是前端js写的。需要在首页的控制台调试面板中粘贴一段代码才能执行,点击这里。主要目的是通过定时爬取异步接口来获取数据。然后通过一定的去重算法得到最终的数据。但这有以下缺点:
  1. 代码只能在浏览器窗口运行,关闭浏览器或关闭电脑都会失效
  2. 只能抓取一个页面的数据,不能整合其他页面的数据
  3. 爬取的数据无法本地存储
  4. 以上异步接口数据会被部分过滤,导致我们的去重算法失败
  由于最近了解了节点爬虫,所以可以在后台模拟请求,爬取页面数据。而且我开通了阿里云服务器,我可以把代码放到云端运行。这样1、2、3都可以解决。4因为我们不知道这个ajax界面每三分钟更新一次,这样我们就可以以此为依据对权重进行排序,保证数据不会重复。说到爬虫,大家想到的更多的是python。确实,python有Scrapy等成熟的框架,可以实现非常强大的爬虫功能。但是node也有自己的优势。凭借其强大的异步特性,可以轻松实现高效的异步并发请求,节省CPU开销。其实节点爬虫还是比较简单的,让'
  在线地址
  一、爬虫进程
  我们的最终目标是爬取利马财经每天的销售额,知道哪些产品在售,哪些用户在什么时间点购买了每种产品。首先介绍一下爬取的主要步骤:
  1. 结构分析
  我们要抓取页面的数据。第一步当然是分析页面结构,抓取哪些页面,页面的结构是什么,不需要登录;有没有ajax接口,返回什么样的数据等
  2. 数据采集
  要分析清楚要爬取哪些页面和ajax,就需要爬取数据。现在网页的数据大致分为同步页面和ajax接口。同步页面数据的爬取需要我们首先分析网页的结构。Python爬取数据一般是通过正则表达式匹配获取需要的数据;node有一个cheerio工具,可以将获取到的页面内容转换成jquery对象。然后就可以使用jquery强大的dom API来获取节点相关的数据了。其实看源码,这些API本质上都是正则匹配。ajax接口数据一般都是json格式,处理起来比较简单。
  3. 数据存储
  捕获数据后,它会做一个简单的筛选,然后保存需要的数据,以便后续的分析和处理。当然我们可以使用MySQL、Mongodb等数据库来存储数据。这里,为了方便,我们直接使用文件存储。
  4. 数据分析
  因为我们最终要展示数据,所以需要按照一定的维度对原创数据进行处理和分析,然后返回给客户端。这个过程可以在存储时进行处理,也可以在显示时,前端发送请求,后台取出存储的数据进行处理。这取决于我们希望如何显示数据。
  5. 结果显示
  做了这么多功课,一点显示输出都没有,怎么不甘心?这又回到我们的老本行了,前端展示页面大家应该都很熟悉了。将数据展示更直观,方便我们进行统计分析。
  二、常见爬虫库介绍1. Superagent
  Superagent 是一个轻量级的 http 库。是nodejs中一个非常方便的客户端请求代理模块。当我们需要进行get、post、head等网络请求时,试试吧。
  2. 啦啦队
  Cheerio可以理解为jquery的一个Node.js版本,用于通过css选择器从网页中获取数据,用法和jquery完全一样。
  3. 异步
  async 是一个过程控制工具包,提供了直接强大的异步函数mapLimit(arr,limit,iterator,callback),我们主要使用这个方法,可以去官网看API。
  4. arr-del
  arr-del 是我自己写的一个删除数组元素的工具。通过传入由要删除的数组元素的索引组成的数组,可以立即删除它。
  5. arr-sort
  arr-sort 是我自己写的一个数组排序方法工具。可以根据一个或多个属性进行排序,支持嵌套属性。而且可以在每个条件中指定排序方向,传入比较函数。
  三、页面结构分析
  让我们重复爬行的想法。利马理财网上的产品主要是普通的和利马金库(新推出的光大银行理财产品,手续繁琐,初期投资额高,基本没人买,所以我们不买)不要在这里计算它们)。定期我们可以爬取财富管理页面的ajax界面:。(更新:近期定期断货,可能看不到数据,1月19日前可以看到数据) 数据如下图所示:
  
  这包括所有在线销售的常规产品。Ajax 数据只有产品本身的信息,如产品 id、募集金额、当前销售额、年化收益率、投资天数等,没有关于谁购买了产品的信息。. 所以我们需要去它的商品详情页面用id参数进行爬取,比如Lima Jucai-December HLB01239511。详情页有一栏投资记录,里面有我们需要的信息,如下图:
  
  但是,详情页只有在我们登录后才能查看,这就需要我们访问cookies,cookies是有有效期的。如何让我们的 cookie 保持登录状态?请稍后再看。
  其实Lima Vault也有类似的ajax接口:,但是里面的相关数据是硬编码的,没有意义。而且金库的详情页也没有投资记录信息。这就需要我们爬取我们开头所说的主页的ajax界面:。但是后来我发现这个界面每三分钟更新一次,也就是说后台每三分钟向服务器请求一次数据。一次有10条数据,所以如果三分钟内购买记录超过10条,数据就会有遗漏。没办法,所以直接金库的统计数据会比真实的少。
  四、爬虫代码分析1. 获取登录cookie
  因为商品详情页需要登录,所以我们需要先获取登录cookie。getCookie 方法如下:
  function getCookie() {
superagent.post(&#39;https://www.lmlc.com/user/s/web/logon&#39;)
.type(&#39;form&#39;)
.send({
phone: phone,
password: password,
productCode: "LMLC",
origin: "PC"
})
.end(function(err, res) {
if (err) {
handleErr(err.message);
return;
}
cookie = res.header[&#39;set-cookie&#39;]; //从response中得到cookie
emitter.emit("setCookeie");
})
}
  手机号和密码参数是从命令行传入的,就是用你的手机号登录的账号和密码。我们使用superagent来模拟请求即时理财登录界面:。传入相应的参数。在回调中,我们获取头部的 set-cookie 信息,并发送一个 setCookeie 事件。因为我们设置了监听事件:emitter.on("setCookie", requestData),一旦拿到cookie,就会执行requestData方法。
  2. 财富管理页面ajax爬取
  requestData 方法的代码如下:
<p>function requestData() {
superagent.get(&#39;https://www.lmlc.com/web/produ ... %2339;)
.end(function(err,pres){
// 常规的错误处理
if (err) {
handleErr(err.message);
return;
}
// 在这里清空数据,避免一个文件被同时写入
if(clearProd){
fs.writeFileSync(&#39;data/prod.json&#39;, JSON.stringify([]));
clearProd = false;
}
let addData = JSON.parse(pres.text).data;
let formatedAddData = formatData(addData.result);
let pageUrls = [];
if(addData.totalPage > 1){
handleErr(&#39;产品个数超过100个!&#39;);
return;
}
for(let i=0,len=addData.result.length; i

nodejs抓取动态网页(让百度下我的动态填充vue单页面ssr解决问题)

网站优化优采云 发表了文章 • 0 个评论 • 65 次浏览 • 2021-10-22 11:03 • 来自相关话题

  nodejs抓取动态网页(让百度下我的动态填充vue单页面ssr解决问题)
  遇到的问题:
  最近在写个人博客的时候,遇到了一个大家可能会遇到的问题。
  vue单页SEO很弱,尤其是百度不会抓取动态脚本
  Vue-router配合前后端分离,防止蜘蛛爬行时动态填充meta标签
  Vue单页是大势所趋,写起来不仅很酷,当然也可以选择多页
  但是即使面对文章和文档有多个页面,也不能说每个文章生成一个vue页面。
  ssr当然可以解决这个问题,但是仔细想想,ssr不是和之前的.php页面一样吗?
  所有数据都是提前拉取然后填充返回浏览器,消耗服务器资源较多,配置麻烦
  当然预渲染不能解决这个问题
  所以问题来了,我只想百度抓取我的动态文章,但是配置一个繁琐的ssr并不是最好的选择
  我的解决方案:
  由于我只是想让百度抓取我的动态文章,其实蜘蛛抓取我的静态页面的时候,html meta标签就已经填好了
  那么就很简单了,我们只需要实现一个很简单的ssr阉割版即可。
  ps:好久没在百度找到相关的文章,不知道是不是我百度姿势不对
  具体思路:
  因为我的服务器后端语言是php,服务是nginx,所以我这里展示的所有后端代码都是基于php的。当然你可以选择nodejs或者其他语言来实现。这是一个简单的想法
  如上所述,我们将实现一个阉割版的 ssr。实际上,当服务器有请求时,在静态html元标记上填写数据,然后将其返回给请求者。
  这里的实现是基于搭建的vue单页,所以请先搭建一个vue单页
  先改造建dist下的index.html
  获取最上面的变量,因为接口都是现成的,所以偷懒直接获取接口
  将获取到的数据输出到meta标签
<p> 查看全部

  nodejs抓取动态网页(让百度下我的动态填充vue单页面ssr解决问题)
  遇到的问题:
  最近在写个人博客的时候,遇到了一个大家可能会遇到的问题。
  vue单页SEO很弱,尤其是百度不会抓取动态脚本
  Vue-router配合前后端分离,防止蜘蛛爬行时动态填充meta标签
  Vue单页是大势所趋,写起来不仅很酷,当然也可以选择多页
  但是即使面对文章和文档有多个页面,也不能说每个文章生成一个vue页面。
  ssr当然可以解决这个问题,但是仔细想想,ssr不是和之前的.php页面一样吗?
  所有数据都是提前拉取然后填充返回浏览器,消耗服务器资源较多,配置麻烦
  当然预渲染不能解决这个问题
  所以问题来了,我只想百度抓取我的动态文章,但是配置一个繁琐的ssr并不是最好的选择
  我的解决方案:
  由于我只是想让百度抓取我的动态文章,其实蜘蛛抓取我的静态页面的时候,html meta标签就已经填好了
  那么就很简单了,我们只需要实现一个很简单的ssr阉割版即可。
  ps:好久没在百度找到相关的文章,不知道是不是我百度姿势不对
  具体思路:
  因为我的服务器后端语言是php,服务是nginx,所以我这里展示的所有后端代码都是基于php的。当然你可以选择nodejs或者其他语言来实现。这是一个简单的想法
  如上所述,我们将实现一个阉割版的 ssr。实际上,当服务器有请求时,在静态html元标记上填写数据,然后将其返回给请求者。
  这里的实现是基于搭建的vue单页,所以请先搭建一个vue单页
  先改造建dist下的index.html
  获取最上面的变量,因为接口都是现成的,所以偷懒直接获取接口
  将获取到的数据输出到meta标签
<p>

nodejs抓取动态网页(服务器请求响应不同的文件路由:根据不同文件请求)

网站优化优采云 发表了文章 • 0 个评论 • 85 次浏览 • 2021-10-21 23:10 • 来自相关话题

  nodejs抓取动态网页(服务器请求响应不同的文件路由:根据不同文件请求)
  昨天创建的服务器只是在浏览器请求时做出响应,而今天要创建的服务器可以根据不同的URL请求响应不同的文件,这就是所谓的文件路由:不同的文件请求响应不同的“路” .
  第一步:创建文件Luyou.js,在其中声明引用模块的变量和要响应的文件路径
  //获取http模块
var http = require("http");
//文件模块
var fs = require(&#39;fs&#39;);
//主页路由模块,file文件夹里的index.js文件
var index = require(&#39;./file/index&#39;);
//错误处理文件路径
var error = "./file/error404.html";
//春晓页面路径
var cx = "./file/cunxiao.html";
  需要提供路由的文件主要包括三个文件。第一个是index模块,负责首页的响应;第二个是404错误页面的响应;三是春晓诗的回复页。文件目录如下:
  
  步骤二:实现文件响应功能和404错误响应功能
  //函数Response,将HTML、css、js等文件响应给客户端
var Response = function(res,filePath){
//读取文件,读取完成后给客户端响应
fs.readFile(filePath,function(err,data){
if(err){ //如果失败,就返回错误文件
if(filePath != error) //如果失败的不是错误文件,才返回错误文件
Response(res,error);
}else{
res.writeHead(200,{ //响应客户端,将文件内容发回去
&#39;Content-type&#39;:"text/html"});
res.end(data);
}
});
};
//404错误响应文件
var error404 = function(res){
Response(res,error);
};
  该函数使用了fs文件模块,用于提取文件内容,提取的内容(或错误)会在回调函数中传回,这是node.js非的思想- 阻塞 I/O 事件编程表现。 查看全部

  nodejs抓取动态网页(服务器请求响应不同的文件路由:根据不同文件请求)
  昨天创建的服务器只是在浏览器请求时做出响应,而今天要创建的服务器可以根据不同的URL请求响应不同的文件,这就是所谓的文件路由:不同的文件请求响应不同的“路” .
  第一步:创建文件Luyou.js,在其中声明引用模块的变量和要响应的文件路径
  //获取http模块
var http = require("http");
//文件模块
var fs = require(&#39;fs&#39;);
//主页路由模块,file文件夹里的index.js文件
var index = require(&#39;./file/index&#39;);
//错误处理文件路径
var error = "./file/error404.html";
//春晓页面路径
var cx = "./file/cunxiao.html";
  需要提供路由的文件主要包括三个文件。第一个是index模块,负责首页的响应;第二个是404错误页面的响应;三是春晓诗的回复页。文件目录如下:
  
  步骤二:实现文件响应功能和404错误响应功能
  //函数Response,将HTML、css、js等文件响应给客户端
var Response = function(res,filePath){
//读取文件,读取完成后给客户端响应
fs.readFile(filePath,function(err,data){
if(err){ //如果失败,就返回错误文件
if(filePath != error) //如果失败的不是错误文件,才返回错误文件
Response(res,error);
}else{
res.writeHead(200,{ //响应客户端,将文件内容发回去
&#39;Content-type&#39;:"text/html"});
res.end(data);
}
});
};
//404错误响应文件
var error404 = function(res){
Response(res,error);
};
  该函数使用了fs文件模块,用于提取文件内容,提取的内容(或错误)会在回调函数中传回,这是node.js非的思想- 阻塞 I/O 事件编程表现。

nodejs抓取动态网页(Web浏览器呈现的HTML,您可以开始网页并JavaScript吗?)

网站优化优采云 发表了文章 • 0 个评论 • 64 次浏览 • 2021-10-21 07:03 • 来自相关话题

  nodejs抓取动态网页(Web浏览器呈现的HTML,您可以开始网页并JavaScript吗?)
  Puppeteer 是一个流行的 Headless Chrome NodeJS API 爬虫库,由 Google 构建。 Puppeteer Sharp 是用 C# 编写的,由 Dario Kondratiuque 于 2017 年发布,为 .NET 开发人员提供相同的功能。
  
  傀儡师标志
  Puppeteer Sharp 使 .NET 开发人员能够以编程方式控制开源 Google Chrome。 Puppeteer API 的便利之处在于能够使用浏览器的 headless 特性而无需显示浏览器来提高性能。
  为什么要使用 Puppeteer Sharp?
  如果您是 .NET 开发人员,可以通过 Nuget 包将其安装到项目中:
  在现代 Web 中,Web 应用程序通常依赖 JavaScript 来加载 UI。如果您使用爬虫加载 Bing 地图,您可能会失望地收到:
  
  Bing 地图为空
  Puppeteer Sharp 除了检索 JavaScript 渲染的 HTML 之外,还可以注入 HTML 进行导航网站;与 UI 元素交互;截取屏幕截图或创建 PDF,现在流行的 Google NodeJS API 中收录更多功能。
  入门
  在新的或现有的 .NET 项目中使用 Puppeteer Sharp。安装最新版本的 Nuget 包“PuppeteeSharp”。
  
  图像.png
  首先我们需要将Chrome浏览器下载到本地。这是 Puppeteer Sharp 将用于与 网站 交互的浏览器。
  幸运的是,我们可以使用 C# 下载默认修订版或开发者指定的修订版。仅当本地计算机上不存在修订版时才会下载。
  // Download the Chromium revision if it does not already exist
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
  如果下载成功,您会在项目目录中看到在操作系统上运行所需的浏览器版本:
  
  图像.png
  加载网页
  现在您已将浏览器下载到本地计算机,您可以开始加载网页并检索 JavaScript 呈现的 HTML。
  首先,我们将启动一个无头 Web 浏览器的实例,加载一个新选项卡并转到“地图”:
  // Create an instance of the browser and configure launch options
Browser browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true
});
// Create a new page and go to Bing Maps
Page page = await browser.NewPageAsync();
await page.GoToAsync("https://www.bing.com/maps");
  
  图像.png
  在无头浏览器中成功加载网页后,让我们通过搜索当地旅游景点与网页进行交互:
  // Search for a local tourist attraction on Bing Maps
await page.WaitForSelectorAsync(".searchbox input");
await page.FocusAsync(".searchbox input");
await page.Keyboard.TypeAsync("CN Tower, Toronto, Ontario, Canada");
await page.ClickAsync(".searchIcon");
await page.WaitForNavigationAsync();
  我们可以使用Puppeteer Sharp与JavaScript渲染的Bing地图HTML进行交互,搜索“加拿大安大略省多伦多CN塔”!
  如果您想存储 HTML 以分析地址或描述等信息,您可以轻松地将 HTML 存储在变量中:
  // Store the HTML of the current page
string content = await page.GetContentAsync();
  完成后关闭浏览器释放资源:
  // Close the browser
await browser.CloseAsync();
  屏幕截图和 PDF 文档
  Puppeteer Sharp 的好处之一是能够生成当前页面的屏幕截图和 PDF 文档。这对于调试、自动化测试或以特定分辨率捕获网页特别有用。
  如果你想截取当前页面的截图:
  await page.ScreenshotAsync("C:\\Files\\screenshot.png");
  
  人偶截图
  或者,生成当前页面的PDF文档:
  await page.PdfAsync("C:\\Files\\document.pdf");
  
  图像.png
  更改页面大小
  如果您需要测试特定显示尺寸的网页(例如,查看页面在手机上的显示方式),您可以使用 Puppeter Sharp 更改当前页面上的网页尺寸:
  // Change the size of the view port to simulate the iPhone X
await page.SetViewportAsync(new ViewPortOptions
{
Width = 1125,
Height = 2436
});
  
  图像.png
  跟踪日志
  除了上述功能外,Puppeteer Sharp 还可用于监控和检测与网络用户界面相关的问题。 .NET 开发人员可以使用 Puppeteer Sharp 检查任何网络性能问题。
  为此,我们可以启动和停止跟踪日志:
  await page.Tracing.StartAsync(new TracingOptions { Path = "C:\\Files\\trace.json" });
...
await page.Tracing.StopAsync();
  
  图像.png
  如果跟踪日志没有捕获调试会话所需的详细信息,您可以启用 Chrome DevTools 进行进一步分析:
  Browser browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Devtools = true
});
  如果在 Puppeteer Sharp 中启用 Chrome DevTools,headless 配置将自动禁用,您将可以查看浏览器,并且 DevTools 将显示查看 Web 应用程序的 JavaScript 渲染代码的选项,以及诸如查看网络活动。
  
  图像.png
  连接到远程浏览器
  Puppeteer Sharp 的最后一个功能是能够连接到远程浏览器。如果您的服务器上无法安装浏览器(例如 Linux),此功能可能会很有用。
  比如老外的这个browserless.io:可以用不花钱的童鞋
  
  图像.png
  var connectOptions = new ConnectOptions()
{
BrowserWSEndpoint = "$wss://chrome.browserless.io/"
};
using (var browser = await Puppeteer.ConnectAsync(connectOptions))
{
...
}
  项目捐赠
  项目官网。 查看全部

  nodejs抓取动态网页(Web浏览器呈现的HTML,您可以开始网页并JavaScript吗?)
  Puppeteer 是一个流行的 Headless Chrome NodeJS API 爬虫库,由 Google 构建。 Puppeteer Sharp 是用 C# 编写的,由 Dario Kondratiuque 于 2017 年发布,为 .NET 开发人员提供相同的功能。
  
  傀儡师标志
  Puppeteer Sharp 使 .NET 开发人员能够以编程方式控制开源 Google Chrome。 Puppeteer API 的便利之处在于能够使用浏览器的 headless 特性而无需显示浏览器来提高性能。
  为什么要使用 Puppeteer Sharp?
  如果您是 .NET 开发人员,可以通过 Nuget 包将其安装到项目中:
  在现代 Web 中,Web 应用程序通常依赖 JavaScript 来加载 UI。如果您使用爬虫加载 Bing 地图,您可能会失望地收到:
  
  Bing 地图为空
  Puppeteer Sharp 除了检索 JavaScript 渲染的 HTML 之外,还可以注入 HTML 进行导航网站;与 UI 元素交互;截取屏幕截图或创建 PDF,现在流行的 Google NodeJS API 中收录更多功能。
  入门
  在新的或现有的 .NET 项目中使用 Puppeteer Sharp。安装最新版本的 Nuget 包“PuppeteeSharp”。
  
  图像.png
  首先我们需要将Chrome浏览器下载到本地。这是 Puppeteer Sharp 将用于与 网站 交互的浏览器。
  幸运的是,我们可以使用 C# 下载默认修订版或开发者指定的修订版。仅当本地计算机上不存在修订版时才会下载。
  // Download the Chromium revision if it does not already exist
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
  如果下载成功,您会在项目目录中看到在操作系统上运行所需的浏览器版本:
  
  图像.png
  加载网页
  现在您已将浏览器下载到本地计算机,您可以开始加载网页并检索 JavaScript 呈现的 HTML。
  首先,我们将启动一个无头 Web 浏览器的实例,加载一个新选项卡并转到“地图”:
  // Create an instance of the browser and configure launch options
Browser browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true
});
// Create a new page and go to Bing Maps
Page page = await browser.NewPageAsync();
await page.GoToAsync("https://www.bing.com/maps";);
  
  图像.png
  在无头浏览器中成功加载网页后,让我们通过搜索当地旅游景点与网页进行交互:
  // Search for a local tourist attraction on Bing Maps
await page.WaitForSelectorAsync(".searchbox input");
await page.FocusAsync(".searchbox input");
await page.Keyboard.TypeAsync("CN Tower, Toronto, Ontario, Canada");
await page.ClickAsync(".searchIcon");
await page.WaitForNavigationAsync();
  我们可以使用Puppeteer Sharp与JavaScript渲染的Bing地图HTML进行交互,搜索“加拿大安大略省多伦多CN塔”!
  如果您想存储 HTML 以分析地址或描述等信息,您可以轻松地将 HTML 存储在变量中:
  // Store the HTML of the current page
string content = await page.GetContentAsync();
  完成后关闭浏览器释放资源:
  // Close the browser
await browser.CloseAsync();
  屏幕截图和 PDF 文档
  Puppeteer Sharp 的好处之一是能够生成当前页面的屏幕截图和 PDF 文档。这对于调试、自动化测试或以特定分辨率捕获网页特别有用。
  如果你想截取当前页面的截图:
  await page.ScreenshotAsync("C:\\Files\\screenshot.png");
  
  人偶截图
  或者,生成当前页面的PDF文档:
  await page.PdfAsync("C:\\Files\\document.pdf");
  
  图像.png
  更改页面大小
  如果您需要测试特定显示尺寸的网页(例如,查看页面在手机上的显示方式),您可以使用 Puppeter Sharp 更改当前页面上的网页尺寸:
  // Change the size of the view port to simulate the iPhone X
await page.SetViewportAsync(new ViewPortOptions
{
Width = 1125,
Height = 2436
});
  
  图像.png
  跟踪日志
  除了上述功能外,Puppeteer Sharp 还可用于监控和检测与网络用户界面相关的问题。 .NET 开发人员可以使用 Puppeteer Sharp 检查任何网络性能问题。
  为此,我们可以启动和停止跟踪日志:
  await page.Tracing.StartAsync(new TracingOptions { Path = "C:\\Files\\trace.json" });
...
await page.Tracing.StopAsync();
  
  图像.png
  如果跟踪日志没有捕获调试会话所需的详细信息,您可以启用 Chrome DevTools 进行进一步分析:
  Browser browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Devtools = true
});
  如果在 Puppeteer Sharp 中启用 Chrome DevTools,headless 配置将自动禁用,您将可以查看浏览器,并且 DevTools 将显示查看 Web 应用程序的 JavaScript 渲染代码的选项,以及诸如查看网络活动。
  
  图像.png
  连接到远程浏览器
  Puppeteer Sharp 的最后一个功能是能够连接到远程浏览器。如果您的服务器上无法安装浏览器(例如 Linux),此功能可能会很有用。
  比如老外的这个browserless.io:可以用不花钱的童鞋
  
  图像.png
  var connectOptions = new ConnectOptions()
{
BrowserWSEndpoint = "$wss://chrome.browserless.io/"
};
using (var browser = await Puppeteer.ConnectAsync(connectOptions))
{
...
}
  项目捐赠
  项目官网。

nodejs抓取动态网页(异步特性()(2)_知乎点开(图))

网站优化优采云 发表了文章 • 0 个评论 • 63 次浏览 • 2021-10-21 07:02 • 来自相关话题

  nodejs抓取动态网页(异步特性()(2)_知乎点开(图))
  写了个小爬虫,貌似很不完善。很多地方都没有处理。例如,当在 知乎 中打开一个问题时,并不是所有的答案都被加载。当你拉到答案的结尾时,点击加载更多来加载一部分答案,所以如果你直接发送一个问题的请求链接,你得到的页面是不完整的。还有就是我们通过访问链接下载图片的时候,是一张一张的下载的。如果图片太多,真的会下载到你睡着了。
  这个爬虫是上一个的升级版。爬虫代码可以在我的github=&gt;NodeSpider上找到。
  整个爬虫的思路是这样的:一开始我们通过请求问题的链接来抓取部分页面数据,然后我们在代码中模拟ajax请求拦截剩余页面的数据,当然,这里也可以通过异步来实现并发。对于小规模的异步过程控制,可以使用这个module=&gt;eventproxy,但是我这里没有用!我们从获取到的页面中截取所有图片的链接,然后通过异步并发实现这些图片的批量下载。
  抓取页面的初始数据很简单,这里就不解释了。
   1 /*获取首屏所有图片链接*/
2 var getInitUrlList=function(){
3 request.get("https://www.zhihu.com/question/34937418")
4 .end(function(err,res){
5 if(err){
6 console.log(err);
7 }else{
8 var $=cheerio.load(res.text);
9 var answerList=$(".zm-item-answer");
10 answerList.map(function(i,answer){
11 var images=$(answer).find('.zm-item-rich-text img');
12 images.map(function(i,image){
13 photos.push($(image).attr("src"));
14 });
15 });
16 console.log("已成功抓取"+photos.length+"张图片的链接");
17 getIAjaxUrlList(20);
18 }
19 });
20 }
  模拟ajax请求获取完整页面
  下一步是如何模拟点击加载更多时发送的ajax请求。去知乎看看吧!
  
  
  
  有了这些信息,您可以模拟发送相同的请求来获取数据。
   1 /*每隔100毫秒模拟发送ajax请求,并获取请求结果中所有的图片链接*/
2 var getIAjaxUrlList=function(offset){
3 request.post("https://www.zhihu.com/node/Que ... 6quot;)
4 .set(config)
5 .send("method=next&params=%7B%22url_token%22%3A34937418%2C%22pagesize%22%3A20%2C%22offset%22%3A" +offset+ "%7D&_xsrf=98360a2df02783902146dee374772e51")
6 .end(function(err,res){
7 if(err){
8 console.log(err);
9 }else{
10 var response=JSON.parse(res.text);/*想用json的话对json序列化即可,提交json的话需要对json进行反序列化*/
11 if(response.msg&&response.msg.length){
12 var $=cheerio.load(response.msg.join(""));/*把所有的数组元素拼接在一起,以空白符分隔,不要这样join(),它会默认数组元素以逗号分隔*/
13 var answerList=$(".zm-item-answer");
14 answerList.map(function(i,answer){
15 var images=$(answer).find('.zm-item-rich-text img');
16 images.map(function(i,image){
17 photos.push($(image).attr("src"));
18 });
19 });
20 setTimeout(function(){
21 offset+=20;
22 console.log("已成功抓取"+photos.length+"张图片的链接");
23 getIAjaxUrlList(offset);
24 },100);
25 }else{
26 console.log("图片链接全部获取完毕,一共有"+photos.length+"条图片链接");
27 // console.log(photos);
28 return downloadImg(50);
29 }
30 }
31 });
32 }
  在代码中post这条请求https://www.zhihu.com/node/QuestionAnswerListV2,把原请求头和请求参数复制下来,作为我们的请求头和请求参数,superagent的set方法可用来设置请求头,send方法可以用来发送请求参数。我们把请求参数中的offset初始为20,每隔一定时间offset再加20,再重新发送请求,这样就相当于我们每隔一定时间发送了一条ajax请求,获取到最新的20条数据,每获取到了数据,我们再对这些数据进行一定的处理,让它们变成一整段的html,便于后面的提取链接处理。
  
异步并发控制下载图片
再获取完了所有的图片链接之后,即判定response.msg为空时,我们就要对这些图片进行下载了,不可能一条一条下对不对,因为如你所看到的,我们的图片足足有

  没错,2万多,还好nodejs有神奇的单线程异步功能,我们可以同时下载这些图片。但是这个时候问题就来了。听说如果同时发送太多请求,会被网站拦截!所以我们绝对不能同时下载超过 20,000 张图片。这时候就需要控制异步并发的数量。
  这里使用了一个神奇的模块=&gt; async,它不仅可以帮助我们询问难以维护的回调金字塔恶魔,还可以轻松帮助我们管理异步进程。具体看文档,这里只使用了一个强大的 async.mapLimit 方法。
<p> 1 var requestAndwrite=function(url,callback){
2 request.get(url).end(function(err,res){
3 if(err){
4 console.log(err);
5 console.log("有一张图片请求失败啦...");
6 }else{
7 var fileName=path.basename(url);
8 fs.writeFile("./img1/"+fileName,res.body,function(err){
9 if(err){
10 console.log(err);
11 console.log("有一张图片写入失败啦...");
12 }else{
13 console.log("图片下载成功啦");
14 callback(null,"successful !");
15 /*callback貌似必须调用,第二个参数将传给下一个回调函数的result,result是一个数组*/
16 }
17 });
18 }
19 });
20 }
21
22 var downloadImg=function(asyncNum){
23 /*有一些图片链接地址不完整没有“http:”头部,帮它们拼接完整*/
24 for(var i=0;i 查看全部

  nodejs抓取动态网页(异步特性()(2)_知乎点开(图))
  写了个小爬虫,貌似很不完善。很多地方都没有处理。例如,当在 知乎 中打开一个问题时,并不是所有的答案都被加载。当你拉到答案的结尾时,点击加载更多来加载一部分答案,所以如果你直接发送一个问题的请求链接,你得到的页面是不完整的。还有就是我们通过访问链接下载图片的时候,是一张一张的下载的。如果图片太多,真的会下载到你睡着了。
  这个爬虫是上一个的升级版。爬虫代码可以在我的github=&gt;NodeSpider上找到。
  整个爬虫的思路是这样的:一开始我们通过请求问题的链接来抓取部分页面数据,然后我们在代码中模拟ajax请求拦截剩余页面的数据,当然,这里也可以通过异步来实现并发。对于小规模的异步过程控制,可以使用这个module=&gt;eventproxy,但是我这里没有用!我们从获取到的页面中截取所有图片的链接,然后通过异步并发实现这些图片的批量下载。
  抓取页面的初始数据很简单,这里就不解释了。
   1 /*获取首屏所有图片链接*/
2 var getInitUrlList=function(){
3 request.get("https://www.zhihu.com/question/34937418";)
4 .end(function(err,res){
5 if(err){
6 console.log(err);
7 }else{
8 var $=cheerio.load(res.text);
9 var answerList=$(".zm-item-answer");
10 answerList.map(function(i,answer){
11 var images=$(answer).find('.zm-item-rich-text img');
12 images.map(function(i,image){
13 photos.push($(image).attr("src"));
14 });
15 });
16 console.log("已成功抓取"+photos.length+"张图片的链接");
17 getIAjaxUrlList(20);
18 }
19 });
20 }
  模拟ajax请求获取完整页面
  下一步是如何模拟点击加载更多时发送的ajax请求。去知乎看看吧!
  
  
  
  有了这些信息,您可以模拟发送相同的请求来获取数据。
   1 /*每隔100毫秒模拟发送ajax请求,并获取请求结果中所有的图片链接*/
2 var getIAjaxUrlList=function(offset){
3 request.post("https://www.zhihu.com/node/Que ... 6quot;)
4 .set(config)
5 .send("method=next&params=%7B%22url_token%22%3A34937418%2C%22pagesize%22%3A20%2C%22offset%22%3A" +offset+ "%7D&_xsrf=98360a2df02783902146dee374772e51")
6 .end(function(err,res){
7 if(err){
8 console.log(err);
9 }else{
10 var response=JSON.parse(res.text);/*想用json的话对json序列化即可,提交json的话需要对json进行反序列化*/
11 if(response.msg&&response.msg.length){
12 var $=cheerio.load(response.msg.join(""));/*把所有的数组元素拼接在一起,以空白符分隔,不要这样join(),它会默认数组元素以逗号分隔*/
13 var answerList=$(".zm-item-answer");
14 answerList.map(function(i,answer){
15 var images=$(answer).find('.zm-item-rich-text img');
16 images.map(function(i,image){
17 photos.push($(image).attr("src"));
18 });
19 });
20 setTimeout(function(){
21 offset+=20;
22 console.log("已成功抓取"+photos.length+"张图片的链接");
23 getIAjaxUrlList(offset);
24 },100);
25 }else{
26 console.log("图片链接全部获取完毕,一共有"+photos.length+"条图片链接");
27 // console.log(photos);
28 return downloadImg(50);
29 }
30 }
31 });
32 }
  在代码中post这条请求https://www.zhihu.com/node/QuestionAnswerListV2,把原请求头和请求参数复制下来,作为我们的请求头和请求参数,superagent的set方法可用来设置请求头,send方法可以用来发送请求参数。我们把请求参数中的offset初始为20,每隔一定时间offset再加20,再重新发送请求,这样就相当于我们每隔一定时间发送了一条ajax请求,获取到最新的20条数据,每获取到了数据,我们再对这些数据进行一定的处理,让它们变成一整段的html,便于后面的提取链接处理。
  
异步并发控制下载图片
再获取完了所有的图片链接之后,即判定response.msg为空时,我们就要对这些图片进行下载了,不可能一条一条下对不对,因为如你所看到的,我们的图片足足有

  没错,2万多,还好nodejs有神奇的单线程异步功能,我们可以同时下载这些图片。但是这个时候问题就来了。听说如果同时发送太多请求,会被网站拦截!所以我们绝对不能同时下载超过 20,000 张图片。这时候就需要控制异步并发的数量。
  这里使用了一个神奇的模块=&gt; async,它不仅可以帮助我们询问难以维护的回调金字塔恶魔,还可以轻松帮助我们管理异步进程。具体看文档,这里只使用了一个强大的 async.mapLimit 方法。
<p> 1 var requestAndwrite=function(url,callback){
2 request.get(url).end(function(err,res){
3 if(err){
4 console.log(err);
5 console.log("有一张图片请求失败啦...");
6 }else{
7 var fileName=path.basename(url);
8 fs.writeFile("./img1/"+fileName,res.body,function(err){
9 if(err){
10 console.log(err);
11 console.log("有一张图片写入失败啦...");
12 }else{
13 console.log("图片下载成功啦");
14 callback(null,"successful !");
15 /*callback貌似必须调用,第二个参数将传给下一个回调函数的result,result是一个数组*/
16 }
17 });
18 }
19 });
20 }
21
22 var downloadImg=function(asyncNum){
23 /*有一些图片链接地址不完整没有“http:”头部,帮它们拼接完整*/
24 for(var i=0;i

nodejs抓取动态网页(登录页面与主页面的判断,下有路由独享钩子就像说的一样 )

网站优化优采云 发表了文章 • 0 个评论 • 63 次浏览 • 2021-10-21 07:01 • 来自相关话题

  nodejs抓取动态网页(登录页面与主页面的判断,下有路由独享钩子就像说的一样
)
  说明:在开发过程中,我们经常会遇到进入登录页面和主页面的判断。通常后台会发回一个session来判断。现在考虑有多少种方法可以达到这种效果;
  1. 之前使用的方法是在app.vue入口文件中直接判断是跳转到登录页面还是主页面。
  优点:简单明了,直接根据是否有session来判断入口文件是登录还是主页面;
  缺点:体验不好,每次判断前都会有登录页面,然后跳转到主页面,
  2. 使用动态路由判断用户登录是跳转到登录页面还是主页面,判断管理员权限,判断页面是否存在,没有跳转到404页面。
  优点:可以应用于多个时钟,体验好,
  添加 main.js 或 router.js
  router.beforeEach((to, from, next) => {
console.log(store.state.token)
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
const route = [&#39;index&#39;, &#39;list&#39;];
let isLogin = store.state.token; // 是否登录
// 未登录状态;当路由到route指定页时,跳转至login
if (route.indexOf(to.name) >= 0) {
if (isLogin == null) {
router.push({ path: &#39;/login&#39;, });
}
}
// 已登录状态;当路由到login时,跳转至home
console.log(to.name)
localStorage.setItem(&#39;routerName&#39;, to.name)
if (to.name === &#39;login&#39;) {
if (isLogin != null) {
router.push({ path: &#39;/HomeMain&#39;, });;
}
}
next();
});
  下面是路由的钩子函数:
  路由钩子主要是为用户定义路由变化时进行一些特殊处理
  一般来说,vue 中提供了三种类型的钩子
  1、全局钩子
  2、一个路由专用的钩子
  3、组件内钩子
  1.全局钩子
  主要包括beforeEach和aftrEach,
  beforeEach 函数有三个参数:
  to:路由器即将进入的路由对象
  from:当前导航即将离开的路线
  next: 函数,管道中的一个钩子。如果执行完成,则确认导航状态;否则为假,导航终止。
  afterEach 函数不需要传递 next() 函数
  这种钩子主要是全局使用的,一般用来判断权限和页面丢失时需要执行的操作,比如上面的
  router.beforeEach((to, from, next) => {
console.log(store.state.token)
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
const route = [&#39;index&#39;, &#39;list&#39;];
let isLogin = store.state.token; // 是否登录
// 未登录状态;当路由到route指定页时,跳转至login
if (route.indexOf(to.name) >= 0) {
if (isLogin == null) {
router.push({ path: &#39;/login&#39;, });
}
}
// 已登录状态;当路由到login时,跳转至home
console.log(to.name)
localStorage.setItem(&#39;routerName&#39;, to.name)
if (to.name === &#39;login&#39;) {
if (isLogin != null) {
router.push({ path: &#39;/HomeMain&#39;, });;
}
}
next();
});
  2.一个路由专属钩子
  如上所述,某个路由的单次使用与后续组件中的钩子本质上是相同的。都是指特定的路线。不同的是,这里的一般定义是在路由器中,而不是在组件中。如下
   {
path: &#39;/dashboard&#39;,
component: resolve => require([&#39;../components/page/Dashboard.vue&#39;], resolve),
meta: { title: &#39;系统首页&#39; },
beforeEnter: (to, from, next) => {

},
beforeLeave: (to, from, next) => {

}

},
  3.组件路由
  主要包括beforeRouteEnter和beforeRouteUpdate,beforeRouteLeave。这些钩子写在组件中,也可以传递三个参数(to、from、next),它们的功能和前面的类似。
  beforeRouteLeave(to, from, next) {
// ....
next()
},
beforeRouteEnter(to, from, next) {
// ....
next()
},
beforeRouteUpdate(to, from, next) {
// ....
next()
},
computed: {},
method: {}
  最后看官网介绍
  to:Route:即将进入的目标路由对象
  from:Route:当前导航即将离开的路线
  next: 功能:必须调用这个方法来解析这个钩子。执行效果取决于next方法的调用参数。
  next():转到管道中的下一个钩子。如果所有钩子都执行完毕,则确认导航的状态。
  next(false):中断当前导航。如果浏览器的 URL 发生变化(可能是用户手动或浏览器后退按钮),URL 地址将重置为 from 路由对应的地址。
  next('/') 或 next({ path:'/' }):跳转到不同的地址。当前导航被中断,然后执行新的导航。
  最后一点,关于页面不存在,跳转到404页面
  由于路由器本身是从上到下匹配的,如果在前面找到匹配的路由,就会跳转。所以可以直接在最后加上404路由,如下
  let routerConfig = [{
path: &#39;/pages&#39;,
component: App,
children: [{
path: &#39;demo/step1/list&#39;,
component: coupon,
name: &#39;coupon-list&#39;,
meta: {
title: &#39;红包&#39;
}
}]
}, {
path: &#39;*&#39;,
component: NotFound,
name: &#39;notfound&#39;,
meta: {
title: &#39;404-页面丢了&#39;
}
}] 查看全部

  nodejs抓取动态网页(登录页面与主页面的判断,下有路由独享钩子就像说的一样
)
  说明:在开发过程中,我们经常会遇到进入登录页面和主页面的判断。通常后台会发回一个session来判断。现在考虑有多少种方法可以达到这种效果;
  1. 之前使用的方法是在app.vue入口文件中直接判断是跳转到登录页面还是主页面。
  优点:简单明了,直接根据是否有session来判断入口文件是登录还是主页面;
  缺点:体验不好,每次判断前都会有登录页面,然后跳转到主页面,
  2. 使用动态路由判断用户登录是跳转到登录页面还是主页面,判断管理员权限,判断页面是否存在,没有跳转到404页面。
  优点:可以应用于多个时钟,体验好,
  添加 main.js 或 router.js
  router.beforeEach((to, from, next) => {
console.log(store.state.token)
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
const route = [&#39;index&#39;, &#39;list&#39;];
let isLogin = store.state.token; // 是否登录
// 未登录状态;当路由到route指定页时,跳转至login
if (route.indexOf(to.name) >= 0) {
if (isLogin == null) {
router.push({ path: &#39;/login&#39;, });
}
}
// 已登录状态;当路由到login时,跳转至home
console.log(to.name)
localStorage.setItem(&#39;routerName&#39;, to.name)
if (to.name === &#39;login&#39;) {
if (isLogin != null) {
router.push({ path: &#39;/HomeMain&#39;, });;
}
}
next();
});
  下面是路由的钩子函数:
  路由钩子主要是为用户定义路由变化时进行一些特殊处理
  一般来说,vue 中提供了三种类型的钩子
  1、全局钩子
  2、一个路由专用的钩子
  3、组件内钩子
  1.全局钩子
  主要包括beforeEach和aftrEach,
  beforeEach 函数有三个参数:
  to:路由器即将进入的路由对象
  from:当前导航即将离开的路线
  next: 函数,管道中的一个钩子。如果执行完成,则确认导航状态;否则为假,导航终止。
  afterEach 函数不需要传递 next() 函数
  这种钩子主要是全局使用的,一般用来判断权限和页面丢失时需要执行的操作,比如上面的
  router.beforeEach((to, from, next) => {
console.log(store.state.token)
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
const route = [&#39;index&#39;, &#39;list&#39;];
let isLogin = store.state.token; // 是否登录
// 未登录状态;当路由到route指定页时,跳转至login
if (route.indexOf(to.name) >= 0) {
if (isLogin == null) {
router.push({ path: &#39;/login&#39;, });
}
}
// 已登录状态;当路由到login时,跳转至home
console.log(to.name)
localStorage.setItem(&#39;routerName&#39;, to.name)
if (to.name === &#39;login&#39;) {
if (isLogin != null) {
router.push({ path: &#39;/HomeMain&#39;, });;
}
}
next();
});
  2.一个路由专属钩子
  如上所述,某个路由的单次使用与后续组件中的钩子本质上是相同的。都是指特定的路线。不同的是,这里的一般定义是在路由器中,而不是在组件中。如下
   {
path: &#39;/dashboard&#39;,
component: resolve => require([&#39;../components/page/Dashboard.vue&#39;], resolve),
meta: { title: &#39;系统首页&#39; },
beforeEnter: (to, from, next) => {

},
beforeLeave: (to, from, next) => {

}

},
  3.组件路由
  主要包括beforeRouteEnter和beforeRouteUpdate,beforeRouteLeave。这些钩子写在组件中,也可以传递三个参数(to、from、next),它们的功能和前面的类似。
  beforeRouteLeave(to, from, next) {
// ....
next()
},
beforeRouteEnter(to, from, next) {
// ....
next()
},
beforeRouteUpdate(to, from, next) {
// ....
next()
},
computed: {},
method: {}
  最后看官网介绍
  to:Route:即将进入的目标路由对象
  from:Route:当前导航即将离开的路线
  next: 功能:必须调用这个方法来解析这个钩子。执行效果取决于next方法的调用参数。
  next():转到管道中的下一个钩子。如果所有钩子都执行完毕,则确认导航的状态。
  next(false):中断当前导航。如果浏览器的 URL 发生变化(可能是用户手动或浏览器后退按钮),URL 地址将重置为 from 路由对应的地址。
  next('/') 或 next({ path:'/' }):跳转到不同的地址。当前导航被中断,然后执行新的导航。
  最后一点,关于页面不存在,跳转到404页面
  由于路由器本身是从上到下匹配的,如果在前面找到匹配的路由,就会跳转。所以可以直接在最后加上404路由,如下
  let routerConfig = [{
path: &#39;/pages&#39;,
component: App,
children: [{
path: &#39;demo/step1/list&#39;,
component: coupon,
name: &#39;coupon-list&#39;,
meta: {
title: &#39;红包&#39;
}
}]
}, {
path: &#39;*&#39;,
component: NotFound,
name: &#39;notfound&#39;,
meta: {
title: &#39;404-页面丢了&#39;
}
}]

nodejs抓取动态网页(需要的jar包代码如下:链接不支持xpath解析)

网站优化优采云 发表了文章 • 0 个评论 • 167 次浏览 • 2021-10-19 13:08 • 来自相关话题

  nodejs抓取动态网页(需要的jar包代码如下:链接不支持xpath解析)
  需要的jar包:
  1
2 org.jsoup
3 jsoup
4 1.10.3
5
  代码如下:
   1 // 请求超时时间,30秒
2 public static final int TIME_OUT = 30*1000;
3 // 模拟浏览器请求头信息
4 public static Map headers = new HashMap();
5 static{
6 headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0");
7 headers.put("Accept", "text/html");
8 headers.put("Accept-Language", "zh-CN,zh");
9 }
10
11 //根据url获取html文档
12 protected Document getDoc(String url) throws IOException{
13 if(logger.isDebugEnabled())
14 logger.debug(url);
15 //新建一个连接
16 Connection conn = Jsoup.connect(url).timeout(TIME_OUT);
17 conn = conn.headers(headers);
18 conn = conn.proxy(Proxy.NO_PROXY);
19 Document doc = conn.get();
20
21 if(logger.isTraceEnabled()){
22 logger.trace("["+url+"]\n"+doc);
23 }
24 return doc;
25 }
   1 public static final String CHINAZ_ICP_URL = "http://icp.chinaz.com/?type=host&s=%s";
2 public List doHandler(String domain) {
3 List results = new ArrayList();
4 String url = String.format(CHINAZ_ICP_URL, domain);
5 Document doc;
6 try {
7 doc = this.getDoc(url);
8 // 获取当前页ICP信息所在标签
9 Elements eles = doc.select("ul.IcpMain01>li:lt(7)>p");
10
11 if(null == eles || eles.isEmpty()){
12 return results;
13 }
14 //获取ICP信息
15 for (Element element : eles) {
16 //当前元素为认证信息时,跳过
17 if("safe".equals(element.attr("id"))){
18 continue;
19 }
20 Node firstNode = element.childNode(0);
21 if(firstNode.childNodeSize() > 0){
22 results.add(element.child(0).text());
23 }else{
24 results.add(((TextNode)firstNode).text());
25 }
26 }
27 } catch (IOException e) {
28 logger.error("get Chinaz ICP message error :",e);
29 }
30 doc = null;
31 return results;
32 }
  参考 Jsoup 的文档:链接
  Jsoup不支持xpath解析,这个很痛苦,不过总得有人做个支持xpath的东西---JsoupXpath,链接,有兴趣的网友可以自己试试!
  三、htmlunit
  支持Xpath分析,可以模拟浏览器动作,比如点击下一页、加载更多等等。文档链接:
  需要的jar包
  1
2 net.sourceforge.htmlunit
3 htmlunit
4 2.18
5
  代码如下:
   1 import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
2 import com.gargoylesoftware.htmlunit.TopLevelWindow;
3 import com.gargoylesoftware.htmlunit.WebClient;
4 import com.gargoylesoftware.htmlunit.html.HtmlPage;
5 import com.gargoylesoftware.htmlunit.html.HtmlTableRow;
6
7 import java.io.IOException;
8 import java.util.ArrayList;
9 import java.util.List;
10
11
12 public class UrlTest {
13
14 public static void main(String[] args) {
15 BaseCollector baseCollector = new BaseCollector();
16 WebClient webClient = baseCollector.getWebClient();
17 String url="http://htmlunit.sourceforge.net/";
18 HtmlPage homePage= null;
19 try {
20 homePage = webClient.getPage(url);
21 if (homePage != null && homePage instanceof HtmlPage) {
22 homePage.getEnclosingWindow().setName("IpHomePage");
23 System.out.println("打开 IPHomePage ");
24 System.out.println("内容是: "+homePage.getBody().getTextContent());
25
26 List htmlTableRows = (List) homePage.getByXPath("/html/body/pre");
27 if (htmlTableRows != null && htmlTableRows.size() > 0) {
28 for (int i = 0; i < htmlTableRows.size(); i++) {
29 HtmlTableRow htmlTableRow = htmlTableRows.get(i);
30 //日期
31 String firstTime = htmlTableRow.getCell(0).getTextContent().trim();
32 System.out.println(firstTime);
33 }
34
35 }
36 closeWindowByName(webClient, "IPHomePage");
37 System.out.println("关闭 IPHomePage ");
38 }
39 webClient.close();
40
41 } catch (IOException e) {
42 System.out.println(e.getMessage()+" ===="+e);
43 }catch (FailingHttpStatusCodeException e){
44 System.out.println(e.getMessage()+" ===="+e);
45 }
46 System.out.println("内容是: "+homePage.getBody().getTextContent());
47 }
48
49 public static void closeWindowByName(WebClient webClient, String name){
50 List list = webClient.getTopLevelWindows();
51 List windowNames = new ArrayList();
52 for (int j = 0; j < list.size(); j++) {
53 if(list.get(j).getName().equals(name)){
54 list.get(j).close();
55 }
56 windowNames.add(list.get(j).getName());
57 }
58 System.out.println("当前窗口 : {}"+list.toString());
59 }
60 }
61
62
  四、HeadlessChrome1,HeadlessChrome 和 PhantomJS 对比
  在 Chrome 未提供原生 Headless 模式之前,Web 开发者可以使用 PhantomJS 等第三方 Headless 浏览器。既然官方已经准备好提供 Headless,PhantomJS 维护者 Vitaly Slobodin 立即在邮件列表中宣布辞职。另一个流行的浏览器 Firefox 也准备提供 Headless 模式。
  2、什么是 HeadlessChrome
  HeadlessChrome 是 Chrome 浏览器的非接口形式。您可以使用 Chrome 支持的所有功能来运行您的程序,而无需打开浏览器。与现代浏览器相比,HeadlessChrome 可以更方便地测试 Web 应用程序、获取 网站 的截图、做爬虫抓取信息等。
  3、环境配置
  首先需要下载chrome-driver,不同版本的Chrome对应不同的Chrome-driver,可以通过这个链接下载对应的Chrome-driver
  支持各种元素的获取,List elements = driver.findElements(By.xpath("///*[@id=\"body\"]/ul[2]/li"));
  可以模拟浏览器的各种动作,driver.findElement(By.linkText("Next page")).click();
  用Python搞HeadlessChrome更方便简单,简直太神奇了。 . . . 链接:
  可以参考
  需要的jar包:
  1
2 org.seleniumhq.selenium
3 selenium-chrome-driver
4 3.11.0
5
  代码如下:
<p> 1 import org.jsoup.Jsoup;
2 import org.jsoup.nodes.Document;
3 import org.openqa.selenium.By;
4 import org.openqa.selenium.WebDriver;
5 import org.openqa.selenium.WebElement;
6 import org.openqa.selenium.chrome.ChromeDriver;
7 import org.openqa.selenium.chrome.ChromeOptions;
8
9 import java.util.List;
10 import java.util.concurrent.TimeUnit;
11
12 /**
13 * Created by sqy on 2018/5/2.
14 */
15 public class HeadlessChromeTest {
16
17 public static void main(String args[]) {
18
19
20
21 //G:\chromedriver
22 System.setProperty("webdriver.chrome.driver","G:\\chromedriver\\chromedriver.exe");
23 ChromeOptions chromeOptions = new ChromeOptions();
24 // 设置为 headless 模式 (必须)
25 chromeOptions.addArguments("--headless");
26 // 设置浏览器窗口打开大小 (非必须)
27 chromeOptions.addArguments("--window-size=1920,1080");
28 WebDriver driver = new ChromeDriver(chromeOptions);
29 driver.get("https://lvyou.baidu.com/scene/s-feb/");
30
31 System.out.println("url: "+driver.getCurrentUrl());
32
33 for(int i=0;i 查看全部

  nodejs抓取动态网页(需要的jar包代码如下:链接不支持xpath解析)
  需要的jar包:
  1
2 org.jsoup
3 jsoup
4 1.10.3
5
  代码如下:
   1 // 请求超时时间,30秒
2 public static final int TIME_OUT = 30*1000;
3 // 模拟浏览器请求头信息
4 public static Map headers = new HashMap();
5 static{
6 headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:55.0) Gecko/20100101 Firefox/55.0");
7 headers.put("Accept", "text/html");
8 headers.put("Accept-Language", "zh-CN,zh");
9 }
10
11 //根据url获取html文档
12 protected Document getDoc(String url) throws IOException{
13 if(logger.isDebugEnabled())
14 logger.debug(url);
15 //新建一个连接
16 Connection conn = Jsoup.connect(url).timeout(TIME_OUT);
17 conn = conn.headers(headers);
18 conn = conn.proxy(Proxy.NO_PROXY);
19 Document doc = conn.get();
20
21 if(logger.isTraceEnabled()){
22 logger.trace("["+url+"]\n"+doc);
23 }
24 return doc;
25 }
   1 public static final String CHINAZ_ICP_URL = "http://icp.chinaz.com/?type=host&s=%s";
2 public List doHandler(String domain) {
3 List results = new ArrayList();
4 String url = String.format(CHINAZ_ICP_URL, domain);
5 Document doc;
6 try {
7 doc = this.getDoc(url);
8 // 获取当前页ICP信息所在标签
9 Elements eles = doc.select("ul.IcpMain01>li:lt(7)>p");
10
11 if(null == eles || eles.isEmpty()){
12 return results;
13 }
14 //获取ICP信息
15 for (Element element : eles) {
16 //当前元素为认证信息时,跳过
17 if("safe".equals(element.attr("id"))){
18 continue;
19 }
20 Node firstNode = element.childNode(0);
21 if(firstNode.childNodeSize() > 0){
22 results.add(element.child(0).text());
23 }else{
24 results.add(((TextNode)firstNode).text());
25 }
26 }
27 } catch (IOException e) {
28 logger.error("get Chinaz ICP message error :",e);
29 }
30 doc = null;
31 return results;
32 }
  参考 Jsoup 的文档:链接
  Jsoup不支持xpath解析,这个很痛苦,不过总得有人做个支持xpath的东西---JsoupXpath,链接,有兴趣的网友可以自己试试!
  三、htmlunit
  支持Xpath分析,可以模拟浏览器动作,比如点击下一页、加载更多等等。文档链接:
  需要的jar包
  1
2 net.sourceforge.htmlunit
3 htmlunit
4 2.18
5
  代码如下:
   1 import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
2 import com.gargoylesoftware.htmlunit.TopLevelWindow;
3 import com.gargoylesoftware.htmlunit.WebClient;
4 import com.gargoylesoftware.htmlunit.html.HtmlPage;
5 import com.gargoylesoftware.htmlunit.html.HtmlTableRow;
6
7 import java.io.IOException;
8 import java.util.ArrayList;
9 import java.util.List;
10
11
12 public class UrlTest {
13
14 public static void main(String[] args) {
15 BaseCollector baseCollector = new BaseCollector();
16 WebClient webClient = baseCollector.getWebClient();
17 String url="http://htmlunit.sourceforge.net/";
18 HtmlPage homePage= null;
19 try {
20 homePage = webClient.getPage(url);
21 if (homePage != null && homePage instanceof HtmlPage) {
22 homePage.getEnclosingWindow().setName("IpHomePage");
23 System.out.println("打开 IPHomePage ");
24 System.out.println("内容是: "+homePage.getBody().getTextContent());
25
26 List htmlTableRows = (List) homePage.getByXPath("/html/body/pre");
27 if (htmlTableRows != null && htmlTableRows.size() > 0) {
28 for (int i = 0; i < htmlTableRows.size(); i++) {
29 HtmlTableRow htmlTableRow = htmlTableRows.get(i);
30 //日期
31 String firstTime = htmlTableRow.getCell(0).getTextContent().trim();
32 System.out.println(firstTime);
33 }
34
35 }
36 closeWindowByName(webClient, "IPHomePage");
37 System.out.println("关闭 IPHomePage ");
38 }
39 webClient.close();
40
41 } catch (IOException e) {
42 System.out.println(e.getMessage()+" ===="+e);
43 }catch (FailingHttpStatusCodeException e){
44 System.out.println(e.getMessage()+" ===="+e);
45 }
46 System.out.println("内容是: "+homePage.getBody().getTextContent());
47 }
48
49 public static void closeWindowByName(WebClient webClient, String name){
50 List list = webClient.getTopLevelWindows();
51 List windowNames = new ArrayList();
52 for (int j = 0; j < list.size(); j++) {
53 if(list.get(j).getName().equals(name)){
54 list.get(j).close();
55 }
56 windowNames.add(list.get(j).getName());
57 }
58 System.out.println("当前窗口 : {}"+list.toString());
59 }
60 }
61
62
  四、HeadlessChrome1,HeadlessChrome 和 PhantomJS 对比
  在 Chrome 未提供原生 Headless 模式之前,Web 开发者可以使用 PhantomJS 等第三方 Headless 浏览器。既然官方已经准备好提供 Headless,PhantomJS 维护者 Vitaly Slobodin 立即在邮件列表中宣布辞职。另一个流行的浏览器 Firefox 也准备提供 Headless 模式。
  2、什么是 HeadlessChrome
  HeadlessChrome 是 Chrome 浏览器的非接口形式。您可以使用 Chrome 支持的所有功能来运行您的程序,而无需打开浏览器。与现代浏览器相比,HeadlessChrome 可以更方便地测试 Web 应用程序、获取 网站 的截图、做爬虫抓取信息等。
  3、环境配置
  首先需要下载chrome-driver,不同版本的Chrome对应不同的Chrome-driver,可以通过这个链接下载对应的Chrome-driver
  支持各种元素的获取,List elements = driver.findElements(By.xpath("///*[@id=\"body\"]/ul[2]/li"));
  可以模拟浏览器的各种动作,driver.findElement(By.linkText("Next page")).click();
  用Python搞HeadlessChrome更方便简单,简直太神奇了。 . . . 链接:
  可以参考
  需要的jar包:
  1
2 org.seleniumhq.selenium
3 selenium-chrome-driver
4 3.11.0
5
  代码如下:
<p> 1 import org.jsoup.Jsoup;
2 import org.jsoup.nodes.Document;
3 import org.openqa.selenium.By;
4 import org.openqa.selenium.WebDriver;
5 import org.openqa.selenium.WebElement;
6 import org.openqa.selenium.chrome.ChromeDriver;
7 import org.openqa.selenium.chrome.ChromeOptions;
8
9 import java.util.List;
10 import java.util.concurrent.TimeUnit;
11
12 /**
13 * Created by sqy on 2018/5/2.
14 */
15 public class HeadlessChromeTest {
16
17 public static void main(String args[]) {
18
19
20
21 //G:\chromedriver
22 System.setProperty("webdriver.chrome.driver","G:\\chromedriver\\chromedriver.exe");
23 ChromeOptions chromeOptions = new ChromeOptions();
24 // 设置为 headless 模式 (必须)
25 chromeOptions.addArguments("--headless");
26 // 设置浏览器窗口打开大小 (非必须)
27 chromeOptions.addArguments("--window-size=1920,1080");
28 WebDriver driver = new ChromeDriver(chromeOptions);
29 driver.get("https://lvyou.baidu.com/scene/s-feb/";);
30
31 System.out.println("url: "+driver.getCurrentUrl());
32
33 for(int i=0;i

nodejs抓取动态网页(puppeteer和浏览器的区别,安装注意先安装nodejs.eachSeries)

网站优化优采云 发表了文章 • 0 个评论 • 258 次浏览 • 2021-10-18 02:00 • 来自相关话题

  nodejs抓取动态网页(puppeteer和浏览器的区别,安装注意先安装nodejs.eachSeries)
  傀儡师
  google chrome 团队制作的 puppeteer 是一个自动化测试库,依赖于 nodejs 和chromium。它最大的优点是可以处理网页中的动态内容,比如JavaScript,可以更好的模拟用户。
  一些网站反爬虫方法在某些javascript/ajax请求中隐藏了部分内容,使得直接获取a标签的方法不起作用。甚至有些网站会设置隐藏元素“陷阱”,用户不可见,脚本触发器被认为是机器。在这种情况下,Puppeteer 的优势就凸显出来了。
  它可以实现以下功能:
  生成页面的屏幕截图和 PDF。获取 SPA 并生成预渲染内容(即“SSR”)。自动表单提交、UI 测试、键盘输入等。创建最新的自动化测试环境。使用最新的 JavaScript 和浏览器功能直接在最新版本的 Chrome 中运行测试。捕获并跟踪您的时间线 网站 以帮助诊断性能问题。
  开源地址:[][1]
  安装
  npm i puppeteer
  注意先安装nodejs,在nodejs文件的根目录下执行(npm文件同级)。
  安装过程中会下载Chromium,大约120M。
  花了两天时间(约10小时)探索绕过了相当多的异步坑。作者对puppeteer和nodejs有一定的掌握。
  一张长图,抢博客文章列表:
  
  抢博客文章
  以csdn博客为例,文章的内容需要点击“阅读全文”才能获取,导致只能读取dom的脚本失败。
  /**
* load blog.csdn.net article to local files
**/
const puppeteer = require('puppeteer');
//emulate iphone
const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1';
const workPath = './contents';
const fs = require("fs");
if (!fs.existsSync(workPath)) {
fs.mkdirSync(workPath)
}
//base url
const rootUrl = 'https://blog.csdn.net/';
//max wait milliseconds
const maxWait = 100;
//max loop scroll times
const makLoop = 10;
(async () => {
let url;
let countUrl=0;
const browser = await puppeteer.launch({headless: false});//set headless: true will hide chromium UI
const page = await browser.newPage();
await page.setUserAgent(userAgent);
await page.setViewport({width:414, height:736});
await page.setRequestInterception(true);
//filter to block images
page.on('request', request => {
if (request.resourceType() === 'image')
request.abort();
else
request.continue();
});
await page.goto(rootUrl);

for(let i= 0; iwindow.scrollTo(0, document.body.scrollHeight));
await page.waitForNavigation({timeout:maxWait,waitUntil: ['networkidle0']});
}catch(err){
console.log('scroll to bottom and then wait '+maxWait+'ms.');
}
}
await page.screenshot({path: workPath+'/screenshot.png',fullPage: true, quality :100, type :'jpeg'});
//#feedlist_id li[data-type="blog"] a
const sel = '#feedlist_id li[data-type="blog"] h2 a';
const hrefs = await page.evaluate((sel) => {
let elements = Array.from(document.querySelectorAll(sel));
let links = elements.map(element => {
return element.href
})
return links;
}, sel);
console.log('total links: '+hrefs.length);
process();
async function process(){
if(countUrl {
if (request.resourceType() === 'image')
request.abort();
else
request.continue();
});
await tab.goto(url);
//execute tap request
try{
await tab.tap('.read_more_btn');
}catch(err){
console.log('there\'s none read more button. No need to TAP');
}
let title = await tab.evaluate(() => document.querySelector('#article .article_title').innerText);
let contents = await tab.evaluate(() => document.querySelector('#article .article_content').innerText);
contents = 'TITLE: '+title+'\nURL: '+url+'\nCONTENTS: \n'+contents;
const fs = require("fs");
fs.writeFileSync(workPath+'/'+tab.url().substring(tab.url().lastIndexOf('/'),tab.url().length)+'.txt',contents);
console.log(title + " has been downloaded to local.");
await tab.close();
}catch(err){
console.log('url: '+tab.url()+' \n'+err.toString());
}finally{
process();
}

}
})();
  实施过程
  录屏可以在我的公众号查看。下面是一个屏幕截图:
  
  结果
  文章内容列表:
  
  文章内容:
  
  结束语
  我以为由于nodejs使用JavaScript脚本语言,它肯定可以处理网页的JavaScript内容,但我还没有找到合适/高效的库。直到找到木偶师,我才下定决心试水。
  话虽如此,nodejs的异步性确实让人头疼。我已经在 10 个小时内抛出了大约数百行代码。
  您可以扩展代码中的 process() 方法以使用 async.eachSeries。我使用的递归方法不是最佳解决方案。
  其实一一处理是没有效率的。本来我写了一个异步的方法来关闭浏览器:
<p>let tryCloseBrowser = setInterval(function(){
console.log("check if any process running...")
if(countDown 查看全部

  nodejs抓取动态网页(puppeteer和浏览器的区别,安装注意先安装nodejs.eachSeries)
  傀儡师
  google chrome 团队制作的 puppeteer 是一个自动化测试库,依赖于 nodejs 和chromium。它最大的优点是可以处理网页中的动态内容,比如JavaScript,可以更好的模拟用户。
  一些网站反爬虫方法在某些javascript/ajax请求中隐藏了部分内容,使得直接获取a标签的方法不起作用。甚至有些网站会设置隐藏元素“陷阱”,用户不可见,脚本触发器被认为是机器。在这种情况下,Puppeteer 的优势就凸显出来了。
  它可以实现以下功能:
  生成页面的屏幕截图和 PDF。获取 SPA 并生成预渲染内容(即“SSR”)。自动表单提交、UI 测试、键盘输入等。创建最新的自动化测试环境。使用最新的 JavaScript 和浏览器功能直接在最新版本的 Chrome 中运行测试。捕获并跟踪您的时间线 网站 以帮助诊断性能问题。
  开源地址:[][1]
  安装
  npm i puppeteer
  注意先安装nodejs,在nodejs文件的根目录下执行(npm文件同级)。
  安装过程中会下载Chromium,大约120M。
  花了两天时间(约10小时)探索绕过了相当多的异步坑。作者对puppeteer和nodejs有一定的掌握。
  一张长图,抢博客文章列表:
  
  抢博客文章
  以csdn博客为例,文章的内容需要点击“阅读全文”才能获取,导致只能读取dom的脚本失败。
  /**
* load blog.csdn.net article to local files
**/
const puppeteer = require('puppeteer');
//emulate iphone
const userAgent = 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1';
const workPath = './contents';
const fs = require("fs");
if (!fs.existsSync(workPath)) {
fs.mkdirSync(workPath)
}
//base url
const rootUrl = 'https://blog.csdn.net/';
//max wait milliseconds
const maxWait = 100;
//max loop scroll times
const makLoop = 10;
(async () => {
let url;
let countUrl=0;
const browser = await puppeteer.launch({headless: false});//set headless: true will hide chromium UI
const page = await browser.newPage();
await page.setUserAgent(userAgent);
await page.setViewport({width:414, height:736});
await page.setRequestInterception(true);
//filter to block images
page.on('request', request => {
if (request.resourceType() === 'image')
request.abort();
else
request.continue();
});
await page.goto(rootUrl);

for(let i= 0; iwindow.scrollTo(0, document.body.scrollHeight));
await page.waitForNavigation({timeout:maxWait,waitUntil: ['networkidle0']});
}catch(err){
console.log('scroll to bottom and then wait '+maxWait+'ms.');
}
}
await page.screenshot({path: workPath+'/screenshot.png',fullPage: true, quality :100, type :'jpeg'});
//#feedlist_id li[data-type="blog"] a
const sel = '#feedlist_id li[data-type="blog"] h2 a';
const hrefs = await page.evaluate((sel) => {
let elements = Array.from(document.querySelectorAll(sel));
let links = elements.map(element => {
return element.href
})
return links;
}, sel);
console.log('total links: '+hrefs.length);
process();
async function process(){
if(countUrl {
if (request.resourceType() === 'image')
request.abort();
else
request.continue();
});
await tab.goto(url);
//execute tap request
try{
await tab.tap('.read_more_btn');
}catch(err){
console.log('there\'s none read more button. No need to TAP');
}
let title = await tab.evaluate(() => document.querySelector('#article .article_title').innerText);
let contents = await tab.evaluate(() => document.querySelector('#article .article_content').innerText);
contents = 'TITLE: '+title+'\nURL: '+url+'\nCONTENTS: \n'+contents;
const fs = require("fs");
fs.writeFileSync(workPath+'/'+tab.url().substring(tab.url().lastIndexOf('/'),tab.url().length)+'.txt',contents);
console.log(title + " has been downloaded to local.");
await tab.close();
}catch(err){
console.log('url: '+tab.url()+' \n'+err.toString());
}finally{
process();
}

}
})();
  实施过程
  录屏可以在我的公众号查看。下面是一个屏幕截图:
  
  结果
  文章内容列表:
  
  文章内容:
  
  结束语
  我以为由于nodejs使用JavaScript脚本语言,它肯定可以处理网页的JavaScript内容,但我还没有找到合适/高效的库。直到找到木偶师,我才下定决心试水。
  话虽如此,nodejs的异步性确实让人头疼。我已经在 10 个小时内抛出了大约数百行代码。
  您可以扩展代码中的 process() 方法以使用 async.eachSeries。我使用的递归方法不是最佳解决方案。
  其实一一处理是没有效率的。本来我写了一个异步的方法来关闭浏览器:
<p>let tryCloseBrowser = setInterval(function(){
console.log("check if any process running...")
if(countDown

nodejs抓取动态网页(.jscvsresovlercvsresovler2 )

网站优化优采云 发表了文章 • 0 个评论 • 89 次浏览 • 2021-10-17 05:10 • 来自相关话题

  nodejs抓取动态网页(.jscvsresovlercvsresovler2
)
  列:·
  介绍本文文章主要介绍Nodejs中的puppeteer抓取浏览器HAR数据(示例代码)及相关经验技巧,文章约4481字,浏览量157,点赞数2,值得一看参考!
  有这样的要求。首先从csv文件中读取要解析的url数据,然后使用puppeteer和puppeteer-har获取浏览器的HAR数据。在调试的过程中,发现for循环中如何操作是异步的,终于找到了解决办法,也记录在这里。
  har.js
  const puppeteer = require(‘puppeteer‘);
const PuppeteerHar = require(‘puppeteer-har‘);
/*
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const har = new PuppeteerHar(page);
await har.start({ path: ‘results.har‘ });
await page.goto(‘http://example.com‘);
await har.stop();
await browser.close();
})();
*/
async function saveHarlog(url,filename){
//启动浏览器实例 [puppeteer.createBrowserFetcher([options])]
let browser = await puppeteer.launch({
// 若是手动下载的chromium需要指定chromium地址, 默认引用地址为 /项目目录/node_modules/puppeteer/.local-chromium/
//executablePath: ‘/Users/huqiyang/Documents/project/z/chromium/Chromium.app/Contents/MacOS/Chromium‘,
//设置超时时间
timeout: 15000,
//如果是访问https页面 此属性会忽略https错误
ignoreHTTPSErrors: true,
// 关闭headless模式, 不会打开浏览器
headless: false,
args:["--disk-cache-size=0","--disable-cache",‘--disable-infobars‘],
//是否为每个选项卡自动打开DevTools面板。 如果此选项为true,则headless选项将设置为false。
devtools:false
});
//创建一个新页面
let page = await browser.newPage();
//Puppeteer 初始化的屏幕大小默认为 800px x 600px。但是这个尺寸可以通过 Page.setViewport() 设置。
await page.setViewport({
width: 800,
height: 600
});
let har = new PuppeteerHar(page);
await har.start({ path: (filename +‘.har‘) });
await page.goto(url);
// Get the "viewport" of the page, as reported by the page.
let dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
});
await har.stop();
await browser.close();
}
exports.saveHarlog = saveHarlog;
  cvsresovler.js
  const fs = require("fs");
const path = require("path");
const csv =require(‘csv‘);
const parse = require(‘csv-parse/lib/sync‘)
const iconv = require(‘iconv-lite‘);
/*
npm install iconv-lite
*/
function readUrlRecord(csvpath){
console.log(‘开始解析文件:‘ + csvpath) ;
//读取文件
const input = fs.readFileSync(csvpath,‘utf8‘) ;
/*
解析文件,生成JSON格式
{ ‘ ‘: ‘142‘,
AREA_NAME: ‘湖北‘,
SITE_LINK: ‘www.banggo.com‘,
BEARING_MODE: ‘移动接入‘,
SITE_NAME: ‘邦购‘,
MENU_TYPE: ‘二级‘ }
*/
const records = parse(input, {
columns: true,
skip_empty_lines: true,
delimiter: ‘,‘,
}) ;
return records ;
}
//readUrlRecord(‘../top300.csv‘) ;
exports.readUrlRecord = readUrlRecord;
  main.js
  const fs = require("fs");
const path = require("path");
const moment = require("moment");
const schedule = require(‘node-schedule‘);
const cvsresovler=require("./module/cvsresovler");
const mhar=require("./module/har");
/*
cnpm install --save moment
cnpm install --save csv
cnpm install --save node-schedule
cnpm install --save puppeteer
cnpm install --save puppeteer-har
cnpm install --save iconv-lite
*/
function init(){
console.log(‘初始化调度器‘) ;
//每分钟的第30秒定时执行一次:
schedule.scheduleJob(‘0 21 * * * *‘,()=>{
let ftime = moment().format(‘YYYYMMDDHHmm‘);
console.log(‘当前调度时间为:‘ + ftime) ;
let dirPath = path.join(__dirname,‘harlogs‘,ftime) ;
console.log("创建目录:" + dirPath) ;
let isExist = false ;
if(fs.existsSync(dirPath)){
//创建文件夹
let stat = fs.lstatSync(dirPath);
if(stats.isDirectory()){
isExist = false ;
}
}
if(!isExist){
//创建文件夹
console.log("创建文件夹" + ftime) ;
fs.mkdirSync(dirPath);
}
//开始解析需要处理的URL
let dataArr = cvsresovler.readUrlRecord(path.join(__dirname,‘top300.csv‘)) ;
console.log("解析出URL共计" + dataArr.length + "条") ;
/*
开始抓取HAR数据【同步的方式执行】。
注意:如果这里直接通过for循环遍历dataArr并调用saveHarlog方法,那么这将是一个异步的过程。
*/
(async function iterator(i){
let data = dataArr[i]
let url = data[‘SITE_LINK‘] ;
console.log("开始处理:" + url ) ;
await mhar.saveHarlog(‘http://‘ + url,path.join(dirPath,url.replace(‘/‘,"-"))) ;
if(i + 1 < dataArr.length){
iterator(i+1) ;
}
})(0) ;
});
console.log(‘应用程序启动完成‘) ;
}
//执行
init(); 查看全部

  nodejs抓取动态网页(.jscvsresovlercvsresovler2
)
  列:·
  介绍本文文章主要介绍Nodejs中的puppeteer抓取浏览器HAR数据(示例代码)及相关经验技巧,文章约4481字,浏览量157,点赞数2,值得一看参考!
  有这样的要求。首先从csv文件中读取要解析的url数据,然后使用puppeteer和puppeteer-har获取浏览器的HAR数据。在调试的过程中,发现for循环中如何操作是异步的,终于找到了解决办法,也记录在这里。
  har.js
  const puppeteer = require(‘puppeteer‘);
const PuppeteerHar = require(‘puppeteer-har‘);
/*
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const har = new PuppeteerHar(page);
await har.start({ path: ‘results.har‘ });
await page.goto(‘http://example.com‘);
await har.stop();
await browser.close();
})();
*/
async function saveHarlog(url,filename){
//启动浏览器实例 [puppeteer.createBrowserFetcher([options])]
let browser = await puppeteer.launch({
// 若是手动下载的chromium需要指定chromium地址, 默认引用地址为 /项目目录/node_modules/puppeteer/.local-chromium/
//executablePath: ‘/Users/huqiyang/Documents/project/z/chromium/Chromium.app/Contents/MacOS/Chromium‘,
//设置超时时间
timeout: 15000,
//如果是访问https页面 此属性会忽略https错误
ignoreHTTPSErrors: true,
// 关闭headless模式, 不会打开浏览器
headless: false,
args:["--disk-cache-size=0","--disable-cache",‘--disable-infobars‘],
//是否为每个选项卡自动打开DevTools面板。 如果此选项为true,则headless选项将设置为false。
devtools:false
});
//创建一个新页面
let page = await browser.newPage();
//Puppeteer 初始化的屏幕大小默认为 800px x 600px。但是这个尺寸可以通过 Page.setViewport() 设置。
await page.setViewport({
width: 800,
height: 600
});
let har = new PuppeteerHar(page);
await har.start({ path: (filename +‘.har‘) });
await page.goto(url);
// Get the "viewport" of the page, as reported by the page.
let dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
});
await har.stop();
await browser.close();
}
exports.saveHarlog = saveHarlog;
  cvsresovler.js
  const fs = require("fs");
const path = require("path");
const csv =require(‘csv‘);
const parse = require(‘csv-parse/lib/sync‘)
const iconv = require(‘iconv-lite‘);
/*
npm install iconv-lite
*/
function readUrlRecord(csvpath){
console.log(‘开始解析文件:‘ + csvpath) ;
//读取文件
const input = fs.readFileSync(csvpath,‘utf8‘) ;
/*
解析文件,生成JSON格式
{ ‘ ‘: ‘142‘,
AREA_NAME: ‘湖北‘,
SITE_LINK: ‘www.banggo.com‘,
BEARING_MODE: ‘移动接入‘,
SITE_NAME: ‘邦购‘,
MENU_TYPE: ‘二级‘ }
*/
const records = parse(input, {
columns: true,
skip_empty_lines: true,
delimiter: ‘,‘,
}) ;
return records ;
}
//readUrlRecord(‘../top300.csv‘) ;
exports.readUrlRecord = readUrlRecord;
  main.js
  const fs = require("fs");
const path = require("path");
const moment = require("moment");
const schedule = require(‘node-schedule‘);
const cvsresovler=require("./module/cvsresovler");
const mhar=require("./module/har");
/*
cnpm install --save moment
cnpm install --save csv
cnpm install --save node-schedule
cnpm install --save puppeteer
cnpm install --save puppeteer-har
cnpm install --save iconv-lite
*/
function init(){
console.log(‘初始化调度器‘) ;
//每分钟的第30秒定时执行一次:
schedule.scheduleJob(‘0 21 * * * *‘,()=>{
let ftime = moment().format(‘YYYYMMDDHHmm‘);
console.log(‘当前调度时间为:‘ + ftime) ;
let dirPath = path.join(__dirname,‘harlogs‘,ftime) ;
console.log("创建目录:" + dirPath) ;
let isExist = false ;
if(fs.existsSync(dirPath)){
//创建文件夹
let stat = fs.lstatSync(dirPath);
if(stats.isDirectory()){
isExist = false ;
}
}
if(!isExist){
//创建文件夹
console.log("创建文件夹" + ftime) ;
fs.mkdirSync(dirPath);
}
//开始解析需要处理的URL
let dataArr = cvsresovler.readUrlRecord(path.join(__dirname,‘top300.csv‘)) ;
console.log("解析出URL共计" + dataArr.length + "条") ;
/*
开始抓取HAR数据【同步的方式执行】。
注意:如果这里直接通过for循环遍历dataArr并调用saveHarlog方法,那么这将是一个异步的过程。
*/
(async function iterator(i){
let data = dataArr[i]
let url = data[‘SITE_LINK‘] ;
console.log("开始处理:" + url ) ;
await mhar.saveHarlog(‘http://‘ + url,path.join(dirPath,url.replace(‘/‘,"-"))) ;
if(i + 1 < dataArr.length){
iterator(i+1) ;
}
})(0) ;
});
console.log(‘应用程序启动完成‘) ;
}
//执行
init();

nodejs抓取动态网页(Rocket.Chat聊天程序的开发版本安装部署(图))

网站优化优采云 发表了文章 • 0 个评论 • 96 次浏览 • 2021-10-17 04:22 • 来自相关话题

  nodejs抓取动态网页(Rocket.Chat聊天程序的开发版本安装部署(图))
  Rocket.Chat聊天程序开发版安装部署,Rocket.Chat开发版安装部署
  你可以在 Linux 机器或 VM 上运行 Rocket.Chat 进行开发。以下说明已在新的 Ubuntu 18.04 LTS 安装上进行了测试。尝试查找并使用未安装其他不必要软件(而不是“桌面”或“客户端”)的新 Ubuntu 服务器安装。
  不要使用安装了 nodeJS 的系统以避免出现问题。
  在构建过程中,内存使用量将接近 8G,这是为开发工作站推荐的最低 RAM 级别。(如果你不做任何开发,只部署Rocket.Chat服务器——所需的RAM可能低至1G。)
  重要信息:请注意,基本操作系统上无需安装 mongo、nodejs 或 npm。如果您安装了其中任何一个;重新开始,或使用另一个 CLEAN 系统。
  一、玩转过程
  node的单线程只是js层面的单线程。它是基于 V8 引擎的单线程。因为V8,前后端js执行模型基本类似,但是node的核心机制还是通过libuv调用epoll或者epoll。IOCP 的多线程机制。换句话说,严格意义上的node并不是真正的单线程架构。节点内核本身有一定的IO线程和IO线程池。通过libuv调度,直接使用操作系统层面的多线程。Node开发者可以通过扩展c/c++模块直接操作多线程来提高效率。但是单线程的好处是程序状态单一,不存在锁、线程同步、线程上下文切换等问题。但是单线程程序并不完美。目前很多服务器都是多cpu和多cpu核的。一个节点实例只能使用一个 CPU 核。那么其他的cpu核就浪费了?而且,单线程的容错能力也很弱。一旦抛出未捕获的异常,必然会导致整个程序崩溃。这样的程序一定非常脆弱。这样的服务器端语言有什么价值?
  13 个中的 12 个
  nodejs模块——jsdom中文文档
  由 织梦 先生于 2019 年 12 月 13 日发布
  jsdom 是一系列完全由 javascript 实现的 web 标准,特别是 WHATWG 组织开发的用于 nodejs 的 DOM 和 HTML 标准。一般来说,该项目的目标是模拟足够多的 Web 浏览器子集,用于测试和挖掘真实世界的 Web 应用程序。
  最新版本的 jsdom 运行环境需要 node.js v6 或更高版本。(jsdom v10以下的版本在nodejs v4以下仍然可用,但我们不再支持维护)
  jsdom 的 v10 版本有一个全新的 API(如下所述)。
  发表于 NodeJS | Tagged jsdom, nodejs 模块, 中文文档
  十二个 12
  【转】Node.js初探及项目结构分析
  由 织梦 先生于 2018 年 12 月 12 日发布
  一个偶然的机会,我有幸跨越浏览器的鸿沟,以真实的方式体验 Node.js。
  首先我想说:“非常荣幸,经过两个月的努力,第一个Node.js项目落地了。” 整个工程顺利完成。
  事情很简单:Node.js 负责访问层。
  有一个原因
  前端技术创新日新月异,Node.js离不开前端工程。现在大多数项目使用前后端分离的架构。后端提供接口,前端通过接口数据进行数据渲染。但是现在前端代码逻辑越来越复杂,场景也越来越多。这种架构是否适合所有应用场景值得考虑。大前端的出现只是一种尝试。尝试通过 Node.js 访问来访问各种应用场景。
  发表于 NodeJS | 标记 angular, angular.js, gulp, jenkins, koa2, node, nodejs, webpack, 架构分析, 项目架构
  PDF.js 是一种使用 HTML5 构建的便携式文档格式 (PDF) 浏览器。
  PDF.js 是由 Mozilla 实验室驱动和支持的社区。目标是创建一个通用的、基于 Web 标准的平台来解析和呈现 pdf。
  下面的方法是从github复制过来的。我这次的项目是对织梦的二次开发,也就是说网站php环境不是nodejs。客户的要求是word文档上传后可以直接在浏览器中查看,所以我可以把这些文档转成PDF格式,然后用PDF.js在浏览器中查看。虽然我全局安装了 gulp,但是我没有使用 gulp server 命令。测试时访问了域名/pdf.js/web/viewer.html。viewer.html没有做任何改动,加载了很多js文件,速度很慢。访问域名/pdf.js/examples/components/simpleviewer .html只加载必要的js,访问速度还可以。
  因此,在使用中,您还需要结合您的实际需要进行考虑和测试。
  关于
  本书致力于教你如何使用 Node.js 开发应用程序,并会教你在这个过程中需要的所有“高级”JavaScript 知识。本书绝不是“Hello World”教程。
  状态
  您正在阅读的是本书的最终版本。因此,只有在纠正错误并对新版本的 Node.js 中的更改进行相应更正时才会更新。
  本书中的代码示例已经在Node.js 0.6.11 版本中测试,可以正常工作。
  观众
  本书最适合与我有类似技术背景的读者:至少有一些面向对象语言的经验,如Ruby、Python、PHP或Java;刚开始使用 JavaScript,完全是 Node.js 的新手。
  这是指对其他编程语言有一定经验的开发者,意味着本书不会介绍数据类型、变量、控制结构等非常基础的概念。为了理解本书,我假设你已经理解了这些基本概念.
  但是,本书仍然会详细介绍 JavaScript 中的函数和对象,因为它们与其他类似编程语言中的函数和对象有很大的不同。
  书籍结构
  阅读本书后,您将完成一个完整的 Web 应用程序,该应用程序允许用户浏览页面和上传文件。
  当然,应用程序本身并没有什么了不起的。相比为实现这个功能而编写的代码本身,我们更关心如何创建一个框架来干净地剥离我们应用程序的不同模块。是不是很神秘?以后你会明白的。
  本书首先介绍了 Node.js 环境下的 JavaScript 开发和浏览器环境下的 JavaScript 开发的区别。
  然后,我将带领大家完成一个最传统的“Hello World”应用,这也是最基本的 Node.js 应用。
  最后,我将与您讨论如何设计一个“真正”完整的应用程序,分析完成应用程序需要实现的不同模块,并逐步介绍如何实现这些模块。
  可以保证的是,在这个过程中,每个人都会学习到一些 JavaScript 中的高级概念,如何使用它们,以及为什么使用这些概念可以实现,而其他编程语言中的类似概念是无法实现的。 查看全部

  nodejs抓取动态网页(Rocket.Chat聊天程序的开发版本安装部署(图))
  Rocket.Chat聊天程序开发版安装部署,Rocket.Chat开发版安装部署
  你可以在 Linux 机器或 VM 上运行 Rocket.Chat 进行开发。以下说明已在新的 Ubuntu 18.04 LTS 安装上进行了测试。尝试查找并使用未安装其他不必要软件(而不是“桌面”或“客户端”)的新 Ubuntu 服务器安装。
  不要使用安装了 nodeJS 的系统以避免出现问题。
  在构建过程中,内存使用量将接近 8G,这是为开发工作站推荐的最低 RAM 级别。(如果你不做任何开发,只部署Rocket.Chat服务器——所需的RAM可能低至1G。)
  重要信息:请注意,基本操作系统上无需安装 mongo、nodejs 或 npm。如果您安装了其中任何一个;重新开始,或使用另一个 CLEAN 系统。
  一、玩转过程
  node的单线程只是js层面的单线程。它是基于 V8 引擎的单线程。因为V8,前后端js执行模型基本类似,但是node的核心机制还是通过libuv调用epoll或者epoll。IOCP 的多线程机制。换句话说,严格意义上的node并不是真正的单线程架构。节点内核本身有一定的IO线程和IO线程池。通过libuv调度,直接使用操作系统层面的多线程。Node开发者可以通过扩展c/c++模块直接操作多线程来提高效率。但是单线程的好处是程序状态单一,不存在锁、线程同步、线程上下文切换等问题。但是单线程程序并不完美。目前很多服务器都是多cpu和多cpu核的。一个节点实例只能使用一个 CPU 核。那么其他的cpu核就浪费了?而且,单线程的容错能力也很弱。一旦抛出未捕获的异常,必然会导致整个程序崩溃。这样的程序一定非常脆弱。这样的服务器端语言有什么价值?
  13 个中的 12 个
  nodejs模块——jsdom中文文档
  由 织梦 先生于 2019 年 12 月 13 日发布
  jsdom 是一系列完全由 javascript 实现的 web 标准,特别是 WHATWG 组织开发的用于 nodejs 的 DOM 和 HTML 标准。一般来说,该项目的目标是模拟足够多的 Web 浏览器子集,用于测试和挖掘真实世界的 Web 应用程序。
  最新版本的 jsdom 运行环境需要 node.js v6 或更高版本。(jsdom v10以下的版本在nodejs v4以下仍然可用,但我们不再支持维护)
  jsdom 的 v10 版本有一个全新的 API(如下所述)。
  发表于 NodeJS | Tagged jsdom, nodejs 模块, 中文文档
  十二个 12
  【转】Node.js初探及项目结构分析
  由 织梦 先生于 2018 年 12 月 12 日发布
  一个偶然的机会,我有幸跨越浏览器的鸿沟,以真实的方式体验 Node.js。
  首先我想说:“非常荣幸,经过两个月的努力,第一个Node.js项目落地了。” 整个工程顺利完成。
  事情很简单:Node.js 负责访问层。
  有一个原因
  前端技术创新日新月异,Node.js离不开前端工程。现在大多数项目使用前后端分离的架构。后端提供接口,前端通过接口数据进行数据渲染。但是现在前端代码逻辑越来越复杂,场景也越来越多。这种架构是否适合所有应用场景值得考虑。大前端的出现只是一种尝试。尝试通过 Node.js 访问来访问各种应用场景。
  发表于 NodeJS | 标记 angular, angular.js, gulp, jenkins, koa2, node, nodejs, webpack, 架构分析, 项目架构
  PDF.js 是一种使用 HTML5 构建的便携式文档格式 (PDF) 浏览器。
  PDF.js 是由 Mozilla 实验室驱动和支持的社区。目标是创建一个通用的、基于 Web 标准的平台来解析和呈现 pdf。
  下面的方法是从github复制过来的。我这次的项目是对织梦的二次开发,也就是说网站php环境不是nodejs。客户的要求是word文档上传后可以直接在浏览器中查看,所以我可以把这些文档转成PDF格式,然后用PDF.js在浏览器中查看。虽然我全局安装了 gulp,但是我没有使用 gulp server 命令。测试时访问了域名/pdf.js/web/viewer.html。viewer.html没有做任何改动,加载了很多js文件,速度很慢。访问域名/pdf.js/examples/components/simpleviewer .html只加载必要的js,访问速度还可以。
  因此,在使用中,您还需要结合您的实际需要进行考虑和测试。
  关于
  本书致力于教你如何使用 Node.js 开发应用程序,并会教你在这个过程中需要的所有“高级”JavaScript 知识。本书绝不是“Hello World”教程。
  状态
  您正在阅读的是本书的最终版本。因此,只有在纠正错误并对新版本的 Node.js 中的更改进行相应更正时才会更新。
  本书中的代码示例已经在Node.js 0.6.11 版本中测试,可以正常工作。
  观众
  本书最适合与我有类似技术背景的读者:至少有一些面向对象语言的经验,如Ruby、Python、PHP或Java;刚开始使用 JavaScript,完全是 Node.js 的新手。
  这是指对其他编程语言有一定经验的开发者,意味着本书不会介绍数据类型、变量、控制结构等非常基础的概念。为了理解本书,我假设你已经理解了这些基本概念.
  但是,本书仍然会详细介绍 JavaScript 中的函数和对象,因为它们与其他类似编程语言中的函数和对象有很大的不同。
  书籍结构
  阅读本书后,您将完成一个完整的 Web 应用程序,该应用程序允许用户浏览页面和上传文件。
  当然,应用程序本身并没有什么了不起的。相比为实现这个功能而编写的代码本身,我们更关心如何创建一个框架来干净地剥离我们应用程序的不同模块。是不是很神秘?以后你会明白的。
  本书首先介绍了 Node.js 环境下的 JavaScript 开发和浏览器环境下的 JavaScript 开发的区别。
  然后,我将带领大家完成一个最传统的“Hello World”应用,这也是最基本的 Node.js 应用。
  最后,我将与您讨论如何设计一个“真正”完整的应用程序,分析完成应用程序需要实现的不同模块,并逐步介绍如何实现这些模块。
  可以保证的是,在这个过程中,每个人都会学习到一些 JavaScript 中的高级概念,如何使用它们,以及为什么使用这些概念可以实现,而其他编程语言中的类似概念是无法实现的。

nodejs抓取动态网页(nodejs抓取动态网页想实现就点击分享按钮把网页发给你的朋友)

网站优化优采云 发表了文章 • 0 个评论 • 83 次浏览 • 2021-10-16 07:01 • 来自相关话题

  nodejs抓取动态网页(nodejs抓取动态网页想实现就点击分享按钮把网页发给你的朋友)
  nodejs抓取动态网页
  想实现就点击分享按钮把网页发给你的朋友,你的朋友再点击分享按钮把网页发给他们的朋友。让全网的人都看见,同时要满足你的需求,动态更新的。
  针对于flash来说,目前可以通过以下方式实现:设置网页不允许从电子邮件自动下载flash页面。如果主动提交动态链接,失败后才会发送二进制下载链接,然后读取二进制下载链接来解析从其他下载。网站是否开启iscroll?例如,如果当前链接显示无效,就是开启了,那么动态下载失败则是需要发送二进制下载链接。通过cookie(ietester之类的)登录使用ns传递到其他网站通过httppost登录,使用httpauthorization:动态登录等方式来解析从别的网站下载flash然后传递给网站下载。
  直接从mainflash登录不需要进行其他操作。在不通过httppost的情况下,一般来说,我们采用httppost就可以实现动态的下载了。
  一般的做法是用新浪的api,直接把链接提交就可以得到对应的动态。
  后台封装json或xml直接调用。
  flashwrite
  使用文本编辑器,把链接分享出去
  最简单的:iframemeta=xxxx然后在除了点击发送,别的时候都用同样的按钮不就行了。 查看全部

  nodejs抓取动态网页(nodejs抓取动态网页想实现就点击分享按钮把网页发给你的朋友)
  nodejs抓取动态网页
  想实现就点击分享按钮把网页发给你的朋友,你的朋友再点击分享按钮把网页发给他们的朋友。让全网的人都看见,同时要满足你的需求,动态更新的。
  针对于flash来说,目前可以通过以下方式实现:设置网页不允许从电子邮件自动下载flash页面。如果主动提交动态链接,失败后才会发送二进制下载链接,然后读取二进制下载链接来解析从其他下载。网站是否开启iscroll?例如,如果当前链接显示无效,就是开启了,那么动态下载失败则是需要发送二进制下载链接。通过cookie(ietester之类的)登录使用ns传递到其他网站通过httppost登录,使用httpauthorization:动态登录等方式来解析从别的网站下载flash然后传递给网站下载。
  直接从mainflash登录不需要进行其他操作。在不通过httppost的情况下,一般来说,我们采用httppost就可以实现动态的下载了。
  一般的做法是用新浪的api,直接把链接提交就可以得到对应的动态。
  后台封装json或xml直接调用。
  flashwrite
  使用文本编辑器,把链接分享出去
  最简单的:iframemeta=xxxx然后在除了点击发送,别的时候都用同样的按钮不就行了。

nodejs抓取动态网页(一个网络爬虫的开发过程及实现过程原理目标分析)

网站优化优采云 发表了文章 • 0 个评论 • 142 次浏览 • 2021-10-14 22:14 • 来自相关话题

  nodejs抓取动态网页(一个网络爬虫的开发过程及实现过程原理目标分析)
  Nodejs 将前端开发语言移植到了服务端。如今,前端开发者可以轻松地使用 Nodejs 实现网络爬虫,这在以前是不可想象的。本文介绍了一个简单的Nodejs爬虫开发过程,只想看代码拉到最后。
  爬行原理目标分析
  这次爬取的目标选择是观察cnBeta的新闻详情页收录到相邻页面的链接,但是通过查看源码发现这个链接是由Js生成的:
  
  这是一种常见的反爬虫措施。关联的页面链接通过异步请求获取,然后由js动态生成。检查网络面板,您可以看到该页面确实发送了一个异步请求。结果具有我们想要的关联页面 ID:
  
  接下来分析这个请求,可以发现有两个参数,这两个参数都可以在HTML中找到:
  
  第一个参数_csrf很容易直接在源码中搜索:
  
  第二个参数全文搜索找不到:
  
  观察这个参数的结构,发现数据被两个逗号分隔成三段,所以猜测数据是由于多部分拼接造成的,单独搜索真的找到了:
  
  但是只找到了最后两段数据,开头是1,不知道是哪来的。由于只有一个字符,检索起来比较困难,观察到这个请求在很多页面中都是以1开头的,所以这里干脆写死了。. .
  至此,对目标的分析结束,下面将执行爬虫。
  实施过程程序结构
  大体思路是从起始页开始爬取,异步获取上一个新闻页面的链接继续爬取,并设置最大爬取次数,防止陷入死循环。伪代码如下:
  1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  let fetchLimit = 50; //最大抓取条数
let fetched = 0; //计数器
let getNext = function(_csrf, op){ //获取下一篇文章ID
return Promise(function(resolve,reject){
let nextID;
...
resolve(nextID);
})
}
let fetchPage = function(ID){ //抓取程序
let _csrf = ...;
let op = ...;
save(ID); //保存内容
fetched++; //计数器累加
getNext(_csrf, op).then(function(nextID) {
fetchPage(nextID); //获取下一篇ID并进入循环
});
}
fetchPage('STARTID'); //开始抓取
  功能点
  关键是保存内容。首先,获取页面的 HTML 代码。主要使用http模块,如下:
  1
2
3
4
5
6
7
8
9
10
11
  const http = require('http');
http.get(pageUrl, function(res){
let html='';
res.setEncoding('utf8');
res.on('data', (chunk) => {
html += chunk;
});
res.on('end', () => {
console.log(html); //这里得到完整的HTML字符串
});
})
  要从 HTML 获取信息,您可以使用常规匹配,或使用cheerio。Cheerio 可以说实现了一个 Nodejs 端的 jQuery。它和jQuery的区别在于它需要先生成一个实例,然后像jQuery一样使用它:
  1
2
3
  const cheerio = require('cheerio');
const $ = cheerio.load(html);
let news_title = $('.cnbeta-article .title h1').text().trim().replace(/\//g, '-');
  fs模块主要用于保存文件,如下:
  1
2
3
4
5
6
  const fs = require('fs');
fs.writeFile(FilePath, FileContent, 'utf-8', function(err) {
if (err) {
console.log(err);
}
});
  这里有个坑。我们希望将文章的文本保存为与标题同名的txt文本,但标题可能收录斜线(/)。保存这样的文件时,程序会误认为标题斜线之前的部分。把它看成是路径,报错,所以需要替换标题中的斜线。
  保存图片与保存文本大致相同。主要区别在于写入格式,需要以二进制方式写入:
  1
2
3
4
5
6
7
8
9
10
11
12
13
  http.get(img_src, function(res) {
let imgData = "";
res.setEncoding("binary"); //注意格式
res.on("data", function(chunk) {
imgData += chunk;
});
res.on("end", function() {
fs.writeFile(imgSavePath, imgData, "binary", function(err) { //注意格式
if (err) {
console.log(err);
}
});
});
  程序的结构和主要功能基本是这样的。
  后记
  实现爬虫说起来容易,但是健壮性真的很难保证。在爬cnBeta的过程中,又发现了一个301跳坑。URL跳转时,程序抓取的HTML为空,无法获取。因此,请求得到响应后,需要判断响应头是否为301,如果是,则需要从响应信息中找到重定向的URL,重新发起请求。好在cnBeta是不需要用户登录的,如果是必须登录才能访问的网站,爬虫会很麻烦。
  本项目完整代码见Nodejs爬虫,感谢cnBeta^^。
  前路原创技术文章,转载请注明出处:爬虫练习笔记/
  
  不甘平庸的你,快来和我一起充电,关注风景,获取更多精彩内容。 查看全部

  nodejs抓取动态网页(一个网络爬虫的开发过程及实现过程原理目标分析)
  Nodejs 将前端开发语言移植到了服务端。如今,前端开发者可以轻松地使用 Nodejs 实现网络爬虫,这在以前是不可想象的。本文介绍了一个简单的Nodejs爬虫开发过程,只想看代码拉到最后。
  爬行原理目标分析
  这次爬取的目标选择是观察cnBeta的新闻详情页收录到相邻页面的链接,但是通过查看源码发现这个链接是由Js生成的:
  
  这是一种常见的反爬虫措施。关联的页面链接通过异步请求获取,然后由js动态生成。检查网络面板,您可以看到该页面确实发送了一个异步请求。结果具有我们想要的关联页面 ID:
  
  接下来分析这个请求,可以发现有两个参数,这两个参数都可以在HTML中找到:
  
  第一个参数_csrf很容易直接在源码中搜索:
  
  第二个参数全文搜索找不到:
  
  观察这个参数的结构,发现数据被两个逗号分隔成三段,所以猜测数据是由于多部分拼接造成的,单独搜索真的找到了:
  
  但是只找到了最后两段数据,开头是1,不知道是哪来的。由于只有一个字符,检索起来比较困难,观察到这个请求在很多页面中都是以1开头的,所以这里干脆写死了。. .
  至此,对目标的分析结束,下面将执行爬虫。
  实施过程程序结构
  大体思路是从起始页开始爬取,异步获取上一个新闻页面的链接继续爬取,并设置最大爬取次数,防止陷入死循环。伪代码如下:
  1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  let fetchLimit = 50; //最大抓取条数
let fetched = 0; //计数器
let getNext = function(_csrf, op){ //获取下一篇文章ID
return Promise(function(resolve,reject){
let nextID;
...
resolve(nextID);
})
}
let fetchPage = function(ID){ //抓取程序
let _csrf = ...;
let op = ...;
save(ID); //保存内容
fetched++; //计数器累加
getNext(_csrf, op).then(function(nextID) {
fetchPage(nextID); //获取下一篇ID并进入循环
});
}
fetchPage('STARTID'); //开始抓取
  功能点
  关键是保存内容。首先,获取页面的 HTML 代码。主要使用http模块,如下:
  1
2
3
4
5
6
7
8
9
10
11
  const http = require('http');
http.get(pageUrl, function(res){
let html='';
res.setEncoding('utf8');
res.on('data', (chunk) => {
html += chunk;
});
res.on('end', () => {
console.log(html); //这里得到完整的HTML字符串
});
})
  要从 HTML 获取信息,您可以使用常规匹配,或使用cheerio。Cheerio 可以说实现了一个 Nodejs 端的 jQuery。它和jQuery的区别在于它需要先生成一个实例,然后像jQuery一样使用它:
  1
2
3
  const cheerio = require('cheerio');
const $ = cheerio.load(html);
let news_title = $('.cnbeta-article .title h1').text().trim().replace(/\//g, '-');
  fs模块主要用于保存文件,如下:
  1
2
3
4
5
6
  const fs = require('fs');
fs.writeFile(FilePath, FileContent, 'utf-8', function(err) {
if (err) {
console.log(err);
}
});
  这里有个坑。我们希望将文章的文本保存为与标题同名的txt文本,但标题可能收录斜线(/)。保存这样的文件时,程序会误认为标题斜线之前的部分。把它看成是路径,报错,所以需要替换标题中的斜线。
  保存图片与保存文本大致相同。主要区别在于写入格式,需要以二进制方式写入:
  1
2
3
4
5
6
7
8
9
10
11
12
13
  http.get(img_src, function(res) {
let imgData = "";
res.setEncoding("binary"); //注意格式
res.on("data", function(chunk) {
imgData += chunk;
});
res.on("end", function() {
fs.writeFile(imgSavePath, imgData, "binary", function(err) { //注意格式
if (err) {
console.log(err);
}
});
});
  程序的结构和主要功能基本是这样的。
  后记
  实现爬虫说起来容易,但是健壮性真的很难保证。在爬cnBeta的过程中,又发现了一个301跳坑。URL跳转时,程序抓取的HTML为空,无法获取。因此,请求得到响应后,需要判断响应头是否为301,如果是,则需要从响应信息中找到重定向的URL,重新发起请求。好在cnBeta是不需要用户登录的,如果是必须登录才能访问的网站,爬虫会很麻烦。
  本项目完整代码见Nodejs爬虫,感谢cnBeta^^。
  前路原创技术文章,转载请注明出处:爬虫练习笔记/
  
  不甘平庸的你,快来和我一起充电,关注风景,获取更多精彩内容。

nodejs抓取动态网页(索性-spider项目页面(/spider))

网站优化优采云 发表了文章 • 0 个评论 • 73 次浏览 • 2021-10-14 22:05 • 来自相关话题

  nodejs抓取动态网页(索性-spider项目页面(/spider))
  之前研究过数据,写了一些数据爬虫的爬虫,不过写的比较随意。现在看来不合理的地方很多。这段时间比较闲,本来想重构一下之前的项目。
  后来利用这个周末,干脆重新写了一个项目,这个项目guwen-spider。目前这个爬虫还是比较简单的类型。它直接抓取页面,然后从页面中提取数据,并将数据保存到数据库中。
  对比我之前写的,我觉得难点在于整个程序的健壮性和相应的容错机制。昨天写代码的过程中,其实反映了真正的主代码其实写的很快,花了大部分时间
  做稳定性调试,寻求更合理的方式处理数据与过程控制的关系。
  背景
  该项目的背景是抓取一个一级页面,它是一个内容列表。单击目录是章节和长度的列表。点击章节或长度是进入具体内容页面。
  概述
  本项目github地址:[guwen-spider](yangfan0095/guwen-spider)(PS:最后还有彩蛋~~逃跑
  项目技术细节
  项目使用了大量的ES7 async函数,更直观的反映了程序的流程。为方便起见,在数据遍历的过程中直接使用了众所周知的async库,所以不可避免地会用到回调promise。因为数据的处理发生在回调函数中,难免会遇到一些数据传输的问题。其实你也可以直接用ES7的async await写一个方法来实现同样的功能。其实这里最好的一点就是使用Class的静态方法来封装数据库的操作。顾名思义,静态方法与原型相同,不占用额外空间。
  该项目主要用于
  * 1 ES7 的 async await 协程做异步逻辑处理。
  * 2 使用 npm 的 async 库做循环遍历和并发请求操作。
  * 3 使用log4js进行日志处理
  * 4 使用cheerio处理dom操作。
  * 5 使用mongoose连接mongoDB进行数据存储和操作。
  目录结构
  ├── bin//入口
  │ ├── booklist.js// 抢书逻辑
  │ ├── Chapterlist.js// 抓取章节逻辑
  │ ├── content.js// 抓取内容逻辑
  │ └── index.js// 程序入口
  ├── config//配置文件
  ├── dbhelper// 数据库操作方法目录
  ├── logs// 项目日志目录
  ├── model// mongoDB 集合操作示例
  ├── node_modules
  ├── utils// 工具函数
  ├── package.json
  项目实现计划分析
  该项目是典型的多级爬取案例,目前只有三个层次,分别是书单、书目对应的章节列表、章节链接对应的内容。有两种方法可以捕获这样的结构。一种是直接从外层抓到内层,抓到内层后再执行下一个外层,另一种是先将外层保存到数据库中。,然后根据外层抓取到所有内层章节的链接,再次保存,然后从数据库中查询对应的链接单元来抓取内容。这两种方案各有利弊。其实这两种方法我都试过了。后者有一个优势,因为三个层次是分别捕获的,以便更方便,尽可能保存到相关章节。数据。试想一下,如果按照正常逻辑采用前者
  遍历一级目录抓取对应的二级章节列表,再遍历章节列表抓取内容。当三级内容单元被捕获并需要保存时,如果你需要大量一级目录信息,你需要在这些分层数据之间进行数据传输,这实际上应该是一个比较复杂的考虑。因此,单独保存数据在一定程度上避免了不必要和复杂的数据传输。
  目前,我们认为我们要捕捉的古籍数量并不多,涵盖各种历史的古籍大约只有180本左右。它和章节内容本身是一小块数据,即一个集合中有180个文档记录。这180本书的所有章节共有16000章,对应爬取相应内容需要访问的16000页。所以选择第二个应该是合理的。
  项目实现
  主程序有bookListInit、chapterListInit、contentListInit三个方法,分别是抓取图书目录、章节列表、图书内容的初始化方法。通过async,可以控制这三种方法的运行过程。图书目录抓取完成后,将数据保存到数据库中,然后将执行结果返回给主程序。如果主程序运行成功,会根据书单抓取章节列表。,同样抢书的内容。
  项目主入口
  /**
* 爬虫抓取主入口
*/
const start = async() => {
let booklistRes = await bookListInit();
if (!booklistRes) {
logger.warn(&#39;书籍列表抓取出错,程序终止...&#39;);
return;
}
logger.info(&#39;书籍列表抓取成功,现在进行书籍章节抓取...&#39;);
let chapterlistRes = await chapterListInit();
if (!chapterlistRes) {
logger.warn(&#39;书籍章节列表抓取出错,程序终止...&#39;);
return;
}
logger.info(&#39;书籍章节列表抓取成功,现在进行书籍内容抓取...&#39;);
let contentListRes = await contentListInit();
if (!contentListRes) {
logger.warn(&#39;书籍章节内容抓取出错,程序终止...&#39;);
return;
}
logger.info(&#39;书籍内容抓取成功&#39;);
}
// 开始入口
if (typeof bookListInit === &#39;function&#39; && typeof chapterListInit === &#39;function&#39;) {
// 开始抓取
start();
}
  介绍bookListInit、chapterListInit、contentListInit,三个方法
  书单.js
  /**
* 初始化方法 返回抓取结果 true 抓取成果 false 抓取失败
*/
const bookListInit = async() => {
logger.info(&#39;抓取书籍列表开始...&#39;);
const pageUrlList = getPageUrlList(totalListPage, baseUrl);
let res = await getBookList(pageUrlList);
return res;
}
  章节列表.js
  /**
* 初始化入口
*/
const chapterListInit = async() => {
const list = await bookHelper.getBookList(bookListModel);
if (!list) {
logger.error(&#39;初始化查询书籍目录失败&#39;);
}
logger.info(&#39;开始抓取书籍章节列表,书籍目录共:&#39; + list.length + &#39;条&#39;);
let res = await asyncGetChapter(list);
return res;
};
  内容.js
  /**
* 初始化入口
*/
const contentListInit = async() => {
//获取书籍列表
const list = await bookHelper.getBookLi(bookListModel);
if (!list) {
logger.error(&#39;初始化查询书籍目录失败&#39;);
return;
}
const res = await mapBookList(list);
if (!res) {
logger.error(&#39;抓取章节信息,调用 getCurBookSectionList() 进行串行遍历操作,执行完成回调出错,错误信息已打印,请查看日志!&#39;);
return;
}
return res;
}
  关于内容抓取的想法
  图书目录爬取的逻辑其实很简单。你只需要使用 async.mapLimit 做一次遍历保存数据,但是我们保存内容时的简化逻辑其实就是遍历章节列表去抓取链接中的内容。但实际情况是链接数多达数万。从内存使用的角度来看,我们无法将它们全部保存到一个数组中然后遍历它们,因此我们需要将内容捕获进行单元化。
  常见的遍历方法是每次查询一定数量进行爬取。缺点是只用一定数量进行分类,数据之间没有相关性,插入是分批进行的。如果出现错误,容错方面会出现一些小问题,我们认为将一本书作为采集会遇到问题。因此,我们使用第二种方法以书为单位捕获和保存内容。
  这里使用了方法`async.mapLimit(list, 1, (series, callback) =&gt; {})` 来遍历。回调是不可避免的使用,感觉很恶心。async.mapLimit() 的第二个参数可以设置同时请求的数量。
  /*
* 内容抓取步骤:
* 第一步得到书籍列表, 通过书籍列表查到一条书籍记录下 对应的所有章节列表,
* 第二步 对章节列表进行遍历获取内容保存到数据库中
* 第三步 保存完数据后 回到第一步 进行下一步书籍的内容抓取和保存
*/
/**
* 初始化入口
*/
const contentListInit = async() => {
//获取书籍列表
const list = await bookHelper.getBookList(bookListModel);
if (!list) {
logger.error(&#39;初始化查询书籍目录失败&#39;);
return;
}
const res = await mapBookList(list);
if (!res) {
logger.error(&#39;抓取章节信息,调用 getCurBookSectionList() 进行串行遍历操作,执行完成回调出错,错误信息已打印,请查看日志!&#39;);
return;
}
return res;
}
/**
* 遍历书籍目录下的章节列表
* @param {*} list
*/
const mapBookList = (list) => {
return new Promise((resolve, reject) => {
async.mapLimit(list, 1, (series, callback) => {
let doc = series._doc;
getCurBookSectionList(doc, callback);
}, (err, result) => {
if (err) {
logger.error(&#39;书籍目录抓取异步执行出错!&#39;);
logger.error(err);
reject(false); return;
}
resolve(true);
})
})
}
/**
* 获取单本书籍下章节列表 调用章节列表遍历进行抓取内容
* @param {*} series
* @param {*} callback
*/
const getCurBookSectionList = async(series, callback) => {
let num = Math.random() * 1000 + 1000;
await sleep(num);
let key = series.key;
const res = await bookHelper.querySectionList(chapterListModel, {
key: key
});
if (!res) {
logger.error(&#39;获取当前书籍: &#39; + series.bookName + &#39; 章节内容失败,进入下一部书籍内容抓取!&#39;);
callback(null, null);
return;
}
//判断当前数据是否已经存在
const bookItemModel = getModel(key);
const contentLength = await bookHelper.getCollectionLength(bookItemModel, {});
if (contentLength === res.length) {
logger.info(&#39;当前书籍:&#39; + series.bookName + &#39;数据库已经抓取完成,进入下一条数据任务&#39;);
callback(null, null);
return;
}
await mapSectionList(res);
callback(null, null);
}
  抓包后如何保存数据是个问题
  这里我们使用key对数据进行分类。每次我们拿到link,根据key遍历,这样做的好处是保存的数据是一个整体。现在我们正在考虑数据存储的问题。
  1 可整体插入
  优点:数据库操作快,不浪费时间。
  缺点:有些书可能有几百章,这意味着几百页的内容在插入之前必须保存。这也会消耗内存并可能导致程序运行不稳定。
  2可以以每篇文章文章的形式插入到数据库中。
  优点:页面抓取保存的方式,可以及时保存数据,即使出现后续错误,也无需重新保存之前的章节。
  缺点:明显慢。想爬几万个页面,做几万次*N的数据库操作,想一想。在这里,您还可以创建一个缓冲区来一次保存一定数量的条目。当条目数达到条目数时,再次保存。好的选择。
  /**
* 遍历单条书籍下所有章节 调用内容抓取方法
* @param {*} list
*/
const mapSectionList = (list) => {
return new Promise((resolve, reject) => {
async.mapLimit(list, 1, (series, callback) => {
let doc = series._doc;
getContent(doc, callback)
}, (err, result) => {
if (err) {
logger.error(&#39;书籍目录抓取异步执行出错!&#39;);
logger.error(err);
reject(false);
return;
}
const bookName = list[0].bookName;
const key = list[0].key;
// 以整体为单元进行保存
saveAllContentToDB(result, bookName, key, resolve);
//以每篇文章作为单元进行保存
// logger.info(bookName + &#39;数据抓取完成,进入下一部书籍抓取函数...&#39;);
// resolve(true);
})
})
}
  两者都有其优点和缺点,我们都在这里尝试过。准备了两个错误保存集合,errContentModel 和 error采集Model。插入错误时,信息会保存到相应的集合中。您可以选择两者之一。添加集合保存数据的原因是为了方便一次性查看和后续操作,无需查看日志。
  (PS,其实可以完全使用error采集Model集合,errContentModel集合完全可以保存章节信息)
  //保存出错的数据名称
const errorSpider = mongoose.Schema({
chapter: String,
section: String,
url: String,
key: String,
bookName: String,
author: String,
})
// 保存出错的数据名称 只保留key 和 bookName信息
const errorCollection = mongoose.Schema({
key: String,
bookName: String,
})
  我们把每本书信息的内容放到一个新的集合中,集合以key命名。
  总结
  其实,编写这个项目的主要难点在于程序稳定性的控制,容错机制的设置,以及错误的记录。目前这个项目基本上可以一次直接运行整个流程。但是,程序设计肯定存在很多问题。请指正并交流。
  复活节彩蛋
  写完这个项目,做了一个基于React的前端网站用于页面浏览,一个基于koa2.x的服务器。整体技术栈相当于React+Redux+Koa2,前后端服务分开部署,各自独立可以更好的去除前后端服务之间的耦合。例如,同一组服务器端代码不仅可以为 Web 提供支持,还可以为移动和应用程序提供支持。目前整套还很简陋,但是可以满足基本的查询和浏览功能。希望以后有时间可以充实一下项目。
  本项目地址:[guwen-spider](yangfan0095/guwen-spider)
  对应前端React+Redux+semantic-ui地址:[guwen-react](yangfan0095/guwen-react)
  对应节点koa2.2+猫鼬地址:[guwen-node](yangfan0095/guwen-node)
  项目很简单,但是从前端到服务器端,多了一个学习和研发的环境。
  谢谢阅读!
  以上です 查看全部

  nodejs抓取动态网页(索性-spider项目页面(/spider))
  之前研究过数据,写了一些数据爬虫的爬虫,不过写的比较随意。现在看来不合理的地方很多。这段时间比较闲,本来想重构一下之前的项目。
  后来利用这个周末,干脆重新写了一个项目,这个项目guwen-spider。目前这个爬虫还是比较简单的类型。它直接抓取页面,然后从页面中提取数据,并将数据保存到数据库中。
  对比我之前写的,我觉得难点在于整个程序的健壮性和相应的容错机制。昨天写代码的过程中,其实反映了真正的主代码其实写的很快,花了大部分时间
  做稳定性调试,寻求更合理的方式处理数据与过程控制的关系。
  背景
  该项目的背景是抓取一个一级页面,它是一个内容列表。单击目录是章节和长度的列表。点击章节或长度是进入具体内容页面。
  概述
  本项目github地址:[guwen-spider](yangfan0095/guwen-spider)(PS:最后还有彩蛋~~逃跑
  项目技术细节
  项目使用了大量的ES7 async函数,更直观的反映了程序的流程。为方便起见,在数据遍历的过程中直接使用了众所周知的async库,所以不可避免地会用到回调promise。因为数据的处理发生在回调函数中,难免会遇到一些数据传输的问题。其实你也可以直接用ES7的async await写一个方法来实现同样的功能。其实这里最好的一点就是使用Class的静态方法来封装数据库的操作。顾名思义,静态方法与原型相同,不占用额外空间。
  该项目主要用于
  * 1 ES7 的 async await 协程做异步逻辑处理。
  * 2 使用 npm 的 async 库做循环遍历和并发请求操作。
  * 3 使用log4js进行日志处理
  * 4 使用cheerio处理dom操作。
  * 5 使用mongoose连接mongoDB进行数据存储和操作。
  目录结构
  ├── bin//入口
  │ ├── booklist.js// 抢书逻辑
  │ ├── Chapterlist.js// 抓取章节逻辑
  │ ├── content.js// 抓取内容逻辑
  │ └── index.js// 程序入口
  ├── config//配置文件
  ├── dbhelper// 数据库操作方法目录
  ├── logs// 项目日志目录
  ├── model// mongoDB 集合操作示例
  ├── node_modules
  ├── utils// 工具函数
  ├── package.json
  项目实现计划分析
  该项目是典型的多级爬取案例,目前只有三个层次,分别是书单、书目对应的章节列表、章节链接对应的内容。有两种方法可以捕获这样的结构。一种是直接从外层抓到内层,抓到内层后再执行下一个外层,另一种是先将外层保存到数据库中。,然后根据外层抓取到所有内层章节的链接,再次保存,然后从数据库中查询对应的链接单元来抓取内容。这两种方案各有利弊。其实这两种方法我都试过了。后者有一个优势,因为三个层次是分别捕获的,以便更方便,尽可能保存到相关章节。数据。试想一下,如果按照正常逻辑采用前者
  遍历一级目录抓取对应的二级章节列表,再遍历章节列表抓取内容。当三级内容单元被捕获并需要保存时,如果你需要大量一级目录信息,你需要在这些分层数据之间进行数据传输,这实际上应该是一个比较复杂的考虑。因此,单独保存数据在一定程度上避免了不必要和复杂的数据传输。
  目前,我们认为我们要捕捉的古籍数量并不多,涵盖各种历史的古籍大约只有180本左右。它和章节内容本身是一小块数据,即一个集合中有180个文档记录。这180本书的所有章节共有16000章,对应爬取相应内容需要访问的16000页。所以选择第二个应该是合理的。
  项目实现
  主程序有bookListInit、chapterListInit、contentListInit三个方法,分别是抓取图书目录、章节列表、图书内容的初始化方法。通过async,可以控制这三种方法的运行过程。图书目录抓取完成后,将数据保存到数据库中,然后将执行结果返回给主程序。如果主程序运行成功,会根据书单抓取章节列表。,同样抢书的内容。
  项目主入口
  /**
* 爬虫抓取主入口
*/
const start = async() => {
let booklistRes = await bookListInit();
if (!booklistRes) {
logger.warn(&#39;书籍列表抓取出错,程序终止...&#39;);
return;
}
logger.info(&#39;书籍列表抓取成功,现在进行书籍章节抓取...&#39;);
let chapterlistRes = await chapterListInit();
if (!chapterlistRes) {
logger.warn(&#39;书籍章节列表抓取出错,程序终止...&#39;);
return;
}
logger.info(&#39;书籍章节列表抓取成功,现在进行书籍内容抓取...&#39;);
let contentListRes = await contentListInit();
if (!contentListRes) {
logger.warn(&#39;书籍章节内容抓取出错,程序终止...&#39;);
return;
}
logger.info(&#39;书籍内容抓取成功&#39;);
}
// 开始入口
if (typeof bookListInit === &#39;function&#39; && typeof chapterListInit === &#39;function&#39;) {
// 开始抓取
start();
}
  介绍bookListInit、chapterListInit、contentListInit,三个方法
  书单.js
  /**
* 初始化方法 返回抓取结果 true 抓取成果 false 抓取失败
*/
const bookListInit = async() => {
logger.info(&#39;抓取书籍列表开始...&#39;);
const pageUrlList = getPageUrlList(totalListPage, baseUrl);
let res = await getBookList(pageUrlList);
return res;
}
  章节列表.js
  /**
* 初始化入口
*/
const chapterListInit = async() => {
const list = await bookHelper.getBookList(bookListModel);
if (!list) {
logger.error(&#39;初始化查询书籍目录失败&#39;);
}
logger.info(&#39;开始抓取书籍章节列表,书籍目录共:&#39; + list.length + &#39;条&#39;);
let res = await asyncGetChapter(list);
return res;
};
  内容.js
  /**
* 初始化入口
*/
const contentListInit = async() => {
//获取书籍列表
const list = await bookHelper.getBookLi(bookListModel);
if (!list) {
logger.error(&#39;初始化查询书籍目录失败&#39;);
return;
}
const res = await mapBookList(list);
if (!res) {
logger.error(&#39;抓取章节信息,调用 getCurBookSectionList() 进行串行遍历操作,执行完成回调出错,错误信息已打印,请查看日志!&#39;);
return;
}
return res;
}
  关于内容抓取的想法
  图书目录爬取的逻辑其实很简单。你只需要使用 async.mapLimit 做一次遍历保存数据,但是我们保存内容时的简化逻辑其实就是遍历章节列表去抓取链接中的内容。但实际情况是链接数多达数万。从内存使用的角度来看,我们无法将它们全部保存到一个数组中然后遍历它们,因此我们需要将内容捕获进行单元化。
  常见的遍历方法是每次查询一定数量进行爬取。缺点是只用一定数量进行分类,数据之间没有相关性,插入是分批进行的。如果出现错误,容错方面会出现一些小问题,我们认为将一本书作为采集会遇到问题。因此,我们使用第二种方法以书为单位捕获和保存内容。
  这里使用了方法`async.mapLimit(list, 1, (series, callback) =&gt; {})` 来遍历。回调是不可避免的使用,感觉很恶心。async.mapLimit() 的第二个参数可以设置同时请求的数量。
  /*
* 内容抓取步骤:
* 第一步得到书籍列表, 通过书籍列表查到一条书籍记录下 对应的所有章节列表,
* 第二步 对章节列表进行遍历获取内容保存到数据库中
* 第三步 保存完数据后 回到第一步 进行下一步书籍的内容抓取和保存
*/
/**
* 初始化入口
*/
const contentListInit = async() => {
//获取书籍列表
const list = await bookHelper.getBookList(bookListModel);
if (!list) {
logger.error(&#39;初始化查询书籍目录失败&#39;);
return;
}
const res = await mapBookList(list);
if (!res) {
logger.error(&#39;抓取章节信息,调用 getCurBookSectionList() 进行串行遍历操作,执行完成回调出错,错误信息已打印,请查看日志!&#39;);
return;
}
return res;
}
/**
* 遍历书籍目录下的章节列表
* @param {*} list
*/
const mapBookList = (list) => {
return new Promise((resolve, reject) => {
async.mapLimit(list, 1, (series, callback) => {
let doc = series._doc;
getCurBookSectionList(doc, callback);
}, (err, result) => {
if (err) {
logger.error(&#39;书籍目录抓取异步执行出错!&#39;);
logger.error(err);
reject(false); return;
}
resolve(true);
})
})
}
/**
* 获取单本书籍下章节列表 调用章节列表遍历进行抓取内容
* @param {*} series
* @param {*} callback
*/
const getCurBookSectionList = async(series, callback) => {
let num = Math.random() * 1000 + 1000;
await sleep(num);
let key = series.key;
const res = await bookHelper.querySectionList(chapterListModel, {
key: key
});
if (!res) {
logger.error(&#39;获取当前书籍: &#39; + series.bookName + &#39; 章节内容失败,进入下一部书籍内容抓取!&#39;);
callback(null, null);
return;
}
//判断当前数据是否已经存在
const bookItemModel = getModel(key);
const contentLength = await bookHelper.getCollectionLength(bookItemModel, {});
if (contentLength === res.length) {
logger.info(&#39;当前书籍:&#39; + series.bookName + &#39;数据库已经抓取完成,进入下一条数据任务&#39;);
callback(null, null);
return;
}
await mapSectionList(res);
callback(null, null);
}
  抓包后如何保存数据是个问题
  这里我们使用key对数据进行分类。每次我们拿到link,根据key遍历,这样做的好处是保存的数据是一个整体。现在我们正在考虑数据存储的问题。
  1 可整体插入
  优点:数据库操作快,不浪费时间。
  缺点:有些书可能有几百章,这意味着几百页的内容在插入之前必须保存。这也会消耗内存并可能导致程序运行不稳定。
  2可以以每篇文章文章的形式插入到数据库中。
  优点:页面抓取保存的方式,可以及时保存数据,即使出现后续错误,也无需重新保存之前的章节。
  缺点:明显慢。想爬几万个页面,做几万次*N的数据库操作,想一想。在这里,您还可以创建一个缓冲区来一次保存一定数量的条目。当条目数达到条目数时,再次保存。好的选择。
  /**
* 遍历单条书籍下所有章节 调用内容抓取方法
* @param {*} list
*/
const mapSectionList = (list) => {
return new Promise((resolve, reject) => {
async.mapLimit(list, 1, (series, callback) => {
let doc = series._doc;
getContent(doc, callback)
}, (err, result) => {
if (err) {
logger.error(&#39;书籍目录抓取异步执行出错!&#39;);
logger.error(err);
reject(false);
return;
}
const bookName = list[0].bookName;
const key = list[0].key;
// 以整体为单元进行保存
saveAllContentToDB(result, bookName, key, resolve);
//以每篇文章作为单元进行保存
// logger.info(bookName + &#39;数据抓取完成,进入下一部书籍抓取函数...&#39;);
// resolve(true);
})
})
}
  两者都有其优点和缺点,我们都在这里尝试过。准备了两个错误保存集合,errContentModel 和 error采集Model。插入错误时,信息会保存到相应的集合中。您可以选择两者之一。添加集合保存数据的原因是为了方便一次性查看和后续操作,无需查看日志。
  (PS,其实可以完全使用error采集Model集合,errContentModel集合完全可以保存章节信息)
  //保存出错的数据名称
const errorSpider = mongoose.Schema({
chapter: String,
section: String,
url: String,
key: String,
bookName: String,
author: String,
})
// 保存出错的数据名称 只保留key 和 bookName信息
const errorCollection = mongoose.Schema({
key: String,
bookName: String,
})
  我们把每本书信息的内容放到一个新的集合中,集合以key命名。
  总结
  其实,编写这个项目的主要难点在于程序稳定性的控制,容错机制的设置,以及错误的记录。目前这个项目基本上可以一次直接运行整个流程。但是,程序设计肯定存在很多问题。请指正并交流。
  复活节彩蛋
  写完这个项目,做了一个基于React的前端网站用于页面浏览,一个基于koa2.x的服务器。整体技术栈相当于React+Redux+Koa2,前后端服务分开部署,各自独立可以更好的去除前后端服务之间的耦合。例如,同一组服务器端代码不仅可以为 Web 提供支持,还可以为移动和应用程序提供支持。目前整套还很简陋,但是可以满足基本的查询和浏览功能。希望以后有时间可以充实一下项目。
  本项目地址:[guwen-spider](yangfan0095/guwen-spider)
  对应前端React+Redux+semantic-ui地址:[guwen-react](yangfan0095/guwen-react)
  对应节点koa2.2+猫鼬地址:[guwen-node](yangfan0095/guwen-node)
  项目很简单,但是从前端到服务器端,多了一个学习和研发的环境。
  谢谢阅读!
  以上です

nodejs抓取动态网页(一下动态网页的另一种方法逆向分析啦!!)

网站优化优采云 发表了文章 • 0 个评论 • 87 次浏览 • 2021-10-14 08:20 • 来自相关话题

  nodejs抓取动态网页(一下动态网页的另一种方法逆向分析啦!!)
  首先介绍动态网页,它使用【客户端语言改变页面的HTML和CSS元素】。例如,一个网页使用加载网页将您介绍到另一个页面,但该页面的 URL 连接没有改变,或者当您单击空白时页面会改变颜色。
  然后是客户端脚本语言,它是一种运行在浏览器而不是服务器上的语言。网上通常会遇到两种客户端语言:ActionScript(开发flash应用程序的语言)和JavaScript。
  JavaScript 常用于为网页添加各种动态功能,为用户提供更流畅美观的浏览效果。通常 JavaScript 脚本通过将它们嵌入到 HTML 中来实现它们的功能。
  比如一个下拉式的动态网页,加载后可以在浏览器的开发者工具的网络中的JS选项中看到它刚刚执行的JavaScript,点击就可以看到代码。
  目前已经写了一堆爬虫。对于动态网页,比如图片的下拉加载,在开发者工具/网络/图片中找到图片,分析图片地址的特征,然后在JS中找到刚才的JavaScript文件,如果可以的话可能收录找到比较规范的图片信息,或者网页的跳转关系,可以很方便的写程序,在图片地址下载图片。
  这种方法是逆向分析。
  另一种爬取动态网页的方式是模拟浏览器,通常是Selenium+PhantomJS。
  Selenium 是一个浏览器自动化测试框架,最初是为自动化测试而开发的,现在也用于网络数据采集。框架底层使用JavaScript模拟真实用户操作浏览器。测试脚本执行时,浏览器会根据脚本代码自动进行点击、输入、打开、验证等操作,就像真实用户一样,站在最终用户的角度测试应用。
  Selenium 本身没有浏览器,需要配合第三方浏览器使用。之前用过火狐,每次运行都会打开一个浏览器窗口,程序运行一目了然。PhantomJS 是一个无头浏览器,不显示浏览器窗口。
  Selenium+PhantomJS 可以运行一个非常强大的网络爬虫,它可以处理 cookie、JavaScript、headers 以及您需要做的任何事情。
  2018-03-01 13:50
  不知道有没有朋友喜欢,嘿嘿,,,啾^啾,,
  没有图的答案注定不喜欢吗?
  这是一条更新线,下面的信息是旧新闻。
  反正都是原答案,舍不得删,不喜欢直接跳过
  ╯▂╰
  我是在寒假期间学习的。几天前我想爬一个网站。我在开发者工具里看数据,把它当成静态网页去爬,然后爬下来就找不到数据了。
  然后才知道在开发者工具里看到的网页源码是通过JavaScript渲染的,通过Requests得到的源码没有我想要的数据。
  之前看书的时候讲了selenium模块。考虑到用它来模拟浏览器的时候不容易被逆转,我就用它爬过12306查票。
  我能想到的第一种方法是使用selenium,但是我认为它的速度不能满足我的需求,而且我非常抗拒。(呃(~_~;)
  那我只好百度了,肯定会有其他的解决办法。
  然后CSDN上有一篇很全面的文章。
  套用大佬的总结:
  动态网页可以通过逆向分析尽可能地进行逆向分析,其稳定性和效率是其他解决方案无法比拟的。通常,爬虫有口耳相传的事实。如果点击浏览器F12大法,90%的爬虫问题都可以解决,剩下的10%需要我们动脑筋。这适用于动态页面抓取。如果你能扭转它,试着扭转它。如果无法逆转,请找到折衷的解决方案。在折中方案中,可以尽量使用深度控制JS脚本执行计划(难度稍高),然后是基于标准的浏览器自动化测试框架(即selenium和PhantomJs)爬取。
  如需进一步了解,请直接前往网站:
  /yanbober/article/details/73822475?locationNum=3&amp;fps=1
  时间:2018-02-10 01:23 查看全部

  nodejs抓取动态网页(一下动态网页的另一种方法逆向分析啦!!)
  首先介绍动态网页,它使用【客户端语言改变页面的HTML和CSS元素】。例如,一个网页使用加载网页将您介绍到另一个页面,但该页面的 URL 连接没有改变,或者当您单击空白时页面会改变颜色。
  然后是客户端脚本语言,它是一种运行在浏览器而不是服务器上的语言。网上通常会遇到两种客户端语言:ActionScript(开发flash应用程序的语言)和JavaScript。
  JavaScript 常用于为网页添加各种动态功能,为用户提供更流畅美观的浏览效果。通常 JavaScript 脚本通过将它们嵌入到 HTML 中来实现它们的功能。
  比如一个下拉式的动态网页,加载后可以在浏览器的开发者工具的网络中的JS选项中看到它刚刚执行的JavaScript,点击就可以看到代码。
  目前已经写了一堆爬虫。对于动态网页,比如图片的下拉加载,在开发者工具/网络/图片中找到图片,分析图片地址的特征,然后在JS中找到刚才的JavaScript文件,如果可以的话可能收录找到比较规范的图片信息,或者网页的跳转关系,可以很方便的写程序,在图片地址下载图片。
  这种方法是逆向分析。
  另一种爬取动态网页的方式是模拟浏览器,通常是Selenium+PhantomJS。
  Selenium 是一个浏览器自动化测试框架,最初是为自动化测试而开发的,现在也用于网络数据采集。框架底层使用JavaScript模拟真实用户操作浏览器。测试脚本执行时,浏览器会根据脚本代码自动进行点击、输入、打开、验证等操作,就像真实用户一样,站在最终用户的角度测试应用。
  Selenium 本身没有浏览器,需要配合第三方浏览器使用。之前用过火狐,每次运行都会打开一个浏览器窗口,程序运行一目了然。PhantomJS 是一个无头浏览器,不显示浏览器窗口。
  Selenium+PhantomJS 可以运行一个非常强大的网络爬虫,它可以处理 cookie、JavaScript、headers 以及您需要做的任何事情。
  2018-03-01 13:50
  不知道有没有朋友喜欢,嘿嘿,,,啾^啾,,
  没有图的答案注定不喜欢吗?
  这是一条更新线,下面的信息是旧新闻。
  反正都是原答案,舍不得删,不喜欢直接跳过
  ╯▂╰
  我是在寒假期间学习的。几天前我想爬一个网站。我在开发者工具里看数据,把它当成静态网页去爬,然后爬下来就找不到数据了。
  然后才知道在开发者工具里看到的网页源码是通过JavaScript渲染的,通过Requests得到的源码没有我想要的数据。
  之前看书的时候讲了selenium模块。考虑到用它来模拟浏览器的时候不容易被逆转,我就用它爬过12306查票。
  我能想到的第一种方法是使用selenium,但是我认为它的速度不能满足我的需求,而且我非常抗拒。(呃(~_~;)
  那我只好百度了,肯定会有其他的解决办法。
  然后CSDN上有一篇很全面的文章。
  套用大佬的总结:
  动态网页可以通过逆向分析尽可能地进行逆向分析,其稳定性和效率是其他解决方案无法比拟的。通常,爬虫有口耳相传的事实。如果点击浏览器F12大法,90%的爬虫问题都可以解决,剩下的10%需要我们动脑筋。这适用于动态页面抓取。如果你能扭转它,试着扭转它。如果无法逆转,请找到折衷的解决方案。在折中方案中,可以尽量使用深度控制JS脚本执行计划(难度稍高),然后是基于标准的浏览器自动化测试框架(即selenium和PhantomJs)爬取。
  如需进一步了解,请直接前往网站:
  /yanbober/article/details/73822475?locationNum=3&amp;fps=1
  时间:2018-02-10 01:23

nodejs抓取动态网页(动态页面静态化的方案与方案)

网站优化优采云 发表了文章 • 0 个评论 • 74 次浏览 • 2021-10-09 08:31 • 来自相关话题

  nodejs抓取动态网页(动态页面静态化的方案与方案)
  动态页面静态
  首先我们需要了解两个概念,静态页面和动态页面
  静态页面
  最早的时候,网站的内容是通过在宿主空间放置大量静态网页来实现的
  静态网页的最大缺点是每个人看到的都一样。
  网站对于静态网页最大的难点在于每次都需要更新和重新上传网站的内容。
  动态页面
  动态页面是网页框架和内容本身的抽象分离
  动态页面是通过执行asp、php、jsp、.net等程序访问数据库并生成客户端web代码的网页。
  动态页面通常可以通过网站后台管理系统对网站的内容进行更新和管理
  静止的
  但是为什么将动态网页发布为静态网页呢?
  一个很重要的原因是因为搜索引擎。所谓面向搜索引擎优化,包括访问地址的改写,让动态网页看起来像静态网页,让越来越多的搜索引擎可以收录,从而最大化内容自己有针对性地获得机会。
  另一个重要原因是提高程序性能。很多大网站一进门就看着自己很复杂的页面,但是加载的时间并不长。除了其他必要的原因,我认为静态化也是必须考虑的技术之一。
  她在用户之前获取资源或数据库数据,然后通过静态处理生成静态页面。每个人都访问这个静态页面。静态页面本身的访问速度比动态页面快很多倍,所以程序的性能会降低。有很大的改进。
  总之,页面静态体现为:访问速度加快,用户体验显着提升;在后台具体体现为:访问与数据库分离,减少了数据库访问的压力。
  动态页面静态
  动态页面非常易于管理。但是,在访问网页时,程序需要先对其进行处理,因此访问速度相对较慢。静态页面访问速度快,但不易管理。那么静态动态页面就可以将两种页面的优点结合起来。
  静态页面的解决方法:
  1、使用文件读写功能生成静态页面
  2、 使用nosql从内存中读取内容(其实这个不是静态的而是缓存的),比如redis,虽然没有纯静态快,但是比查询数据库快很多
  静态解决方案需要注意的问题:
  1、静态页面中的动态(即时)数据问题。可以通过ajax解决
  2、静态内容,一旦改变,静态页面需要重新生成。
  伪静态
  伪静态是相对真实的静态。通常,为了增强搜索引擎的友好性,我们会生成带有文章内容的静态页面,但有些朋友会实时显示一些信息。或者你想使用动态脚本来解决一些问题。网站 的内容不能静态显示。
  但这失去了搜索引擎的友好性。如何找到介于两者之间的中间方法?这产生了伪静态技术。
  它以html等静态页面URL的形式显示,但实际上是由ASP等动态脚本处理的。
  综上,在SEO方面,伪静态页面和静态页面的功能是一样的,但是伪静态本质上是动态页面,所以资源消耗和动态页面是一样的,而且因为Rewrite服务器还需要消耗额外的资源 查看全部

  nodejs抓取动态网页(动态页面静态化的方案与方案)
  动态页面静态
  首先我们需要了解两个概念,静态页面和动态页面
  静态页面
  最早的时候,网站的内容是通过在宿主空间放置大量静态网页来实现的
  静态网页的最大缺点是每个人看到的都一样。
  网站对于静态网页最大的难点在于每次都需要更新和重新上传网站的内容。
  动态页面
  动态页面是网页框架和内容本身的抽象分离
  动态页面是通过执行asp、php、jsp、.net等程序访问数据库并生成客户端web代码的网页。
  动态页面通常可以通过网站后台管理系统对网站的内容进行更新和管理
  静止的
  但是为什么将动态网页发布为静态网页呢?
  一个很重要的原因是因为搜索引擎。所谓面向搜索引擎优化,包括访问地址的改写,让动态网页看起来像静态网页,让越来越多的搜索引擎可以收录,从而最大化内容自己有针对性地获得机会。
  另一个重要原因是提高程序性能。很多大网站一进门就看着自己很复杂的页面,但是加载的时间并不长。除了其他必要的原因,我认为静态化也是必须考虑的技术之一。
  她在用户之前获取资源或数据库数据,然后通过静态处理生成静态页面。每个人都访问这个静态页面。静态页面本身的访问速度比动态页面快很多倍,所以程序的性能会降低。有很大的改进。
  总之,页面静态体现为:访问速度加快,用户体验显着提升;在后台具体体现为:访问与数据库分离,减少了数据库访问的压力。
  动态页面静态
  动态页面非常易于管理。但是,在访问网页时,程序需要先对其进行处理,因此访问速度相对较慢。静态页面访问速度快,但不易管理。那么静态动态页面就可以将两种页面的优点结合起来。
  静态页面的解决方法:
  1、使用文件读写功能生成静态页面
  2、 使用nosql从内存中读取内容(其实这个不是静态的而是缓存的),比如redis,虽然没有纯静态快,但是比查询数据库快很多
  静态解决方案需要注意的问题:
  1、静态页面中的动态(即时)数据问题。可以通过ajax解决
  2、静态内容,一旦改变,静态页面需要重新生成。
  伪静态
  伪静态是相对真实的静态。通常,为了增强搜索引擎的友好性,我们会生成带有文章内容的静态页面,但有些朋友会实时显示一些信息。或者你想使用动态脚本来解决一些问题。网站 的内容不能静态显示。
  但这失去了搜索引擎的友好性。如何找到介于两者之间的中间方法?这产生了伪静态技术。
  它以html等静态页面URL的形式显示,但实际上是由ASP等动态脚本处理的。
  综上,在SEO方面,伪静态页面和静态页面的功能是一样的,但是伪静态本质上是动态页面,所以资源消耗和动态页面是一样的,而且因为Rewrite服务器还需要消耗额外的资源

nodejs抓取动态网页(nodejs一个#模块导出的两种方式Puppeteer)

网站优化优采云 发表了文章 • 0 个评论 • 77 次浏览 • 2021-10-08 03:30 • 来自相关话题

  nodejs抓取动态网页(nodejs一个#模块导出的两种方式Puppeteer)
  Intro# 最近需要用nodejs做一个爬虫。 Google 有一个 Puppeteer 项目,可以用来制作爬虫。网上对Puppeteer的介绍很多,这里就不详细介绍了。节点小白,刚开始有点晕,模块导出不行。官方文档说支持*.mjs,但是需要修改文件扩展名。感觉有点怪怪的,没用,主要是基于js-based模块的使用。模块导出的两种方式#因为熟悉C#,所以从我对C#的理解,我把nodejs中的模块导出分为两种形式:1.需要实例化才能调用的模块2. a 一个无需实例化就可以调用的静态类,提供了一些静态方法来导出一个要实例化的类
  module.exports = exports = function (){ };module.exports = exports = function() { this.syncCompanyList = async function(developerName){ await syncCompanyInfo(developerName); }; async function syncCompanyInfo(developerName){ // ... }}
  导出静态类
  exports.funcName = function (){};var getDistrictCode = function (districtName) { if (districtName) { for (let i= 0; i< DistrictInfo.length; i++) { let district = DistrictInfo[i]; if (district["name"] == districtName || district["aliasName"] == districtName) { return district["code"]; } } } return "";};var getNormalDistrictName = function (districtName) { if (districtName) { if (districtName.indexOf("区") > 0) { return districtName; } for (let i= 0; i< DistrictInfo.length; i++) { let district = DistrictInfo[i]; if (district["name"] == districtName || district["aliasName"] == districtName) { return district["name"]; } } } return "";}// 设置导出的方法及属性exports.getDistrictCode = getDistrictCode;exports.getNormalDistrictName = getNormalDistrictName;
  引用模块导出方法#在node中使用require引用模块引用npm包const log4js = require("log4js");参考自己写的模块 const DistrictUtil = require("./utils/districtUtil"); use Exported module#要使用某个模块,需要先引用某个模块,引用的模块可以参考前面的示例类
  const company = require("./company");// ...// 实例化一个 company 对象var comp = new company();// 调用 company 里的 syncCompanyList comp.syncCompanyList ();
  静态类 查看全部

  nodejs抓取动态网页(nodejs一个#模块导出的两种方式Puppeteer)
  Intro# 最近需要用nodejs做一个爬虫。 Google 有一个 Puppeteer 项目,可以用来制作爬虫。网上对Puppeteer的介绍很多,这里就不详细介绍了。节点小白,刚开始有点晕,模块导出不行。官方文档说支持*.mjs,但是需要修改文件扩展名。感觉有点怪怪的,没用,主要是基于js-based模块的使用。模块导出的两种方式#因为熟悉C#,所以从我对C#的理解,我把nodejs中的模块导出分为两种形式:1.需要实例化才能调用的模块2. a 一个无需实例化就可以调用的静态类,提供了一些静态方法来导出一个要实例化的类
  module.exports = exports = function (){ };module.exports = exports = function() { this.syncCompanyList = async function(developerName){ await syncCompanyInfo(developerName); }; async function syncCompanyInfo(developerName){ // ... }}
  导出静态类
  exports.funcName = function (){};var getDistrictCode = function (districtName) { if (districtName) { for (let i= 0; i< DistrictInfo.length; i++) { let district = DistrictInfo[i]; if (district["name"] == districtName || district["aliasName"] == districtName) { return district["code"]; } } } return "";};var getNormalDistrictName = function (districtName) { if (districtName) { if (districtName.indexOf("区") > 0) { return districtName; } for (let i= 0; i< DistrictInfo.length; i++) { let district = DistrictInfo[i]; if (district["name"] == districtName || district["aliasName"] == districtName) { return district["name"]; } } } return "";}// 设置导出的方法及属性exports.getDistrictCode = getDistrictCode;exports.getNormalDistrictName = getNormalDistrictName;
  引用模块导出方法#在node中使用require引用模块引用npm包const log4js = require("log4js");参考自己写的模块 const DistrictUtil = require("./utils/districtUtil"); use Exported module#要使用某个模块,需要先引用某个模块,引用的模块可以参考前面的示例类
  const company = require("./company");// ...// 实例化一个 company 对象var comp = new company();// 调用 company 里的 syncCompanyList comp.syncCompanyList ();
  静态类

官方客服QQ群

微信人工客服

QQ人工客服


线