Giter VIP home page Giter VIP logo

videospider's Introduction

VideoSpider

        此项目是利用python2.7编写爬虫将的视频网站的web_url进行一系列操作,解析其真实视频地址和其视频相关的一些信息,例如视频的名称、ID、观看数、点赞数、评论数等,最后将其视频下载到本地文件夹内,然后将其他的信息保存至mysql数据库中。(目前项目还在开发当中,尽情期待新的内容)
        那么如何由网站的视频地址找到其视频的真实地址呢?
        我们一步一步来看O(^_^)O(最新一期加入了视频评论部分,最近比较忙,等过段时间继续优化与文档说明)

数据库

        首先先来了解一下这个项目的数据库操作。
        我所使用的是mysql数据库,利用SQLyog来管理我的数据库。
        利用SQL语句:

import MYSQLdb
import sys
conn = MYSQLdb.connect('localhost', 'root', 'root', 'video')        // 连接video数据库
try:
  cur = conn.cursor()
  cur.execute("DROP TABLE IF EXISTS rr")
  sql = "CREAT TABLE rr(
    Video_Id varchar(10) NOT NULL,      // Id
    Video_Name varchar(50),             // 名称
    Video_Url varchar(100),             // 真实url
    Video_Author varchar(20),           // 作者
    Video_Time varchar(10),             // 时长
    Video_Size varchar(20),             // 大小
    Video_ViewCount varchar(20),        // 观看数
    Video_CommentCount varchar(20),     // 评论数
    Video_FavCount varchar(20),         // 点赞数
    Video_Web varchar(50),              // web_url
    File_Url varchar(100),              // 本地存放地址
    PRIMARY KEY (Video_Id)              // 设置主键
  )"
  cur.execute(sql)
  cur.close()
  conn.commit()
except:
  conn.rollback()
conn.close()

创建出我们所需要的表rr去存放数据。(我是直接利用SQLyog里面可视化建表,方便、靠谱)
(为什么要全部设置为varchar类型呢?因为参数传入数据库的时候比较方便,不用过于担心参数类型。)
(参数类型这个问题是超级头痛,type(xx)这个函数很好用,搞不懂了自己就试一试这个。)
表建好了,接下来就是将获取下来的存放到数据库中。
这个比较简单,编写

  def rr_mysql(url, video_url, video_message, video_url_file):

方法,传入web_url、视频真实地址、视频的一些信息、视频的本地存放地址等参数。
        在方法中先connection连接数据库,接着建立cursor事务,再编写SQL语句,接着用execute实现SQL语句。最后可别忘了关闭事务和连接。
        有些初学者可能对于传这么多的参数不知道如何下手,这里有一些方法:

// 第一种方法
  sql_1 = "INSERT INTO rr(x1, x2, x3, x4) " \
          "VALUES (%s, %s, %s, %s)"             
   cur.execute(sql, (n1, n2, n3, n4))
// 第二种方法
  sql_1 = "INSERT INTO rr(x1, x2, x3, x4) " \
          "VALUES (%s, %s, %s, %s)" % (n1, n2, n3, n4)            
   cur.execute(sql)
 // 错误方法
   sql_1 = "INSERT INTO rr(x1, x2, x3, x4) " \
           "VALUES (n1, n2, n3, n4)"

        然后就是关于实时的将视频的动态变化信息存储到数据库中,对于数据库的操作大致没有变化,思路就是通过 SELECT...WHERE...语句查找数据库中关于相同web_url的那一条信息,然后和网上获取下来的数据进行对比,将变化的值通过UPDATE...SET...WHERE...语句进行实时的更新。
        由于种种可能,例如不小心将文件夹里的视频删除掉了或者数据库的某条信息丢失,我们就要在数据更新的的时候快速发现这个问题,并且解决掉:将缺失的数据、丢失的视频文件补回来。所以解决这个问题的项操作和上面的更新数据方法雷同。数据库中的信息缺失很好解决,就是将没有的数据重新再写回数据库,那么此处有什么难点呢?(确实有,困扰了我几个小时,最后还是将其解决掉了)\(^.^)/
        问题就在于如何判断存放这个视频的文件夹中是否缺失了某个视频:彻底丢失或者视频大小不完整。因为程序是在不断地运行中,文件夹中的视频量也是在变化。
        我的第一个想法就是每次向文件夹中添加一个完整的视频就会使文件夹的size变大,我们在获取视频的时候已经将视频的大小得到了,那么每次下载玩一个视频,将这个文件的大小加上视频的大小,那么可以每次计算文件夹的大小来判断是否缺失视频。但是问题又来了,怎么实时的获取文件夹大小呢?我从网上查了查,找到了利用python获取文件夹的大小的方法:

from os.path import join, getsize

    def file_size(file_url):
        size = 0
        for root, dirs, files in os.walk(file_url):
            size = size + sum([getsize(join(root, name)) for name in files])
        return size

        但是问题又来了,经过我的测试,每次只有当视频已经完全下载后这个大小才会显示正常,并且还不知道如何实时的获取视频流的大小。
        第二个想法(已经成功)是在第一个想法的基础上更加细致了一些。每次通过某种方法获取到这个视频(已下载的)在文件夹中的大小,与网站中的视频大小进行对比判断是否文件损坏。但是这个前提是必须要判断出来视频(已下载)是否存在于文件夹中。 所以具体思路为:判断视频是否在文件夹中并且视频的大小等于网站上视频的大小,是的话就不做任何事,否的话则重新下载。经过我努力以及细心的在网上查找后发现,可以调用系统函数来实现上述的方法。

