Python(00):豆瓣電影爬蟲


python豆瓣電影爬蟲

可以爬取豆瓣電影信息,能夠將電影信息存進mysql數據庫,還能夠下載電影預告片。2、3、 4功能使用到selenium庫

一個例程運行截圖



下載好的電影預告片



MySQL存儲的數據



數據表構造



這是程序流程圖,詳細寫明了本爬蟲的運行流程

爬蟲程序代碼

# Author:YFAN
import random
import requests
import lxml.etree
from time import sleep
import re
import pymysql
from selenium import webdriver
 
#瀏覽器請求頭
headerlist = [
        'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
        'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
        'Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/61.0',
        'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36',
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',
        'Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15',
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
        'Mozilla/5.0 (Windows NT 6.1; rv:50.0) Gecko/20100101 Firefox/50.0',
        'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0',
        'Mozilla/5.0 (X11; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0',
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36',
        'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0;  Trident/5.0)',
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0.1 Safari/602.2.14',
        'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36',
        'Mozilla/5.0 (iPad; CPU OS 10_1_1 like Mac OS X) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0 Mobile/14B100 Safari/602.1',
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0',
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:50.0) Gecko/20100101 Firefox/50.0',
        'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0']
 
#---------分 --割 --線 -------------------
#數據庫類
#完成數據插入 查詢操作
class mysql():
    # 查詢函數1
    def select1(self, str):
        # 打開數據庫連接
        db = pymysql.connect("localhost", 'root', 'yf123457.', 'yf')
        # 獲取游標
        cursor = db.cursor()
        # SQL 查詢語句
        sql = "select * from movie2 where name like '%{}%'".format(str)
        try:
            cursor.execute(sql)
            result = cursor.fetchall()#獲取查詢內容
            print("搜索結果有{}個".format(len(result)))
            index = 1
            for i in result:
                print("\nIndex:{}".format(index))
                print("豆瓣鏈接:{}".format(i[10]))
                print("片名:{}".format(i[0]))
                print("導演:{}".format(i[1]))
                print("編劇:{}".format(i[2]))
                print("主演:{}".format(i[3]))
                print("類型:{}".format(i[4]))
                print("上映日期:{}".format(i[5]))
                print("片長:{}".format(i[6]))
                print("IMDb:{}".format(i[7]))
                print("簡介:\n{}".format(i[8]))
                print("預告片鏈接:{}".format(i[9]))
                index += 1
            print()
        except:
            print("數據查詢異常")
            db.rollback()#回滾
        db.close()#關閉數據庫連接
 
    # 查詢函數2
    def select2(self, str):
        # 打開數據庫連接
        db = pymysql.connect("localhost", 'root', 'yf123457.', 'yf')
        # 獲取游標
        cursor = db.cursor()
        # SQL 查詢語句
        sql = "select * from movie2 where type like '%{}%'".format(str)
        try:
            cursor.execute(sql)
            result = cursor.fetchall()#獲取查詢內容
            print("搜索結果有{}個".format(len(result)))
            index = 1
            for i in result:
                print("\nIndex:{}".format(index))
                print("豆瓣鏈接:{}".format(i[10]))
                print("片名:{}".format(i[0]))
                print("導演:{}".format(i[1]))
                print("編劇:{}".format(i[2]))
                print("主演:{}".format(i[3]))
                print("類型:{}".format(i[4]))
                print("上映日期:{}".format(i[5]))
                print("片長:{}".format(i[6]))
                print("IMDb:{}".format(i[7]))
                print("簡介:\n{}".format(i[8]))
                print("預告片鏈接:{}".format(i[9]))
                index += 1
            print()
        except:
            db.rollback()
            print("數據查詢異常")
        db.close()#關閉數據庫連接
 
    #插入函數1
    def insert(self,top,moviename,director,writer,actors,type,date,timelong,IMDburl,introduction,tralerurl,movieurl):
        # 打開數據庫連接
        db = pymysql.connect("localhost", "root", "yf123457.", "yf")
        # 使用cursor()方法獲取操作游標
        cursor = db.cursor()
        # 電影簡介內容出現""雙引號 無法直接插入數據表中 需要進行處理
        # 使用正則替換,便可以插入數據表中
        temp = re.compile("\"")
        text = temp.sub("\\\"", introduction)
        #SQL 插入語句
        sql = """INSERT INTO movie VALUES ({}, "{}","{}","{}","{}","{}","{}","{}","{}","{}","{}","{}")""" \
            .format(top,moviename, director, writer, actors, type, date, timelong, IMDburl, text, tralerurl, movieurl)
        # SQL 查詢語句
        sql2 = "select name from movie where name='{}'".format(moviename)
        try:
            # 執行sql語句
            cursor.execute(sql2)
            # 提交到數據庫執行
            result = cursor.fetchall()
            if len(result)==0:
                # 執行sql語句
                cursor.execute(sql)
                # 提交到數據庫執行
                db.commit()
                print("數據插入成功")
            else:
                print("數據表中記錄已存在")
        except:
            # 如果發生錯誤則回滾
            db.rollback()
        # 關閉數據庫連接
        db.close()
 
    #插入函數2
    def insert2(self, moviename, director, writer, actors, type, date, timelong, IMDburl, introduction,
                tralerurl, movieurl):
        # 打開數據庫連接
        db = pymysql.connect("localhost", "root", "yf123457.", "yf")
        # 使用cursor()方法獲取操作游標
        cursor = db.cursor()
        # 電影簡介內容出現""雙引號 無法直接插入數據表中 需要進行處理
        # 使用正則替換,便可以插入數據表中
        temp = re.compile("\"")
        text = temp.sub("\\\"", introduction)
        # SQL 插入語句
        sql = """INSERT INTO movie2 VALUES ("{}","{}","{}","{}","{}","{}","{}","{}","{}","{}","{}")""" \
            .format(moviename, director, writer, actors, type, date, timelong, IMDburl, text, tralerurl, movieurl)
        sql2 = "select name from movie2 where name='{}'".format(moviename)
        try:
            # 執行sql語句
            cursor.execute(sql2)
            # 提交到數據庫執行
            result = cursor.fetchall()
            if len(result)==0:
                # 執行sql語句
                cursor.execute(sql)
                # 提交到數據庫執行
                db.commit()
                print("數據插入成功")
            else:
                print("數據表中記錄已存在")
        except:
            # 如果發生錯誤則回滾
            db.rollback()
        # 關閉數據庫連接
        db.close()
 
