Python多線程爬蟲獲取電影下載鏈接

一些電影資源網站往往廣告太多,不想看廣告所以做了這個程序

首先需要先分析網站的搜索鏈接,這裏只用到了“愛下電影網”和“電影天堂”兩個網站

愛下電影:http://www.aixia.cc/plus/search.php?searchtype=titlekeyword&q=%E9%80%9F%E5%BA%A6%E4%B8%8E%E6%BF%80%E6%83%85

電影天堂:http://s.dydytt.net/plus/so.php?kwtype=0&searchtype=title&keyword=%CB%D9%B6%C8%D3%EB%BC%A4%C7%E9

可以發現這兩個網站搜索結果鏈接前部分可以固定死,後面肩上keyword(關鍵詞)即可

所以我們的搜索鏈接可以按照這個規律直接拼接出來

爬蟲基本思考:



二三級均爲線程


首先對於queue模塊,這是一種隊列類型,也就是具有先入先出的特點,用這個來存放需要下載的鏈接

使用:

1.寫入

object=queue.Queue()

object.put('what you want')

object.task_done()

2.讀出

object.get()

注意如果沒有對象在object中會出現堵塞

使用前一定先判斷是否爲空object.emoty()

其次就是thread模塊

我是用的辦法是創建自定義類繼承與threading.Thread類


關於正則表達式這裏就不再提了,我覺得正則表達式比BeautifulSoup和LXML好用一些



爲了擴展性,每一個網站都用一個字典存放相關信息,這樣以後需要添加其他網

站可以通過添加網站字典完成

設置一個url列表存放所有的網站信息字典

爲了方便管理任務列表,我這裏把所有任務放在一個key爲網站名的字典中

變量展示:

task1=queue.Queue()
task2=queue.Queue()

Cannel={'愛下電影':task1,
        '電影天堂':task2
        }#隊列的字典
downloadurl={'愛下電影':[],
             '電影天堂':[]}

"""
website中字典數據格式:
{
    'name':'網站名',
    'url':'網站地址半加工',
    'pat':[正則1,正則2],
    'root':'原本地址'
    'encode':'編碼格式',
}
"""
aixiamovie={
    'name':'愛下電影',
    'url':r'http://www.aixia.cc/plus/search.php?searchtype=titlekeyword&q=',
    'root':r'http://www.aixia.cc',
    'pat':['<h1 class=".*?"><a href="(.*?)" target="_blank">','οnclick="copyUrl(.*?)">'],
    'encode':'utf-8'
    }
tiantang={
    'name':'電影天堂',
    'url':r'http://s.dydytt.net/plus/so.php?kwtype=0&searchtype=title&keyword=',
    'root':r'http://s.dydytt.net',
    'pat':["<td width='.*?'><b><a href='(.*?)'>",'<td style=.*? bgcolor=.*?><a href="(.*?)">'],
    'encode':'gb2312',
    }
weblist=[]
weblist.append(aixiamovie)
weblist.append(tiantang)



任務啓動類:

class taskstart():
    def __init__(self,keyword):
        for item in weblist:#加工搜索地址
            temp=str(keyword.encode(item['encode']))
            temp=temp.replace(r'\x','%')
            temp=temp[2:]
            item['url']=item['url']+temp
        be=findurls(website=weblist)
        be.start()


在這個類中輸入關鍵詞,並根據網站的編碼方式加工網站地址爲搜索結果地址。並啓動尋找詳細鏈接的線程

這個對於多個關鍵詞可以實例化多個該類型對象。


鏈接獲取類:

