nodejs cheerio模块提取html页面内容

nodejs cheerio模块提取html页面内容

Table of Contents

  • 1. nodejs cheerio模块提取html页面内容

    • 1.1. 找到目标元素
    • 1.2. 美化文本输出
    • 1.3. 提取答案文本
    • 1.4. 最终代码

本文给出使用一个用cheerio模块提取html文件中指定内容的例子,并说明具体步骤、涉及到的API、以及其它模块。 cheerio模块是一个类似jquery的模块,具有相似的API、功能,能够将一个网页解析为DOM,以及通过selector选择元素,设置、获取元素属性。

以下为我们待解析网页截图:

目标是将task1-5中的所有题目、以及答案提取出来,以文本形式保存。最终提取出的效果如下。 这是题目文本:

Task 1: You will be given 10 minutes to read the text for the first time and then choose an appropriate answer for each of the following questions.

1. What’s the passage mainly about?
A. How to learn online successfully.
B. How to set up a learning goal.
C. The future of online learning.
D. The benefits of online learning.

2. Charles fruitlessly applied for job after job because of the following reasons EXCEPT  ________.
A. he lacked in qualifications.
B. he had no special training.
C. he is too old and can’t walk.
D. he wasn’t even able to do office work.

3. Weather has great __________ on our health.
A. communicative.
B. effective.
C. student-centered.
D. teacher-centered.

这是答案文本:

Task 1:
1. D
2. C
3. C
4. D
5. A

注:其中答案保存在网页中,但在网页中没有显示出来。

1.1 找到目标元素

提取问题文本的整体思路:先找到包含题目的所有元素,然后再获取这些元素的内容即可。 通过chrome的devtool(或者firefox的firebug)看出,所有的目标元素为:hr元素的所有兄弟结点。cheerio的nextAll函数满足需求,这个函数获取当前结点的所有后续的兄弟结点。程序如下:

var fs = require(‘fs‘);
var cheerio = require(‘cheerio‘);

var myHtml = fs.readFileSync("a.html");
var $ = cheerio.load(myHtml);
var t = $(‘html‘).find(‘hr‘);
var t2 = t.nextAll();

t2.each(function(i, elem) {
    getContent($(this));
    console.log($(this).text());
});

首先将网页读取为一个字符串,传给cheerio.load函数,返回值即是一个cheerio对象(类似于一个jquery对象)。然后使用find函数,通过selector查找hr元素。再调用 nextAll函数得到hr元素的所有兄弟结点。 最后在each函数中, 通过text函数将所有包含问题的元素的见容打印出来。

结果中有乱码,问题原因是fs模块不支持中文。通过iconv-lite先解码为中文解决。修改后代码如下:

var fs = require(‘fs‘);
var cheerio = require(‘cheerio‘);
var iconv = require(‘iconv-lite‘);  

var myHtml = fs.readFileSync("a.html");
var $ = cheerio.load(iconv.decode(myHtml, ‘gbk‘));
var t = $(‘html‘).find(‘hr‘);
var t2 = t.nextAll();

t2.each(function(i, elem) {
    getContent($(this));
    console.log($(this).text());
});

最终结果如下:

           Task  1: You will be given 10 minutes to read the text for the first time and then
                                         choose  an appropriate answer for each of the following questions.

       1. What does the “true gratitude” mean?
          

         A. A way of life.
            
          B. A joyous occasion.
            

   C. A much deeper level of gratitude.
       

         D. The improvement of the quality of life.

         2. Who has so many things to be grateful for?
          
        A. A successful man.
            

   B. A miserable person.
            

   C. A good-tempered man.

            

   D.All of us.

           3.
        In the sentence “Expressing love and gratitude  satisfies our deep sense of purpose”, the purpose includes all of the following  EXCEPT ________.
            

             A. spiritual health.
                

           B. emotional health.
              

           C. physical health.

               

             D. social health.

         4. What kind of gift is suitable  for mother on Mother’s Day according to the author?
    

           A. Carnation.
              

           B. Lily.
              

           C. Accessories.

               

             D. Greeting cards.

       5. If a friend does you a favor, you can do all of the following EXCEPT ________.
          

       A. treat him in a restaurant.

            

   B. buy ice-cream for him.
            
    C. just say “thank you”.

            
    D. ask him to help next time.

