記錄一次關於python爬取視頻的過程

前兩天忽然間腦子抽風想要用python來爬一下視頻網站,獲取視頻。一開始無從下手,在網上搜了不少相關的博客,然而也並未找到一個理想的解決方案,可是好在最終可以將視頻網站的視頻給爬下來,儘管吃相難看了點。特此將整個過程以及思考給記錄下來。html

個人目標是爬取騰訊視頻的視頻內容,在網上搜索出來的結果是利用第三方解析網站對視頻進行解析,而後在爬取,這是最簡單的解決方案。因而乎也就照搬照作了。詳細過程以下:python

打開:http://jx.618g.com/?url=這個第三方解析網站,將待解析的視頻url加在後面就好了。如:https://jx.618g.com/?url=https://v.qq.com/x/cover/c949qjcugx9a7gh.htmlapi

這個時候對https://jx.618g.com/?url=https://v.qq.com/x/cover/c949qjcugx9a7gh.html進行抓包。會發現有不少的.ts的文件,後來由查看了不少相關的博客知道了原來這些.ts的文件就是咱們要抓去的對象。關於詳細的介紹.m3u8以及.ts文件推薦一篇博客給你們,若是不懂的話能夠去看看http://www.javashuo.com/article/p-xuctieys-mp.html緩存

仔細的查看下這些ts文件發現並無須要攜帶的其餘參數直接訪問其url即可實現下載,固然了下載下來的也只是一小段視頻片斷。網絡

 按照網上的一種作法直接請求這些url將其下載下來而後在合併成一個完整的視頻片斷。這種作法的代碼我先貼出來以供參考。多線程

 1 import time
 2 import requests
 3 
 4 
 5 def loder(i):
 6     """直接請求ts文件的url而後在寫入到本地"""
 7     url = 'https://doubanzyv3.tyswmp.com:888/2018/12/12/UEtWtHwTc0UniIDQ/out%03d.ts' % i  # %03d 左邊補0方式
 8     html = requests.get(url).content
 9 
10     with open(r"D:\txsp_test\%s%03d.ts" % ("a", i), "wb") as f:
11         f.write(html)
12 
13 
14 if __name__ == "__main__":
15     pool.map(loder, range(400))
16     pool.join()
17     pool.close()

 這裏你可使用多進程或者多線程來進行優化,我在這裏就不將代碼貼出來了。經過這種方式下載下來的ts文件極可能會由於網絡的問題出現漏下,少下的狀況,爲此我也是嘗試了各類方法都沒有找到一個最優解。我嘗試了一個方法是爲進程加鎖,以信號量的形式對文件進行下載,確保ts文件的完整,同時也能保證異步、併發。可是這樣作的話就至關於開啓了400個進程。你的內存必定會溢出。(有興趣的能夠試下線程鎖)併發

 1 import time
 2 import requests
 3 
 4 
 5 def loder(i, sem):
 6     """直接請求ts文件的url而後在寫入到本地"""
 7     sem.acquire()  # 獲取鑰匙
 8     url = 'https://doubanzyv3.tyswmp.com:888/2018/12/12/UEtWtHwTc0UniIDQ/out%03d.ts' % i  # %03d 左邊補0方式
 9     html = requests.get(url).content
10 
11     with open(r"D:\txsp_test\%s%03d.ts" % ("a", i), "wb") as f:
12         f.write(html)
13     sem.release()
14 
15 
16 if __name__ == "__main__":
17     start_time = time.time()
18     print(start_time)
19     sem = Semaphore(5)  # 規定鎖的個數
20     # pool = Pool(5)
21     p_l = []
22     for i in range(400):
23         p = Process(target=loder, args=(i, sem))
24         p.start()
25         p_l.append(p)
26     for i in p_l:
27         i.join()
28     print(time.time()-start_time)

 而後在對下載好的文件進行合成app

 1 file_dir = r"D:\txsp_test"  # 文件的保存路徑
 2 new_file = u"%s\out.ts" % file_dir  # 合併以後的視頻
 3 f = open(new_file, 'wb+')  # 二進制文件寫操做
 4 
 5 for i in range(0, 338):
 6     file_path = r"D:\txsp_test\%s%03d.ts" % ("a", i)  # 視頻片斷名稱
 7     print(file_path)
 8     for line in open(file_path, "rb"):
 9         f.write(line)
10     f.flush()
11 
12 f.close()

 ok上面的是一種作法,很顯然能夠將視頻的下載與合併發在一塊兒。我也不貼出來了。接下來就是另一種作法。異步



 

這種作法相對來講更加的合理,就是先找到.m3u8的文件。scrapy

而後向其發送get請求,獲得的響應結果就是一段段的.ts集合。(不知道我在說什麼的請看上面的博客,或者本身動手requests.get()試下)這時能夠經過正則匹配出.ts的文件而後在下載下來。最後合併成完整的視頻。