import os
from contextlib import closing
video_in_file = os.path.exists(video_in_file_url)         // 判断视频是否在文件夹里返回TRUE OR FALSE
video_in_file_size = os.path.getsize(video_in_file_url)   // 得到视频在文件夹中的真实大小

此外,我们从数据库中获取数据后怎么显示出来呢?看一下这个例子:

  conn = MySQLdb.connect(
       host='localhost', 
       port='3306', 
       user='root,
       passwd='root',
       db='XX',
       charset='utf8',
  )                                         // 连接数据库
  try:
    cur = conn.cursor()                     // 创建事务
    sql = "SELECT * " \
           "FROM XX " \
    cur.execute(sql)                        // 执行sql
    result_1 = cur.fetchone()               //  
    result_2 = cur.fetchmany(n)             // }获取数据
    result_3 = cur.fetchall()               //

    print result_1                          // 打印result_1结果
    
    for raw_2 in result_2:
      print raw_2                           // 打印result_2结果
      
    for raw_3 in result_3:
      print raw_3                           // 打印result_3结果
      
    cur.close()
    conn.commit()
  except:
    conn.rollback()
  conn.close()

        假设数据库中有很多条的数据,那么利用fetchone()这个函数可以获取所获取数据的第一行,同时如果知道数据库有n条数据的话,可以用

  for i in n:
    result = cur.fetchone()

上面这个方法一行一行的获取数据库中的所有数据;而fetchmany(n)可以一次性的获取n条数据,也是需要利用for循环来输出数据;最后fetchall()是一次性获取数据库中的所有数据,同理利用for循环来输出数据。

        以上就是关于MySQL在这个项目中的一些应用与开发的过程中遇到的一些问题和解决方法。
        如果对于Python操作MySQL数据库还有疑问的话,可以查阅这个文档:http://www.runoob.com/python/python-mysql.html

进度条显示ProgressBar

        因为下载视频的时候一直在等并且控制台也不会显示任何的东西,我们不能实时的看到下载的快慢与过程,这显得就有些让人着急,在结合项目本身,我们需要实时的获取视频流的大小来显示出来。所以我从网上查找了很多的信息,但效果都不是很好,不是我想要的结果,最后终于查到了一个文档:http://blog.csdn.net/supercooly/article/details/51046561
        但是这个是利用Python3.Xrequests模块,我这边用的是Python2.7。所以经过我的尝试与修改终于改为Python2.7可用的版本:
import sys
    ....//以上内容和原文当中类似改变相应参数即可
    def refresh(self, count=1, status=None):              //在控制台显示下载进度
        self.count += count
        self.status = status or self.status
        sys.stdout.write('\r')                            //
        sys.stdout.write(self.__get_info())               // }利用sys.studout.write()/flush()
        sys.stdout.flush()                                //
        if self.count >= self.total:
            self.status = status or self.fin_status
            sys.stdout.write('\r')
            sys.stdout.write(self.__get_info())
            sys.stdout.write('\n')

with....as...小语法

        再介绍一个很好用的语法:with....as...。它的作用是什么呢?来看一下这两个例子就明白了!
//example one
  file = open('log.txt', 'w')
  file.write('I am a text!')
  file.close()
  
//example two
  with open('log.txt') as file:
    file.write('I am a text!')

        上述俩个例子都可以实现将'I am a text!'存放到已建好的文件log.txt中,但是第一个事例是先将文件打开open,再写入内容write,最后再关闭close。而第二个实例利用with....as...语句就可以直接实现打开文件和关闭文件的功能,这样大大减少了因代码繁多而忘记写close的问题。

...接下来我们就切入正题/(¥_¥)\

