Python爬蟲教程:Python爬取視頻之日本愛情電影

肉身翻墻后,感受一下外面的骯臟世界。墻內(nèi)的朋友叫苦不迭,由于某些原因,VPN能用的越來越少。上周我的好朋友狗子和我哭訴說自己常用的一個(gè)VPN終于也壽終正寢了,要和眾多的日本小姐姐說再見了。作為“外面人”,我還是要幫他一把……
初探
狗子給我的網(wǎng)站還算良心,只跳了五個(gè)彈窗就消停了。
然后看到的就是各種穿不起衣服的女生的賣慘視頻,我趕緊閉上眼睛,默念了幾句我佛慈悲。
Tokyo真的有那么hot?
給狗子發(fā)了一張大的截圖,狗子用涂鴉給我圈出了其中一個(gè)。
我和狗子說“等著吧”
(放心網(wǎng)站截圖我是打了碼也不敢放的。。。)

點(diǎn)進(jìn)去之后,可以在線播放。
右下角有一個(gè) Download 按鈕,點(diǎn)擊之后需要注冊付費(fèi)。
當(dāng)時(shí)我就火了,這種賣慘視頻毒害我兄弟精神,還敢收錢?!
自己動手,豐衣足食!
環(huán)境 & 依賴
Win10 64bit
IDE: PyCharm
Python 3.6
python-site-packegs: requests + BeautifulSoup + lxml + re + m3u8
在已經(jīng)安裝pip的環(huán)境下均可直接命令行安裝
網(wǎng)站解析
將鏈接復(fù)制到Chrome瀏覽器打開
(我平時(shí)用獵豹,也是Chrome內(nèi)核,界面比較舒服,但是這個(gè)時(shí)候必須大喊一聲谷歌大法好)
菜單——更多工具——開發(fā)者選項(xiàng)(或者快捷鍵F12)進(jìn)入Chrome內(nèi)置的開發(fā)者模式
大概界面是這樣
(唉打碼真的累。。。。)
然后,根據(jù)提示,逐層深入標(biāo)簽找到視頻的具體位置
這個(gè)網(wǎng)站存放的位置是 …->flash->video_container->video-player
顯然視頻就放在這個(gè)這個(gè)video-player中
在這個(gè)標(biāo)簽中,有一個(gè)名字為 source 的鏈接,src=”http://#%@就不告訴你#@¥”
Easy好吧!
這點(diǎn)小把戲還難得到我?我已經(jīng)準(zhǔn)備和狗子要紅包了
復(fù)制該鏈接到地址欄粘貼并轉(zhuǎn)到,然后,神奇的一幕出現(xiàn)了!!
What???
這是什么???
為啥這么小???

科普概念如上,那也就是說,m3u8記錄了真實(shí)的視頻所在的地址。
Network Traffic
想要從源碼直接獲得真實(shí)下載地址怕是行不通了。
這時(shí)候再和我一起讀“谷歌大法好!”
很簡單,瀏覽器在服務(wù)器中Get到視頻呈現(xiàn)到我們面前,那這個(gè)過程必定經(jīng)過了解析這一步。
那我們也可以利用瀏覽器這個(gè)功能來進(jìn)行解析
依舊在開發(fā)者模式,最上面一行的導(dǎo)航欄,剛剛我們在Elements選項(xiàng)卡,現(xiàn)在切換到Network
我們監(jiān)聽視頻播放的時(shí)候的封包應(yīng)該就可以得到真實(shí)的視頻地址了,試試看!
我們驚喜的發(fā)現(xiàn),一個(gè)又一個(gè)的 .ts 文件正在載入了
(如果在圖片里發(fā)現(xiàn)任何url請友情提醒我謝謝不然怕是水表難保)
知識點(diǎn)!這都是知識點(diǎn)!(敲黑板!)
點(diǎn)開其中的一個(gè).ts文件看一下
這里可以看到請求頭,雖然url被我走心的碼掉了,但這就是真實(shí)的視頻地址了
復(fù)制這個(gè)URL到地址欄,下載
9s。。。。。
每一個(gè)小視頻只有9s,難道要一個(gè)又一個(gè)的去復(fù)制嗎?
視頻片段爬取
答案是當(dāng)然不用。
這里我們要請出網(wǎng)絡(luò)數(shù)據(jù)采集界的裝逼王:Python爬蟲!!!
首先進(jìn)行初始化,包括路徑設(shè)置,請求頭的偽裝等。
采集部分主要是將requests的get方法放到了for循環(huán)當(dāng)中
這樣做可行的原因在于,在Network監(jiān)聽的圖中我們可以看到.ts文件的命名是具有規(guī)律的 seg-i-v1-a1,將i作為循環(huán)數(shù)
那么問題又來了,我怎么知道循環(huán)什么時(shí)候結(jié)束呢?也就是說我怎么知道i的大小呢?
等等,我好像記得在視頻播放的框框右下角有時(shí)間來著?
在開發(fā)者模式中再次回到Element選項(xiàng)卡,定位到視頻框右下角的時(shí)間,標(biāo)簽為duration,這里的時(shí)間格式是 時(shí):分:秒格式的,我們可以計(jì)算得到總時(shí)長的秒數(shù)
但是呢,這樣需要我們先獲取這個(gè)時(shí)間,然后再進(jìn)行字符串的拆解,再進(jìn)行數(shù)學(xué)運(yùn)算,太復(fù)雜了吧,狗子已經(jīng)在微信催我了
Ctrl+F全局搜索duration

