相信作为一个移动端的老狗,当你遇到一个有内涵的网站的时候,而且当你发现里面有太多的噪音尤其是铺天盖地的 广告
的时候,你是不是有种想把它净化一下的感觉呢?比如来一个纯净版本的。
没错,我最近就在折腾这个,如何把自己欣赏的网站 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,可以少手写的点吗,免去耗费脑细胞。
嗯,总结:
- request请求网页
- cheerio解析网页,提取自己想要的元素,组合到一个dic中
- 输出
一般来说,我们需要使用路由把这个微服务挂钩到express下面
类似于这样app.use('/xx', require('./routes/xx'))
那么,有没有坑
有,当然有,而且我踩了三个
- 网页内容是异步加载的,怎么办呢?
- async、await操作对node版本是有要求的,怎么办?
- 拿到这个页面的数据,发现某个字段只是一个中间数据,还需要再次请求,才能拿到真正的那个数据,又怎么办?
网页内容是异步加载的,怎么办呢?
这里你该去了解一下phantomjs,简单的说,他就像是一个浏览器,不过没有界面而已,因此,异步加载的内容这个坑就可以交给他,只不过,这个库巨慢无比,感觉投入使用不大靠谱,但总是可以搞定异步加载的内容。
async、await操作对node版本是有要求的,怎么办
这时候,就要升级你的node了
我记得貌似我之前是6.x版本,使用async/await操作是报错了的。
发现某个字段只是一个中间数据,还需要再次请求
这种你应该从代码中可以看到我已经爬了这个坑,那就是对列表中每一项在发起一个异步请求。
我这里尝试过直接for,也用过rx,都是ok的,个人感觉rx的concatMap操作更好,因为他帮你做了并发,你用for await,本质上是一个个在做。
大功告成
来试试吧,这是我的https://brzhang.club/nodeapi/api/joke_gif/1