实用小技巧

从网页上批量获取数据

Array.prototype.forEach.call($$(‘#filter-accordion-0 > web-category-tree-v2 > ul > li > a’), function (a) {
console.log(a.href);
});

selector用$$(),xpath用$x()

写一个Composer包的Composer.json模板

目录结构

{
    "name": "myjoyoo/affiliate-sdk",
    "description": "如LinkShare、CJ、PJ、Awin之类的联盟的SDK接口封装",
    "type": "l",
    "require": {
        "php": ">=8.0",
        "guzzlehttp/guzzle": "^7.3",
        "nesbot/carbon": "^2.48"
    },
    "license": "Apache",
    "authors": [
        {
            "name": "myjoyoo",
            "email": "[email protected]"
        }
    ],
    "minimum-stability": "dev",
    "autoload": {
        "psr-4": {
            "AffiliateSDK\\": "src"
        },
        "files": ["src/include/commonFunction.php"]
    }
}

改了之后,需要执行下面的命令去生效
composer dump-auotload

以Api的方式获取Instagram博主数据

Ins Api调用说明

Ins分2种用户,一种是普通的,一种是专业的,普通用户是无法满足我们需求的。

专业用户需要满足以下准备工内容:
1、一个Ins的商务账户
2、一个Facebook开发者账号
3、Facebook开发者中心建立一个App并审核通过(注意APP的类型要选择为business)
4、增加2个Product,一个facebook登录和instagram graph api
5、在app preview里去选择”pages_show_list”和”instagram_basic” 2个权限,具体别的权限看情况去选择,在正式使用之前需要要去申请这些权限。

开始,参照:https://developers.facebook.com/docs/instagram-api/getting-started

把个人的Ins账户转为专业账户
关联 https://help.instagram.com/399237934150902?fbclid=IwAR1VFQ403f16ZUvApnMtsDYuNRHcwg9il0qh4G5DDluZHNSfJU3wKK4-Zb4

登录FaceBook账户指南
https://developers.facebook.com/docs/facebook-login/web

通过php登录的话,要参考github上的代码:
https://github.com/facebookarchive/php-graph-sdk/tree/master
有一个坑是要开启session,sdk是通过session来判断state参数是否一样的,这样防止非法调用

我们需要用到这个instagram_basic api的权限,需要进行“business verification”,
https://developers.facebook.com/docs/permissions/reference/instagram_basic

验证的帮助说明在这里:
https://developers.facebook.com/docs/development/release/business-verification

 

验证步骤:
https://developers.facebook.com/apps/305829777821658/app-review/submissions/current-request/?business_id=219068326242405

在提交审核的时候,有一个deletion url,是当对方删除数据的时候,我们也要一起删除,见:
https://developers.facebook.com/docs/development/create-an-app/app-dashboard/data-deletion-callback

API的调用参考:https://developers.facebook.com/docs/instagram-api/getting-started

获取用户信息
https://graph.facebook.com/v11.0/me/accounts?access_token=
获取用户在IG里的信息
https://graph.facebook.com/v11.0/103288701887981?fields=instagram_business_account&access_token={access-token}
根据IG ID获取用户在IG里的信息
https://graph.facebook.com/v3.2/17841405339389255?fields=biography,id,username,website,followers_count,follows_count,media_count,profile_picture_url&access_token=
获取用户的IG Media列表
https://graph.facebook.com/v11.0/17841405339389255/media?access_token=
根据IG的ID获取详情
https://graph.facebook.com/v11.0/17912414743825369?fields=id,media_type,like_count,comments_count,media_url,owner,timestamp&access_token=

 

MySQL笔记

MySQL里的日期与时间

格式化:select DATE_FORMAT(now(), ‘%Y-%m-%d %T’);
从1970年1月1日到指定时间的天数:select to_days(now());

取当前时间:
select now(), current_timestamp(),current_timestamp,current_time, curtime(),current_date, curdate(), sysdate();
sysdate()函数是这个函数执行时候的时间,其它的是语句开始执行的时间。

时间的加减:

DATE_ADD(date,INTERVAL expr type)
DATE_SUB(date,INTERVAL expr type)
DATEDIFF(date1,date2),2个时间的天数
TIMESTAMPDIFF(type,date1,date2),2个时间的差额
TIMESTAMPADD(type,int_expr,date1),加时间

 

存储过程

