Python爬蟲獲取電影鏈接(續)

上一篇文章中的兩個網站都是用的是get方法,獲取很簡單並且沒有任何防爬手段,後面我再嘗試BT天堂,影視大全網發現更多的網站搜索頁面是post請求並需要提交表單,

所以這裏給之前的程序作出一些補充,使之可以爬蟲需要post請求的網站。


首先提出一個使用fiddler的小技巧,斷點查詢,在這裏點擊Rules在其下拉列表中選擇Automatic breakpoint之後選擇After Request 這樣更容易查詢到瀏覽器提交的相關信息以及請求的url


這裏以BT天堂爲例子  其網站url:http://www.bttiantangs.com/  BT天堂  當然最好的方案是先進入該網頁,獲取該網頁的cookies,但是我在實踐中發現該網站並不會檢查cookies。

通過fiddler發現提交請求的url爲http://www.bttiantangs.com/e/search/new.php 請求類型爲post 提交的數據只有一個‘keyboard’,值得注意的是這一項數據直接提交字符串不需要其他處理。

爲了擴展性,將網址所有相關信息放在一個字典中:

btmovie={
        'name':'BT天堂',
        'root':'http://www.bttiantangs.com',
        'posturl':'http://www.bttiantangs.com/e/search/new.php',
        'dict':{'keyboard':''},
        'encode':'utf-8',
        'pat':['<a href="(.*?)" class="zoom" rel="bookmark" target="_blank" title="(.*?)">',r'<em>.*?</em></a><a href="(.*?)" target="_blank">']
    }

其信息包括網站主頁地址,網站名稱,請求url,編碼格式,提交表單,以及提取信息的相關正則表達式,這樣做好處在於需要添加更多網頁時可以增加一個字典即可。數據存儲的最後一步將此類型網站數據放入一個列表中:

postlist=[]
postlist.append(btmovie)
創建一個線程用以加工所需要提交的表單數據:

class posttask(threading.Thread):
    def __init__(self,keyword):
        self.keyword=keyword
        threading.Thread.__init__(self)
    def run(self):
        for item in postlist:#對發送的字典進行加工
            if item['name'] == 'BT天堂':   
                item['dict']['keyboard']=self.keyword
        se=posturlget(urls=postlist)
        se.start()
這個類可以通過判斷網站名對不同網站進行提交表單的加工,並傳入請求url

接下來即需要請求搜索結果,並提取搜索結果中的相關連接,創建一個新的線程即上面程序中的posturlget類

class posturlget(threading.Thread):
    def __init__(self,urls):
        self.urls=urls
        self.encode=''
        self.pat=[]
        self.id=''
        self.data={}
        self.res=''
        self.root=''
        threading.Thread.__init__(self)
    def postlist(self,url,counts=3):
        try:
            webpage=requests.post(headers=headers,data=self.data,url=url)
            webpage.encoding=self.encode
            self.res=webpage.text
        except Exception as f:
            print(f)
            if counts>0:
                counts-=1
                time.sleep(1)
                self.postlist(url,counts)
            else:
                print('請求錯誤')
    def fenpei(self):
        urllist=re.findall(self.pat[0],self.res)
        canshu={
                    'name':self.id,
                    'pat':self.pat[1],
                    'encode':self.encode
               }
        task=spdier(dic=canshu)
        task.start()
        if urllist:
            for item in urllist:
                item=self.root+item[0]
                Cannel[self.id].put(item)
                Cannel[self.id].task_done()
                print("分配完成")
        else:
            print("沒有匹配項")
    def run(self):
        for item in self.urls:
            self.encode=item['encode']
            self.pat=item['pat']
            self.data=item['dict']
            self.id=item['name']
            self.postlist(url=item['posturl'])
            self.root=item['root']
            if self.data:
                self.fenpei()

