分类 编程语言 下的文章

做过爬虫的小伙伴应该都很清楚要伪装自己的请求,包括请求头等,同时也要隐藏自己的ip,不然的话很快自己的ip就被封了,访问不了了。免费的代理ip很多,也有收费的,要是你的资金允许,或者对ip要求比较高的话,建议直接去购买收费的代理ip,直接通过接口就能获取。小编在这里主要是说下爬取的快代理的免费ip的部分,并且验证他对自己要访问的网站是否可以访问,并且保存到redis或者文件当中。
我们打开快代理的网站,免费代理的页面https://www.kuaidaili.com/free/inha/1/
好了,下面下面分享下爬取的代码吧

import requests
from lxml import etree
from fake_useragent import UserAgent
import redis
import threading
import time


pool = redis.ConnectionPool(host='127.0.0.1', port=6379, db=1, password='')

# 测试的URL
test_url = "https://www.1688.com/"

def get_proxy(url):
    '''爬取快代理的免费代理ip'''
    headers= {'User-Agent':str(UserAgent(verify_ssl=False).random)}
    response = requests.get(url,headers=headers)
    selector = etree.HTML(response.text)
    proxies = []
    for each in selector.xpath('//table[@class="table table-bordered table-striped"]/tbody/tr')[1:]:
        ip = each.xpath("./td[1]/text()")[0]
        port = each.xpath("./td[2]/text()")[0]
        proxy = ip + ":" + port
        proxies.append(proxy)
    test_proxies(proxies, test_url, 'ip_proxy')

def test_proxies(proxies, test_url , ipname):
    '''检查IP是否可以使用'''
    proxies = proxies
    normal_proxies = []
    count = 1
    for proxy in proxies:
        print("第{}个, {}".format(count, proxy))
        count += 1
        try:
            headers= {'User-Agent':str(UserAgent(verify_ssl=False).random)}
            response = requests.get(test_url, headers=headers, proxies={"http": proxy}, timeout=1)
            time = response.elapsed.total_seconds()
            if response.status_code == 200:
                print("{}可以使用".format(proxy))
                normal_proxies.append(proxy)
            else:
                print("{}--{}--可以不可以访问".format(proxy, response.status_code))
        except Exception as e:
            print("{}无效".format(proxy))
    write_proxy(normal_proxies, ipname)

def write_proxy(proxies, name):
    '''写入文件或redis'''
    for proxy in proxies:
        # 写入redis
        r = redis.Redis(connection_pool=pool)
        r.lpush(name, proxy)
        print(r.lrange(name, 0, -1))
        # 写入文件
        with open("./{}.txt".format(name), 'a+') as f:
            f.write(proxy + '\n')
    print("录入完成!!!")

if __name__ == "__main__":
    base_url = "https://www.kuaidaili.com/free/inha/{}/"
    for i in range(1, 3000):
        print("第{}页".format(i))
        url = base_url.format(i)
        get_proxy(url)

5.jpg

小编今天在使用npm install安装东西的时候出现了这样的错误

gyp: No Xcode or CLT version detected!
gyp ERR! configure error
gyp ERR! stack Error: `gyp` failed with exit code: 1
gyp ERR! stack     at ChildProcess.onCpExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/configure.js:351:16)
gyp ERR! stack     at ChildProcess.emit (events.js:315:20)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:277:12)
gyp ERR! System Darwin 20.3.0
gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Users/zero/projects/test/vue_test/big-screen-vue-datav/node_modules/fsevents
gyp ERR! node -v v14.16.0
gyp ERR! node-gyp -v v5.1.0
gyp ERR! not ok

看上面的提示可能是Xcode的问题,那么我们就重装下Xcode的吧,果然解决了问题。

xcode-select --install

ThinkPHP实现微信分享好友转发朋友圈自定义图片和文字的方法,在开始之前呢,我们需要准备好一些东西:

前期准备

  • 认证的公众号,订阅号或者服务号都可以,只要是认证过的就可以。
  • 公众号的AppID和AppSecret,登录微信公众平台,开发—基本配置,就可以看到啦。
  • 设置JSJS接口安全域名,登录微信公众平台,设置—公众号设置—功能设置里,填写就可以了。
  • 官方SDK,我们直接下载案例就可以,下载地址:http://demo.open.weixin.qq.com/jssdk/sample.zip里面包含php、java、nodejs以及python的示例代码。

服务端准备(ThinkPHP)

  • 将刚刚下载的jssdk.php文件重命名为Jssdk.php,然后和access_token.php、jsapi_ticket.php一起放入到ThinkPHP框架的第三方接口扩展目录下(extend/org/wechat/)。
  • 修改Jssdk.php文件

    1. 首先我们需要在构造函数中设置 $this->path = __DIR__ . DS; 即:

      namespace wechat;
      class Jssdk  {
       private $appId;
       private $appSecret;
       private $path;
       public function __construct($appId, $appSecret) {
       $this->appId = $appId;
       $this->appSecret = $appSecret;
       $this->path = __DIR__ . DS;
       }
       ...
      }
    2. get_php_file函数返回值中的$filename改为 $this->path.$filename ,即:

      private function get_php_file($filename) {
        return trim(substr(file_get_contents($this->path.$filename), 15));
      }
    3. 设置token的缓存,修改getAccessToken,加入cache

      private function getAccessToken() {
        // access_token 应该全局存储与更新,以下代码以写入到文件中做示例
        // cache('at', ['access_token'=>'sss', 'expire_time' => '123']);
        // $data = json_decode($this->get_php_file("access_token.php"));
        $data = cache('at');
        if ($data['expire_time'] < time()) {
       // 如果是企业号用以下URL获取access_token
       // $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret";
       $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret";
       $res = json_decode($this->httpGet($url));
       $access_token = $res->access_token;
       if ($access_token) {
         // $data->expire_time = time() + 7000;
         // $data->access_token = $access_token;
         cache('at', ['access_token'=>$access_token, 'expire_time' => time() + 7000]);
         // $this->set_php_file("access_token.php", json_encode($data));
       }
        } else {
       $access_token = $data['access_token'];
        }
        return $access_token;
      }
  • 在控制器里面调用

    1. 导入jssdk, use wechat\Jssdk;
    2. 传给前端

      $jssdk = new Jssdk($AppID, $AppSecret);
      $res = $jssdk->getSignPackage();
      $appId = $res['appId'];
      $timestamp = $res['timestamp'];
      $nonceStr = $res['nonceStr'];
      $signature = $res['signature'];
      $this->assign(
       array(
           'appId'=>$appId,
           'timestamp'=>$timestamp,
           'nonceStr'=>$nonceStr,
           'signature'=>$signature,
       )
      );

      网页端

  • 导入jssdk

    <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
    
  • 设置分享内容

    <script type="text/javascript">
      var title = '分享标题';
      var imgUrl = '分享图片';
      var link = '分享地址';
      var desc = '分享描述';
     
      wx.config({
          debug: false,//开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
          appId: '{$appId}', // 必填,公众号的唯一标识
          timestamp: '{$timestamp}', // 必填,生成签名的时间戳
          nonceStr: '{$nonceStr}', //必填, 生成签名的随机串
          signature: '{$signature}', //必填,签名
          jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareQZone'] //必填, JS接口列表,这里只填写了分享需要的接口
      })
      wx.ready(function () {
          wx.onMenuShareTimeline({
              title: title,
              link: link,
              desc:desc,
              imgUrl: imgUrl,
              success: function() {
                  // 用户确认分享后执行的回调函数
              },
              cancel: function() {
                  // 用户取消分享后执行的回调函数
              }
          });
          wx.onMenuShareAppMessage({
              title: title, // 分享标题
              desc: desc, // 分享描述
              link: link, // 分享链接
              imgUrl: imgUrl, // 分享图标
              type: 'link', // 分享类型,music、video或link,不填默认为link
              dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
              success: function() {
                  // 用户确认分享后执行的回调函数
              },
              cancel: function() {
                  // 用户取消分享后执行的回调函数
              }
          });
      })
    </script>