set @var = '';
set @var2 = 'fff';
set @var = @var2;

-- 这种只能用在declare了的变量
set var = 'sdfdf';

-- 这里不会输出返回结果,只会设置变量
select `day` into @var from redash_queue_flush_sem_base_data limit 1;
-- 这里在设置变量的同时,也会返回
select @day:=`day` from redash_queue_flush_sem_base_data limit 1;


CREATE DEFINER=`dealam_stat_prod`@`%` PROCEDURE `flush_sem_base_data`()
-- 注意这里的BYEBYE,在里面用leave BYEBYE就可以退出了,存储过程里没有return之类的功能
BYEBYE:BEGIN
    declare vday varchar(100);
    declare vvar VARCHAR(50);
    declare vvalue VARCHAR(200);
    declare select_done int default 0;
    -- 定义select游标
    declare cur_queue cursor for select `var`, `value` from redash_variable limit 2;
    -- 定义游标是否循环完成的标记变量
    declare continue handler for not found set select_done = 1;
    
    if @queue_count > 0 then
        insert into redash_logs (`what`) values (concat('刷新队列里还有数据:', @queue_count));
        select concat('刷新队列里还有数据:', @queue_count) as msg;
        leave BYEBYE;
    else
        -- 找出待更新订单对应的订单日期,放入待刷新队列中
        insert ignore redash_queue_flush_sem_base_data 
            select distinct(from_unixtime(transaction_time, '%Y-%m-%d')) from da_sem_order where updated_time>@sem_last_update_time order by updated_time;
    end if;
    
    open cur_queue;
    
    loop_label:loop
        -- 注意这里变量前没有加@
        fetch cur_queue into vvar, vvalue;
        -- select_done 前没有加@
        if select_done = 1 then
            leave loop_label;
        end if;
        
        -- 注意这里变量前也没有加@,也就是说如果是declare的变量,好像是不用加的
        select vvar, vvalue;
        set @vvar2 = vvar;
        select @vvar2;
        insert into redash_logs (`what`) values (concat('刷新了sem base data,日期:', vvar));
        
    end loop;
    
END

 

查看未提交的事务
select trx_state, trx_started, trx_mysql_thread_id, trx_query from information_schema.innodb_trx\G;

查看锁信息
select * from information_schema.INNODB_TRX WHERE trx_state=’LOCK WAIT’\G

 

Postman里计算md5到form表单里

FORM表单里有一个字段是根据几个字段hash得到的值,每次去算hash很不方便,可以这样去设置。

在pre-request script里输入:

var data = request.data.c_source + 
request.data.c_affiliate_id + 
request.data.c_order_id + 
'xxxxx';

var hash = CryptoJS.MD5(data).toString();
pm.environment.set("token", hash);

// 常用操作
// 获取query参数
var str = pm.request.url.query.get("version");

// 获取post表单里的值
var str = request.data.字段名

// 获取提交的json数据
var post = JSON.parse(pm.request.body.raw);

// 向header加头
pm.request.headers.add({
    key: 'x-ShareASale-Date',
    value: myTimeStamp
});

在Form表单里输入: {{token}} 就可以自动计算了

怎么取值,可以参考PostMan的官方文档

https://learning.postman.com/docs/writing-scripts/script-references/postman-sandbox-api-reference/#scripting-with-request-data

自动计算token并加到postman的请求里:

var salt = "xxxx";
var post = JSON.parse(pm.request.body.raw);
var hash = CryptoJS.MD5(post.source_account_id + salt).toString();
post.token = hash;
pm.request.body.raw = JSON.stringify(post);

// 更多的加密方法 SHA256
var SHA256 = CryptoJS.SHA256(str).toString();
// BASE64
const data = CryptoJS.enc.Utf8.parse("test")
const base64 = CryptoJS.enc.Base64.stringify(data)

在PostMan里使用请求获取到的token当成认证参数

// linkhaitao
var new_Client_id = "x"
var new_Client_secret = "x"
var username = "x"
var scope = "x"

var token = MyUtils.BASE64(new_Client_id+":"+new_Client_secret)

// 请求可以参考: http://www.postmanlabs.com/postman-collection/Request.html#~definition

var postData = [
    {key: 'grant_type', value: 'password'},
    {key: 'username', value: username},
    {key: 'scope', value: scope}
];

