爬蟲demo——爬取電影天堂的資源,存儲到本地json文件

電影天堂里面的數(shù)據(jù)還是非常豐富的,這次的爬蟲demo,是對電影天堂中的電影數(shù)據(jù)進行爬取,包括電影片名,導(dǎo)演,主演,演員等信息以及最后的迅雷下載地址。
經(jīng)過4000部電影的爬取測試,我對代碼多次進行優(yōu)化,目前為止已沒有什么bug,至少可以順利對網(wǎng)站中的電影進行爬取。

一、基本介紹

文章的最后,我會給出爬蟲的完成代碼,文章中的代碼片段如果看上去比較亂的話,可以在了解爬蟲步驟和思想之后,通過完成代碼來梳理自己的思路。

本次爬蟲使用到三個庫,用于請求網(wǎng)頁內(nèi)容的requests庫,用于對網(wǎng)頁內(nèi)容數(shù)據(jù)進行過濾處理的lxml庫,已經(jīng)用于json格式轉(zhuǎn)換的json。

所以在使用之前要引入這些庫,并且保證自己項目中包含這些庫,如果沒有,自行進行安裝。

import requests
from lxml import etree
import json

二、分析電影的鏈接,為爬蟲做準備

首先對電影天堂進行分析,我注意到網(wǎng)站首頁有【2018新片精品】這一個版塊,點擊右邊的更多按鈕,可以來到電影的列表頁。

通過對點一個列表頁的分析,我發(fā)現(xiàn)這不僅僅是2018的最新電影,一共有179頁,共4473條數(shù)據(jù)。仔細分析之后,發(fā)現(xiàn)最早的影片是2009年的。所以當時就決定對和4000多部電影進行爬取。

分析這些列表的URL,不難發(fā)現(xiàn)其中的規(guī)律,列表的URL如下:

其中首頁比較特殊,我們第一次點進行,看到的URL是http://www.dytt8.net/html/gndy/dyzz/index.html,但是我們從其他頁面跳轉(zhuǎn)到首頁,會發(fā)現(xiàn)地址為http://www.dytt8.net/html/gndy/dyzz/list_23_1.html,完全符合上面的規(guī)律。

于是我寫了下面代碼,一次性生成全部的電影列表頁(第1頁~第179頁)的URL,并存儲到列表中:

def movie_list_page():
    base_url = "http://www.dytt8.net/html/gndy/dyzz/list_23_{}.html"
    page_urls = []
    for x in range(1, 180):
        page_urls.append(base_url.format(x))

    return page_urls

現(xiàn)在我們只是獲取到電影的列表頁地址,下一步我們是要從這些列表頁中,獲取每一步電影的詳情頁面地址,比如對于《人類清除計劃》這部電影,我們需要獲取這個地址:http://www.dytt8.net/html/gndy/dyzz/20180919/57492.html

明確這一點,下面我們要開始爬取列表頁中的內(nèi)容。這一步是非常簡單的,簡單看一下頁面就會知道,這些電影的詳情頁地址肯定是很規(guī)律的。大多數(shù)是ul標簽下的li標簽或者是table標簽,于是我寫了下面這些代碼,獲取電影的詳情頁地址:

# 傳入電影列表頁地址,返回這一頁中每一部電影的詳情頁面鏈接
def get_detail_url(url):
    BASE_DIMAIN = "http://www.dytt8.net"  # 定義基礎(chǔ)域名
    response = requests.get(url, headers=HEADERS)
    text = response.text
    html = etree.HTML(text)
    detail_urls = html.xpath("http://table[@class='tbspan']//a[@href!='/html/gndy/jddy/index.html']/@href")
    detail_urls = map(lambda x: BASE_DIMAIN + x, detail_urls)

    return detail_urls

三、請求電影的詳情頁面,過濾數(shù)據(jù)

現(xiàn)在我們拿到了所有的電影列表頁地址,即從第1頁到第179頁的地址。在代碼中使用循環(huán)語句,通過這些地址我們又能夠獲取每一頁中所有電影的詳情頁面信息。這樣一來我們就相當于成功一半,下面的工作就是請求電影詳情頁面中的數(shù)據(jù),以及對這些數(shù)據(jù)進行過濾和處理。

首先我們使用requests庫,將電影詳情頁面中的所有內(nèi)容全部請求下來,然后獲取存放電影信息的那塊內(nèi)容,縮小我們的數(shù)據(jù)范圍,方便我們進一步過濾數(shù)據(jù)。代碼如下:

movie = {}        # 用作后面的存放電影的數(shù)據(jù)
HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36"
}
detail_response = requests.get(url, headers=HEADERS)
detail_text = detail_response.content.decode(encoding="gb18030", errors="ignore")    # 注意這里設(shè)置的編碼格式是根據(jù)電影天堂的編碼格式來的,同時設(shè)置errors="ignore",忽略一些極其特殊的字符的解碼錯誤
detail_html = etree.HTML(detail_text)
if len(detail_html.xpath("http://div[@id='Zoom']")) > 0:
    zoom = detail_html.xpath("http://div[@id='Zoom']")[0]
