python爬取youtube視頻 多線程 非中文自動翻譯

 

 

聲明:我寫的所有文章都是發在博客園的,我看到其他複製粘貼過去的 連個出處也不寫,直接打上自己的水印。。。真是沒的說了。

前言:前段時間搞了一些爬視頻的項目,代碼都寫好了,這裏寫文章那就在來重新分析一遍吧。有不好的地方 莫見怪 : )

環境:python2.7 + win10

 

開始先說一下,訪問youtube需要科學上網,請自行解決,最好是全局代理。

ok,現在開始,首先打開網站觀察

 

 

 網站很乾淨清爽,這次做的是基於關鍵字搜索來爬那些相關視頻,這樣就能很好的分類了,若輸入中文搜索,那結果也一般都是國內視頻,英文的話 那就是國外的。

這裏先來測試中文的 ,輸入''搞笑'',搜出來很多視頻,也可以根據條件篩選,YouTube視頻鏈接很有規律,都是這種https://www.youtube.com/watch?v=v_OVBHGwOaU,只有後面的 v值不一樣,這裏就叫id吧。

 

 

ok,先從最簡單的開始,查看網頁源代碼看看這些視頻鏈接是否都是在裏面,我睜大了我的24k單身狗的眼睛找出來了。。。看了一下,視頻信息全在這個<script>標籤裏面。

 

 

 既然如此,那就直接上正則表達式來匹配

"url":"/watch\?v=(.*?)","webPageType"

這樣就能匹配出ID來。但是 這好像只有第一頁的視頻,那第二頁的呢,經常觀察,此方法不行,視頻翻頁是基於ajax請求來的,源碼裏面的信息始終都是第一頁的數據,ok 那既然這樣,我們來分析ajax請求,我喜歡用谷歌瀏覽器,打開開發者工具,network,來抓包。

鼠標一直往下拉,會自動請求,是個post請求,一看就是返回的視頻信息。

 

 看到這裏很高興,離勝利已經不遠了。但,我們先來看下headers 以及發送的post參數,看了之後 就一句 wtf。。。

 

一萬個羊駝在奔騰,我把那些加密的參數都標記了,前後端交互,既然是發過去的數據,那肯定已經在前端產生了,至於什麼產生的,那就要一步一步分析來了,最後。對 我沒有分析出來。。。剛開始挨着挨查看js文件,參數的確是在js裏面產生的,但。。。tmd寫的太複雜了。。。能力有限,解決不了。難道就這樣放棄了嗎。肯定不會,不然 各位也不會看到這篇文章了。於是,我靈機一動,在地址欄裏面輸入&page=  結果,真的返回視頻了。。。臥槽  哈哈哈,我當時真是很開心呢。因爲前端頁面上並沒有翻頁按鈕,沒想到竟然還真的可以這樣翻頁。。。哈哈

 

 既然這都被我猜出來了,那思路就很清晰了,翻頁--獲得源代碼-- 正則匹配 --就可以批量得到視頻鏈接了,然後去重後 在想辦法直接通過這個鏈接去下載。於是,一陣百度 谷歌 找到很多方法,也找到很多api,ok 那就不必要重複造輪子,直接拿來用吧。

有一個開源項目youtube-dl 在github上 是個命令行的應用,安裝之後,他是這樣的。

 

youtube-dl -F https://www.youtube.com/watch?v=_iupLGTX890

這樣就能直接分析出所有視頻格式的信息,然後通過id 就可以下載下來了。是很好用的一個工具。

在代碼裏面怎麼使用呢,直接調用cmd命令就行了,但是。經過我測試發現呢,批量下載時候,老是有一些視頻沒有下載完整,所以 我就沒用這個方法了,在國外網站上找到一個api 還蠻不錯。

怎麼找 怎麼使用api 我就不用介紹了吧,等會直接貼代碼,大家一看便知。

這裏在說下,當我輸入關鍵字是英文的話,搜出來的結果全部是英文的,於是 我就下載成功後,保存文件 翻譯一下他的標題。翻譯成中文的,我去找翻譯,最後就用金山詞霸了,如果使用官方api的話,好像也有收費。。那不行,我要直接爬頁面,於是,我就直接還是爬他的翻譯頁面,提交英文,返回中文,解析網頁,正則匹配出來。就這樣 嘿嘿嘿。。

ok。說了這麼多了 現在上代碼。

 

複製代碼

# -*-coding:utf-8-*-
# author : Corleone
from bs4 import BeautifulSoup
import lxml
import Queue
import requests
import re,os,sys,random
import threading
import logging
import json,hashlib,urllib
from requests.exceptions import ConnectTimeout,ConnectionError,ReadTimeout,SSLError,MissingSchema,ChunkedEncodingError
import random

reload(sys)
sys.setdefaultencoding('gbk')

# 日誌模塊
logger = logging.getLogger("AppName")
formatter = logging.Formatter('%(asctime)s %(levelname)-5s: %(message)s')
console_handler = logging.StreamHandler(sys.stdout)
console_handler.formatter = formatter
logger.addHandler(console_handler)
logger.setLevel(logging.INFO)

