我正在一个网站上加载多个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个配额点将是一个很大的帮助。
答案 0 :(得分:1)
在Bman70的答案中,我在得知频道正在直播之后,尝试消除了提出昂贵的搜索请求的需求。我在频道页面的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的状态。该请求仅花费1个单位。
答案 1 :(得分:0)
由于该问题仅指定在查找通道是否正在流式传输时不应使用Search API配额,因此我想我会分享一种解决方法。与简单的API调用相比,它可能需要做更多的工作,但实际上减少了API配额的使用:
我使用了一个简单的Perl GET
请求来检索YouTube频道的主页。在实时流式传输的频道页面的HTML中找到了几个独特的元素:
实时观看者代码的数量,例如
<li>753 watching</li>
。LIVE NOW
徽章标签:<span class="yt-badge yt-badge-live" >Live now</span>
。
要确定某个频道当前是否正在直播,需要进行简单的匹配,以查看GET请求结果中是否包含唯一的HTML标签。类似于:if ($get_results =~ /$unique_html/)
(Perl)。然后,可以仅对实际正在流式传输的频道ID进行API调用,以获得流的视频ID。
这样做的好处是您已经知道频道正在流式传输,而不是使用数千个配额点来查找。我的测试脚本通过在HTML代码中查找<span class="yt-badge yt-badge-live" >
(请注意,来自YouTube的代码中的奇怪的多余空格)来成功识别频道是否正在流式传输。
我不知道OP使用的是哪种语言,否则我将以该语言处理基本的GET请求。我使用了Perl,并包含了浏览器标头,用户代理和cookie,使它们看起来像是正常的计算机访问。
Youtube的robots.txt似乎并不禁止抓取频道的主页,而仅禁止抓取频道的社区页面。
让我知道您对这种方法的优缺点有何看法,请评论可能会改进的地方,而不是发现缺陷后就不满意。谢谢,祝您编程愉快!
答案 2 :(得分:0)
这是我的两个建议:
https://www.youtube.com/channel/<CHANNEL_ID>/live
CHANNEL_ID
是您要检查的频道ID,如果该频道正在直播 1 。
1 请注意,URL可能不会在所有频道中起作用(这取决于频道本身)。
例如,如果您检查 channel_id UC7_YxT-KID8kRbqZo7MyscQ
-link to this channel livestreaming-https://www.youtube.com/channel/UC4nprx9Vd84-ly7N-1Ce6Og/live,则此频道将显示他是否正在直播,但是会显示他的频道id UC4nprx9Vd84-ly7N-1Ce6Og
-link to this channel livestreaming-,它将显示其主页。
答案 3 :(得分:0)
考虑到搜索操作的成本,我发现 youtube API 非常严格。显然,接受的答案对我不起作用,因为我也在非直播流上找到了字符串。使用 aiohttp 和 beautifulsoup 进行网页抓取不是一种选择,因为更好的指标需要 javascript 支持。因此我转向硒。我找了 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
运算符使用不当的问题
我希望 youtube 能提供比解决方法更好的替代方法。