else:
    return movie            # 說明沒有爬取成功,直接跳過返回一個空字典,放棄對這一步電影的爬取

由于電影天堂中,關(guān)于電影內(nèi)容的部分的數(shù)據(jù)表示不夠明顯,比如沒有特定的class和id來標識。所有我們需要通過xpath語法中的text()獲取表示電影內(nèi)容的文本信息列表,然后對這些列表進行遍歷,過濾我們需要的信息,具體代碼如下:

movie = {}        # 用作后面的存放電影的數(shù)據(jù)
# text_list = zoom.xpath(".//p/text()|.//p/span/text()")        # 版本1.0,沒有考慮到有的頁面中會多出span標簽
# text_list = zoom.xpath(".//p/span/text()|.//p/text()")        # 版本2.0,沒有考慮到有的頁面中會缺少標簽
text_list = zoom.xpath(".//text()")                             # 版本3.0,直接獲取頁面中的文本,進行過濾
for (index, text) in enumerate(text_list):
    # print(text)
    if text.startswith("◎譯  名"):
        movie["teanslation_title"] = text.replace("◎譯  名", "").strip()
    elif text.startswith("◎片  名"):
        movie["real_title"] = text.replace("◎片  名", "").strip()
    elif text.startswith("◎年  代"):
        movie["time"] = text.replace("◎年  代", "").strip()
    elif text.startswith("◎產(chǎn)  地"):
        movie["place"] = text.replace("◎產(chǎn)  地", "").strip()
    elif text.startswith("◎類  別"):
        movie["category"] = text.replace("◎類  別", "").strip()
    elif text.startswith("◎語  言"):
        movie["language"] = text.replace("◎語  言", "").strip()
    elif text.startswith("◎上映日期"):
        movie["release_time"] = text.replace("◎上映日期", "").strip()
    elif text.startswith("◎豆瓣評分"):
        movie["douban_score"] = text.replace("◎豆瓣評分", "").strip()
    elif text.startswith("◎片  長"):
        movie["length"] = text.replace("◎片  長", "").strip()
    elif text.startswith("◎?qū)А ⊙?):
        movie["director"] = text.replace("◎?qū)А ⊙?, "").strip()
    elif text.startswith("◎主  演"):
        actors = []
        actors.append(text.replace("◎主  演", "").strip())
        for num in range(index + 1, index + 10):
            if (text_list[num].startswith("◎簡  介")):
                break
            else:
                actors.append(text_list[num].strip())
        movie["actors"] = actors
    elif text.startswith("◎簡  介"):
        conttent_index = index + 1
        movie["introduction"] = text_list[conttent_index].strip()

# 由于頁面的原因,對下載鏈接進行特殊過濾
if len(zoom.xpath(".//td/a/@href")) > 0:
    download_url = zoom.xpath(".//td/a/@href")[0]
elif len( zoom.xpath(".//td//a/@href")) > 0 :
    download_url = zoom.xpath(".//td//a/@href")[-1]
else:
    download_url = "爬取失敗,手動修改迅雷下載鏈接!"

movie["download_url"] = download_url
print("·", end=" ")             # 簡單的標識,在爬取的時候,成功爬取一部電影,就會打印出一個“·”
return movie

四、將數(shù)據(jù)處理成json格式,保存到本地json文件中

完成上述任務(wù),我們的爬蟲也基本上已經(jīng)接近尾聲。下面要做的就是,調(diào)用封裝上述代碼的函數(shù),將數(shù)據(jù)處理成json格式,然后以每一列表為單位,存儲到本地json文件中。

page_num = 1
page_urls = movie_list_page()
# 以每一列表頁為單位,完成每一列表頁中電影的爬取,處理成json,寫入到本地文件中
for (index, page_url) in enumerate(page_urls):
    file_name = "new_movie_" + str(index + page_num) + ".json"      # 設(shè)置存放每一頁電影信息的json文件的名稱
    one_page_movie_content = []     # 每一頁中所有電影的信息
    movie_detail_urls = get_detail_url(page_url)
    for movie_detail_url in movie_detail_urls:
        movie_content = get_movie_content(movie_detail_url)
        one_page_movie_content.append(movie_content)
    # 將爬取的每一頁的電影數(shù)據(jù),分別寫入到一個json文件中
    one_page_movie_content_str = json.dumps(one_page_movie_content, ensure_ascii=False, indent=2)
    with open(file_name, "w", encoding="utf-8") as f:
        f.write(one_page_movie_content_str)
    print("第" + str(index + page_num) + "頁電影爬取完成,寫入到" + file_name + "文件中")

四、爬蟲完成代碼下載:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容