一步一步教你如何搭建自己的视频聚合站 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
ieiayaobb
V2EX    Python

一步一步教你如何搭建自己的视频聚合站

  •  
  •   ieiayaobb
    ieiayaobb 2017-07-22 18:20:17 +08:00 6273 次点击
    这是一个创建于 3005 天前的主题,其中的信息可能已经有所发展或是发生改变。

    炉石吧

    前言

    作为一个炉石传说玩家,经常有事没事开着直播网站看看大神们的精彩表演。不过因为各个平台互相挖人的关系,导致关注的一些主播分散到了各个直播平台,来回切换有点麻烦,所以萌生了做一个视频聚合站的想法。

    我主要去采集斗鱼、熊猫等的炉石区的主播信息。虽然各个站点的人气信息有水分,但还是做了个简单的排名。

    上图: 手机上的效果图:

    话不多说,上网站: http://lushi8.leanapp.cn/

    项目部输在了 leancloud 上,比较省心,但有一定的免费额度(如果显示超出限制,需要晚一些来访问,毕竟免费的,每天 6 个小时限制) 源码地址: https://github.com/ieiayaobb/lushi8, 欢迎 Star

    • master 分支是 redis 方式存储实现
    • lean 分支是基于 lean cloud 的实现

    基础介绍

    聚合站的思路就是采集目标站点的相关信息,通过数据处理将想要的信息做提取,整理入库,然后通过 web 展示。因为直播平台数据实时在变,所以考虑将存储的数据放在缓存中( redis ),因为部署在了 lean cloud 上,所以示例就直接存储在了 lean cloud 的存储上。

    为了方便讲解,我们以斗鱼为目标采集的网站,介绍解析和存储部分的内容,其他网站的处理大同小异。

    功能说明

    整体项目就分为数据采集解析、数据存储、web 展现三大功能。后续我们会对这三个部分的功能做逐一展开说明。

    技术选型

    • 语言( Python )

    轻量级的项目,直接就是用了 Python 来做,Python 在爬虫、web 方面都有着不错的库支持,而且 lean cloud 也支持 Python 部署,所以毫不犹豫的就采用了 Python 来做

    • 数据采集( requests )

    requests 的特点就是轻量,且简单易用。虽然这是个爬虫项目,但实在规模太小,所以没必要上 scrapy 了 requests 的介绍地址: http://docs.python-requests.org/zh_CN/latest/index.html

    请求模拟

    url = 'http://www.douyu.com/directory/game/How' session = requests.Session() respOnse= session.get(url, verify=False) 

    数据解析

    解析部分主要有两种:正则,BeautifulSoup 这里为了通用,直接使用了正则来解析。 正则处理要求比较高,但是几乎能应对所有的情况,属于大杀器。 BeautifulSoup4 的详细介绍: https://www.crummy.com/software/BeautifulSoup/bs4/doc/

    • web 框架( Django )

    Django 是 Python 比较重量级的框架,Django 自带了 orm 的框架,可惜这个项目中用不到。但是我们会使用 Django 的模板引擎,Django 的模板引擎也是很方便的一个特性。Django 还提供了 django-rest-framework,方便开发 RESTful 的接口,这个项目后续做了个配搭的 React Native 的 mobile 应用,所以引入了 django-rest-framework。 详细介绍在此: https://www.djangoproject.com/

    • 存储( lean cloud 的数据存储)

    既然用了 lean cloud,存储就直接用了 lean 提供的存储功能。 详细的介绍在这里: https://leancloud.cn/docs/leanstorage_guide-python.html

    • 部署(用了 lean cloud 的引擎)

    参考了 lean cloud 官方的项目骨架: https://github.com/leancloud/django-getting-started

    • 前端展示( pureCSS )

    pureCss 还是为了简单,支持响应式,并且提供了基础的 UI 组件 详细介绍在这里: https://purecss.io/

    环境准备

    Python 的开发环境网上比较多,主要是 virtualenv 的准备,可以看廖老师的博客了解具体信息: https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001432712108300322c61f256c74803b43bfd65c6f8d0d0000 requirments.txt 内容如下:

    Django==1.9.7 requests==2.10.0 wheel==0.24.0 gunicorn leancloud-sdk>=1.0.9 

    分析与采集

    视频站内容解析

    • 斗鱼炉石区 目标是采集炉石区所有主播的链接地址和人气情况

    页面内容(单个主播的信息)

    <a class="play-list-link" data-rid='48699' data-tid='2' data-sid='167' data-rpos="0" data-sub_rt="0" href="/yechui" title="衣锦夜行:狂野 登顶登顶" target="_blank"> <span class="imgbox"> <span class="imgbox-corner-mark"></span> <b></b> <i class="black"></i> ![]( http://upload-images.jianshu.io/upload_images/2485846-20d3cbfd6e33df69.gif?imageMogr2/auto-orient/strip) </span> <div class="mes"> <div class="mes-tit"> <h3 class="ellipsis">衣锦夜行:狂野 登顶登顶</h3> <span class="tag ellipsis">炉石传说</span> </div> <p> <span class="dy-name ellipsis fl">衣锦夜行</span> <span class="dy-num fr" >8.1 万</span> </p> </div> </a> 

    我们需要采集的有几部分内容:

    • 直播间 url (节点里的 href,/yechui )
    • 直播间的标题(节点里的 title,衣锦夜行:狂野 登顶登顶)
    • 直播间的截图(节点里的 img 标签的 src,https://rpic.douyucdn.cn/a1707/22/16/48699_170722164951.jp
    • 直播间的人气( 8.1 万)(这里有个注意的地方,斗鱼的人气可能是 X 万,需要把这个万转化成数值方便排序)
    • 主播名称(衣锦夜行)

    页面处理与采集

    所有完整的直播站处理代码在 fetch.py

    命中主播信息节点

    re.finditer('<a class="play-list-link" .*?>([\s\S]*?)<\/a>', response.content.decode('utf8')): 

    简单的说明一下代码:

    response.content.decode('utf8') 
    • 主要是讲 requests 请求的页面以 utf8 编码返回
    • 正则部分就是命中上述的主播节点的内容,截取整个 a 标签

    解析代码

    采集 href 信息(主播房间链接)

    href = re.search('href=".*?"', group).group().lstrip('href="').rstrip('"') 

    采集标题信息

    title = re.search('title=".*?"', group).group().lstrip('title="').rstrip('"') 

    采集截图信息

    img = re.search('data-original=".*?"', group).group().lstrip('data-original="').rstrip('"') 

    采集主播名称

    name = re.search('<span class="dy-name ellipsis fl">.*?</span>', group).group().lstrip('<span class="dy-name ellipsis fl">').rstrip('</span>') 

    采集人气数量信息

    num = re.search('<span class="dy-num fr.*?</span>', group).group().lstrip('<span class="dy-num fr">').rstrip('</span>') 

    处理‘万’字

    int(round(float(num.replace('万', '').replace('\r', '').replace('\n', '')) * 10000)) 

    存储与刷新

    采集到的信息需要存储到 lean cloud 的存储中,会调用 lean cloud 所提供的 API

    字段设计

    Chairman

    • id 直播间的唯一 id
    • name 直播间主播名称
    • title 直播间的标题
    • href 直播间的页面地址
    • num 直播间的人气
    • img 直播间的截图

    接口设计

    /fetch

    Fetch 的接口包含了清空、采集、解析、存储所有的更新逻辑,设计这个接口的目的主要是方便后面使用云函数进行定时调用,以更新数据,调用逻辑如下( lean cloud 不支持全部遍历,所以用了 while 循环来遍历所有,先清空,再采集):

    leancloud.init(LEAN_CLOUD_ID, LEAN_CLOUD_SECRET) query = leancloud.Query('Chairman') allDataCompleted = False batch = 0 limit = 1000 while not allDataCompleted: query.limit(limit) query.skip(batch * limit) query.add_ascending('createdAt') resultList = query.find() if len(resultList) < limit: allDataCompleted = True leancloud.Object.destroy_all(resultList) batch += 1 fetcher = Fetcher() fetcher.fetch_douyu() 

    /chairmans ( redis 版本才支持)

    Django-rest-framework 提供,可以通过分页的方式展现当前库中的信息

    /chairman/{id}( redis 版本才支持)

    Django-rest-framework 提供,可以根据指定 id 获取某一个主播的信息

    刷新机制

    lean cloud 提供了一种云函数的概念,并且可以像配置 cron 一样,定期的去触发某一个请求,为了能够定期的更新排行榜,我们会通过配置这个云函数,实现定期的数据刷新 云函数是一个 cloud.py 文件,内容如下

    engine = Engine(get_wsgi_application()) @engine.define def fetch(**params): leancloud.init(LEAN_CLOUD_ID, LEAN_CLOUD_SECRET) # fetch 逻辑 

    在 lean cloud 中配置定时执行

    页面展示

    页面部分比较简单,以一个列表的形式,展现了主播的排行榜信息,点击某一个主播,直接跳转到对应直播网站的目标直播间。因为考虑到在手机上的显示,所以做了自适应

    列表页

    列表页的渲染使用了 Django 的模板引擎 由于 lean cloud 的存储和 Django 的 orm 不一样,所以这里需要将 attributes 放到列表中,页面上才能用模板语法进行访问 view 部分代码:

    def get_index(request): leancloud.init(LEAN_CLOUD_ID, LEAN_CLOUD_SECRET) query = leancloud.Query('Chairman') chairmans = [] for chairman in query.add_descending('num').find(): chairmans.append(chairman.attributes) return render_to_response('index.html', locals(), context_instance=RequestContext(request)) 

    页面部分代码:

    {% for chairman in chairmans %} <a href="{{ chairman.href }}" class="chairman-wrapper"> <div class="pure-g chairman"> <div class="pure-u-1-5"> ![]({{ chairman.img }}) </div> <div class="pure-u-2-5"> <div class="name">{{ chairman.name }}</div> <div class="title">{{ chairman.title }}</div> </div> <div class="pure-u-1-5"> <span class="type {{ chairman.type }}"></span> </div> <div class="pure-u-1-5"> <div class="num">{{ chairman.num }}人</div> </div> </div> </a> {% endfor %} 

    项目部署

    因为部署在了 lean cloud 上,可以直接使用提供的 lean-cli 进行部署, lean-cli 的详细介绍在这里: https://www.leancloud.cn/docs/leanengine_cli.html#部署 这里为了方便直接在页面上进行配置

    • 配置 git 库

    • 配置 Deploy Key

    • 设置域名

    • 部署

    • 配置定时任务

    后言

    整个项目比较简单,目的是为了练手。如有疑问,欢迎在 github 上面发 issue。

    第 1 条附言    2017-07-23 12:06:02 +08:00
    3 条回复    2017-07-25 02:00:28 +08:00
    nazor
        1
    nazor  
       2017-07-22 19:56:06 +08:00
    我以为你把斗鱼 flash 的协议分析了,原来是这个。
    expy
        2
    expy  
       2017-07-22 20:02:15 +08:00
    F12 找出视频链接,然后拿 Livestreamer 喂给播放器。链接一般几天才变一次。好处是没广告、资源占用低,坏处是看不见聊天、估计也不会给主播提供收入。
    wanglaihuai
        3
    wanglaihuai  
       2017-07-25 02:00:28 +08:00 via Android
    大佬厉害了,一时半会儿还消化不了…
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1295 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 17:14 PVG 01:14 LAX 10:14 JFK 13:14
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86