如何在不使用搜索的情况下查找 youtube 频道当前是否正在直播?

How to find if a youtube channel is currently live streaming without using search?

我正在开发一个网站来加载多个 YouTube 频道直播。起初我试图找出一种不使用 youtube 的方法来做到这一点 api 但我决定放弃。

要查找某个频道是否正在直播并获取我一直在使用的直播链接:

https://www.googleapis.com/youtube/v3/search?part=snippet&channelId={CHANNEL_ID}&eventType=live&maxResults=10&type=video&key={API_KEY}

然而,最小配额为 10000,每次搜索值 100,我只能进行大约 100 次搜索,然后才能超过我的配额限制,这根本无济于事。我最终在大约 10 分钟内超过了配额限制。 :(

有谁知道使用尽可能少的配额点来确定频道当前是否正在直播以及直播链接是什么的更好方法?

我想每 3 分钟为每个用户重新加载 youtube 数据,将其保存到数据库中,并使用我自己的 api 显示信息以节省服务器资源和配额点。

希望有人能解决这个问题!

如果对链接无能为力,则每次不使用 100 个配额点来确定用户是否在线将是一个很大的帮助。

由于问题仅指定不应使用搜索 API 配额来确定频道是否正在播放,我想我会分享一种 work-around 方法。它可能比简单的 API 调用需要更多的工作,但它几乎可以减少 API 配额使用:

我使用了一个简单的 Perl GET 请求来检索 Youtube 频道的主页。在正在直播的频道页面 HTML 中发现了几个独特的元素:

The number of live viewers tag, e.g. <li>753 watching</li>. The LIVE NOW badge tag: <span class="yt-badge yt-badge-live" >Live now</span>.

要确定一个频道当前是否正在直播,需要一个简单的匹配来查看 GET 请求结果中是否包含唯一的 HTML 标签。类似于:if ($get_results =~ /$unique_html/) (Perl)。然后,可以只调用API实际流媒体的频道ID,以获得流媒体的视频ID。

这样做的好处是您已经知道该频道正在播放,而不是使用数千个配额点来查找。我的测试脚本通过查看 HTML 代码中的 <span class="yt-badge yt-badge-live" >(注意来自 Youtube 的代码中奇怪的额外空格)成功识别频道是否正在流式传输。

我不知道 OP 使用的是什么语言,或者我会帮助处理该语言的基本 GET 请求。我使用 Perl,并包括浏览器 headers、用户代理和 cookie,看起来就像正常的计算机访问。

Youtube 的 robots.txt 似乎没有禁止抓取频道的主页,只禁止抓取频道的社区页面。

让我知道您对这种方法的优缺点的看法,如果您发现缺陷,请评论可能改进的地方而不是不喜欢。谢谢,编码愉快!

2020 年更新 yt-badge-live 似乎已被弃用,它不再可靠地显示频道是否正在流式传输。相反,我现在检查 HTML 这个字符串:

{"text":" watching"}

如果我得到匹配项,则表示页面正在流式传输。 (Non-streaming 频道不包含此字符串。)再次注意奇怪的额外空格。我还转义了所有引号,因为我使用的是 Perl。

这是我的两个建议:

  • Check my answer 我在这里解释了如何检查如何从正在直播的频道中检索视频。
  • 另一个选项可以使用以下URL和以某种方式每次都发出请求以检查是否有直播。

https://www.youtube.com/channel/<CHANNEL_ID>/live

其中 CHANNEL_ID 是您要检查该频道是否正在直播的频道 ID1.


1 请注意,也许 URL 不会在所有频道中工作 (这取决于频道本身) .

例如,如果您检查channel_id UC7_YxT-KID8kRbqZo7MyscQ - link to this channel livestreaming - https://www.youtube.com/channel/UC4nprx9Vd84-ly7N-1Ce6Og/live, this channel will show if he is livestreaming, but, with his channel id UC4nprx9Vd84-ly7N-1Ce6Og - link to this channel livestreaming -,它将显示他的主页。

添加到 的答案中,我尝试在知道该频道正在直播后不再需要进行昂贵的搜索请求。我使用来自正在直播的频道页面的 HTML 响应中的两个指标来执行此操作。

function findLiveStreamVideoId(channelId, cb){
  $.ajax({
    url: 'https://www.youtube.com/channel/'+channelId,
    type: "GET",
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Accept-Language': 'en-US, en;q=0.5'
  }}).done(function(resp) {
      
      //one method to find live video
      let n = resp.search(/\{"videoId[\sA-Za-z0-9:"\{\}\]\[,\-_]+BADGE_STYLE_TYPE_LIVE_NOW/i);

      //If found
      if(n>=0){
        let videoId = resp.slice(n+1, resp.indexOf("}",n)-1).split("\":\"")[1]
        return cb(videoId);
      }

      //If not found, then try another method to find live video
      n = resp.search(/https:\/\/i.ytimg.com\/vi\/[A-Za-z0-9\-_]+\/hqdefault_live.jpg/i);
      if (n >= 0){
        let videoId = resp.slice(n,resp.indexOf(".jpg",n)-1).split("/")[4]
        return cb(videoId);
      }

      //No streams found
      return cb(null, "No live streams found");
  }).fail(function() {
    return cb(null, "CORS Request blocked");
  });
}