#---------分 --割 --線 -------------------
#豆瓣爬蟲類
#豆瓣網 [url=https://movie.douban.com/]https://movie.douban.com/[/url]
#豆瓣TOP250電影榜 [url=https://movie.douban.com/top250?start=0&filter=]https://movie.douban.com/top250?start=0&filter=[/url]
class spider():
    path=""#文件下載路徑
    mysqlflag=True#是否將數據插入數據庫
    downloadflag=True#是否下載預告片
 
    # 構造函數 設置預告片存儲路徑 是否下載預告片? 是否插入數據表?
    def __init__(self,path,downloadflag,mysqlflag):
        self.path=path
        self.downloadflag=downloadflag
        self.mysqlflag=mysqlflag
 
    #遍歷頁面
    def foreach(self):
        print()
        print("豆瓣電影TOP250榜電影")
        i=0
        while i<10:
            #遍歷
            #flag 為獲取當前頁電影鏈接返回的值 如果為1則遍歷下一頁 不為1繼續遍歷本頁
            flag=self.foreachpPageurl(i)
            if flag==1:
                i+=1
            else:
                print("重新訪問當前頁")
 
    # 獲取當前頁電影鏈接
    def foreachpPageurl(self, page):
        url = "https://movie.douban.com/top250?start={}&filter=".format(page * 25)
        header = dict()
        header["user-agent"] = random.choice(headerlist)
        try:
            #通過requests.get()方法 獲取網頁信息
            r = requests.get(url, headers=header)
            r.raise_for_status()#網頁狀態碼 200 2開頭表示正常 4或5開頭的拋出異常
            #使用lxml將網頁的源碼轉換成xml,然后使用xpath()進行xml解析
            xml = lxml.etree.HTML(r.text)
            #獲取xml下類名為hd的div標簽下的所有a標簽的href屬性  href裝着電影鏈接url
            a = xml.xpath("//div[@class='hd']/a/@href")
            i = 0
            count = page * 25 + 1
            while i < (len(a)):
                print("TOP {}".format(count))
                #遍歷當前頁面的所有電影鏈接
                flag = self.getMovieMessage(count, a[i],0)
                sleep(3)  # 休息兩秒 應付反爬
                if flag == 1:
                    i += 1
                    count += 1
                    print("")
                else:
                    print("重新獲取電影詳細信息")
            return 1
        except:
            print("當前頁無法訪問!")
            return 0
 
    #搜索豆瓣電影
    def seachMovie(self,name):
        chrome_path = 'C:/Program Files (x86)/Google/Chrome/Application/chromedriver.exe'
        # os.environ['webdriver.chrome.driver'] = chrome_path#設置系統環境變量
        drive = webdriver.Chrome(chrome_path)  # 打開谷歌瀏覽器
        # 打開一個網址
        drive.get('https://movie.douban.com/subject_search?search_text={}&cat=1002'.format(name))
        try:
            #通過xpath查找網頁標簽
            drive.implicitly_wait(3)  # 等待3秒
            a = drive.find_element_by_xpath('//*[@id="root"]/div/div[2]/div[1]/div[1]/div[1]/div[1]/a')
            url = a.get_attribute('href')
            a.click()#點擊連接
            #獲取電影詳細信息
            self.getMovieMessage(0,url,1)
            drive.implicitly_wait(5)  # 等待5秒
        except:
            print("error")
        #drive.close()#關閉頁面
 
    #最新上映電影
    def LatestReleases(self):
        chrome_path = 'C:/Program Files (x86)/Google/Chrome/Application/chromedriver.exe'
        # os.environ['webdriver.chrome.driver'] = chrome_path#設置系統環境變量
        drive = webdriver.Chrome(chrome_path)  # 打開谷歌瀏覽器
        drive.get('https://movie.douban.com/')  # 打開一個網址
        try:
            #等待3秒  給予時間給瀏覽器加載javascript
            drive.implicitly_wait(3)
            # 通過xpath查找網頁標簽
            a = drive.find_elements_by_xpath('//*[@id="screening"]/div[2]/ul/li/ul/li[1]/a')
            #定義一個集合 用來存儲最新上映電影鏈接地址
            s = set()
            for i in a:
                s.add(i.get_attribute("href"))
            print("最新上映的電影有{}個".format(len(s)))
            l=list(s)#set轉為list
            drive.implicitly_wait(3)  # 等待3秒
            for i in range(len(l)):
                print()
                print("第{}個".format(i+1))
                # 獲取電影詳細信息
                self.getMovieMessage(i+1,l[i],1)
                print()
        except:
            print("error")
        # drive.close()#關閉頁面
 
    #最近熱門電影
    def recentot(self):
        chrome_path = 'C:/Program Files (x86)/Google/Chrome/Application/chromedriver.exe'
        # os.environ['webdriver.chrome.driver'] = chrome_path#設置系統環境變量
        drive = webdriver.Chrome(chrome_path)#打開谷歌瀏覽器
        drive.get( 'https://movie.douban.com/' )#打開一個網址
        try:
            # 等待3秒  給予時間給瀏覽器加載javascript
            drive.implicitly_wait(3)
            # 通過xpath查找網頁標簽
            a=drive.find_elements_by_xpath('//*[@id="content"]/div/div[2]/div[4]/div[3]/div/div[1]/div/div//a')
            # 定義一個集合 用來存儲最近熱門電影鏈接地址
            s= set()
            for i in a:
                s.add(i.get_attribute("href"))
            l=list(s)#set轉為list
            print("最近熱門電影有{}個".format(len(l)))
            for i in range(len(l)):
                print()
                print("第{}個".format(i+1))
                #獲取電影詳細信息
                self.getMovieMessage(0,l[i],1)
                print()
            drive.implicitly_wait(3)#等待3秒
        except:
            print("error")
    # drive.close()#關閉瀏覽器
 
    # 獲取電影詳細信息
    def getMovieMessage(self, top, url,insertflag):#其中flag 用來決定插入哪個數據表 movie或movie2 用來判斷使用哪一個數據庫插入函數
        print("豆瓣電影鏈接:{}".format(url))
        header = dict()
        #獲取請求頭的user-agent字典,應付反爬處理
        header["user-agent"] = random.choice(headerlist)#通過random.choice隨機抽取一個user-agent
        director = ''  # 導演
        writer = ''  # 編劇
        actors = ''  # 主演
        type = ''  # 類型
        date = ''  # 上映時間
        timelong = ''  # 時長
        IMDb = ''  # IMDb鏈接
        text = ''  # 簡介
        video = ''  # 預告片
        try:
            #通過requests.get獲取url鏈接
            r = requests.get(url, headers=header)
            r.raise_for_status()#網頁狀態碼 200
            xml = lxml.etree.HTML(r.text)#將網頁源碼轉為xml 用lxml庫進行解析
            # 獲取電影名
            n = xml.xpath("//div[@id='content']/h1/span")#通過xpath獲取網頁標簽
            name = n[0].text + n[1].text
            print("片名:{}".format(name))
            div1 = xml.xpath("//div[@id='info']//span[@class='attrs']")
            for i in range(len(div1)):
                if i == 0:
                    #獲取電影導演
                    x1 = div1[0].xpath("a")
                    for i in x1:
                        director += i.text + " "
                elif i == 1:
                    #獲取電影編劇
                    x2 = div1[1].xpath("a")
                    for i in x2:
                        writer += i.text + " "
                elif i == 2:
                    #獲取電影的前幾個主演
                    x3 = div1[2].xpath("a")
                    for i in range(5):
                        if i >= len(x3): break
                        actors += x3[i].text + " "
            # 以上這么寫原因:有些電影無編劇 無主演 健壯代碼
            print("導演:{}".format(director))
            print("編劇:{}".format(writer))
            print("主演:{}".format(actors))
            # 獲取電視;類型
            x4 = xml.xpath("//span[@property='v:genre']")
            for i in x4:
                type += i.text + " "
            print("類型:{}".format(type))
            # 獲取電視上映日期
            x5 = xml.xpath("//span[@property='v:initialReleaseDate']")
            for i in x5:
                date += i.text + " "
            print("上映日期:{}".format(date))
            # 獲取電影片長
            x6 = xml.xpath("//span[@property='v:runtime']")
            for i in x6:
                timelong += i.text + ' '
            print("片長:{}".format(timelong))
            #獲取電影的IMDb鏈接
            div2 = xml.xpath("//div[@id='info']/a/@href")
            if len(div2)!=0:
                IMDb = div2[0]
            print("IMDb鏈接:{}".format(IMDb))
            #獲取電影簡介
            x7 = xml.xpath("//span[@property='v:summary']/text()")
            for i in range(len(x7)):
                text += "  " + x7[i].strip()
                if i < len(x7) - 1: text += '\n'
            print("簡介:\n{}".format(text))
            #獲取預告片鏈接
            video = xml.xpath("//a[@title='預告片']/@href")
            if len(video) >= 1:
                print("預告片鏈接:{}".format(video[0]))
                while True:
                    sleep(2)  # 休息2秒
                    #前往預告片鏈接頁面,獲取預告片的播放地址,實現下載預告片功能
                    flag = self.getMovieTrailer(name, video[0])
                    if flag == 1:
                        #下載成功后,數據庫插入數據
                        #根據myusql來決定是否將數據插入數據表中
                        if self.mysqlflag:
                            if insertflag==0:
                                #將電影詳細信息插入數據庫
                                mysql().insert(top, name,director,writer,actors,type,date,timelong,IMDb,text,video[0],url)
                            elif insertflag == 1:
                                mysql().insert2(name,director,writer,actors,type,date,timelong,IMDb,text,video[0],url)
                        return 1
                    else:
                        print("重新獲取電影預告片")
            else:
                #有些電影沒有預告片,數據庫插入數據
                # 根據myusql來決定是否將數據插入數據表中
                print("該電影找不到預告片")
                if self.mysqlflag:
                    if insertflag==0:
                        #執行插入函數1
                        mysql().insert(top, name, director, writer, actors, type, date, timelong, IMDb, text,"", url)
                    elif insertflag==1:
                        #執行插入函數2
                        mysql().insert2(name, director, writer, actors, type, date, timelong, IMDb, text, "", url)
                return 1
        except:
            print("無法訪問電影詳細信息")
            return 0
 
    # 獲取電影預告片
    def getMovieTrailer(self, name, url):
        header = dict()
        header['user-agent'] = random.choice(headerlist)
        try:
            r = requests.get(url, headers=header)
            xml = lxml.etree.HTML(r.text)
            #預告片頁面使用了javascript特性,預告片url地址藏在了<script></script>內
            #獲取到指定的script標簽后,對script標簽下代碼轉換成字符串后進行字符串處理
            #處理后便可以獲取到預告片的實際播放地址url
            #獲取url后便可以進行下載操作,獲取不到返回0表示該電影沒有預告片
            script = xml.xpath("//script[@type='application/ld+json']/text()")
            str1 = str(script)
            start = str1.find("http://vt1")
            end = str1.find(".mp4")
            result = str1[start:end + 4]
            #根據downloadflag開關來決定是否下載預告片
            if self.downloadflag :
                while True:
                    sleep(2)  # 休息2秒
                    # 下載預告片
                    flag = self.download(name, result)
                    if flag == 1:
                        return 1
                    else:
                        print("下載失敗,重新下載")
            else:
                return 1
        except:
            print("無法獲取電影預告片")
            return 0
 
    # 下載預告片
    def download(self, name, url):
        header = dict()
        header['user-agent'] = random.choice(headerlist)
        try:
            r = requests.get(url, headers=header, timeout=30)
            # 處理一下文件名 有些電影名中帶 /\*:"?<>| ,windows中的文件名是不能有這些字符
            NAME = ''
            for i in range(len(name)):
                if name[i] not in '/\*:"?<>|':
                    NAME += name[i]
                else:
                    NAME += ""
            filename = self.path + '\\' + NAME + ".mp4"#預告片路徑
            print("保存路徑:{}".format(filename))
            #通過二進制寫入文件
            with open(filename, 'wb') as f:
                f.write(r.content)
                f.close()#關閉流
            print("下載成功")
            return 1
        except:
            print("下載電影預告片失敗")
            return 0
 