class findurls(threading.Thread):
    def __init__(self,website):
        threading.Thread.__init__(self)#website是一個key爲網站名,網址,正則表達式的字典集合成的列表
        self.website=website
        self.data=''
        self.id=''
        self.pat=''
        self.root=''
        self.encode=''
    def connect(self,url,counts=3):
        try:
            webpage=requests.get(headers=headers,url=url)
            webpage.encoding=self.encode
            self.data=webpage.text
        except Exception as f:
            print(f)
            if counts > 0:
                print('%s 連接失敗,即將重新連接'%url)
                time.sleep(1)
                counts-=1
                self.connect(url=url,counts=counts)
            else:
                print("爬取失敗")
    def urlgets(self):
        if self.data:
            res=re.findall(self.pat[0],self.data)
            canshu={
                'name':self.id,
                'pat':self.pat[1],
                'encode':self.encode
                }
            if res:
                 #這裏可以開啓爬蟲線程了
                thread1=spdier(dic=canshu)
                thread1.start()
                for item in res:
                    item=self.root+item
                    Cannel[self.id].put(item)#根據網站名投入隊列
                    Cannel[self.id].task_done()
            else:
                print("沒有相關結果")
        else:
            print("沒有返回數據,爬蟲失敗")
    def run(self):
        for item in self.website:
            self.id=item['name']
            self.pat=item['pat']#第一個正則獲取詳情連接 第二個正則獲取下載連接
            self.encode=item['encode']
            self.connect(url=item['url'])
            self.root=item['root']
            self.urlgets()
        print("任務分配完成")
該類對象可以根據列表中的信息鏈接url獲取信息,並通過正則表達式提取詳情頁面的鏈接並載入隊列中,在載入隊列前開啓爬蟲線程,開始對詳情頁面的提取

可以根據情況實例化多個該類型對象,分配url池


spdier類:

class spdier(threading.Thread):#通用爬蟲
    def __init__(self,dic):#dic是一個字典key爲pat,encode,name
        threading.Thread.__init__(self)
        self.id=dic['name']
        self.pat=dic['pat']
        self.encode=dic['encode']
        self.data=[]
        self.wait=5
        self.timeout=3
    def connect(self):
        try:
            if not Cannel[self.id].empty():#檢測隊列是否爲空
                url=Cannel[self.id].get()
                print("%s has running for %s"%(self.id,url))
                webpage=requests.get(url=url,headers=headers,timeout=5)
                webpage.encoding=self.encode
                self.data.append(webpage.text)
                self.timeout=3
                self.wait=5
                self.connect()
               
            else:
                print("%s wait for task!"%(self.id))
                if self.wait>0:
                    self.wait-=1
                    time.sleep(1)
                    self.connect()
                else:
                    print("%s connect compelet!"%(self.id))
        except Exception as f:
            print(f)
            if self.timeout>0:
                self.timeout-=1
                time.sleep(1)
                self.connect()
            else:
                print("連接失敗")
                self.connect()
    def getres(self):
        for each in self.data:
            res=re.findall(self.pat,each)
            #title=re.findall('<title>(.*?)</title>',each)#獲取標題
            if res:
                for item in res:
                    downloadurl[self.id].append(item)
            else:
                print("沒有相關連接")
    def run(self):
        self.connect()
        if self.data:
            self.getres()
            print("%s has make the result!"%self.id)
            save=open(r'f://'+self.id+'.txt','w')
            for d in downloadurl[self.id]:
                save.write(d)
                save.write('\n')
            save.close()
            print("%s work compelet!"%self.id)
        else:
            print("%s 缺少相關信息"%self.id)
這個類就直接根據相關信息獲取下載鏈接並存在f盤中

貼上整體代碼:

import requests
import re
import threading
import queue
import time

headers={
    'User-Agent':r'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0;  TheWorld 7)', 
    }
proxies={#代理配置

    }
task1=queue.Queue()
task2=queue.Queue()

Cannel={'愛下電影':task1,
        '電影天堂':task2
        }#隊列的字典
downloadurl={'愛下電影':[],
             '電影天堂':[]}

"""
website中字典數據格式:
{
    'name':'網站名',
    'url':'網站地址半加工',
    'pat':[正則1,正則2],
    'root':'原本地址'
    'encode':'編碼格式',
}
"""
aixiamovie={
    'name':'愛下電影',
    'url':r'http://www.aixia.cc/plus/search.php?searchtype=titlekeyword&q=',
    'root':r'http://www.aixia.cc',
    'pat':['<h1 class=".*?"><a href="(.*?)" target="_blank">','οnclick="copyUrl(.*?)">'],
    'encode':'utf-8'
    }