var requestOption = {
    url: 'https://api.xxx.com/token',
    method: 'POST',
    header: {
        "Authorization": "Basic " + token,
        "Content-Type": "application/json"
    },
    body: {
        mode: 'urlencoded', // 这里为什么值,下面就为什么key,如果raw, formdata, urlencoded之类
        urlencoded: postData
        // mode: 'raw',
        // raw: JSON.stringify(postData)
    }
}

pm.sendRequest(requestOption, function (err, res) {
    console.log("获取到的token", res.json());
    pm.environment.set("token", res.json().access_token)
    pm.request.headers.add({
        key: "Authorization",
        value: "Bearer " + res.json().access_token
    });
})

向PostMan的Query地址里加参数

let token = "xxoo"

pm.request.addQueryParams({
    key: "token",
    value: token
});

 

PostMan里定义和使用全局函数

在某一个文件夹里的”pre request script”里,定义下面的内容

MyUtils = {
    SHA256: function SHA256(s){
        var chrsz = 8;
        var hexcase = 0;
        function safe_add (x, y) {
            var lsw = (x & 0xFFFF) + (y & 0xFFFF);
            var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
            return (msw << 16) | (lsw & 0xFFFF);
        }
        function S (X, n) { return ( X >>> n ) | (X << (32 - n)); }
        function R (X, n) { return ( X >>> n ); }
        function Ch(x, y, z) { return ((x & y) ^ ((~x) & z)); }
        function Maj(x, y, z) { return ((x & y) ^ (x & z) ^ (y & z)); }
        function Sigma0256(x) { return (S(x, 2) ^ S(x, 13) ^ S(x, 22)); }
        function Sigma1256(x) { return (S(x, 6) ^ S(x, 11) ^ S(x, 25)); }
        function Gamma0256(x) { return (S(x, 7) ^ S(x, 18) ^ R(x, 3)); }
        function Gamma1256(x) { return (S(x, 17) ^ S(x, 19) ^ R(x, 10)); }
        function core_sha256 (m, l) {
            var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2);
            var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
            var W = new Array(64);
            var a, b, c, d, e, f, g, h, i, j;
            var T1, T2;
            m[l >> 5] |= 0x80 << (24 - l % 32);
            m[((l + 64 >> 9) << 4) + 15] = l;
            for ( var i = 0; i<m.length; i+=16 ) {
            a = HASH[0];
            b = HASH[1];
            c = HASH[2];
            d = HASH[3];
            e = HASH[4];
            f = HASH[5];
            g = HASH[6];
            h = HASH[7];
            for ( var j = 0; j<64; j++) {
                if (j < 16) W[j] = m[j + i];
                else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
                T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
                T2 = safe_add(Sigma0256(a), Maj(a, b, c));
                h = g;
                g = f;
                f = e;
                e = safe_add(d, T1);
                d = c;
                c = b;
                b = a;
                a = safe_add(T1, T2);
            }
            HASH[0] = safe_add(a, HASH[0]);
            HASH[1] = safe_add(b, HASH[1]);
            HASH[2] = safe_add(c, HASH[2]);
            HASH[3] = safe_add(d, HASH[3]);
            HASH[4] = safe_add(e, HASH[4]);
            HASH[5] = safe_add(f, HASH[5]);
            HASH[6] = safe_add(g, HASH[6]);
            HASH[7] = safe_add(h, HASH[7]);
            }
            return HASH;
        }
        function str2binb (str) {
            var bin = Array();
            var mask = (1 << chrsz) - 1;
            for(var i = 0; i < str.length * chrsz; i += chrsz) {
            bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
            }
            return bin;
        }
        function 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;
        }
        function binb2hex (binarray) {
            var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
            var str = "";
            for(var i = 0; i < binarray.length * 4; i++) {
            str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
            hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
            }
            return str;
        }
        s = Utf8Encode(s);
        return binb2hex(core_sha256(str2binb(s), s.length * chrsz));
    }
}

 

PHP Carbon时区操作

在没有指定时区的时候,系统是以date_default_timezone_get 获取到的时区在进行转换。
如果我们要为某个时间单独指定时区的话,可以按以下的方式。

// 没有指定时间前
var_dump(Carbon::parse('now')->toDateTimeString());

// 实例化一个时区对象
$tz = new CarbonTimeZone('+08:00');
// 在解析的时候,把时区带上
var_dump(Carbon::parse('now', $tz)->toDateTimeString());

 

CentOS 7 编译安装Redis和RediSearch

安装Redis

安装gcc
yum install gcc

从 https://redis.io/ 下载最新的代码,解压进入

执行: make 报如下错误:
找不到“jemalloc/jemalloc.h”,我们换一种分配内存的方式
make MALLOC=libc

把redis安装到: /usr/local/redis6 目录
make PREFIX=/usr/local/redis6 install

把配置文件复制到/etc/redis.conf
cp redis.conf /etc/redis.conf

把 protected-mode yes 改成 protected-mode no
注释掉:bind 127.0.0.1 -::1

安装RediSearch

下载 代码,解压,报错

make setup
deps/readies/mk/main:6: *** GNU Make version is too old. Aborting.。 停止。

去https://www.gnu.org/software/make/ 下载最新的代码,解压,

./configure
make

生成的可执行文件make会在当前目录,后面直接用这个make就行了,不用安装,

去RedisSearch目录
../make-4.3/make setup
../make-4.3/make build

修改redis.conf,增加:loadmodule /usr/local/redis6/bin/redisearch.so

启动Redis
redis-server /etc/redis.conf

PHP在进程相关的笔记

exec和pcntl_exec的区别

echo "开始执行\n";
pcntl_exec('/usr/bin/php', ['-r', 'sleep(10);']);

// 等待10秒后,程序结束
// 这行代码不会被执行
echo "PHP代码结束\n";

exec是在当前进程里新开了一个进程,并等待新进程执行完后再接着执行,新开进程的进程号和当前进程号不同。

pcntl_exec不新开进程,在当前进程接着执行,pcntl_exec后面的php代码不再被执行,接收不到输出。

进程信号处理

常用信号:

代码 数值 说明 操作
HUP 1 通常是重启、加载配置文件
INT 2 终端中断 Ctrl – C
QUIT 3 终端退出 Ctro – \
KILL 9 强制中断,信息不能被捕获
ALRM 14 警告
TERM 15 终止运行,会生成dump?
CONT 18 恢复执行
STOP 19 暂停执行
<?php
declare(ticks = 1);
$run = true;

function signal_handler($signal) {
    global $run;
    print "Caught SIGALRM: {$signal}\n";

    switch ($signal) {
        case SIGHUP:
            # 重新加载
            break;

        case SIGINT:
        case SIGQUIT:
            $run = false;
            break;
        
        default:
            # code...
            break;
    }
}

pcntl_signal(SIGHUP, "signal_handler");
pcntl_signal(SIGINT, "signal_handler");
pcntl_signal(SIGQUIT, "signal_handler");

while ($run) {
    sleep(1);
}

echo "end.";

注意啊,这东西不能在VS Code的控制台下运行,不然中断回调函数不会被执行。

 

PHP发送AWS邮件

安装SDK:
https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/getting-started_installation.html

发送demo代码:
https://docs.aws.amazon.com/ses/latest/DeveloperGuide/examples-send-using-sdk.html

准备好key, sec

use Aws\Ses\SesClient;
use Aws\Exception\AwsException;

$credentials = new Aws\Credentials\Credentials('xx', 'xx');

$SesClient = new SesClient([
    'version' => '2010-12-01',
    'region'  => 'us-east-1',
    'credentials' => $credentials,
]);

$sender_email = '[email protected]';

$recipient_emails = ['[email protected]'];

$subject = 'Amazon SES test (AWS SDK for PHP)';
$plaintext_body = 'This email was sent with Amazon SES using the AWS SDK for PHP.' ;
$html_body =  '<h1>AWS Amazon Simple Email Service Test Email</h1>'.
              '<p>This email was sent with <a href="https://aws.amazon.com/ses/">'.
              'Amazon SES</a> using the <a href="https://aws.amazon.com/sdk-for-php/">'.
              'AWS SDK for PHP</a>.</p>';
$char_set = 'UTF-8';

