背景
官网想在新闻那里通过接口同步公众号上发布的文章,实际上微信官方是提供了相关 API
的:通过 article_id 获取已发布文章,但是后端说他没法获取到 access_token 且这个接口也没法得到所有的文章,所以他就自己使用 python
爬取,但是最终文章内容,只能返回一个微信文章链接给我(一般不都直接是富文本嘛),所以我要实现的就是在网站上,根据一个链接,将其内容展示出来
解决方案和遇到问题
那我当即就想到了用 iframe
啊,于是有下面代码:
<iframe
src="https://mp.weixin.qq.com/s?xxxxxxxxxe#rd"
frameborder="0"
width="1000"
height="900"
/>
但是得到的结果却是:
查了查才知道,这是因为源服务器进行了某些设置,不允许被 iframe
嵌入,这就麻烦了,怎么搞呢?
解决iframe被源服务器阻止
我突然想起了我之前一个需求:
那 iframe
中的请求也能被代理吗? 于是我决定也先本地配置代理试一试:
<iframe
src="/weixin-article/s?xxxxxxxxxe#rd"
frameborder="0"
width="1000"
height="900"
/>
// vue.config.js
module.exports = {
// ...
devServer: {
// ...
proxy: {
'/weixin-article': {
target: 'https://mp.weixin.qq.com/',
secure: true,
changeOrigin: true,
pathRewrite: {
'^/weixin-article': ''
}
}
}
}
}
结果发现真的可以:
但是图片不知道为什么一直处于加载中,检查元素并问了 chatgpt
后才发现,好家伙,src
属性搞了个不可见的元素,真正的图片路径存储在了 data-src
属性中:
这可如何是好?
解决iframe中图片始终处于加载中状态
要想解决就得想办法将 iframe
中的 img
标签的 src
属性用它的 data-src
属性替换,等于说要对 iframe
内部进行 DOM
操作,等于说是要在 iframe
元素内容都渲染之后再修改内部元素属性,这时候我突然想,既然 iframe
会自动通过 src
属性获取内容并渲染,那我可不可以主动请求 src
得到内容后(大概率是一串文档片段)修改内部 img
元素的 src
属性?关键就在于主动请求 iframe
的 src
后获取到的内容,是不是就是 iframe
渲染的内容
先主动请求iframe的src获取内容看看:
import axios from 'axios'
const realurl = '/weixin-article/s?xxxxxx#rd'
axios.get(realurl).then(res => {
console.log('##################res:', res)
})
发现它返回的就是一个 html
文档:
那后续就容易了:
<iframe id="iFrame" src="" frameborder="0" width="1000" :height="height" />
import axios from 'axios'
export default {
data() {
return {
height: 0
}
},
mounted() {
const realurl = '/weixin-article/s?xxxxxxx#rd'
axios.get(realurl).then(res => {
if (res.data) {
let html = res.data
html = html.replace(/data-src/g, 'src') // 将 data-src 转化为 src
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/g, '') // 移除HTML内容中所有的<script>标签,这样可以避免在iframe中执行潜在的不受信任的脚本。
.replace(/https/g, 'http') // 将HTML内容中所有的https替换为http,可能是为了避免在HTTPS环境下加载非HTTPS资源导致浏览器警告
.replace(/<html/g, '<html style="overflow: hidden;"') // 去掉滚动条
// 还有一种方法去除滚动条,将 iframe 标签的 scrolling 属性设置为 no
const html_src = html
const iframe = document.getElementById('iFrame')
iframe.src = html_src
// 这时候就成功拿到了微信公众号文章的静态网页
const doc = iframe.contentDocument || iframe.document
// 将静态页面写入iframe中
setTimeout(() => {
// 获取文档高度赋值Iframe去除滚动条
this.height = doc.documentElement.scrollHeight
}, 500)
// 发现 iframe 内容还是为空,找了半天发现这个元素被默认设置为不可见了,这里设置为可见
doc.getElementById('js_content').style.visibility = 'visible'
}
})
},
}
看一下效果:
这个。。。大概就是传说中的图片防盗链了吧?怎么解决呢?
解决图片防盗链
上 chatgpt
搜了搜:
好,那就试试,将 iframe
中 referrer
设置为不发送,完善代码:
import axios from 'axios'
export default {
data() {
return {
height: 0
}
},
mounted() {
const realurl = '/weixin-article/s?xxxxxxx#rd'
axios.get(realurl).then(res => {
if (res.data) {
let html = res.data
html = html.replace(/data-src/g, 'src') // 将 data-src 转化为 src
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/g, '') // 移除HTML内容中所有的<script>标签,这样可以避免在iframe中执行潜在的不受信任的脚本。
.replace(/https/g, 'http') // 将HTML内容中所有的https替换为http,可能是为了避免在HTTPS环境下加载非HTTPS资源导致浏览器警告
.replace(/<html/g, '<html style="overflow: hidden;"') // 去掉滚动条
// 还有一种方法去除滚动条,将 iframe 标签的 scrolling 属性设置为 no
const html_src = html
const iframe = document.getElementById('iFrame')
iframe.src = html_src
// 这时候就成功拿到了微信公众号,这个html_src是个静态网页
const doc = iframe.contentDocument || iframe.document
// 设置 iframe 中请求不发送 referrer,以绕过图片防盗链
const htmlArr = html_src.split('</head>')
const html_src_add = htmlArr[0] + '<meta name="referrer" content="never"></head>' + htmlArr[1]
doc.write(html_src_add)
// 将静态页面写入iframe中
setTimeout(() => {
this.height = doc.documentElement.scrollHeight
}, 500)
// 通过延时获取文档高度赋值Iframe去除滚动条,根据实际情况增加延时时间
doc.getElementById('js_content').style.visibility = 'visible'
}
})
},
}
ok!实现了:
线上代理配置
之前的代理只配置了本地开发时的代理,现在配置一下 nginx
实现线上代理,主要是在 nginx
的配置文件中添加以下代码:
server {
#生产环境下获取微信公众号文章代理
location /weixin-article/ {
proxy_pass https://mp.weixin.qq.com/;
}
}