tiantang={
    'name':'電影天堂',
    'url':r'http://s.dydytt.net/plus/so.php?kwtype=0&searchtype=title&keyword=',
    'root':r'http://s.dydytt.net',
    'pat':["<td width='.*?'><b><a href='(.*?)'>",'<td style=.*? bgcolor=.*?><a href="(.*?)">'],
    'encode':'gb2312',
    }
weblist=[]
weblist.append(aixiamovie)
weblist.append(tiantang)

class spdier(threading.Thread):#通用爬蟲
    def __init__(self,dic):#dic是一個字典key爲pat,encode,name
        threading.Thread.__init__(self)
        self.id=dic['name']
        self.pat=dic['pat']
        self.encode=dic['encode']
        self.data=[]
        self.wait=5
        self.timeout=3
    def connect(self):
        try:
            if not Cannel[self.id].empty():#檢測隊列是否爲空
                url=Cannel[self.id].get()
                print("%s has running for %s"%(self.id,url))
                webpage=requests.get(url=url,headers=headers,timeout=5)
                webpage.encoding=self.encode
                self.data.append(webpage.text)
                self.timeout=3
                self.wait=5
                self.connect()
               
            else:
                print("%s wait for task!"%(self.id))
                if self.wait>0:
                    self.wait-=1
                    time.sleep(1)
                    self.connect()
                else:
                    print("%s connect compelet!"%(self.id))
        except Exception as f:
            print(f)
            if self.timeout>0:
                self.timeout-=1
                time.sleep(1)
                self.connect()
            else:
                print("連接失敗")
                self.connect()
    def getres(self):
        for each in self.data:
            res=re.findall(self.pat,each)
            #title=re.findall('<title>(.*?)</title>',each)#獲取標題
            if res:
                for item in res:
                    downloadurl[self.id].append(item)
            else:
                print("沒有相關連接")
    def run(self):
        self.connect()
        if self.data:
            self.getres()
            print("%s has make the result!"%self.id)
            save=open(r'f://'+self.id+'.txt','w')
            for d in downloadurl[self.id]:
                save.write(d)
                save.write('\n')
            save.close()
            print("%s work compelet!"%self.id)
        else:
            print("%s 缺少相關信息"%self.id)
           
        
class findurls(threading.Thread):
    def __init__(self,website):
        threading.Thread.__init__(self)#website是一個key爲網站名,網址,正則表達式的字典集合成的列表
        self.website=website
        self.data=''
        self.id=''
        self.pat=''
        self.root=''
        self.encode=''
    def connect(self,url,counts=3):
        try:
            webpage=requests.get(headers=headers,url=url)
            webpage.encoding=self.encode
            self.data=webpage.text
        except Exception as f:
            print(f)
            if counts > 0:
                print('%s 連接失敗,即將重新連接'%url)
                time.sleep(1)
                counts-=1
                self.connect(url=url,counts=counts)
            else:
                print("爬取失敗")
    def urlgets(self):
        if self.data:
            res=re.findall(self.pat[0],self.data)
            canshu={
                'name':self.id,
                'pat':self.pat[1],
                'encode':self.encode
                }
            if res:
                 #這裏可以開啓爬蟲線程了
                thread1=spdier(dic=canshu)
                thread1.start()
                for item in res:
                    item=self.root+item
                    Cannel[self.id].put(item)#根據網站名投入隊列
                    Cannel[self.id].task_done()
            else:
                print("沒有相關結果")
        else:
            print("沒有返回數據,爬蟲失敗")
    def run(self):
        for item in self.website:
            self.id=item['name']
            self.pat=item['pat']#第一個正則獲取詳情連接 第二個正則獲取下載連接
            self.encode=item['encode']
            self.connect(url=item['url'])
            self.root=item['root']
            self.urlgets()
        print("任務分配完成")

class taskstart():
    def __init__(self,keyword):
        for item in weblist:#加工搜索地址
            temp=str(keyword.encode(item['encode']))
            temp=temp.replace(r'\x','%')
            temp=temp[2:]
            item['url']=item['url']+temp
        be=findurls(website=weblist)
        be.start()

keyword=input("請輸入電影名稱 ")
main=taskstart(keyword=keyword)


最終獲取的鏈接如圖所示


這個程序缺點在於只適合於GET請求類型的網站,在後續中再加入了通用POST請求的方法。鏈接點擊打開鏈接

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