try {
    $result = $SesClient->sendEmail([
        'Destination' => [
            'ToAddresses' => $recipient_emails,
        ],
        'ReplyToAddresses' => [$sender_email],
        'Source' => $sender_email,
        'Message' => [
          'Body' => [
              'Html' => [
                  'Charset' => $char_set,
                  'Data' => $html_body,
              ],
              'Text' => [
                  'Charset' => $char_set,
                  'Data' => $plaintext_body,
              ],
          ],
          'Subject' => [
              'Charset' => $char_set,
              'Data' => $subject,
          ],
        ],
    ]);
    $messageId = $result['MessageId'];
    echo("Email sent! Message ID: $messageId"."\n");
} catch (AwsException $e) {
    // output error message if fails
    echo $e->getMessage();
    echo("The email was not sent. Error message: ".$e->getAwsErrorMessage()."\n");
    echo "\n";
}

 

对于使用php比较老的版本,可以用这个第三方实现库。

https://github.com/daniel-zahariev/php-aws-ses

 

获取YouTube的博主和视频信息

有3种报表:
1、通过DataApi,这种方式相对较直接,通过调用api获取数据,也可以操作数据(需要登录验证)2、Analytics API:这个报表是实时获取。
3、Reporting API:这个是生成报表数据再异步下载的,可以批量生成,如果数据量很大的话,建议用这个。

现在我们用的是第一种方式。

打开:https://console.cloud.google.com/,建立项目。
“API和服务“里,开通”库“里的”YouTube Data API v3”
开通后会得到token,后面会需要。

在这里可以查看文档,可以方便的用他们提供的api tools进行测试。
https://developers.google.com/youtube/v3/docs

有一个比较坑人的地方是,打开测试Widget后,下面的用OAuth 2.0是默认开启的,如下图:

当把这个取消后,右边生成的代码不会改变,要在上面手动改一个参数后,才会变。
要注意这个问题,不然代码是有问题的,始终会按OAuth 2.0的方式生成。

PHP的使用教程在这里:
https://developers.google.com/youtube/v3/quickstart/php

获取博主信息

请求:
GET https://youtube.googleapis.com/youtube/v3/channels?part=snippet%2CcontentDetails%2Cstatistics&forUsername=GoogleDevelopers&key=[YOUR_API_KEY] HTTP/1.1

Authorization: Bearer [YOUR_ACCESS_TOKEN]
Accept: application/json

返回:
{
  "kind": "youtube#channelListResponse",
  "etag": "LdG9BNUGur4iY7vuvSdW3_7PNP8",
  "pageInfo": {
    "totalResults": 1,
    "resultsPerPage": 5
  },
  "items": [
    {
      "kind": "youtube#channel",
      "etag": "6MkQzgqGne9K0Ol8mcuIHT0K8HQ",
      "id": "UC_x5XG1OV2P6uZZ5FSM9Ttw",
      "snippet": {
        "title": "Google Developers",
        "description": "The Google Developers channel features talks from events, educational series, best practices, tips, and the latest updates across our products and platforms.\n\nSubscribe to Google Developers → https://goo.gle/developers\n",
        "customUrl": "googledevelopers",
        "publishedAt": "2007-08-23T00:34:43Z",
        "thumbnails": {
          "default": {
            "url": "https://yt3.ggpht.com/ytc/AAUvwngOju7AKiAvKEs1wtsZN366tyNPyMq3nD8eFkMF7bE=s88-c-k-c0x00ffffff-no-rj",
            "width": 88,
            "height": 88
          },
          "medium": {
            "url": "https://yt3.ggpht.com/ytc/AAUvwngOju7AKiAvKEs1wtsZN366tyNPyMq3nD8eFkMF7bE=s240-c-k-c0x00ffffff-no-rj",
            "width": 240,
            "height": 240
          },
          "high": {
            "url": "https://yt3.ggpht.com/ytc/AAUvwngOju7AKiAvKEs1wtsZN366tyNPyMq3nD8eFkMF7bE=s800-c-k-c0x00ffffff-no-rj",
            "width": 800,
            "height": 800
          }
        },
        "localized": {
          "title": "Google Developers",
          "description": "The Google Developers channel features talks from events, educational series, best practices, tips, and the latest updates across our products and platforms.\n\nSubscribe to Google Developers → https://goo.gle/developers\n"
        },
        "country": "US"
      },
      "contentDetails": {
        "relatedPlaylists": {
          "likes": "",
          "favorites": "",
          "uploads": "UU_x5XG1OV2P6uZZ5FSM9Ttw"
        }
      },
      "statistics": {
        "viewCount": "182712151",
        "subscriberCount": "2160000",
        "hiddenSubscriberCount": false,
        "videoCount": "5340"
      }
    }
  ]
}

 

