如何通过程序自动抓取微信公众号文章的内容和视频

如何通过程序自动抓取微信公众号文章的内容和视频

月光魔力鸭

2020-12-04 09:02 阅读 1296 喜欢 4 puppeteer 爬虫

最近看到知乎上一话题:微信公众号文章里的视频怎么下载?。看还是有很多人推荐啥工具啊,很是捉急,当然本次的主题也是通过程序来获取内容,但是目前来说仅仅是娱乐吧。

微信公众号文章里的视频怎么下载

其实,如果只是针对一个文章的话是很简单的,直接通过复制文章地址 ,在谷歌浏览器打开后,然后在视频右下角点击全屏,点击右下角的点点点,点击下载即可。

当然也可以通过F12来查看video标签或查看network数据请求。

这些都是一些基本的操作了,这里不细说了,下边主要是通过puppeteer来获取视频并下载。

之前通过puppeteer其实已经做过很多事情了,登录啊 签到啊 等等,我也不知道我为啥会继续做,因为总体来说没有什么技术含量的,目前主要是做个铺垫,后续研究下如何抓取所有公众号的所有文章。

话不多说,直接上代码

/**
 * 小爬虫。
 * 目标:
 * 将微信公众号的视频下载下来,将文章保存PDF。
 * 
 * 问题:如果等待资源文件全部加载完毕后,才打印PDF。
 * 
 * 后续计划:
 * 获取某公众号内所有的文章地址,并进行轮询获取所有视频/文章PDF。
 * --微信。
 */

const puppeteer = require('puppeteer');
const path = require('path');
const fs = require('fs');
const helper = require('think-helper');//创建目录api
const axios = require('axios');
const folder = __dirname;

let url = `https://mp.weixin.qq.com/s/fwY6FPFCfgdJNTd22qqjKg`;
// let url = `https://mp.weixin.qq.com/s?__biz=MjM5MDAwNTk0MA==&mid=2653094471&idx=1&sn=8f430a2f1764714815ee9f523091b88b`;

function wsf(rs,ws){
    return new Promise((r,j)=>{
        rs.pipe(ws);
        ws.on('close',e=>{
            r();
        })
    });
}


(async function(){

    let browser = await puppeteer.launch({
        headless : true,
        executablePath : 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
    });

    console.log(`开启新页面:[${url}]`);
    const page = await browser.newPage();

    let asyncArr = [];
    page.on('response',async (res)=>{
        try{
            let json = await res.json();//
            if(json && json.url_info){
                console.log(`获取到异步视频数据.`);
                asyncArr = asyncArr.concat(json.url_info);
            }
        }catch(e){}
    })
    console.log(`等待页面数据请求加载完成。`)
    await page.goto(url,{
        waitUntil : 'networkidle2'//请求结束
    });

    //url
    let videoArr = await page.evaluate(()=>{
        return $('video').get().map(t=>{
            return $(t).attr('src');
        })
    });
    
    //获取公众号信息
    let name = await page.evaluate(()=>{
        return $('.account_nickname_inner').length > 0 ? $('.account_nickname_inner').text() : $('.rich_media_meta_nickname #js_name').text();
    });
    let title = await page.evaluate(()=>{
        return $('.common_share_title').length > 0 ? $('.common_share_title').text() : $('.rich_media_title').text();
    });
    name = name.trim();
    title = title.trim();
    console.log(`获取到公众号信息:[${name}-${title}]`)


    //创建目录
    let folderPath = path.join(folder,name,title);
    helper.mkdir(folderPath);//创建目录
    //对视频做处理。
    let vr = [];

    vr =vr.concat(videoArr.map(t=>{
        let u = new URL(t);
        let fileName = title+path.extname(u.pathname);
        let filePath = path.join(folderPath,fileName);
        return {
            title : fileName,
            filePath : filePath,
            url : t
        }
    }));

    vr = vr.concat(asyncArr.map(t=>{
        let u = new URL(t.url);
        let fileName = t.video_quality_wording +'-'+ title+path.extname(u.pathname);
        let filePath = path.join(folderPath,fileName);
        return {
            title : fileName,
            filePath : filePath,
            url : t.url
        }
    }));

    console.log(`开始准备下载视频文件`);
    for(let video of vr){
        console.log(`开始下载视频:[${video.title}]`)
        await axios.get(video.url,{
            responseType : 'stream'
        }).then(async rs=>{
            await wsf(rs.data,fs.createWriteStream(video.filePath));
        })
    }

    //将页面滚动到最底部
    await page.evaluate(()=>{
        $('html').get(0).scrollTop = $('html').get(0).scrollHeight
    });
    console.log('等待资源加载.....')
    // await page.evaluate(function(){
    //     let timeLimit = 60 * 1000;
    //     return new Promise((r,j)=>{
    //         let isLoaded = true;
    //         let start = +new Date();
    //         let t = setInterval(function(){
    //             $('img').get().forEach(t=>{
    //                 var img = $(t).attr('src');
    //                 var pimg = $(t).data('src');
    //                 if(img.indexOf(pimg) > -1){
    //                     isLoaded  = isLoaded && true;
    //                 }else{
    //                     isLoaded  = isLoaded && false;
    //                 }
    //             })
    //             if(isLoaded || (+new Date()) - start > timeLimit){
    //                 clearInterval(t);
    //                 r();
    //             }
    //         },500);
    //     })
    // });


    //pdf
    console.log(`保存文章到PDF`)
    await page.pdf({
        path : path.join(folder,name,title,title+'.pdf'),
        format  : 'A4',
        printBackground  : true
    });

    console.log(`关闭浏览器`);
    await browser.close();

    process.exit(0);


})();