人人视频

        打开人人视频的网站 http://www.rr.tv/#/
        虽然网站给人的印象比较简答,但其内容还算广泛,也没有多余的广告.......
        随便打开一个视频,例如其web_url:http://rr.tv/#/video/394450
        按照一般的操作,我们肯定先会右键查看网页源代码:view-source:http://rr.tv/#/video/394450
        可是.....事实却是只有一些基础的HTML + javascript代码,对于我们这个项目没有多大的帮助。

        所以我们就要用到抓包利器开发者工具,我用的是Chrome浏览器,快捷键是F12,其他的浏览器一般都可以在设置里面找得到(点击 https://jingyan.baidu.com/article/c843ea0bb9433577921e4a50.html 学习谷歌浏览器开发者工具的基本操作)。         获取视频真实地址的第一种方法。刷新后,点击Network,我们可以清楚的看到最上面的时间线中有一条很长的蓝色线段,我们将区间拖动到这条蓝色的线条范围内,下面左边就会被过滤出一个URL,点击这个URL我们可以看到右边的PreviewResponse为空的,但是Headers里面就有我们所需要的内容:Request URL:http://qcloud.rrmj.tv/2017/10/14/15b944f6c44541a1979769db41841e23.mp4.f30.mp4?sign=2719acf1b797caa7e631a6ff2d15de0e&t=59e7059d&r=1544495093411242940 ,我们发现里面有.mp4的字符串,我们复制这个URL到浏览器,奇迹出现啦!出现的视频正是我们在网站看到的视频,所以视频的真实地址已经被我们找到,它的获取方式是GET

        获取视频真实地址的第二种方法。上面的方法确实可行,但是结合项目本身,我决定还是放弃第一个做法。还是利用开发者工具,刷新后左边出现了很多的数据,整体浏览一遍后我们可以发现在中间偏上的一块区域内出现了两次“getVideoPlayLinkByVideodetaillistprofileallprofile”,我们挨个点击发现最前面的数据在Preview中没有显示任何的数据,而后面都会有数据。我们点开etVideoPlayLinkByVideo,发现Preview中有JSON数据,逐级展开后,我们就会惊奇的发现,里面有playLink:http://qcloud.rrmj.tv/2017/10/14/15b944f6c44541a1979769db41841e23.mp4.f30.mp4?sign=237a2538b6310983f1913ad8b687505f&t=59e7288b&r=6224854028442601914 ,我们发现里面有.mp4的字符串,我们复制这个playLink到浏览器,奇迹出现啦!出现的视频正是我们在网站看到的视频,所以视频的真实地址已经被我们找到,在Headers中我们可以看到它的获取方式是向http://web.rr.tv/v3plus/video/getVideoPlayLinkByVideoId发送POST请求。
所以代码可以这样写:

import js
import requests

  headers = {'clientVersion': '0.1.0', 'clientType': 'web',}                                  # 添加客户端版本信息
  video_id = re.search(r'[0-9]+', web_url).group()                                            # 获取视频的ID
  api_url = 'http://api.rr.tv/v3plus/video/getVideoPlayLinkByVideoId'                         # 调用视频接口
  req_js = requests.post(api_url, data={'videoId': video_id}, headers=headers).content        # 发送请求获取数据
  req_py = json.loads(req_js)                                                                 # 将JSON数据转换为Python数据
  video_url = req_py["data"]["playLink"]                                                      # 获取视频真实地址
  print video_url

这样就可以将视频的真实地址以字符串的形式输出。
        接着再往下看,会看到有detail,点击detail,在右边的Preview中有JSON数据,逐级展开后,我们最后会在videoDetailView中发现有该视频的信息:authortitlecommentCountfavCountviewCount。在Headers中我们可以看到它的获取方式是向http://web.rr.tv/v3plus/video/detail发送POST请求。
参考上面的代码,变换一下api_url和其对应的pyhon获取JSON中重要信息的代码:

  api_url = 'http://web.rr.tv/v3plus/video/detail'                         # 调用信息接口

        如果对于python操作JSON不太清楚,可以参考文档:http://www.jb51.net/article/73450.htm
        通过以上的步骤,我们就可以将与视频相关的所有信息获取到了,剩下的就是调用下载函数将视频下载下来,将数据存入数据库。

        因为项目的增加,之前写的代码的利用性降低了,所以对其中几个地方做了些改变,以至于更好的适应其他的项目。

        DownloadVideo文件中牵扯到将不同网站的的视频下载到不同的文件夹中,所以传入video_file这个参数,只要调用下载函数时多传入一个字符串来表明是哪个文件夹,视频就会被下载到不同的文件中,不会被搞混。然后发现这次的视频是.flv文件格式,而不是上次的.mp4文件格式,所以就会出现文件已经下载下来但是播放不了的问题,在这里只需要判断一下视频原下载地址是什么格式的然后对其分配相应格式的后缀名就好了,如下:

  if '.flv' in self.url:
    file_f = '存放文件路径' + '视频名称' + '.flv'
  elif '.mp4' in self.url:
    file_f = '存放文件路径' + '视频名称' + '.mp4'
  else:
    file_f = '存放文件路径' + '视频名称' + '.mp4'

(其实下载一个很好的视频播放器,不管是.mp4还是.flv还是等等,都可以随便观看啦,大家可以从网上找找!!)

...接下来我们就切入这次的正题/(¥_¥)\

网易视频

网易视频包含的种类繁多,所以我只挑了其中3部分来做,包括:http://v.163.com/paikehttp://v.163.com/zixunhttp://v.163.com/jishi ,大家可以将以上三个分支下的视频下载下来。

        打开其中一个视频:http://v.163.com/paike/VBFGBLNBF/VBHBGAC2L.html
        其实很多网页视频的url都还是蛮规整的0(!_ !)0
        先来介绍最最简单的一个获取视频原下载地址的方法:鼠标右键点击检查网页源代码,大致浏览一下网页的html发现其实代码挺乱的,大量的<script></script>标签嵌套在html中,但这对于我们我们有好处,仔细的再浏览一遍发现有很多有用的JSON文件,类似于:

  var _param = {
    pltype : 6,
    videoadv : 'http://v.163.com/special/008547FN/vo_zixun.xml',
    openSub : false,
    width : '100%',
    height : '100%'
  };

        所以我们很快的发现了带有的标签,其中就有我们想要的视频原地址:

  <source src="http://flv.bn.netease.com/videolib3/1603/16/aUzmv6975/SD/aUzmv6975-mobile.mp4" type="video/mp4">

        将其复制到浏览器地址栏中,回车你就会惊奇的发现出现了下载对话框,点击确认,你就会将视频下载到本地。所以这段的代码可以利用正则表达式来获取地址:

  html = requests.get(web_url)
  pattern = re.compile(r'source src="(.*?)"', re.S | re.M | re.I)
  real_s_url = pattern.findall(html.text)[0].encode('utf8')

        但是,我肯定不会这样做滴,原因有三:首先下载下来的视频是标准视频,很多视频都会有高清和超高清视频,我们当自然要下载质量高的视频了;其二如果哪天人家看着直接放出视频原地址不顺眼,将该隐藏的隐藏掉,那就不好玩了,找也找不到了;其三牵扯到视频相关信息的下载。所以我们还是一步一步来吧。

视频原地址
        通过开发者工具,我们挨个的找取以.js.xml结尾的请求url,发现带有1000_VBHBGAC2L.xm的这个url中有我们想要的东西:在中就是我们要找的视频原地址,一共有6个,分别在、、中,这三种标签分别代表视频为:标清、高清、超高清。(flv和flv4对我们没有很大的影响,所以可以归纳为三个url) 我们要做的就是将这里面对应的视频清晰度最好的地址获取到。         这段文件的请求url为:http://xml.ws.126.net/video/2/L/1000_VBHBGAC2L.xml ,打开多个视频我们可以看到其中有些部分是不会改变的:http://xml.ws.126.net/video/.xml。所以我们要做就是构造这类请求的url。 通过观察我们可以发现2LVBHBGAC2L的最后两个字母,而_VBHBGAC2L前面的1000,我们可以在网页源代码中找得到:topicId='1000'。所以我们可以通过以上的分析得到下面这段代码(我们将VBHBGAC2L作为id):

  html = requests.get(web_url, headers=header)
  pattern = re.compile(r'topicid : "(.*?)"', re.S | re.M | re.I)
  cid = pattern.findall(html.text)[0].encode('utf8')  # 获取topicid
  id = [web_url][0]  # 将url进行list处理获取id
  # 构造url
  req_url = 'http://xml.ws.126.net/video/' + id[-7] + '/' + id[-6] + '/' + cid + '_' + id[-14: -5] + '.xml'

然后通过GET请求访问这个req_url得到xml文件,进行解析,得到清晰度较高的视频地址。

视频信息
        接着再通过开发者工具,找到了http://sdk.comment.163.com/api/v1/products/a2869674571f77b5a0867c3d71db5856/threads/BHBGAC2L008535RB?ibc=jssdk&callback=tool1002321329669789156_1509093181749&_=1509093181750这个url,它所返回的数据正是要找的大部分视频信息。要获取这个信息的办法还是构造请求的url,但是发现这个url很长,url中还包含着很多的参数,再怎么查找发现其他的包里面没有关于其参数的信息。所以我们要想办法去简化其url。经过测试发现,将?ibc=jssdk&callback=tool1002321329669789156_1509093181749&_=1509093181750这些参数去掉我们还是可以访问到这个文件。所以我们只需要构造类似于http://sdk.comment.163.com/api/v1/products/a2869674571f77b5a0867c3d71db5856/threads/BHBGAC2L008535RBurl。经过多次试验,发现其中http://sdk.comment.163.com/api/v1/products//threads/不会改变。我们只需要获取到a2869674571f77b5a0867c3d71db5856BHBGAC2L008535RB就可以了。结合之前的思路,我们很快在网页源代码中发现了其参数:"docId" : "BHBGAC2L008535RB""productKey" : "a2869674571f77b5a0867c3d71db5856"。所以我们可以通过以上的分析得到下面这段代码:

  pattern = re.compile(r'"productKey" : "(.*?)"', re.S | re.M | re.I)
  product_key = pattern.findall(html.text)[0].encode('utf8')
  try:
      pattern = re.compile(r'"docId" :  "(.*?)"', re.S | re.M | re.I)
      doc_id = pattern.findall(html.text)[0].encode('utf8')
  except:
      doc_id = pattern.findall(html.text)[0].encode('utf8')
  mess_url = 'http://sdk.comment.163.com/api/v1/products/' + product_key + '/threads/' + doc_id

然后通过GET请求访问这个mess_url得到JSON文件,进行解析,得到其中关于视频的信息。(还有其他的信息的获取方法类似与上面所说)

        最后就是将获取的内容全部存入数据库中,然后进行更新操作,这部分的内容和前面的类似,就不在这里赘述了。

        因为项目数量的增加,发现不同项目中的类似代码冗余量太大,一方面解析模块代码量过于庞大,另一方面程序运行容易出问题。所以对以上问题作出了修改:
        (1)由于长时间运行代码,在不断地向目的服务器发送url,请求数据的时候可能会由于对方服务器访问量过多或者本机的网络太差,一次请求不能获得我们所需要的数据(404或者页面丢失),我们需要多次的请求,所以在src模块中加入retey_get/retry_post:当返回的状态码错误时自动的再次请求。

  def retry_get(req_url, headers, count=5):           # 网络差时多次get请求
      try:
          request = requests.get(req_url, headers=headers, timeout=60)
          request.raise_for_status()
          data = request.content
      except requests.HTTPError as e:
          data = None
          if count > 0:
              count -= 1
              time.sleep(10)
              retry_get(req_url, headers, count-1)
      return data

        (2)将lost_update从各个解析模块中提出,进行整合放入一个lostupdate模块中,在模块内对不同的需求进行不同的任务分配。
        (3)将lost_mess从各个解析模块中提出,进行整合放入一个lostmess模块中,在模块内对不同的需求进行不同的任务分配。
        (4)将ckeck_url从各个解析模块中提出,进行整合放入一个urlcheck模块中,在模块内对不同的需求进行不同的任务分配。
(更改后的调用与之前的一样)

凤凰视频

 凤凰视频包含的种类繁多,除了直通车、天天看、星期7、大放送四个模块,其余都可以下载。
        打开其中一个视频:http://v.ifeng.com/video_9119714.shtml
        这个网页视频的url都还是蛮规整的0(!_ !)0
        观察网页,我们可以获取到的信息有:视频名称、视频上传时间、视频播放时长、视频的观看量、视频的点赞数、视频的不点赞数、视频的评论数等等。

        右键点击网页查看源代码,仔细浏览后发现唯一可能有用的信息:

 <script>
        var videoinfo = {
            "id":"9119714",
            "vid": "56ca21e4-59df-49ab-8afa-bb38317a13cd",
            "name": "贾玲爆笑上演多版本“来自星星的你” 还是东北女神最传情",
            "duration": "572",
            "url": "http://v.ifeng.com/video_9119714.shtml",
            "skey":"746F26",
            "videoLargePoster": "http://p1.ifengimg.com/a/2017_44/3ce23a9f6783667.jpg",
            "playerName":"vFreePlayer",  
            "mUrl":"http://v.ifeng.com/dyn/m/video/9119714/index.shtml",
            "categoryId":"57-60",
            "mediaId":"764981",
            "adtype":"5",
            "categoryPath":"variety",
            "danmaku": "",
            "columnName":"开心大米粒",
            "CPId": '166',
            "keywords": '百变大咖秀 贾玲 何炅',
            "createdate": '2017-11-01 20:55:02'
        };
    </script>

信息量不全,所以我们暂且先放弃从源代码中获取信息的方式。
视频信息+原地址(这次两个内容是连在一起的,所以一并介绍)
        接着再通过开发者工具,找到了http://tv.ifeng.com/h6/9119714/video.json?callback=callback&msg=9119714&rt=js&param=play&_=1509805191192这个url,其Preview中有我们想要获取的部分信息,包括视频的原地址。经过简化后发现http://tv.ifeng.com/h6/9119714/video.json?callback=callback&msg=9119714&rt=js也可以访问到我们所想问的数据(json):

{
   "preview": {
       "path": "http://p0.ifengimg.com/pmop/2017/11/01/70006145-b563-40f6-bad2-cc974bd09e5a.jpg",
       "sub_width": 128,
       "time_span": 6,
       "sub_height": 72,
       "width": 1280,
       "sub_picture_number": 95,
       "picture_number": 1,
       "height": 720
   },
   "videoLargePoster": "http://p1.ifengimg.com/a/2017_44/3ce23a9f6783667.jpg",
   "cpId": "166",
   "createdate": "2017-11-01 20:55:02",
   "bqSrc": "http://ips.ifeng.com/video19.ifeng.com/video09/2017/11/01/12610851-102-009-205616.mp4",
   "mediaId": 764981,
   "title": "贾玲爆笑上演多版本“来自星星的你” 还是东北女神最传情",
   "url": "http://v.ifeng.com/video_9119714.shtml",
   "videoPlayUrl": "http://ips.ifeng.com/video19.ifeng.com/video09/2017/11/01/12610851-102-009-205616.mp4",
   "vid": "56ca21e4-59df-49ab-8afa-bb38317a13cd",
   "duration": 572,
   "posterUrl": "http://p1.ifengimg.com/a/2017_44/3ce23a9f6783667.jpg",
   "anchor": [],
   "CPId": "166",
   "aspect": "16:9",
   "name": "贾玲爆笑上演多版本“来自星星的你” 还是东北女神最传情",
   "guid": "56ca21e4-59df-49ab-8afa-bb38317a13cd",
   "gqSrc": "http://ips.ifeng.com/video19.ifeng.com/video09/2017/11/01/12610851-102-009-205616.mp4",
   "id": 9119714,
   "keyword": "百变大咖秀 贾玲 何炅",
   "categoryId": "57-60",
   "mediaName": "开心大米粒",
   "columnName": "开心大米粒"
}

其中"createdate"是视频的上传时间,"bqSrc""videoPlayUrl""gqSrc"为视频的原地址(就目前测试来看下载下来的视频没有什么区别),"title"为视频的名称,"mediaName""columnName"为视频上传者(只有极少数不一样),"vid""guid"为重要的参数(两者都一样),之后要用的到。
        所以接下里的任务就是如何构造此请求url。经过测试发现只有msg=9119714中的参数和前面的id在变化,其实两者是同一个东西,所以只要获取到web_url中的id,拼接到其中即可:

 pattern = re.compile(r'http://v.ifeng.com/video_(.*?)\.shtml', re.S | re.I | re.M)
 video_id = pattern.findall(web_url)[0]
 req_url = 'http://tv.ifeng.com/h6/' + video_id + '/video.json?callback=callback&msg=' + video_id + '&rt=js'

进行访问时一定要加头部(headers),之后对其进行解析获取信息就好。         但是还是有一些信息没有获取到:视频的播放量、点赞数、不点赞数、评论量。通过逐一排查后,我们发现 分别在一下几个url中(逐一对应):
http://survey.news.ifeng.com/getaccumulator_weight.php?format=js&serverid=2&key=56ca21e4-59df-49ab-8afa-bb38317a13cd&callback=f15f8766f6240
http://survey.news.ifeng.com/getaccumulator_ext.php?callback=jQuery17107289447875246187_1509805190738&key=56ca21e4-59df-49ab-8afa-bb38317a13cdding&format=js&serverid=1&var=ding
http://survey.news.ifeng.com/getaccumulator_ext.php?callback=jQuery17107289447875246187_1509805190739&key=56ca21e4-59df-49ab-8afa-bb38317a13cdcai&format=js&serverid=1&var=cai
http://comment.ifeng.com/getv.php?callback=jQuery17107289447875246187_1509805190737&job=3&format=js&docurl=56ca21e4-59df-49ab-8afa-bb38317a13cd。由于其url太过于复杂,所以首先还是先进性简化,简化后为(逐一对应):
http://survey.news.ifeng.com/getaccumulator_weight.php?format=js&key=56ca21e4-59df-49ab-8afa-bb38317a13cd
http://survey.news.ifeng.com/getaccumulator_ext.php?key=56ca21e4-59df-49ab-8afa-bb38317a13cdding
http://survey.news.ifeng.com/getaccumulator_ext.php?key=56ca21e4-59df-49ab-8afa-bb38317a13cdcai
http://comment.ifeng.com/getv.php?docurl=56ca21e4-59df-49ab-8afa-bb38317a13cd。其中每一个对应的key都是相同的,即前面所获取到的"vid""guid",所以讲其拼接到其中即可:

  # 获取视频观看数(str)
  req_url = 'http://survey.news.ifeng.com/getaccumulator_weight.php?key=' + vid
  # 视频点赞数(str)
  req_url = 'http://survey.news.ifeng.com/getaccumulator_ext.php?key=' + vid + 'ding'
  # 视频不点赞数(str)
  req_url = 'http://survey.news.ifeng.com/getaccumulator_ext.php?key=' + vid + 'cai'
  # 视频评论数(str)
  req_url = 'http://comment.ifeng.com/getv.php?job=3&format=js&docurl=' + vid

同理进行访问时一定要加头部(headers),之后对其进行解析获取信息就好。         最后就是将获取的内容全部存入数据库中,然后进行更新操作,这部分的内容和前面的类似,就不在这里赘述了。

芒果TV

下载前须知:该视频网页web_url的格式必须为https://www.mgtv.com/b/cid/vid.html , 其中cid为6位数字,vid为7位数字!!!
芒果TV是湖南广播电视台旗下唯一互联网视频平台,独家提供湖南卫视所有栏目高清视频直播点播,并为用户提供各类热门电影、电视剧、综艺、动漫、音乐、娱乐等内容。由湖南快乐阳光互动娱乐传媒有限公司(简称“快乐阳光”)负责具体运营。系湖南广播电视台全力发展网络视频业务的唯一新媒体机构。----百度百科
        之前一直想爬取芒果TV的视频,经过几度的查找和测试,发现芒果TV的视频原地址种类很多,可能由于前期准备工作没有做好,所以放弃掉了。之后的偶然来了一些灵感,我就沿着之前的方法继续找了下去,最终还是被我找到了。\\(O_O)/
        接下来我们就一步一步的来看(有点绕,不过很有趣!)
        老办法,利用开发者工具,刷新视频页面,当视频加载到一半后暂停视频,转到开发者工具中,在左上角的输入框中输入video,它会自动的筛选出含有video的url。我们可以发现有几行是带有mp4.ts字段的,将其复制到浏览器地址栏,点击回车我们可以发现出来的就是该视频,不过只是一部分。所以将其余类似的url复制到地址栏,我们就会得到完整的视频。所以我们就可以大致推断出该视频网站的视频是分段存储的。
        仔细观察视频原地址url发现此url很复杂,所需要的参数非常的多,即使你找遍所有的js包也不可能找全所有的参数,但是唯一有用的一点就是这个url的格式,我们之后会用得到。
        因为视频的原地址是在是不好构造,那么就先来找到关于视频信息的url。通过仔细查找后发现:https://pcweb.api.mgtv.com/player/video?video_id=4153263&cid=167299&keepPlay=0&vid=&watchTime=0&\_support=10000000, 这是一个json格式的文件,里面有很多有用的信息。简化后变为:https://pcweb.api.mgtv.com/player/video?video_id=4153263&cid=167299。info中有关于视频的名称、时长、上传日期等。最最令人惊喜的是我们第一眼就可以看到有:标清、高清、超清的字段。很多人的第一感觉是:喔喔,终于找到了视频的原地址。但是当逐级展开后才发现,这里面的url只有一半,并且里面没有mp4或是flv的字段,这就令人很恼火。我之前的想法是拼凑到其它的url后是不是可以获得一些东西。经过多次尝试发现好像没有什么卵用。知道这次我突然发现在stream下面还有一个stream_domain标签,展开后里面也是有三个url,并且是完整的,将其复制到地址栏并访问后发现毛也没有。但是something hit me!就在不经意间我突然想到之前没有将上面缺失的url和下面这个url拼凑到一起去访问,不试不知道,一试吓一跳,还果真跳出来了点数据。还是json数据,那就更有用了。回到前面,仔细观察你会发现在stream中有三个缺失url,在下面的stream_domain有三个完整的url,经过几次尝试,发现以下规律:stream0中的url只能与stream_domain0中的url相结合,并且访问后会出现想要的数据,如果不按以上套路的话,之后的数据会出现差错。接着我们访问https://web-disp.titan.mgtv.com/vod.do?pm=yTDK6Bwlnfm5mniUXWM_xheXsHJy9ovX86dePBwXVxUbBQeBryHvwZ~LUUvHXAXROsTMsdWnfpbJ3lZCjqUCwdiI0_CJO6Pd2ZtY7zVbRT~owCHcFbdzVaEBIldRQ4kSyj7kcr~quvbGJARwntQcFBvDY~zqWkQZwgPDfvOBZdDLFXWP8Hid1uMTV9PuA1F3flDMUYLNsxIeTzJ~3qTqzlOwUmY-&fid=363B4D5D6ACE4B4EC42D69C10AF4726E&arange=0&gsid=164acb88-2301-4d64-af3c-c67b922111ca, 后发现info标签中有MP4字段,且其格式为.m3u8格式的(m3u8不知道的话网上搜一搜)完整url:http://202.109.167.175/c1/2017/10/30_0/47EF3DBB09BD495F5945834205F098D2_20171030_1_1_256_mp4/23270BB618347E98C7237180E54B1A73.m3u8?arange=0&pm=Q22CEwhDc4ERhxMu8vqX_goZwoHgJzf9NNpAFgnaky48OlgH~6yJpExVAhRFjzLDPH_wOgbhwVyMP5UgcJdpVKhe5syenJ2WMyZk81oOxftPW6lQ4GKaXkKVL1rtk0RyfFAr6lCYh_mKrCWWXl~PE9GyCc3UYwPB2Sdxs_qnANmk8RqYaLbJpNBanjbXB6Khq6ke5kJ3rwlAXUYLBZ1KBrs~K0NlpfYuEq0hUHG54GmR5G11IRMi~bGqSjP2vAvJGi5Qhz35kTXno1sUO4ccPBaQ98Zl9QDlbS_1PatCd~I2MqUp0RE8u0bv9A4JWqEYK4vw_lU1QZN3l_3u08Rr1TpPAsHB~SGq4Isr8KltDU0N67gjqXs_XIv1~qX1y0MD7l2KAY7ZJCyLf3Xz, 很多人又很兴奋了:终于终于找到了!!快速的将其粘贴到地址栏并访问,还真是有东西可以下载下来,下载下来后突然傻眼,怎么看不了?exc me?看看其属性大小,实在是小的可怜,那当然是下错了。所以我们换一种思路,不通过地址栏去访问,我们通过编写一段代码去访问或者利用现有的软件去访问,看看能不能获取一些有用的数据。我是用的Postman(大家可以在网上下载,很好用,教程网上都有的!)。通过postman访问后发现,出来了很多的数据,不清楚是什么格式的,但是确实非常有用。仔细观察后发现,隔几行就会有一些很长很长的字符串,其中带有很多的参数,并且在每一个长字符串的上两行都会有#EXT-MGTV-File-SIZE字段,后面会标有数字,一般都是万以上的数字。我们之前说过里的视频是分段存储的,其实到这里我们就已经恍然大悟了,这里的每段长字符串其实是分段视频每段的参数,上面的SIZE字段所对应的数字是这段视频的大小。
        前面还说过某个url的格式很重要。对,就是抓包抓出来可以在网页上观看的url。通过对比后我们发现在_mp4/的后面出现的那一大串东西很像上面的那串很长的字符串,我们经过替换粘贴到地址栏并访问后发现并不能得到视频。那么问题出在哪呢?要怎么解决呢?时间证明:实践才是最好的老师。我发现http://202.109.167.175/c1/2017/10/30_0/47EF3DBB09BD495F5945834205F098D2_20171030_1_1_256_mp4 http://pcvideows.titan.mgtv.com/c1/2017/10/30_0/47EF3DBB09BD495F5945834205F098D2_20171030_1_1_256_mp4 很相似,我何不将那段长字符串粘贴到http://202.109.167.175/c1/2017/10/30_0/47EF3DBB09BD495F5945834205F098D2_20171030_1_1_256_mp4 后面呢,经过测试,最终获得成功。通过构造所得到的url是原视频的地址。然后将所得到的长字符串挨个更换,我们发现就可以获得完整的视频。(以上对应的是标清,通过同样的方法可以获得高清和超清,代码提供的是超清)。
        接着找到了https://vc.mgtv.com/v2/dynamicinfo?callback=jQuery182012115295195808984_1510472757793&_support=10000000&vid=4153263&_=1510472758501 , 简化后https://vc.mgtv.com/v2/dynamicinfo?vid=4153263,同样可以得到相同的内容。里面有关于视频的播放量、点赞数、不点赞数。
        在后面的代码编写和测试过程中可能你会发现是不是的就会报代码错误(例如:Connection aborted.', error(10054, '')),但是通过软件或者浏览器就正确,但是有时候就会发现都不好使了,这是怎么回事呢?观察http://202.109.167.175/c1/2017/10/30_0/47EF3DBB09BD495F5945834205F098D2_20171030_1_1_256_mp4 我们们可以发现,其中有个202.109.167.175 IP字段,上网查了后发现这是一个江西省吉安市的电信IP号,极有可能芒果TV是用的此服务器,所以通过移动宽带去访问的过程中移动与电信之间的“桥”经常断,所易导致代码一直报错,或者视频一直下载不下来。要彻底解决这个问题,那就尽可能在电信网下工作。

搜狐视频

        通过上面四个实例,我就不在多说废话了,先讲一下大致的思路。搜狐视频主要有两种类型:
        1.https://my.tv.sohu.com/us/312813498/94766453.shtml;
        2.https://tv.sohu.com/20171106/n600241945.shtml;
        这两个url最大的最大的不同是在/94766453和/n600241945,一般来说最后的参数基本上都是vid并且只是为单纯的数字,但此时多出了一个n,那么就要将解析部分分为两种:vid带n的和不带n的。
        (1)vid不带n的。         通过正则匹配,获得网页源代码中的vid号,或者正则匹配从web_url中得到vid。然后我们通过开发者工具找到了关于视频一些信息的请求url,进行构造:
  req_url = "http://my.tv.sohu.com/play/videonew.do?vid=%s&referer=http://my.tv.sohu.com" % vid

我们可以通过访问上面这个url,来获取到许多有用的数据(获取下来的是JSON格式的需要进行json.loads()操作):host、video_name、video_size、video_author、video_time、su、clip、ck。接着通过对比:
sohuStartMonitor:"https://vm.aty.sohu.com/pvlog?vp=s&c=14908&v1=19843&v2=19897&p=wrapframe1&loc=CN3301&adstyle=wrapframe&du=65&adtime=15&trule=0&mx=1&al=9406238&out=0&au=1&vid=94766453&tvid=94766453&rt=6033a2f3380235b8feab335f171711f3&uv=15033813019205634143&uuid=9e712dd8-5868-3696-951f-e6cd95135608&UUID=9e712dd8-5868-3696-951f-e6cd95135608&vt=pgc&rd=my.tv.sohu.com&fee=0&isIf=0&suv=1708162045006877&uid=15033813019205634143&myTvUid=312813498&sz=1583_779&md=DClKrU8xiwkZo4meDBJol0sqMrdsUjP7w1nJdg==214&crid=0&scookie=1&ugcode=MTQoFmYvjCUqoPQu8A7_CvIVpcdohzfi4gHqggBZx_EoameDpqYl9JOaJqWpvX_zisALyWc5GUkBepTkx_PTCYkHgig3HWkuLxx0&ugu=312813498&ar=0&sign=GzWbD8e8dNE8mlctLpMPoB1KXSi34NeqK71v5H93xMRWeC8M3nJVw-vBZhvdnSYx&rip=60.12.8.177&sip=10.11.161.90&fip=60.12.8.177&url=https%3A//my.tv.sohu.com/us/312813498/94766453.shtml&ti=5byg5p+P6Iqd5L+p5YS/5a2Q5ZSx5q2M6Ieq5L+h5ruh5ruhIOaJi+iInui2s+i5iOeUu+mdouWPr+eIsQ==&tag=5byg5p+P6IqdIOiwoumchumUiyDosKLotKQg5aix5LmQ5pKt5oql&plat=pc&adplat=0&v1=19843&v2=19897&pagetype=1&suid=1708162045006877&w=1040&h=620&cheattype=0&sperotime=1511405757&tuv=15033813019205634143&encrysig=lVUqoJj2mcCfN2P2uEgsCfZjCrv5P-yn24E2ySXN8o8dAY6v
我们将利用上获得的部分参数进行构造原视频的url:

  video_url = []
    for su, clip, ck in zip(data['data']['su'], data['data']['clipsURL'], data['data']['ck']):
      req_url = 'http://' + host + '/?prot=9&prod=flash&pt=1&file=' + clip + '&new=' + su + '&key=' + \
          ck + '&vid=' + video_id + '&uid=' + str(int(time.time()*1000)) + '&t=' + str(random.random()) + '&rb=1'
      data = json.loads(retry_get(req_url, self.headers))
      video_url.append(str(data['url']))

因为经过测试后发现,搜狐视频有一部分视频是不分段的,有一部分是分段的,但相比于芒果来说要好的很多(大家自行体会)。所以通过for循环,将视频原地址存到video_url中。         (2)vid带n的。         测试后发现,将带有n的vid带入上面的URL中,会返回回来错误的信息,所以通过实践与测试,我们发现在网页源代码中有vid,次vid与web_url中的vid不同,但是可以取得正确的数据,所以通过网页源代码中获取该视频的vid。
        构造请求url:

  req_url = 'http://hot.vrs.sohu.com/vrs_flash.action?vid=%s' % video_id

我们可以通过访问上面这个url,来获取到许多有用的数据(获取下来的是JSON格式的需要进行json.loads()操作):host、video_name、video_size、video_author、video_time、su、clip、ck。然后同理利用这些参数构造原视频url:

  video_url = []
    for su, clip, ck in zip(data['data']['su'], data['data']['clipsURL'], data['data']['ck']):
      req_url = 'http://' + host + '/?prot=9&prod=flash&pt=1&file=' + clip + '&new=' + su + '&key=' + \
          ck + '&vid=' + video_id + '&uid=' + str(int(time.time()*1000)) + '&t=' + str(random.random()) + '&rb=1'
      data = json.loads(retry_get(req_url, self.headers))
      video_url.append(str(data['url']))

接下来就是下载与更新的操作了(人人那边有,就不在重复)。
        项目一开始着手做的就是搜狐视频的视频与信息爬取,当时因为技术、知识有限,半个学期下来一直都没有太大的进展,所以这学期就着手从最简单的人人视频开始爬取,然后是网易视频,接着是凤凰视频,再接着是芒果TV,最后是搜狐视频。整体来看,获取视频的方法大同小异,重要的是细节问题,不同网站对自己的视频资源管理也是不同的,想要顺利的获取到大网站的视频原地址也是不容易的,贵在坚持,自己还有待提高,加油!(至于图片的话,因为图片不好操作,也没有太多时间整理图片,座椅暂时就先文字介绍,之后图片慢慢补上)


以上纯属个人兴趣爱好,欢迎多多提意见,如有冒犯,尽请谅解,不喜勿喷!

videospider's People

Contributors

goghvan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.