根据用户获取视频列表,分页的时候需要注意传pageTaken参数进去,该值是该API上一次返回的,如果不传的话,是第一页。

发送:
List编号是上一个接口返回的UU开始的那个
GET https://youtube.googleapis.com/youtube/v3/playlistItems?part=contentDetails%2Csnippet%2Cstatus&playlistId=[List编号]&key=[YOUR_API_KEY] HTTP/1.1

Authorization: Bearer [YOUR_ACCESS_TOKEN]
Accept: application/json

返回:
{
  "kind": "youtube#playlistItemListResponse",
  "etag": "sKFBg0QAFkDAaRtysTrebMkkcN8",
  "nextPageToken": "CDIQAA",
  "items": [
    {
      "kind": "youtube#playlistItem",
      "etag": "n0z9esMo-NzLrFpY_pRAVr9dd7w",
      "id": "VVVfeDVYRzFPVjJQNnVaWjVGU005VHR3LlYtUXRjd0txOUpr",
      "snippet": {
        "publishedAt": "2021-05-13T20:17:27Z",
        "channelId": "UC_x5XG1OV2P6uZZ5FSM9Ttw",
        "title": "Google I/O 2021, Android Studio 4.2, Google Play update, and more!",
        "description": "TL;DR 246 | The Google Developer News Show\n \n0:00 - Google I/O 2021 → https://goo.gle/3tKActE \n0:37 - Android Studio 4.2 → https://goo.gle/3hjinPQ  \n1:14 - New safety section in Google Play → https://goo.gle/3eH5rBB  \n1:46 - New Dashboard in Google Developer Profiles → https://goo.gle/33DWeDB  \n2:16 - Please remember to like, subscribe, and share! \n\nHere to bring you the latest developer news from across Google is Developer Advocate Meghan Metha. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below! 😃\n\nFollow Google Developers on Instagram → https://goo.gle/googledevs             \n\nWatch more #DevShow → https://goo.gle/GDevShow               \nSubscribe to Google Developers → https://goo.gle/developers \n\n#Google #Developers",
        "thumbnails": {
          "default": {
            "url": "https://i.ytimg.com/vi/V-QtcwKq9Jk/default.jpg",
            "width": 120,
            "height": 90
          },
          "medium": {
            "url": "https://i.ytimg.com/vi/V-QtcwKq9Jk/mqdefault.jpg",
            "width": 320,
            "height": 180
          },
          "high": {
            "url": "https://i.ytimg.com/vi/V-QtcwKq9Jk/hqdefault.jpg",
            "width": 480,
            "height": 360
          },
          "standard": {
            "url": "https://i.ytimg.com/vi/V-QtcwKq9Jk/sddefault.jpg",
            "width": 640,
            "height": 480
          },
          "maxres": {
            "url": "https://i.ytimg.com/vi/V-QtcwKq9Jk/maxresdefault.jpg",
            "width": 1280,
            "height": 720
          }
        },
        "channelTitle": "Google Developers",
        "playlistId": "UU_x5XG1OV2P6uZZ5FSM9Ttw",
        "position": 0,
        "resourceId": {
          "kind": "youtube#video",
          "videoId": "V-QtcwKq9Jk"
        },
        "videoOwnerChannelTitle": "Google Developers",
        "videoOwnerChannelId": "UC_x5XG1OV2P6uZZ5FSM9Ttw"
      },
      "contentDetails": {
        "videoId": "V-QtcwKq9Jk",
        "videoPublishedAt": "2021-05-13T21:00:21Z"
      },
      "status": {
        "privacyStatus": "public"
      }
    }
  ],
  "pageInfo": {
    "totalResults": 5340,
    "resultsPerPage": 50
  }
}

根据视频ID获取视频信息

请求:
GET https://youtube.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&id=Ks-_Mh1QhMc%2CV-QtcwKq9Jk&key=[YOUR_API_KEY] HTTP/1.1

Authorization: Bearer [YOUR_ACCESS_TOKEN]
Accept: application/json

id字段可以传以逗号分隔的多个id,该id是从上面的一个请求的返回的,contentDetails下的VideoId

返回:
{
  "kind": "youtube#videoListResponse",
  "etag": "zWPULojPN0SgmjLS7RzRh6217I4",
  "items": [
    {
      "kind": "youtube#video",
      "etag": "H0Be3-hhwR0AH_bFMPSsvwMkFhg",
      "id": "Ks-_Mh1QhMc",
      "snippet": {
        "publishedAt": "2012-10-01T15:27:35Z",
        "channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
        "title": "Your body language may shape who you are | Amy Cuddy",
        "description": "Body language affects how others see us, but it may also change how we see ourselves. Social psychologist Amy Cuddy argues that \"power posing\" -- standing in a posture of confidence, even when we don't feel confident -- can boost feelings of confidence, and might have an impact on our chances for success. (Note: Some of the findings presented in this talk have been referenced in an ongoing debate among social scientists about robustness and reproducibility. Read Amy Cuddy's response here: http://ideas.ted.com/inside-the-debate-about-power-posing-a-q-a-with-amy-cuddy/)\n\nGet TED Talks recommended just for you! Learn more at https://www.ted.com/signup.\n\nThe TED Talks channel features the best talks and performances from the TED Conference, where the world's leading thinkers and doers give the talk of their lives in 18 minutes (or less). Look for talks on Technology, Entertainment and Design -- plus science, business, global issues, the arts and more.\n\nFollow TED on Twitter: http://www.twitter.com/TEDTalks\nLike TED on Facebook: https://www.facebook.com/TED\n\nSubscribe to our channel: https://www.youtube.com/TED",
        "thumbnails": {
          "default": {
            "url": "https://i.ytimg.com/vi/Ks-_Mh1QhMc/default.jpg",
            "width": 120,
            "height": 90
          },
          "medium": {
            "url": "https://i.ytimg.com/vi/Ks-_Mh1QhMc/mqdefault.jpg",
            "width": 320,
            "height": 180
          },
          "high": {
            "url": "https://i.ytimg.com/vi/Ks-_Mh1QhMc/hqdefault.jpg",
            "width": 480,
            "height": 360
          },
          "standard": {
            "url": "https://i.ytimg.com/vi/Ks-_Mh1QhMc/sddefault.jpg",
            "width": 640,
            "height": 480
          },
          "maxres": {
            "url": "https://i.ytimg.com/vi/Ks-_Mh1QhMc/maxresdefault.jpg",
            "width": 1280,
            "height": 720
          }
        },
        "channelTitle": "TED",
        "tags": [
          "Amy Cuddy",
          "TED",
          "TEDTalk",
          "TEDTalks",
          "TED Talk",
          "TED Talks",
          "TEDGlobal",
          "brain",
          "business",
          "psychology",
          "self",
          "success"
        ],
        "categoryId": "22",
        "liveBroadcastContent": "none",
        "defaultLanguage": "en",
        "localized": {
          "title": "Your body language may shape who you are | Amy Cuddy",
          "description": "Body language affects how others see us, but it may also change how we see ourselves. Social psychologist Amy Cuddy argues that \"power posing\" -- standing in a posture of confidence, even when we don't feel confident -- can boost feelings of confidence, and might have an impact on our chances for success. (Note: Some of the findings presented in this talk have been referenced in an ongoing debate among social scientists about robustness and reproducibility. Read Amy Cuddy's response here: http://ideas.ted.com/inside-the-debate-about-power-posing-a-q-a-with-amy-cuddy/)\n\nGet TED Talks recommended just for you! Learn more at https://www.ted.com/signup.\n\nThe TED Talks channel features the best talks and performances from the TED Conference, where the world's leading thinkers and doers give the talk of their lives in 18 minutes (or less). Look for talks on Technology, Entertainment and Design -- plus science, business, global issues, the arts and more.\n\nFollow TED on Twitter: http://www.twitter.com/TEDTalks\nLike TED on Facebook: https://www.facebook.com/TED\n\nSubscribe to our channel: https://www.youtube.com/TED"
        },
        "defaultAudioLanguage": "en"
      },
      "contentDetails": {
        "duration": "PT21M3S",
        "dimension": "2d",
        "definition": "hd",
        "caption": "true",
        "licensedContent": true,
        "contentRating": {},
        "projection": "rectangular"
      },
      "statistics": {
        "viewCount": "19630769",
        "likeCount": "293174",
        "dislikeCount": "5600",
        "favoriteCount": "0",
        "commentCount": "8671"
      }
    },
    {
      "kind": "youtube#video",
      "etag": "eLxJnVkjRvCXeqs4kfxlWfT5iSQ",
      "id": "V-QtcwKq9Jk",
      "snippet": {
        "publishedAt": "2021-05-13T21:00:21Z",
        "channelId": "UC_x5XG1OV2P6uZZ5FSM9Ttw",
        "title": "Google I/O 2021, Android Studio 4.2, Google Play update, and more!",
        "description": "TL;DR 246 | The Google Developer News Show\n \n0:00 - Google I/O 2021 → https://goo.gle/3tKActE \n0:37 - Android Studio 4.2 → https://goo.gle/3hjinPQ  \n1:14 - New safety section in Google Play → https://goo.gle/3eH5rBB  \n1:46 - New Dashboard in Google Developer Profiles → https://goo.gle/33DWeDB  \n2:16 - Please remember to like, subscribe, and share! \n\nHere to bring you the latest developer news from across Google is Developer Advocate Meghan Metha. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below! 😃\n\nFollow Google Developers on Instagram → https://goo.gle/googledevs             \n\nWatch more #DevShow → https://goo.gle/GDevShow               \nSubscribe to Google Developers → https://goo.gle/developers \n\n#Google #Developers",
        "thumbnails": {
          "default": {
            "url": "https://i.ytimg.com/vi/V-QtcwKq9Jk/default.jpg",
            "width": 120,
            "height": 90
          },
          "medium": {
            "url": "https://i.ytimg.com/vi/V-QtcwKq9Jk/mqdefault.jpg",
            "width": 320,
            "height": 180
          },
          "high": {
            "url": "https://i.ytimg.com/vi/V-QtcwKq9Jk/hqdefault.jpg",
            "width": 480,
            "height": 360
          },
          "standard": {
            "url": "https://i.ytimg.com/vi/V-QtcwKq9Jk/sddefault.jpg",
            "width": 640,
            "height": 480
          },
          "maxres": {
            "url": "https://i.ytimg.com/vi/V-QtcwKq9Jk/maxresdefault.jpg",
            "width": 1280,
            "height": 720
          }
        },
        "channelTitle": "Google Developers",
        "tags": [
          "GDS: Yes",
          "google i/o",
          "google i/o 2021",
          "google i/o 21",
          "android studio 4.2",
          "google play",
          "google play update",
          "google play safety section",
          "google developer profiles",
          "google developer profiles dashboard",
          "android",
          "android developers",
          "google developer show",
          "the google developer show",
          "the developer show",
          "dev show",
          "dev news",
          "tl;dr",
          "developer news",
          "news",
          "updates",
          "ds",
          "google developers",
          "google news",
          "developers",
          "developer",
          "google latest",
          "google",
          "Meghan Metha"
        ],
        "categoryId": "28",
        "liveBroadcastContent": "none",
        "defaultLanguage": "en",
        "localized": {
          "title": "Google I/O 2021, Android Studio 4.2, Google Play update, and more!",
          "description": "TL;DR 246 | The Google Developer News Show\n \n0:00 - Google I/O 2021 → https://goo.gle/3tKActE \n0:37 - Android Studio 4.2 → https://goo.gle/3hjinPQ  \n1:14 - New safety section in Google Play → https://goo.gle/3eH5rBB  \n1:46 - New Dashboard in Google Developer Profiles → https://goo.gle/33DWeDB  \n2:16 - Please remember to like, subscribe, and share! \n\nHere to bring you the latest developer news from across Google is Developer Advocate Meghan Metha. Tune in every week for a new episode, and let us know what you think of the latest announcements in the comments below! 😃\n\nFollow Google Developers on Instagram → https://goo.gle/googledevs             \n\nWatch more #DevShow → https://goo.gle/GDevShow               \nSubscribe to Google Developers → https://goo.gle/developers \n\n#Google #Developers"
        },
        "defaultAudioLanguage": "en-US"
      },
      "contentDetails": {
        "duration": "PT2M33S",
        "dimension": "2d",
        "definition": "hd",
        "caption": "true",
        "licensedContent": false,
        "contentRating": {},
        "projection": "rectangular"
      },
      "statistics": {
        "viewCount": "6744",
        "likeCount": "301",
        "dislikeCount": "8",
        "favoriteCount": "0",
        "commentCount": "10"
      }
    }
  ],
  "pageInfo": {
    "totalResults": 2,
    "resultsPerPage": 2
  }
}