总体来说大约用了不到一个小时,包括实现啊调试啊,相对来说没有什么难度。不过也碰到了一个问题,资源懒加载的问题。

目前还没有啥比较好的思路来去确定资源加载完毕的回调,暂时搁置。

准备抽时间把这个小东西做成服务,扔在博客的工具里面,到时候看看情况。

转载请注明出处: https://chrunlee.cn/article/weixin-video-fetch.html


感谢支持!

赞赏支持
提交评论
评论信息 (请文明评论)
暂无评论,快来快来写想法...
推荐
最近在折腾的时候又想写less了,但是换框架了,成了thinkjs,考虑到开发阶段一直编译编译less的情况..最终根据middleware的特点实现了一个超级简单的less中间件。
最近由于系统需要一些数据进行测试,但是正常的流程都是下载pdf ,打印pdf,然后通过涂写答题卡,将涂写的扫描上传..太麻烦了,想做成简单点,通过程序直接生成..卡在了pdf转图片上,今天抽空找了下库,通过gm可以将pdf转为图片,起码第一步已经实现了,后边的涂学号之前已经做过了。
为什么要读取图片呢?需求来源于这里。我有一大堆的ppt文件,里面全是图片,想将这些图片全部拿到,然后存储在数据库中,在线上预览,一张张的保存我自然是不乐意的。
最近有客户提出了这么一个需求:微信dat文件在解码后的图片无法按照时间进行排序。 是的,解码后的文件的时间都是解码的时间,由于软件比较多,当时没做自动更新,所以在这里做一个小工具,可以将对应的解码后的图片的时间修改为微信dat文件对应的时间
关于js的编译和压缩,之前做过一个小工具了,主要就是自己项目成员大都没有这部分的技能,导致发布的时候总需要去编译压缩下.. 最终做了个命令行小工具.. 问题不在这里,前一阵子做压缩的时候发现压缩后竟然是undefined.最终才发现是es6的语法问题。
互联网应用经常需要存储用户上传的图片,比如facebook相册。 facebook目前存储了2600亿张照片,总大小为20PB,每张照片约为80KB。用户每周新增照片数量为10亿。(总大小60TB),平均每秒新增3500张照片(3500次写请求),读操作峰值可以达到每秒百万次
从上面那篇文章过来的,这里分享下nodejs对文件夹以及子文件进行批量删除的实现。
因为自己的记录笔记的应用是有道云,又想着把有道云跟自己的小网站联通起来,所以查找了有道云的,然后实现了nodejs版本的sdk.