代碼以下:(採用了多線程對.ts文件進行下載)

 1 import re
 2 import os
 3 import shutil
 4 from concurrent.futures import ThreadPoolExecutor
 5 from urllib.request import urlretrieve
 6 
 7 import requests
 8 from scrapy import Selector
 9 
10 
11 class VideoDownLoader(object):
12     def __init__(self, url):
13         self.api = 'https://jx.618g.com'
14         self.get_url = 'https://jx.618g.com/?url=' + url
15         self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
16                         'Chrome/63.0.3239.132 Safari/537.36'}
17 
18         self.thread_num = 32
19         self.i = 0
20         html = self.get_page(self.get_url)
21         if html:
22             self.parse_page(html)
23 
24     def get_page(self, get_url):
25         """獲取網頁"""
26         try:
27             print('正在請求目標網頁....', get_url)
28             response = requests.get(get_url, headers=self.headers)
29             if response.status_code == 200:
30                 # print(response.text)
31                 print('請求目標網頁完成....\n準備解析....')
32                 self.headers['referer'] = get_url
33                 return response.text
34         except Exception:
35             print('請求目標網頁失敗,請檢查錯誤重試')
36             return None
37 
38     def parse_page(self, html):
39         """解析網頁"""
40         print('目標信息正在解析........')
41         selector = Selector(text=html)
42         self.title = selector.xpath("//head/title/text()").extract_first()  # 獲取標題(電影名稱)
43         print(self.title)
44         m3u8_url = selector.xpath("//div[@id='a1']/iframe/@src").extract_first()[14:]  # 獲取視頻地址(m3u8)
45         self.ts_list = self.get_ts(m3u8_url)  # 獲得一個包含ts文件的列表
46         print('解析完成,下載ts文件.........')
47         self.pool()
48 
49     def get_ts(self, m3u8_url):
50         """解析m3u8文件獲取ts文件"""
51         try:
52             response = requests.get(m3u8_url, headers=self.headers)
53             html = response.text
54             print('獲取ts文件成功,準備提取信息')
55             ret_list = re.findall("(out.*?ts)+", html)  # 匹配.ts的字段
56             ts_list = []
57             for ret in ret_list:
58                 ts_url = m3u8_url[:-13] + ret
59                 ts_list.append(ts_url)
60             return ts_list
61         except Exception:
62             print('緩存文件請求錯誤1,請檢查錯誤')
63 
64     def pool(self):
65         print('經計算須要下載%d個文件' % len(self.ts_list))
66         if self.title not in os.listdir():
67             os.mkdir(r"D:" + self.title)  # 新建視頻目錄
68         print('正在下載...所需時間較長,請耐心等待..')
69         # 開啓多進程下載
70         pool = pool = ThreadPoolExecutor(max_workers=16)  # 多線程下載
71         pool.map(self.save_ts, self.ts_list)
72         pool.shutdown()
73         print('下載完成')
74         self.ts_to_mp4()
75 
76     def ts_to_mp4(self):
77         print('ts文件正在進行轉錄mp4......')
78         str = 'copy /b ' + self.title+'\*.ts ' + self.title + '.mp4'  # copy /b 命令
79         os.system(str)
80         filename = self.title + '.mp4'
81         if os.path.isfile(filename):
82             print('轉換完成,祝你觀影愉快')
83             shutil.rmtree(self.title)
84 
85     def save_ts(self, ts_list):
86         print(self.title)
87         self.i += 1
88         print('當前進度%d' % self.i)
89         urlretrieve(url=ts_list, filename=r"D:" + self.title + '\{}'.format(ts_list[-9:]))
90 
91 
92 if __name__ == '__main__':
93     url = "https://v.qq.com/x/cover/c949qjcugx9a7gh.html"   # 視頻url
94     video_down_loader = VideoDownLoader(url)

 運行代碼,喝一杯coffee等待10來分鐘視頻就自動下載好了。可是這裏依然會存在這下載下來的.ts文件不完整的狀況,博客寫到這裏我腦海裏面又想到了一種解決方法,明天試一試把。哦對了,關於視頻的文件下載多線程,多進程我分別都試過,二者的下載速度區別並不大,由於這涉及到了網絡的請求以及文件的讀寫等IO操做。因此採用多線程/進程沒啥區別,建議仍是用多線程來。結果以下所示:

1 # 下載400個.ts文件測試線程、進程的性能
2 
3 # >>> multiprocessing ——> 196.01457595825195  默認開啓4個進程
4 # >>> multiprocessing ——> 196.01457595825195  強制開啓16個進程,實際上5個
5 # >>> threading ——> 174.57704424858093  默認開啓4個線程
6 # >>> threading ——> 202.30066895484924  默認開啓40個線程(網絡卡頓)
7 # >>> threading ——> 155.5946135520935  默認開啓16個線程

 

 測試的結果表名,線程開啓的速度確實比進程開啓速度快。然並卵!

相關文章
相關標籤/搜索