如何利用node把别人的html变成你想要的json

2019-06-05 17:54:47 浏览数 (1)

相信作为一个移动端的老狗,当你遇到一个有内涵的网站的时候,而且当你发现里面有太多的噪音尤其是铺天盖地的 广告 的时候,你是不是有种想把它净化一下的感觉呢?比如来一个纯净版本的。

没错,我最近就在折腾这个,如何把自己欣赏的网站 html 转变为 json,嗯,然后使用flutter啊,小程序啊稍微包装一下,就是自己的小应用,自己自嗨那是多么怡然自得啊!

好,废话多说干嘛,说干就干咯。

那么,如何把别人的网站变成你的小json

首先,你得有一个server。

那么,server怎么选择呢,我现在玩node,那自然是选择的express了,如果你在玩python,你选择flask也没任何问题,思路嘛,全部都是相通的,人生码路没有绕不过去的坎。自如如何在你自己的小服务器上部署额外的一个express服务,相信读过我之前的文章《服务器上起两个服务,nginx如何转发》和《自己搭建一个server并用nginx反向代理》的化,你很容易起一个express了。当然,如果你只是在本地做做研究,并没打算部署到服务器上,造化千千万万的洁癖狗们,那就免了。

起一个express服务

这个完全不用多说什么,直接按照https://expressjs.com/zh-cn/starter/installing.html一通操作就好了。

然后,我想强调的是,express如何做到后台启动,请了解一下pm2最近在用这个,简直爽的不要不要的。

如图所示,查看由pm2管理的node 服务的各种参数,不要太清晰,服务配置有改动,代码有改动直接 pm2 restart 1[id]即可,id上面都有的,一清二楚。听到某个服务,只需要pm2 stop 1

然后,日志相关的全部放在.pm2的文件夹下,如下图所示

撸码环节,直接上代码了
代码语言:txt复制
const express = require('express')
const cheerio = require('cheerio')
const app = express()
const request = require('request')
var rp = require('request-promise');
const Iconv = require('iconv-lite')
const pc = require('phantomjs-cheerio')
var Rx = require('rxjs')


async function getImages(val) {
    return await pc.request({url: val.link}).then(($) => {
        let images = []
        $('.view-box .view-main p').each(function () {
            let image = 'http://www.xiaoliaoba.cn'.concat($(this).find('img').attr('data-src'))
            images.push(image)
        })
        val.images = images
        return val
    }).catch(function (err) {
        return val
    })
}

async function list(req, res) {
    let page = parseInt(req.params.page)
    let url = `http://www.xiaoliaoba.cn/tags/动态图?page=${page}`
    let option = {
        url: url,
        encoding: null,
        transform: function (body) {
            body = Iconv.decode(body, 'utf-8')
            return cheerio.load(body);
        }
    }

    let results = await rp(option).then(function ($) {
        links = []
        $('.cont-item').each(function () {
            let header = $(this).find('.cont-editor-head a img').attr('src')
            let name = $(this).find('.view-editor-name a').text()
            let time = $(this).find('.view-editor-name span').text()
            let id = $(this).children().find('.cont-list-in a').attr('href').match(/d /)[0]
            let cover = $(this)
                .children('.cont-list-main')
                .find('img')
                .attr('data-src')
            let count = $(this)
                .children('.cont-list-main')
                .find('span')
                .text()
            if (cover != undefined) {
                let tmp = {
                    name: name,
                    header: header,
                    time: time,
                    title: $(this)
                        .children('.cont-list-title')
                        .text(),
                    cover: 'http://www.xiaoliaoba.cn'.concat(cover),
                    count:count,
                    id: id
                }
                links.push(tmp)
            }
        })
        return links
    }).catch(function (err) {
        console.log(err)
        return []
    })
    res.send({
        code: 200,
        data: results,
        msg: ''
    })
    //使用套一个异步
    // for (result of results) {
    //     console.log(result)
    //     result.images = await getImages(result.link)
    // }
    //使用rxjs的concatmap来做,一样的,你大爷,二次异步请求,网页加载,太耗时!!!
    // const source = Rx.Observable.of(results)
    // const example = source.pipe(Rx.Operator.concatMap(val=>getImages(val)),Rx.Operator.toArray())
    // const subscribe = example.subscribe(val =>
    //     res.send({
    //         code: 200,
    //         data: val,
    //         msg: ''
    //     })
    // );

}

app.get('/:page', function (req, res) {
    if (isNaN(req.params.page)) {
        res.send({
            msg: '请正确填写page参数 int类型',
            code: 2
        })
        return false
    }
    list(req, res)
})
module.exports = app

其实基本的原理就是使用request把网页请求回来,再用cheerio来解析网页,cheerio和jQuery查找dom的操作很像,个人很喜欢这种风格,当然也有xpath那样的库,想用也并不是没有,xpath有一个好处,就是Chrome浏览器中可以直接查看你想要的元素的xpath,可以少手写的点吗,免去耗费脑细胞。

嗯,总结:

  1. request请求网页
  2. cheerio解析网页,提取自己想要的元素,组合到一个dic中
  3. 输出

一般来说,我们需要使用路由把这个微服务挂钩到express下面

类似于这样app.use('/xx', require('./routes/xx'))

那么,有没有坑

有,当然有,而且我踩了三个

  1. 网页内容是异步加载的,怎么办呢?
  2. async、await操作对node版本是有要求的,怎么办?
  3. 拿到这个页面的数据,发现某个字段只是一个中间数据,还需要再次请求,才能拿到真正的那个数据,又怎么办?
网页内容是异步加载的,怎么办呢?

这里你该去了解一下phantomjs,简单的说,他就像是一个浏览器,不过没有界面而已,因此,异步加载的内容这个坑就可以交给他,只不过,这个库巨慢无比,感觉投入使用不大靠谱,但总是可以搞定异步加载的内容。

async、await操作对node版本是有要求的,怎么办

这时候,就要升级你的node了

我记得貌似我之前是6.x版本,使用async/await操作是报错了的。

发现某个字段只是一个中间数据,还需要再次请求

这种你应该从代码中可以看到我已经爬了这个坑,那就是对列表中每一项在发起一个异步请求。

我这里尝试过直接for,也用过rx,都是ok的,个人感觉rx的concatMap操作更好,因为他帮你做了并发,你用for await,本质上是一个个在做。

大功告成

来试试吧,这是我的https://brzhang.club/nodeapi/api/joke_gif/1

0 人点赞