這裏加入一些異常的處理措施讓爬蟲遇到異常時不至於崩潰,並且提取出相關詳情頁面的連接,存放在列表中,生成一個字典存放爬蟲需要的相關數據傳入最後的爬蟲線程

這一步其實就是一個post請求獲取搜索結果頁面並提取出其中的詳情連接,存放到相應隊列中


最後跟上一個程序一樣,具有較強通用性的下載連接獲取爬蟲:

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))
                    return
        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)
            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)
            return
        else:
            print("%s 缺少相關信息"%self.id)
            return


該線程啓動需要一個字典包含正則信息,編碼信息,和url集合

最終該程序將會將結果生成一個簡單的txt文檔存放在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()
task3=queue.Queue()
Cannel={'愛下電影':task1,
        '電影天堂':task2,
        'BT天堂':task3
        }#隊列的字典
downloadurl={'愛下電影':[],
             '電影天堂':[],
             'BT天堂':[]
             }

"""
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',
    }
btmovie={
        'name':'BT天堂',
        'root':'http://www.bttiantangs.com',
        'posturl':'http://www.bttiantangs.com/e/search/new.php',
        'dict':{'keyboard':''},
        'encode':'utf-8',
        'pat':['<a href="(.*?)" class="zoom" rel="bookmark" target="_blank" title="(.*?)">',r'<em>.*?</em></a><a href="(.*?)" target="_blank">']
    }
weblist=[]
weblist.append(aixiamovie)
weblist.append(tiantang)

postlist=[]
postlist.append(btmovie)


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))
                    return
        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)
            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)
            return
        else:
            print("%s 缺少相關信息"%self.id)
            return
        
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("爬取失敗")
                return
    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("任務分配完成")
        return

class taskstart(threading.Thread):
    def __init__(self,keyword):
        threading.Thread.__init__(self)
        self.keyword=keyword
    def run(self):
        for item in weblist:#加工搜索地址
            temp=str(self.keyword.encode(item['encode']))
            temp=temp.replace(r'\x','%')
            temp=temp[2:]
            item['url']=item['url']+temp
        be=findurls(website=weblist)
        be.start()
        return
class posturlget(threading.Thread):
    def __init__(self,urls):
        self.urls=urls
        self.encode=''
        self.pat=[]
        self.id=''
        self.data={}
        self.res=''
        self.root=''
        threading.Thread.__init__(self)
    def postlist(self,url,counts=3):
        try:
            webpage=requests.post(headers=headers,data=self.data,url=url)
            webpage.encoding=self.encode
            self.res=webpage.text
        except Exception as f:
            print(f)
            if counts>0:
                counts-=1
                time.sleep(1)
                self.postlist(url,counts)
            else:
                print('請求錯誤')
    def fenpei(self):
        urllist=re.findall(self.pat[0],self.res)
        canshu={
                    'name':self.id,
                    'pat':self.pat[1],
                    'encode':self.encode
               }
        task=spdier(dic=canshu)
        task.start()
        if urllist:
            for item in urllist:
                item=self.root+item[0]
                Cannel[self.id].put(item)
                Cannel[self.id].task_done()
                print("分配完成")
        else:
            print("沒有匹配項")
    def run(self):
        for item in self.urls:
            self.encode=item['encode']
            self.pat=item['pat']
            self.data=item['dict']
            self.id=item['name']
            self.postlist(url=item['posturl'])
            self.root=item['root']
            if self.data:
                self.fenpei()
                        
class posttask(threading.Thread):
    def __init__(self,keyword):
        self.keyword=keyword
        threading.Thread.__init__(self)
    def run(self):
        for item in postlist:#對發送的字典進行加工
            if item['name'] == 'BT天堂':   
                item['dict']['keyboard']=self.keyword
        se=posturlget(urls=postlist)
        se.start()
            
        
keyword=input("請輸入電影名稱 ")
get=taskstart(keyword=keyword)
post=posttask(keyword=keyword)
get.start()
post.start()      
    



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章