以上结果有多余的空格、换行符,输出文本看起来很散乱,但至少内容是获取正确了。再在task2-5的html文件验证一下,也获取到了正确的内容,证明方法可行。接下来我们可以集中精力解决格式散乱的问题。

1.2 美化文本输出

最主要的问题是有多余的空格、换行符。想到的一个办法是:将所有结点的内容(包括文本结果)trim,即去年前后的所有空白字符,并对于br元素,加入一个换行符。也即模拟了一下html文档的render效果(因为在浏览器中显示是正确的,所以采用同样的方法,也能得到相同的结果)。 要实现这个方法,要获取一个元素的所有的子结果,使用cheerio的contents函数,这个函数获取一个元素的所有子元素(包括文本元素)。然后调用字符串的trim函数去除首尾的空白文本。由于子元素又有子元素,因此使用递归函数。代码如下:

function getContent(node){
    var a = node.contents();
    if (a.length == 0) {
        if (node.is(‘br‘)){
            RST+=‘\n‘;
        } else {
            RST+=node.text().trim();;
        }
    } else {
        node.contents().each(function(i, elem){
            getContent($(this));
        });

        if (node.is(‘p‘) || node.is(‘tr‘)){
            RST+=‘\n‘;
        }
    }
}

getContent函数用于获取一个元素的文本内容,输入参数为元素,这个函数会被递归调用。首先调用contents函数获取所有子元素。如果子元素数目为0,表示这个元素是叶结点,则首先判断如果是br元素,则在结果中加入一个换行符,否则,调用text函数获取这个元素的文本内容。 如果子元素大于0,则递归地处理所有子元素。如果当前元素为p或tr元素,则在结果中加入一个换行。 其中RST为一个全局变量,用于保存结果文本。在调用函数前需要设置为空字符串。 这样处理后,结果如下:

Task  1: You will be given 10 minutes to read the text for the first time and then
choose  an appropriate answer for each of the following questions.
1. What does the “true gratitude” mean?
A.A way of life.
B.A joyous occasion.
C.A much deeper level of gratitude.
D.The improvement of the quality of life.
2. Who has so many things to be grateful for?
A.A successful man.
B.A miserable person.
C.A good-tempered man.
D.All of us.
3.In the sentence “Expressing love and gratitude  satisfies our deep sense of purpose”, the purpose includes all of the following  EXCEPT ________.
A.spiritual health.
B.emotional health.
C.physical health.
D.social health.
4. What kind of gift is suitable  for mother on Mother’s Day according to the author?
A.Carnation.
B.Lily.
C.Accessories.
D.Greeting cards.
5. If a friend does you a favor, you can do all of the following EXCEPT ________.
A.treat him in a restaurant.
B.buy ice-cream for him.
C.just say “thank you”.
D.ask him to help next time.

看起来漂亮多了。 问题文本提取成功,接下来再提取答案文本。

1.3 提取答案文本

在html源文件中搜索answer,可以看出,答案是保存在script中的,如下:

<SCRIPT LANGUAGE="JavaScript">
  var StandardAnswer = new Array()
  StandardAnswer =["C","D","D","D","D"]
</SCRIPT>

选择题的答案保存在StandardAnswer的数组中。则获取答案文本的方法为:先获取script元素中的代码文本,再通过eval函数得到这个数组值,最后生成答案文本。代码如下:

var t = $(‘html‘).find(‘script‘);
var A = undefined;
t.each(function(i, elem) {
    var text = $(this).text();
    if (text.match(‘StandardAnswer‘)){
        var a = eval(text);
        console.log("standardanswer: "+ a);
        A= a;
    }
});

变量A保存答案数组。通过判断文本中是否包含‘StandardAnswer‘来判断是否是目标代码。然后将这个代码传给eval,返回值即为‘["C","D","D","D","D"]‘这个数组。 有了答案数组,生成答案文本就比较容易了。

1.4 最终代码