做前端的小伙伴应该都很清楚使用vue的v-for就可以很方便的一个数组,然后对数组中的元素就行展示或者操作,那么有没有考虑过遍历对象呢。我们在学习js的时候都应该知道,我们使用js是可以遍历对象的,比如:

var oj = {"a":1, "b": 2, "c": 3}
for(var i in oj){
    console.log(i,"----->" ,oj[i])
}
// 输出
// a -----> 1
// b -----> 2
// c -----> 3

其实使用vue的v-for的效果是一样的,也是用的in。下面案例的输出结果是一样的:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <p v-for="(val, key, index) in list">{{key}}----->{{val}}</p>
    </div>

    <script>
        var obj=
        new Vue({
            el: '#app',
            data:{
                list:{
                    'a':1,
                    'b':2,
                    'c':3
                }
            }
        })
    </script>
</body>
</html>

今天在进行测试的时候,发现前端后端签名经常不一致,而且是在参数里面含有中文的时候才会出现这样的问题。为了排查问题,小编又找了几个第三方的md5加密工具,对需要签名的字符串进行加密,经过比较发现是前端出现了问题,后来小编又重新找了个加密文件,在这里跟大家分享下。具体的原理小编也没有深究,好像是需要将中文先转为utf-8。最后几行是在es6中导出使用的,如果需要自己解开注释。
源代码:

function md5(string) {
    function md5_RotateLeft(lValue, iShiftBits) {
        return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
    }

    function md5_AddUnsigned(lX, lY) {
        var lX4, lY4, lX8, lY8, lResult;
        lX8 = (lX & 0x80000000);
        lY8 = (lY & 0x80000000);
        lX4 = (lX & 0x40000000);
        lY4 = (lY & 0x40000000);
        lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
        if (lX4 & lY4) {
            return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
        }
        if (lX4 | lY4) {
            if (lResult & 0x40000000) {
                return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
            } else {
                return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
            }
        } else {
            return (lResult ^ lX8 ^ lY8);
        }
    }

    function md5_F(x, y, z) {
        return (x & y) | ((~x) & z);
    }

    function md5_G(x, y, z) {
        return (x & z) | (y & (~z));
    }

    function md5_H(x, y, z) {
        return (x ^ y ^ z);
    }

    function md5_I(x, y, z) {
        return (y ^ (x | (~z)));
    }

    function md5_FF(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_F(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };

    function md5_GG(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_G(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };

    function md5_HH(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_H(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };

    function md5_II(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_I(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };

    function md5_ConvertToWordArray(string) {
        var lWordCount;
        var lMessageLength = string.length;
        var lNumberOfWords_temp1 = lMessageLength + 8;
        var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
        var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
        var lWordArray = Array(lNumberOfWords - 1);
        var lBytePosition = 0;
        var lByteCount = 0;
        while (lByteCount < lMessageLength) {
            lWordCount = (lByteCount - (lByteCount % 4)) / 4;
            lBytePosition = (lByteCount % 4) * 8;
            lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
            lByteCount++;
        }
        lWordCount = (lByteCount - (lByteCount % 4)) / 4;
        lBytePosition = (lByteCount % 4) * 8;
        lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
        lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
        lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
        return lWordArray;
    };

    function md5_WordToHex(lValue) {
        var WordToHexValue = "",
            WordToHexValue_temp = "",
            lByte, lCount;
        for (lCount = 0; lCount <= 3; lCount++) {
            lByte = (lValue >>> (lCount * 8)) & 255;
            WordToHexValue_temp = "0" + lByte.toString(16);
            WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length - 2, 2);
        }
        return WordToHexValue;
    };

    function md5_Utf8Encode(string) {
        string = string.replace(/\r\n/g, "\n");
        var utftext = "";
        for (var n = 0; n < string.length; n++) {
            var c = string.charCodeAt(n);
            if (c < 128) {
                utftext += String.fromCharCode(c);
            } else if ((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            } else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    };
    var x = Array();
    var k, AA, BB, CC, DD, a, b, c, d;
    var S11 = 7,
        S12 = 12,
        S13 = 17,
        S14 = 22;
    var S21 = 5,
        S22 = 9,
        S23 = 14,
        S24 = 20;
    var S31 = 4,
        S32 = 11,
        S33 = 16,
        S34 = 23;
    var S41 = 6,
        S42 = 10,
        S43 = 15,
        S44 = 21;
    string = md5_Utf8Encode(string);
    x = md5_ConvertToWordArray(string);
    a = 0x67452301;
    b = 0xEFCDAB89;
    c = 0x98BADCFE;
    d = 0x10325476;
    for (k = 0; k < x.length; k += 16) {
        AA = a;
        BB = b;
        CC = c;
        DD = d;
        a = md5_FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
        d = md5_FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
        c = md5_FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
        b = md5_FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
        a = md5_FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
        d = md5_FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
        c = md5_FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
        b = md5_FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
        a = md5_FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
        d = md5_FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
        c = md5_FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
        b = md5_FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
        a = md5_FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
        d = md5_FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
        c = md5_FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
        b = md5_FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
        a = md5_GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
        d = md5_GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
        c = md5_GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
        b = md5_GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
        a = md5_GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
        d = md5_GG(d, a, b, c, x[k + 10], S22, 0x2441453);
        c = md5_GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
        b = md5_GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
        a = md5_GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
        d = md5_GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
        c = md5_GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
        b = md5_GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
        a = md5_GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
        d = md5_GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
        c = md5_GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
        b = md5_GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
        a = md5_HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
        d = md5_HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
        c = md5_HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
        b = md5_HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
        a = md5_HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
        d = md5_HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
        c = md5_HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
        b = md5_HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
        a = md5_HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
        d = md5_HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
        c = md5_HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
        b = md5_HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
        a = md5_HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
        d = md5_HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
        c = md5_HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
        b = md5_HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
        a = md5_II(a, b, c, d, x[k + 0], S41, 0xF4292244);
        d = md5_II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
        c = md5_II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
        b = md5_II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
        a = md5_II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
        d = md5_II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
        c = md5_II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
        b = md5_II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
        a = md5_II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
        d = md5_II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
        c = md5_II(c, d, a, b, x[k + 6], S43, 0xA3014314);
        b = md5_II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
        a = md5_II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
        d = md5_II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
        c = md5_II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
        b = md5_II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
        a = md5_AddUnsigned(a, AA);
        b = md5_AddUnsigned(b, BB);
        c = md5_AddUnsigned(c, CC);
        d = md5_AddUnsigned(d, DD);
    }
    return (md5_WordToHex(a) + md5_WordToHex(b) + md5_WordToHex(c) + md5_WordToHex(d)).toLowerCase();
}

# es6导出使用
//module.exports = {
//    md5: md5
//}

小编今天在使用Sqlalchemy连接不同数据库,生成两个相同的表名的时候,出现了这样的错误Table 'XXX' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object。导致第二个表无法生成,下面小编说下解决办法:

给第二个表使用一个新的metadata
代码示例:

from sqlalchemy import MetaData
 
class A(db.Model):
    __tablename__ = 'A'
 
    ···
 
class A(db.Model):
    __bind_key__ = 'newdb'
    __tablename__ = 'A'
    metadata = MetaData()
 
    ···

小编今天在制作视频播放组件的时候,想让视频默认直接就是全屏播放,或者点击之后全屏播放,并不是点击全屏之后再进行全屏,实现方式可能有很多种,小编在这里介绍一种比较简单的,通过官方的VideoContext就可以实现的。
首先我们要引入视频组件:

<video id="myVideo" :src="videoUrl" @click="playvideo" @fullscreenchange="fullscreenchange"></video>

然后设置点击事件

playvideo: function(e){
    this.videoContext = uni.createVideoContext('myVideo', this);
    this.videoContext.requestFullScreen({direction: 0});
    this.videoContext.play()
},

这样就成功实现了全屏播放了,不过我们发现退出全屏后视频还在播放,那么我们就要设置下在退出全屏后,就要停止播放视频了。

fullscreenchange (e){
    if(!e.detail.fullScreen){  
        this.videoContext.pause()
        this.showvideo = false
    }
},

User-Agent 就是用户代理,又叫报头,是一串字符串,相当于浏览器的身份证号,我们在利用python发送请求的时候,默认为: python-requests/2.22.0,所以我们在利用爬虫爬取网站数据时,频繁更换它可以避免触发相应的反爬机制。为了解决这个问题,我们引入了fake-useragent,fake-useragent对频繁更换User-Agent提供了很好的支持,可谓防反扒利器。下面我们就介绍fake-useragent。
安装

pip install fake-useragent

使用

from fake_useragent import UserAgent
headers= {'User-Agent':str(UserAgent().random)}
r = requests.get(url, proxies=proxies, headers=headers, timeout=10)

fake-useragent Maximum amount of retries reached 错误的解决方法

添加参数verify_ssl=False也就是:UserAgent(verify_ssl=False).random

在Python中,我们使用json.dumps()将 Python 对象编码成 JSON 字符串的时候,我们发现会出现很多空格,这个在json.dumps()官方文档里也说明了,为了美观默认会加上逗号空格和冒号空格。因为有时候我们需要处理字符串,比如加密等,但是由于多了空格,加密后肯定不一致的,那么我们需要去掉这些空格,按照文档里说的,我们只需要加上separators=(',',':')这个参数就可以解决了。
示例代码

import json
ll = ["123, "1234"]
json.dumps(ll, separators=(',', ':'))

不知道大家平时从哪里获取小图标,矢量图等。小编呢通常是从iconfont上获取图标,大家一搜就出来了。在这之前小编都是下载到本地,保存为png文件使用的,其实还有一种方式,可以在线使用的。下面小编就说下具体的操作步骤:
首先登陆iconfont,使用github以及微博登录都可以。
登录之后找到自己想要的图标,点击添加到购物车(虽然叫购物车,但是是免费的哦)
1.png
然后点击右上交购物车图标,然后会弹出添加到项目,如果没有的话可以新建项目
2.png
新建项目后,可以管理项目
4.png
点击跟多操作修改项目前缀,不能为 el-icon- ,因为这个可能会和Element-UI自带的图标重合
5.png
然后点击Font class,在点击,查看在线链接,然后复制生成的链接就可以了。需要注意的是:每次新增或删除,特别是新增如果要引入新增的图标,要重新生成CSS文件,这时候需要把新生成的CSS文件链接重新在 App.vue 中引入,替换掉原来的链接。
6.png

然后在项目的APP.vue中引入,这时我们就可以通过class="iconfont el-icon-mo-xxx" 的方式在项目中使用引入的第三方图标了。

如果想要去掉 iconfont 这个类直接用 el-icon-mo-xxx 来使用图标的话我们还需要这么使用(链接换成自己的):

<style>
  @import "//at.alicdn.com/t/font_2209515_4j5tnkfc104.css";

  [class^="el-icon-mo"], [class^=" el-icon-mo"] {
    font-family: "iconfont", serif !important;
    font-size: 16px;
    font-style: normal;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }
</style>