Yes!!!
好了,可以點(diǎn)擊運(yùn)行然后去喝杯咖啡,哦不,我喜歡喝茶。
一杯茶的功夫,回來之后已經(jīng)下載完成。我打開文件夾check一下,發(fā)現(xiàn)從編號312之后的clip都是只有573字節(jié),打開播放的話,顯示的是數(shù)據(jù)損壞。
沒關(guān)系,從312開始繼續(xù)下載吧。然而下載得到的結(jié)果還是一樣的573字節(jié),而且下了兩百多個(gè)之后出現(xiàn)了拒絕訪問錯(cuò)誤。
動態(tài)代理
顯然我的IP被封了。之前的多個(gè)小項(xiàng)目,或是因?yàn)榫W(wǎng)站防護(hù)不夠嚴(yán)格,或是因?yàn)閿?shù)據(jù)條目數(shù)量較少,一直沒有遇到過這種情況,這次的數(shù)據(jù)量增加,面對這種情況采取兩種措施,一種是休眠策略,另一種是動態(tài)代理。現(xiàn)在我的IP已經(jīng)被封了,所以休眠也為時(shí)已晚,必須采用動態(tài)IP了。
主要代碼如下所示
合并文件
然后,我們得到了幾百個(gè)9s的.ts小視頻
然后,在cmd命令行下,我們進(jìn)入到這些小視頻所在的路徑
執(zhí)行
copy/b %s\*.ts %s\new.ts
很快,我們就得到了合成好的視頻文件
當(dāng)然這個(gè)前提是這幾百個(gè).ts文件是按順序排列好的。
成果如下
優(yōu)化—調(diào)用DOS命令 + 解析m3u8
為了盡可能的減少人的操作,讓程序做更多的事
我們要把盡量多的操作寫在code中
引用os模塊進(jìn)行文件夾切換,在程序中直接執(zhí)行合并命令
并且,在判斷合并完成后,使用清除幾百個(gè)ts文件
這樣,我們運(yùn)行程序后,就真的可以去喝一杯茶,回來之后看到的就是沒有任何多余的一個(gè)完整的最終視頻
也就是說,要獲得一個(gè)完整的視頻,我們現(xiàn)在需要輸入視頻網(wǎng)頁鏈接,還需要使用chrome的network解析得到真實(shí)下載地址。第二個(gè)部分顯然不夠友好,還有提升空間。
所以第一個(gè)嘗試是,可不可以有一個(gè)工具或者一個(gè)包能嗅探到指定網(wǎng)頁的network traffic,因?yàn)槲覀儎倓傄呀?jīng)看到真實(shí)地址其實(shí)就在requestHeader中,關(guān)鍵在于怎樣讓程序自動獲取。
查閱資料后,嘗試了Selenium + PhantomJS的組合模擬瀏覽器訪問,用一個(gè)叫做browsermobProxy的工具嗅探保存HAR(HTTP archive)。在這個(gè)上面花費(fèi)了不少時(shí)間,但是關(guān)于browsermobProxy的資料實(shí)在是太少了,即使是在google上,搜到的也都是基于java的一些資料,面向的python的API也是很久沒有更新維護(hù)了。此路不通。
在放棄之前,我又看一篇網(wǎng)站的源碼,再次把目光投向了m3u8,上面講到這個(gè)文件應(yīng)該是包含文件真實(shí)地址的索引,索引能不能把在這上面做些文章呢?
Python不愧是萬金油語言,packages多到令人發(fā)指,m3u8處理也是早就有熟肉。
pip install m3u8
這是一個(gè)比較小眾的包,沒有什么手冊,只能自己讀源碼。
這個(gè)class中已經(jīng)封裝好了不少可以直接供使用的數(shù)據(jù)類型,回頭抽時(shí)間可以寫一寫這個(gè)包的手冊。
現(xiàn)在,我們可以從requests獲取的源碼中,首先找到m3u8的下載地址,首先下載到本地,然后用m3u8包進(jìn)行解析,獲取真實(shí)下載地址。
并且,解析可以得到所有地址,意味著可以省略上面的獲取duration計(jì)算碎片數(shù)目的步驟。
最終
最終,我們現(xiàn)在終于可以,把視頻網(wǎng)頁鏈接丟進(jìn)url中,點(diǎn)擊運(yùn)行,然后就可以去喝茶了。
再來總結(jié)一下實(shí)現(xiàn)這個(gè)的幾個(gè)關(guān)鍵點(diǎn):
- 網(wǎng)頁解析
- m3u8解析
- 動態(tài)代理設(shè)置
- DOS命令行的調(diào)用
動手是最好的老師,尤其這種網(wǎng)站,兼具趣味性和挑戰(zhàn)性,就是身體一天不如一天。。。
完整代碼
# -*- coding:utf-8 -*-import requestsfrom bs4 import BeautifulSoupimport osimport lxmlimport timeimport randomimport reimport m3u8class ViedeoCrawler():def __init__(self):self.url = ""self.down_path = r"F:\Spider\VideoSpider\DOWN"self.final_path = r"F:\Spider\VideoSpider\FINAL"try:self.name = re.findall(r'/[A-Za-z]*-[0-9]*',self.url)[0][1:]except:self.name = "uncensord"self.headers = {'Connection': 'Keep-Alive','Accept': 'text/html, application/xhtml+xml, */*','Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3','User-Agent':'Mozilla/5.0 (Linux; U; Android 6.0; zh-CN; MZ-m2 note Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/40.0.2214.89 MZBrowser/6.5.506 UWS/2.10.1.22 Mobile Safari/537.36'}def get_ip_list(self):print("正在獲取代理列表...")url = 'http://www.xicidaili.com/nn/'html = requests.get(url=url, headers=self.headers).textsoup = BeautifulSoup(html, 'lxml')ips = soup.find(id='ip_list').find_all('tr')ip_list = []for i in range(1, len(ips)):ip_info = ips[i]tds = ip_info.find_all('td')ip_list.append(tds[1].text + ':' + tds[2].text)print("代理列表抓取成功.")return ip_listdef get_random_ip(self,ip_list):print("正在設(shè)置隨機(jī)代理...")proxy_list = []for ip in ip_list:proxy_list.append('http://' + ip)proxy_ip = random.choice(proxy_list)proxies = {'http': proxy_ip}print("代理設(shè)置成功.")return proxiesdef get_uri_from_m3u8(self,realAdr):print("正在解析真實(shí)下載地址...")with open('temp.m3u8', 'wb') as file:file.write(requests.get(realAdr).content)m3u8Obj = m3u8.load('temp.m3u8')print("解析完成.")return m3u8Obj.segmentsdef run(self):print("Start!")start_time = time.time()os.chdir(self.down_path)html = requests.get(self.url).textbsObj = BeautifulSoup(html, 'lxml')realAdr = bsObj.find(id="video-player").find("source")['src']# duration = bsObj.find('meta', {'property': "video:duration"})['content'].replace("\"", "")# limit = int(duration) // 10 + 3ip_list = self.get_ip_list()proxies = self.get_random_ip(ip_list)uriList = self.get_uri_from_m3u8(realAdr)i = 1 # countfor key in uriList:if i%50==0:print("休眠10s")time.sleep(10)if i%120==0:print("更換代理IP")proxies = self.get_random_ip(ip_list)try:resp = requests.get(key.uri, headers = self.headers, proxies=proxies)except Exception as e:print(e)returnif i < 10:name = ('clip00%d.ts' % i)elif i > 100:name = ('clip%d.ts' % i)else:name = ('clip0%d.ts' % i)with open(name,'wb') as f:f.write(resp.content)print('正在下載clip%d' % i)i = i+1print("下載完成!總共耗時(shí) %d s" % (time.time()-start_time))print("接下來進(jìn)行合并……")os.system('copy/b %s\\*.ts %s\\%s.ts' % (self.down_path,self.final_path, self.name))print("合并完成,請您欣賞!")y = input("請檢查文件完整性,并確認(rèn)是否要刪除碎片源文件?(y/n)")if y=='y':files = os.listdir(self.down_path)for filena in files:del_file = self.down_path + '\\' + filenaos.remove(del_file)print("碎片文件已經(jīng)刪除完成")else:print("不刪除,程序結(jié)束。")if __name__=='__main__':crawler = ViedeoCrawler()crawler.run()

搜索下方加老師微信
老師微信號:XTUOL1988【切記備注:學(xué)習(xí)Python】
領(lǐng)取Python web開發(fā),Python爬蟲,Python數(shù)據(jù)分析,人工智能等精品學(xué)習(xí)課程。帶你從零基礎(chǔ)系統(tǒng)性的學(xué)好Python!
源自:https://blog.csdn.net/JosephPai/article/
聲明:文章著作權(quán)歸作者所有,如有侵權(quán),請聯(lián)系小編刪除