可以在这查看最终代码。 最终的代码还解决了一些小问题,如问题文本中包含了多余的文本(对于task4),task4的答案也会被显示在问题文本中,没有留下空白填写答案等。 整个分析、编码过程大致3个小时。其中文件a.js用于生成问题文本,b.js用于生成答案文本。a.js与b.js有很大的重复(b.js是直接复制了一份a.js修改而来)。这个代码基本上是解决一次性问题,没有什么重用性(在编写的过程中也没有考虑这些)。但是最重要的是:它解决了问题,它能够工作。它不需要那么好!最后,这个程序被用于处理几十个文件,成功正确地生成了问题文本及答案文本。

Author: Astropeak

Created: 2016-12-18 Sun 16:36

Emacs 25.1.1 (Org mode 8.2.10)

Validate

.title { text-align: center }
.todo { font-family: monospace; color: red }
.done { color: green }
.tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal }
.timestamp { color: #bebebe }
.timestamp-kwd { color: #5f9ea0 }
.right { margin-left: auto; margin-right: 0px; text-align: right }
.left { margin-left: 0px; margin-right: auto; text-align: left }
.center { margin-left: auto; margin-right: auto; text-align: center }
.underline { text-decoration: underline }
#postamble p,#preamble p { font-size: 90%; margin: .2em }
p.verse { margin-left: 3% }
pre { border: 1px solid #ccc; padding: 8pt; font-family: monospace; overflow: auto; margin: 1.2em }
pre.src { position: relative; overflow: visible; padding-top: 1.2em }
pre.src::before { display: none; position: absolute; background-color: white; top: -10px; right: 10px; padding: 3px; border: 1px solid black }
pre.src:hover::before { display: inline }
pre.src-sh::before { content: "sh" }
pre.src-bash::before { content: "sh" }
pre.src-emacs-lisp::before { content: "Emacs Lisp" }
pre.src-R::before { content: "R" }
pre.src-perl::before { content: "Perl" }
pre.src-java::before { content: "Java" }
pre.src-sql::before { content: "SQL" }
table { border-collapse: collapse }
caption.t-above { caption-side: top }
caption.t-bottom { caption-side: bottom }
td,th { vertical-align: top }
th.right { text-align: center }
th.left { text-align: center }
th.center { text-align: center }
td.right { text-align: right }
td.left { text-align: left }
td.center { text-align: center }
dt { font-weight: bold }
.footpara:nth-child(0n+2) { display: inline }
.footpara { display: block }
.footdef { margin-bottom: 1em }
.figure { padding: 1em }
.figure p { text-align: center }
.inlinetask { padding: 10px; border: 2px solid gray; margin: 10px; background: #ffffcc }
#org-div-home-and-up { text-align: right; font-size: 70%; white-space: nowrap }
textarea { }
.linenr { font-size: smaller }
.code-highlighted { background-color: #ffff00 }
.org-info-js_info-navigation { border-style: none }
#org-info-js_console-label { font-size: 10px; font-weight: bold; white-space: nowrap }
.org-info-js_search-highlight { background-color: #ffff00; color: #000000; font-weight: bold }

时间: 2024-10-10 17:15:06

nodejs cheerio模块提取html页面内容的相关文章

[实战演练]python3使用requests模块爬取页面内容

本文摘要: 1.安装pip 2.安装requests模块 3.安装beautifulsoup4 4.requests模块浅析 + 发送请求 + 传递URL参数 + 响应内容 + 获取网页编码 + 获取响应状态码 5.案例演示 后记 1.安装pip 我的个人桌面系统用的linuxmint,系统默认没有安装pip,考虑到后面安装requests模块使用pip,所以我这里第一步先安装pip. $ sudo apt install python-pip 安装成功,查看PIP版本: $ pip -V 2.

基于Metronic的Bootstrap开发框架经验总结(9)--实现Web页面内容的打印预览和保存操作

在前面介绍了很多篇相关的<Bootstrap开发框架>的系列文章,这些内容基本上覆盖到了我这个Bootstrap框架的各个主要方面的内容,总体来说基本达到了一个稳定的状态,随着时间的推移可以会引入一些更好更新的内容进行完善,本篇继续这个系列,主要介绍如何实现Web页面内容的打印预览和保存操作. 1.Web页面打印的问题 在此之前,我一般使用比较好用的LODOP来执行打印的操作,这个在我之前有很多文章都有涉及,这个控件是一个ActiveX的控件,需要下载安装后就可以在页面是进行打印的排版设计,预

Nodejs基于Express4的动态页面静态化

上个星期在慕课网上提了个问:Express4开发的动态页面访问好慢的说?不幸的是并没有得到可行的回答,周末自己折腾了一番,一并将小站(花满楼)的几个页面全部静态化了:事后想了想,虽不是什么特别标准好的搞法,但纠结这么久了,这个问题终于有办法了,还是有些欣慰的: 一开始页面都是动态的,由jade模板render出来的,可不明白为什么不论页面内容多少,打开都至少要一秒多...:后来专门弄个站点作为静态资源站,缓存加gzip(Nodejs打造静态资源服务器与文件上传):OK,静态资源现在没啥大问题了:

Linux必会原理之输入网址到看到页面内容原理

用户从浏览器输入网址到页面显示,细分了一下基本上由八大快原理组成,他们是:dns解析原理.TCP三次连接.http请求数据包.数据包的封装.数据包的解封装.集群内部的一个请求.服务器的响应报文.四次断开> dns解析原理:当用户在客户端输入网址后,客户端会先访问本地的hosts文件和dns缓存,我们hosts一般都是做测试使用来配置的.所以设备第一访问这个网址,在本地的hosts和local是没有这个解析的,这个时候会向LDNS(也交本地dns寻求解析),如果lDNS这里有记录,就会反馈给客户端

Zabbix使用Pycurl模块监控web页面状态

由于网络的问题,zabbix自带web模块用不了,后台研发2b,老是更新正式环境安装包,导致一直出问题,老是给他们擦屁股,早说过这事,他们不配合,现在出问题了,挺爽,这锅我表示不背,就找了pycurl这个模块写个监控. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 c = pycurl.Curl()    #创建一个curl对象  c.setopt(pycurl.CONNECTTIMEOUT, 5)   

Drupal 7 模块开发 建立模块 第一个页面(hook_menu)

建立模块请参考 <Drupal 7 模块开发 建立> 如果你要支持中文,文件格式必须保存为 UTF-8,NO BOM ------------------------------ hook_menu 定义菜单项和页面反馈. 我们要用自己模块名替换 hook.在这里我们建立一个 my_first_module_menu() 在 my_first_module.module文件里 <?php function my_first_module_menu() { $items = array()

在Bootstrap开发框架的前端视图中使用@RenderPage实现页面内容模块化的隔离,减少复杂度

在很多开发的场景中,很多情况下我们需要考虑抽象.以及模块化等方面的内容,其目的就是为了使得开发的时候关注的变化内容更加少一些,整体开发更加简单化,从而减少开发的复杂度,在Winform开发的时候,往往可以通过定义基类模块.用户控件的方式实现这个目的,而在Web开发的时候,我们是否也可以利用这些特性呢?特别在MVC的视图模板里面的HTML,是否可以利用这些特点,实现变化部分的隔离,从而减少整个页面的复杂度,同时又可以提高模块的重用性呢?本篇随笔介绍在Asp.NET的MVC视图处理上,使用@Rend

PHP 爬虫体验(三) - 使用PHP + puppeteer爬取js动态渲染的页面内容

之前写的两篇爬虫体验基本上涵盖了一般的Html页面提取场景,但是有些时候,如果目标页面不是纯静态的页面,而是使用js动态渲染的页面(比如one),之前的爬虫就不好使了,这种时候就要借助一些其他工具来进行实现. 一般爬取动态页面的思路是通过软件模拟浏览器行为获取到渲染后的页面镜像,然后再对渲染后的页面进行分析,常用的工具有selenium,phantomJs,puppeteer等,通过对项目维护程度.对PHP友好度的对比,我选用的是puppeteer. 根据官方介绍,谷歌在2017年开发了自家Ch

Nodejs mkdirP 模块导致CPU占用高的问题

Nodejs mkdirP 模块导致CPU占用高的问题 近期将nodejs项目部署到服务器上并启动时,发现node进程的cpu占用率在40%左右,当时表示非常不解,刚启动的服务并没有运行什么需要大量消耗cpu的逻辑,且此时还未有请求发送到服务器端. 鉴于这种情况,只能猜测是某段程序在初始化一些东西的时候异常,所以才导致了这种情况. 经过对代码的排查后,最终锁定出为题的代码块如下: router.use(multer({ dest: config.uploadDir, limits:{ fileS