q = Queue.Queue()   # url隊列
page_q = Queue.Queue()  # 頁面

def downlaod(q,x,path):
    urlhash = "https://weibomiaopai.com/"
    try:
        html = requests.get(urlhash).text
    except SSLError:
        logger.info(u"網絡不穩定 正在重試")
        html = requests.get(urlhash).text
    reg = re.compile(r'var hash="(.*?)"', re.S)
    result = reg.findall(html)
    hash_v = result[0]
    while True:
        data = q.get()
        url, name = data[0], data[1].strip().replace("|", "")
        file = os.path.join(path, '%s' + ".mp4") % name
        api = "https://steakovercooked.com/api/video/?cached&hash=" + hash_v + "&video=" + url
        api2 = "https://helloacm.com/api/video/?cached&hash=" + hash_v + "&video=" + url
        try:
            res = requests.get(api)
            result = json.loads(res.text)
        except (ValueError,SSLError):
            try:
                res = requests.get(api2)
                result = json.loads(res.text)
            except (ValueError,SSLError):
                q.task_done()
                return False
        vurl = result['url']
        logger.info(u"正在下載:%s" %name)
        try:
            r = requests.get(vurl)
        except SSLError:
            r = requests.get(vurl)
        except MissingSchema:
            q.task_done()
            continue
        try:
            with open(file,'wb') as f:
                f.write(r.content)
        except IOError:
            name = u'好開心麼麼噠 %s' % random.randint(1,9999)
            file = os.path.join(path, '%s' + ".mp4") % name
            with open(file,'wb') as f:
                f.write(r.content)
        logger.info(u"下載完成:%s" %name)
        q.task_done()

def get_page(keyword,page_q):
    while True:
        headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0'
        }
        page = page_q.get()
        url = "https://www.youtube.com/results?sp=EgIIAg%253D%253D&search_query=" + keyword + "&page=" + str(page)
        try:
            html = requests.get(url, headers=headers).text
        except (ConnectTimeout,ConnectionError):
            print u"不能訪問youtube 檢查是否已翻牆"
            os._exit(0)
        reg = re.compile(r'"url":"/watch\?v=(.*?)","webPageType"', re.S)
        result = reg.findall(html)
        logger.info(u"第 %s 頁" % page)
        for x in result:
            vurl = "https://www.youtube.com/watch?v=" + x
            try:
                res = requests.get(vurl).text
            except (ConnectionError,ChunkedEncodingError):
                logger.info(u"網絡不穩定 正在重試")
                try:
                    res = requests.get(vurl).text
                except SSLError:
                    continue
            reg2 = re.compile(r"<title>(.*?)YouTube",re.S)
            name = reg2.findall(res)[0].replace("-","")
            if u'\u4e00' <= keyword <= u'\u9fff':
                q.put([vurl, name])
            else:
                # 調用金山詞霸
                logger.info(u"正在翻譯")
                url_js = "http://www.iciba.com/" + name
                html2 = requests.get(url_js).text
                soup = BeautifulSoup(html2, "lxml")
                try:
                    res2 = soup.select('.clearfix')[0].get_text()
                    title = res2.split("\n")[2]
                except IndexError:
                    title = u'好開心麼麼噠 %s' % random.randint(1, 9999)
                q.put([vurl, title])
        page_q.task_done()


def main():
    # 使用幫助
    keyword = raw_input(u"請輸入關鍵字:").decode("gbk")
    threads = int(raw_input(u"請輸入線程數量(建議1-10): "))
    # 判斷目錄
    path = 'D:\youtube\%s' % keyword
    if os.path.exists(path) == False:
        os.makedirs(path)
    # 解析網頁
    logger.info(u"開始解析網頁")
    for page in range(1,26):
        page_q.put(page)
    for y in range(threads):
        t = threading.Thread(target=get_page,args=(keyword,page_q))
        t.setDaemon(True)
        t.start()
    page_q.join()
    logger.info(u"共 %s 視頻" % q.qsize())
    # 多線程下載
    logger.info(u"開始下載視頻")
    for x in range(threads):
        t = threading.Thread(target=downlaod,args=(q,x,path))
        t.setDaemon(True)
        t.start()
    q.join()
    logger.info(u"全部視頻下載完成!")

main()

複製代碼

 

在這裏在說一下,我當時用的win10 所有編碼全是gbk的,若在linux上面跑,請自行修改。也是多線程下載的,默認下載目錄 d:\youtube 然後會根據關鍵字在創建子目錄,視頻都放在裏面。對了 還有我代碼裏面用篩選了,只爬1天之內更新的。每天爬一遍即可。

來測試一下。下載的時候 就是考驗網速的時候了,網不好了,可能會出現一些我沒捕獲的異常。。。可能是我找的fq服務器網速還行。。

 

 

ok 到這裏整篇文章就結束了,寫文章都快弄了一個小時了。。不容易呢 :- 

我的github地址 https://github.com/binglansky/spider

發佈了112 篇原創文章 · 獲贊 35 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章