V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
dawncold
V2EX  ›  分享创造

抓取微信公众号中的图片

  •  1
     
  •   dawncold ·
    dawncold · 2015-12-13 23:39:15 +08:00 · 7804 次点击
    这是一个创建于 3274 天前的主题,其中的信息可能已经有所发展或是发生改变。

    因为 GF 经常找我帮忙把某个微信号发的谋篇文章中的图片提取出来用,所以才写这样的一个工具。

    之前我的做法是用 Chrome 打开要提取图片的页面,用 jquery-injection 这个浏览器插件插入一个 jquery 库,把所有 img 都提取出来,并只要 data-src 中的 url ,因为当你打开时服务端会根据你的设备给出最好的图片格式,例如 ios 就给原始的 gif , jpg 等, chrome 等会给出 webp 格式的,而 data-src 中的是原始文件 url 。

    得到一个一行行的 url 后复制到编辑器中,替换\n 为',',这样就可以构造一个 python 中列表的样式,直接复制到 python 中处理,当然现在我可能会用 awk ,那样更快,不过得查一些函数。到 python 中主要是给 url 加 index ,例如 url 基本都是 HOST:/mmbiz/HASH/0?wx_fmt=gif 这样的格式,我会把 /0 替换成 /0.0 ,/0.1 ,/0.2 ,并把扩展名加好,扩展名来自 url 中的 wx_fmt 参数(现在看可能从 data-type 中取比较好), index 根据列表的 index 来,之后再把处理好的 url 复制到编辑器中保存,最后用 wget 下载,大约是:

    for i in `cat urls`;do wget $i;done
    

    前面加的 index 在这里就用得上了,下载好之后会按照顺序排好。

    因为最近在搞 js 方面的东西,就用 nodejs 完成上面的工作,大约是这样:

    var request = require('request');
    var _ = require('lodash');
    
    var url;
    
    if (process.argv.length > 2) {
        url = process.argv[2];
    } else {
      console.error('please provide url');
      process.exit(1);
    }
    
    request.get({
      url: url
    }, function(err, response, body) {
      if (response.statusCode != 200) {
        console.log(err);
        console.log(body);
      } else {
        var imgUrlPattern = /( http:\/\/mmbiz\.qpic\.cn\/mmbiz\/.*?(\?wx_fmt=(.*?))?)"/g;
        var result;
        var urls = [];
        while((result = imgUrlPattern.exec(body)) != null) {
          urls.push(result[1]);
        }
        urls = _.map(urls, function(url, index) {
          return url.replace('/0', '/0.' + index);
        });
        _.forEach(urls, function(url) {
          console.log(url);
        });
      }
    });
    

    之后可能会结合 express , kue , redis 等做一个和之前发布的提取微博视频似的那样的工具。

    希望你们的 GF 找你们时也用得上。

    第 1 条附言  ·  2015-12-14 23:38:35 +08:00

    修改了一下昨晚贴出的代码(因为长度超了所以不再贴出,只是多输出了 biz 参数)

    下载部分的代码大概这样:

    var fs = require('fs');
    var mkdirp = require('mkdirp');
    var request = require('request');
    var _ = require('lodash');
    var urls;
    var lines;
    var data = '';
    var biz;
    var SUPPORT_EXTS = {
      'image/gif': 'gif',
      'image/png': 'png',
      'image/jpeg': 'jpg',
      'image/jpg': 'jpg'
    };
    
    process.stdin.setEncoding('utf8');
    
    process.stdin.on('readable', function() {
      var chunk = process.stdin.read();
      if (chunk !== null) {
        data += chunk;
      }
    });
    
    process.stdin.on('end', function() {
      lines = data.split('\n');
      if (lines.length <= 1) {
        console.log('no image list');
        process.exit(1)
      }
      biz = lines[0];
      urls = _.filter(lines, function(url, index) {
        return index > 0 && !_.isEmpty(_.trim(url));
      });
      if (_.isEmpty(urls)) {
        console.log('no image list');
        process.exit(1);
      }
    
      mkdirp('data/' + biz, function(err) {
        if (err) {
          console.log(err);
        } else {
          _.forEach(urls, function(url, index) {
            request.head({
              url: url
            }, function(err, response, body) {
              if (err) {
                console.log(err);
              } else {
                var contentType = response.headers['content-type'];
                if (contentType == 'text/plain') {
                  return;
                }
                var ext = SUPPORT_EXTS[contentType];
                if (!ext) {
                  console.log(contentType);
                  console.log(url);
                }
                var stream = fs.createWriteStream('data/' + biz + '/' + index + '.' + ext);
                request.get(url).pipe(stream);
              }
            });
          });
        }
      });
    });
    

    保存路径中使用 biz 的内容其实不太好, biz 的内容是 base64 编码后的 id ,可能是公众号 id ,这里作为一级目录显然可能会在提取同一个号的多篇文章时冲突,可以考虑添加其他几个参数作为目录的名字或者自己想办法保证

    目前可以这样用:node cli.js URL | node download.js,会在当前目录中创建 data 目录并保存图片到data/公众号 id

    第 2 条附言  ·  2015-12-15 00:30:27 +08:00
    暂时先这样了各位,可能等我懒得用命令行的时候会去写界面的吧
    第 3 条附言  ·  2015-12-15 12:55:57 +08:00

    微信公众号文章中必须有的参数是:

    1. __biz , base64 编码的
    2. mid ,可能是 medium_id 的意思
    3. idx ,序号,每个图文可包含最多 3 篇文章,这个是第几篇的意思
    4. sn ,可能是 sign ,对前面三个参数的签名?
    7 条回复    2015-12-17 12:04:19 +08:00
    sdsnyx
        1
    sdsnyx  
       2015-12-13 23:59:01 +08:00 via iPhone
    求一个这样的有好图片的公众号
    yanwen
        2
    yanwen  
       2015-12-14 00:05:01 +08:00
    可以做成一个小工具不?
    dawncold
        3
    dawncold  
    OP
       2015-12-14 00:30:06 +08:00
    @sdsnyx 这个我不造啊,那些视觉、设计、艺术方面的公众号都有很多图吧
    dawncold
        4
    dawncold  
    OP
       2015-12-14 00:31:54 +08:00
    @yanwen 可能会做成一个单页应用,提交一个文章 url 把图链接给出或者把图抓好后打包给出
    chanssl
        5
    chanssl  
       2015-12-14 08:48:04 +08:00
    @dawncold 期待!
    dawncold
        6
    dawncold  
    OP
       2015-12-14 09:13:44 +08:00
    @chanssl 用命令行不是也可以吗,比如: for i in `node cli.js <URL>`;do wget $i;done
    joshz
        7
    joshz  
       2015-12-17 12:04:19 +08:00
    写成油猴脚本用户体验更好吧,不用复制粘贴了,设置个默认下载路径,看到微信公众号文章就提取。当然工作要复杂很多,我就放个嘴炮。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2483 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 01:21 · PVG 09:21 · LAX 17:21 · JFK 20:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.