但是,需要权衡取舍。此方法会将最近结束的流与当前直播的流混淆。此问题的解决方法是获取从 Youtube API 返回的 videoId 的状态(从您的配额中花费一个单位)。

我发现 youtube API 考虑到搜索操作的成本非常严格。显然,接受的答案对我不起作用,因为我也在非直播流中发现了该字符串。使用 aiohttp 和 beautifulsoup 进行网络抓取不是一种选择,因为更好的指标需要 javascript 支持。因此我转向 selenium。我寻找 css 选择器

#info-text 然后搜索字符串 Started streaming 或其中包含 watching now

为了减少我的小型服务器上的负载,否则它会需要更多资源,我将此功能测试转移到带有小型烧瓶应用程序的 heroku dyno。

# import flask dependencies
import os
from flask import Flask, request, make_response, jsonify
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

base = "https://www.youtube.com/watch?v={0}"
delay = 3
# initialize the flask app
app = Flask(__name__)

# default route
@app.route("/")
def index():
    return "Hello World!"

# create a route for webhook
@app.route("/islive", methods=["GET", "POST"])
def is_live():
    chrome_options = Options()
    chrome_options.binary_location = os.environ.get('GOOGLE_CHROME_BIN')
    chrome_options.add_argument('--disable-gpu')
    chrome_options.add_argument('--no-sandbox')
    chrome_options.add_argument('--disable-dev-shm-usage')
    chrome_options.add_argument('--headless')
    chrome_options.add_argument('--remote-debugging-port=9222')
    driver = webdriver.Chrome(executable_path=os.environ.get('CHROMEDRIVER_PATH'), chrome_options=chrome_options)
    url = request.args.get("url")
    if "youtube.com" in url:
        video_id = url.split("?v=")[-1]
    else:
        video_id = url
        url = base.format(url)
    print(url)
    response = { "url": url, "is_live": False, "ok": False, "video_id": video_id }
    driver.get(url)
    try:
        element = WebDriverWait(driver, delay).until(EC.presence_of_element_located((By.CSS_SELECTOR, "#info-text")))
        result = element.text.lower().find("Started streaming".lower())
        if result != -1:
            response["is_live"] = True
        else:
            result = element.text.lower().find("watching now".lower())
            if result != -1:
                response["is_live"] = True
        response["ok"] = True
        return jsonify(response)
    except Exception as e:
        print(e)
        return jsonify(response)
    finally:
        driver.close()

# run the app
if __name__ == "__main__":
   app.run()

但是您需要在设置中添加以下构建包

https://github.com/heroku/heroku-buildpack-google-chrome
https://github.com/heroku/heroku-buildpack-chromedriver
https://github.com/heroku/heroku-buildpack-python

在设置中设置以下配置变量

CHROMEDRIVER_PATH=/app/.chromedriver/bin/chromedriver
GOOGLE_CHROME_BIN=/app/.apt/usr/bin/google-chrome

您可以找到受支持的 python 运行时 here 但低于 python 3.9 的任何东西都应该是好的,因为 selenium 在 is 运算符 [=21= 使用不当方面存在问题]

我希望 youtube 能提供比解决方法更好的替代方案。