Python爬蟲---爬取騰訊動漫全站漫畫

操做環境

  1. 編譯器:pycharm社區版
  2. python 版本:anaconda python3.7.4
  3. 瀏覽器選擇:Google瀏覽器
  4. 須要用到的第三方模塊:requests , lxml , selenium , time , bs4,os

網頁分析

明確目標

首先咱們打開騰訊動漫首頁,分析要抓取的目標漫畫。
找到騰訊動漫的漫畫目錄頁,簡單看了一下目錄,發現全站的漫畫數量超過了三千部(感受就是爬下來也會把內存撐爆)
html

因而我以爲爬取首頁的推薦漫畫會是一個比較好的選擇(爬取全站漫畫只須要稍稍改一下網址構造就能夠作到了)
python

提取漫畫地址

選定了對象以後,就應該想辦法來搞到漫畫的地址了
右擊檢查元素,粗略看一遍網頁的源代碼,這時我發現裏面有不少連續的git

  • 標籤,我猜想每部漫畫的地址信息就存儲在這些標籤裏面

    隨便打開一個《li》標籤,點擊裏面包裹的連接地址會跳轉到一個新的網頁,這個網頁正是我想要找的漫畫地址,能夠見得個人猜想是正確的,等到實際操做的時候再用表達式提取信息就很是容易了github

    提取漫畫章節地址

    進入漫畫的目錄頁,發現一頁最多能夠展現20章的漫畫目錄,要想更換顯示還須要點擊章節名上面的選項卡來顯示其餘章節的地址
    web

    接下來就須要咱們來檢查網頁元素想辦法來獲取章節地址了,一樣右擊檢查元素
    在看到了源代碼後,我發現了一個很是驚喜的事情,這個源碼裏面包含這全部的章節連接,而不是經過動態加載來展現的,這就省去了咱們提取其餘章節連接的功夫,只須要花心思提取漫畫圖片就能夠了
    chrome

    這裏每一個《p》標籤下包含了五個《a》標籤,每一個《li》標籤下包含了四個《p》標籤,而每一個漫畫的連接就存在每一個《a》標籤中,能夠輕鬆經過語法來提取到每頁的連接信息瀏覽器

    提取漫畫圖片

    怎麼將漫畫的圖片地址提取出來並保存到本地,這是這個代碼的難點和核心
    先是打開漫畫,這個漫畫頁應該是被加上了某些措施,因此它沒辦法使用右鍵查看網頁源代碼,可是使用快捷鍵[ctrl + shift +i]是能夠看到的
    less

    按下[ctrl + shift + i],檢查元素
    異步

    經過第一次檢查,能夠發現網頁的元素中只有前幾張圖片的地址信息,後面的信息都爲後綴.gif的文件表示,這些gif文件就是圖片的加載動畫
    ide

    接着向下滑動到底部,等待圖片所有顯示出來再次檢查元素

    如今全部的漫畫圖片所有顯示出來,下方並沒有.gif 的文件,由此可知,騰訊動漫是以js異步加載來顯示圖片的,要想獲取頁面的所有圖片,就必需要滑動滾動條,將所有的圖片加載完成再進行提取,這裏我選擇selenium模塊和chromedriver來幫助我完成這些操做。下面開始進行代碼的編寫。

    編寫代碼

    導入須要的模塊

    import requests
    from lxml import etree
    from selenium import webdriver     #selenium模擬操做
    from time import sleep
    from bs4 import BeautifulSoup
    from selenium.webdriver.chrome.options import Options        #谷歌無頭瀏覽器
    import os

    獲取漫畫地址

    這裏我使用的是xpath提取漫畫地址信息,在谷歌瀏覽器中使用xpath helper插件輔助編寫xpath表達式

    #打開騰訊動漫首頁
    url = 'https://ac.qq.com/'
    #給網頁發送請求
    data = requests.get(url).text
    #將網頁信息轉換成xpath可識別的類型
    html = etree.HTML(data)
    #提取到每一個漫畫的目錄頁地址
    comic_list = html.xpath('//a[@class="in-rank-name"]/@href')
    print(comic_list)

    print一下輸出的comic_list,提取成功

    提取漫畫的內容頁

    內容頁的提取也很簡單,就像上面的分析同樣,使用簡單的xpath語法便可提取

    而後咱們再將漫畫的名字提取出來,方便爲保存的文件夾命名

    #遍歷提取到的信息
    for comic in comic_list:
        #拼接成爲漫畫目錄頁的網址
        comic_url = url + str(comic)
        #從漫畫目錄頁提取信息
        url_data = requests.get(comic_url).text
        #準備用xpath語法提取信息
        data_comic = etree.HTML(url_data)
        #提取漫畫名--text()爲提取文本內容
        name_comic = data_comic.xpath("//h2[@class='works-intro-title ui-left']/strong/text()")
        #提取該漫畫每一頁的地址
        item_list = data_comic.xpath("//span[@class='works-chapter-item']/a/@href")
        print(name_comic)
        print(item_list)

    print打印的信息:

    提取章節名

    剛剛咱們輸出的是漫畫頁的地址字段,可是經過這些字段並不能請求到信息,還需在前面加上域名才能夠構成一個完整的網址
    提取章節名是爲了在漫畫名的文件夾下再爲每一個章節建立一個文件夾保存漫畫圖片

    for item in item_list:
            #拼接每一章節的地址
            item_url = url + str(item)
            #print(item_url)
            #請求每一章節的信息
            page_mes = requests.get(item_url).text
            #準備使用xpath提取內容
            page_ming = etree.HTML(page_mes)
            #提取章節名
            page_name = page_ming.xpath('//span[@class="title-comicHeading"]/text()')
            print(page_name)

    打印章節名:

    獲取漫畫源網頁代碼

    這個部分的代碼是這個代碼的核心部分,也是花費時間最久的部分
    首先咱們知道經過正常的方式沒有辦法請求到全部的圖片地址信息,如果使用抓包方法會變得很是難分析,因此我採用的是模擬瀏覽器滑動的方法來得到圖片的地址信息
    爲了方便看到結果,先將webdriver設置爲有界面模式,等到實現想要的功能以後,再將它隱藏起來

    #webdriver位置
            path = r'/home/jmhao/chromedriver'
            #瀏覽器參數設置
            browser = webdriver.Chrome(executable_path=path)
            #開始請求第一個章節的網址
            browser.get(item_url)
            #設置延時,爲後續作緩衝
            sleep(2)
            #嘗試執行下列代碼
            try:
                #設置自動下滑滾動條操做
                for i in range(1, 100):
                    #滑動距離設置
                    js = 'var q=document.getElementById("mainView").scrollTop = ' + str(i * 1000)
                    #執行滑動選項
                    browser.execute_script(js)
                    #延時,使圖片充分加載
                    sleep(2)
                sleep(2)
                #將打開的界面截圖保存,證實無界面瀏覽器確實打開了網頁
                browser.get_screenshot_as_file(str(page_name) + ".png")
                #獲取當前頁面源碼
                data = browser.page_source
                #在當前文件夾下建立html文件,並將網頁源碼寫入
                fh = open("dongman.html", "w", encoding="utf-8")
                #寫入操做
                fh.write(data)
                #關掉瀏覽器
                fh.close()
            # 若上述代碼執行報錯(大機率是因爲付費漫畫),則執行此部分代碼
            except Exception as err:
                #跳過錯誤代碼
                pass

    運行以後會自動打開漫畫的內容頁,並拖動右側的滑動條(模擬了手動操做,緩慢拖動是爲了讓圖片充分加載),其中的sleep方法和網速有必定的關係,網速好的能夠適當減小延時的時間,網速差可適當延長
    在寫拖動滑動條的代碼時,我嘗試了很是多種拖動寫法,也模擬了按下方向鍵的操做,但是隻有這一種方法使用成功了。我認爲失敗的緣由多是剛打開界面的時候會有一個導航條擋住滑塊,致使沒法定位到滑塊的座標(由於我用其餘網頁測試的時候都是能夠拖動的)

    使用的try是爲了防止有一些章節會彈出付費窗口,致使程序報錯,使後續沒法運行,即遇到會報錯的狀況就跳過此段代碼,執行except中的選項
    這段程序運行完以後有一個dongman.html文件保存在當前文件夾下,裏面就包含了全部圖片的url,接下來只要讀取這個文件的內容就能夠提取到全部的漫畫地址了

    下載漫畫圖片

    當咱們保存完網頁的源代碼以後,接下來的操做就變得簡單了 咱們要作的就是提取文件內容,將圖片下載到本地

    #用beautifulsoup打開本地文件
                html_new = BeautifulSoup(open('dongman.html', encoding='utf-8'), features='html.parser')
                #提取html文件中的主體部分
                soup = html_new.find(id="mainView")
                #設置變量i,方便爲保存的圖片命名
                i = 0
                #提取出主體部分中的img標籤(由於圖片地址保存在img標籤中)
                for items in soup.find_all("img"):
                    #提取圖片地址信息
                    item = items.get("src")
                    #請求圖片地址
                    comic_pic = requests.get(item).content
                    #print(comic_pic)
                    #嘗試提取圖片,若發生錯誤則跳過
                    try:
                        #打開文件夾,將圖片存入
                        with open('comic/' + str(name_comic) + '/' + str(page_name) + '/' + str(i + 1) + '.jpg', 'wb') as f:
                            #print('正在下載第 ', (i + 1), ' 張圖片中')
                            print('正在下載' , str(name_comic) , '-' , str(page_name) , '- 第' , (i+1) , '張圖片')
                            #寫入操做
                            f.write(comic_pic)
                            #更改圖片名,防止新下載的圖片覆蓋原圖片
                            i += 1
                    #若上述代碼執行報錯,則執行此部分代碼
                    except Exception as err:
                        #跳過錯誤代碼
                        pass

    下載結果

    到了這裏代碼就寫完了,來看一下運行結果:

    打開文件夾看到:

    完整代碼

    import requests
    from lxml import etree
    from selenium import webdriver
    from time import sleep
    from bs4 import BeautifulSoup
    from selenium.webdriver.chrome.options import Options
    import os
    
    #打開騰訊動漫首頁
    url = 'https://ac.qq.com/'
    #給網頁發送請求
    data = requests.get(url).text
    #將網頁信息轉換成xpath可識別的類型
    html = etree.HTML(data)
    #提取到每一個漫畫的目錄頁地址
    comic_list = html.xpath('//a[@class="in-rank-name"]/@href')
    #print(comic_list)
    #遍歷提取到的信息
    for comic in comic_list:
        #拼接成爲漫畫目錄頁的網址
        comic_url = url + str(comic)
        #從漫畫目錄頁提取信息
        url_data = requests.get(comic_url).text
        #準備用xpath語法提取信息
        data_comic = etree.HTML(url_data)
        #提取漫畫名--text()爲提取文本內容
        name_comic = data_comic.xpath("//h2[@class='works-intro-title ui-left']/strong/text()")
        #提取該漫畫每一頁的地址
        item_list = data_comic.xpath("//span[@class='works-chapter-item']/a/@href")
        # print(name_comic)
        # print(item_list)
        #以漫畫名字爲文件夾名建立文件夾
        os.makedirs('comic/' + str(name_comic))
        #將一本漫畫的每一章地址遍歷
        for item in item_list:
            #拼接每一章節的地址
            item_url = url + str(item)
            #print(item_url)
            #請求每一章節的信息
            page_mes = requests.get(item_url).text
            #準備使用xpath提取內容
            page_ming = etree.HTML(page_mes)
            #提取章節名
            page_name = page_ming.xpath('//span[@class="title-comicHeading"]/text()')
            #print(page_name)
            #再以章節名命名一個文件夾
            os.makedirs('comic/' + str(name_comic) + '/' + str(page_name))
    
            #如下爲代碼的主體部分
    
            #設置谷歌無界面瀏覽器
            chrome_options = Options()
            chrome_options.add_argument('--headless')
            chrome_options.add_argument('--disable-gpu')
            #webdriver位置
            path = r'/home/jmhao/chromedriver'
            #瀏覽器參數設置
            browser = webdriver.Chrome(executable_path=path, options=chrome_options)
            #開始請求第一個章節的網址
            browser.get(item_url)
            #設置延時,爲後續作緩衝
            sleep(2)
            #browser.get_screenshot_as_file(str(page_name) + ".png")
            #嘗試執行下列代碼
            try:
                #設置自動下滑滾動條操做
                for i in range(1, 100):
                    #滑動距離設置
                    js = 'var q=document.getElementById("mainView").scrollTop = ' + str(i * 1000)
                    #執行滑動選項
                    browser.execute_script(js)
                    #延時,使圖片充分加載
                    sleep(2)
                sleep(2)
                #將打開的界面截圖保存,證實無界面瀏覽器確實打開了網頁
                browser.get_screenshot_as_file(str(page_name) + ".png")
                #獲取當前頁面源碼
                data = browser.page_source
                #在當前文件夾下建立html文件,並將網頁源碼寫入
                fh = open("dongman.html", "w", encoding="utf-8")
                #寫入操做
                fh.write(data)
                #關掉無界面瀏覽器
                fh.close()
    
                #下面的操做爲打開保存的html文件,提取其中的圖片信息,並保存到文件夾中
    
                #用beautifulsoup打開本地文件
                html_new = BeautifulSoup(open('dongman.html', encoding='utf-8'), features='html.parser')
                #提取html文件中的主體部分
                soup = html_new.find(id="mainView")
                #設置變量i,方便爲保存的圖片命名
                i = 0
                #提取出主體部分中的img標籤(由於圖片地址保存在img標籤中)
                for items in soup.find_all("img"):
                    #提取圖片地址信息
                    item = items.get("src")
                    #請求圖片地址
                    comic_pic = requests.get(item).content
                    #print(comic_pic)
                    #嘗試提取圖片,若發生錯誤則跳過
                    try:
                        #打開文件夾,將圖片存入
                        with open('comic/' + str(name_comic) + '/' + str(page_name) + '/' + str(i + 1) + '.jpg', 'wb') as f:
                            #print('正在下載第 ', (i + 1), ' 張圖片中')
                            print('正在下載' , str(name_comic) , '-' , str(page_name) , '- 第' , (i+1) , '張圖片')
                            #寫入操做
                            f.write(comic_pic)
                            #更改圖片名,防止新下載的圖片覆蓋原圖片
                            i += 1
                    #若上述代碼執行報錯,則執行此部分代碼
                    except Exception as err:
                        #跳過錯誤代碼
                        pass
            # 若上述代碼執行報錯(大機率是因爲付費漫畫),則執行此部分代碼
            except Exception as err:
                #跳過錯誤代碼
                pass

    github:https://github.com/jjjjmhao/Sprider/commit/16d15a34d722f3c50060f9f3015c9ff577deb05a

  • 相關文章
    相關標籤/搜索