#---------------------------
 
#----------------分割線-----------
#菜單函數
def menu():
    print("""------------------主-----菜-----單--------------------------
         1 豆瓣電影TOP250榜電影
         2 豆瓣電影最新上映電影
         3 豆瓣電影最近熱門電影
         4 豆瓣電影動態搜索電影
         5 查詢數據庫中電影信息
         0     退-出-爬-蟲
    """)
    try:
        i=int(input("輸入操作(0-5):"))
        if i < 0 or i > 5:
            print("操作有誤,重新操作")
            return 1
        if i == 0:
            return 0
        if i == 5:
            return menu2()#次級帶單2
        s = menu1()#次級菜單1
        if s != 0:
            if i == 1:
                s.foreach()#執行豆瓣電影TOP250榜遍歷函數
            elif i == 2:
                s.LatestReleases()#獲取豆瓣最新上映電影
            elif i == 3:
                s.recentot()#獲取豆瓣最近熱門電影
            elif i == 4:
                s.seachMovie(input("輸入電影關鍵詞:"))#豆瓣搜索電影
        else:
            print("操作有誤,重新操作")
            return 1
        return 1
    except:
        print("輸入有誤,請按規定輸入數字")
 
#二級菜單1 獲取spider爬蟲類對象
def menu1():
    print("""------------------次---級---菜---單-----------------------
        1 不下載電影預告片,數據庫不存儲電影信息
        2 下載電影預告片,數據庫存儲電影信息
        3 數據庫只存儲電影信息
        4 只下載電影預告片
        """)
    try:
        i = int(input("輸入操作(1-4):"))
        if i == 1:
            return spider("", False, False)#返回一個spider類對象
        elif i == 2:
            return spider(input("輸入預告片存儲路徑:"), True, True)#返回一個spider類對象
        elif i == 3:
            return spider("", False, True)
        elif i == 4:
            return spider(input("輸入預告片存儲路徑:"), True, False)#返回一個spider類對象
        else:
            print("操作有誤,返回上一級")
            return 0
    except:
        print("輸入有誤,請按規定輸入數字")
        return 0
 
#二級菜單2  查詢數據庫電影信息
def menu2():
    m=mysql()
    print("""------------------次---級---菜---單-----------------------
        1 電影名查詢
        2 類型查詢
    """)
    try:
        i=int(input("輸入操作(1-2):"))
    except:
        print("輸入有誤,請按規定輸入數字")
        return 0
    try:
        if i==1:
            m.select1(input("輸入查詢電影名關鍵字:"))
        elif i==2:
            m.select2(input("輸入查詢電影類型關鍵字:"))
        else:
            print("操作有誤,返回上次菜單")
        return 1
    except:
        print("查詢異常")
        return 0
 
# ----------------分----割----線-----------
#主函數
if __name__ == '__main__':
    while True:
        print()
        flag=menu()#
        if flag==0:break
        sleep(3)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM