Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

1、前言

做者:Jack Cuijavascript

不少人學習python,不知道從何學起。
不少人學習python,掌握了基本語法事後,不知道在哪裏尋找案例上手。
不少已經作案例的人,殊不知道如何去學習更加高深的知識。
那麼針對這三類人,我給你們提供一個好的學習平臺,免費領取視頻教程,電子書籍,以及課程的源代碼!
QQ羣:1097524789css

通過上兩篇文章的學習,Python爬蟲三步走:發起請求、解析數據、保存數據,已經掌握,算入門爬蟲了嗎?html

不,還遠遠不夠!只掌握這些,還只能算門外漢級別。java

今天,就來帶你們繼續學習,怎麼爬的更優雅!python

按照慣例,仍是從實戰出發,今天我們就爬個圖片,盤點那些遇到的問題,和優雅的解決方案。瀏覽器

本文男女老幼皆宜,什麼妹子圖、肌肉男,學會了本文的方法,一切盡收囊中!服務器

私信小編001便可獲取大量Python學習資料!網絡

2、實戰背景

咱不來吸睛勁爆的圖片下載,咱來點清淡的家常菜。less

動漫之家漫畫下載!工具

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

這個實戰,你會遇到動態加載、初級反爬,會了本文的方法,你還怕爬不到心心念的"美圖"嗎?

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

3、漫畫下載

咱不下載整站資源,就挑一本下載,別給服務器太大壓力。

挑來挑去,找了本動漫之家排名靠前的一本《妖神記》,說實話,看了漫畫第一章的內容,濃濃的火影氣息。

URL:https://www.dmzj.com/info/yaoshenji.html

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

想下載這本動漫,咱們須要保存全部章節的圖片到本地。咱們先捋捋思路:

  • 拿到全部章節名和章節連接
  • 根據章節連接章節裏的全部漫畫圖片
  • 根據章節名,分類保存漫畫

看似簡單,實際作起來,可能遇到各類各樣的問題,讓咱們一塊兒優雅的解決這些問題吧!

一、獲取章節名和章節連接

一個網頁,是由不少div元素組成的

不一樣的div存放不一樣的內容,如上圖,有存放標題Jack Cui的div,有存放菜單的div,有存放正文內容的div,有存放版權信息的div。

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

瞧,不難發現,只要拿到class屬性爲zj_list的div標籤,就能拿到章節名和章節連接,都存放在這個div標籤下的a標籤中。

再仔細觀察一番,你會發現,div標籤下還有個ul標籤,ul標籤是距離a標籤最近的標籤。

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

用上一篇文章講解的BeautifulSoup,實際上直接匹配最近的class屬性爲list_con_li的ul標籤便可。編寫以下代碼:

Python

import requests
from bs4 import BeautifulSoup
 
target_url = "https://www.dmzj.com/info/yaoshenji.html"
r = requests.get(url=target_url)
bs = BeautifulSoup(r.text, 'lxml')
list_con_li = bs.find('ul', class_="list_con_li")
comic_list = list_con_li.find_all('a')
chapter_names = []
chapter_urls = []
for comic in comic_list:
    href = comic.get('href')
    name = comic.text
    chapter_names.insert(0, name)
    chapter_urls.insert(0, href)
 
print(chapter_names)
print(chapter_urls)

瞧,章節名和章節連接搞定了!

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

沒有難度啊?別急,難的在後面。

二、獲取漫畫圖片地址

咱們只要分析在一個章節裏怎麼獲取圖片,就能批量的在各個章節獲取漫畫圖片。

咱們先看第一章的內容。

URL:https://www.dmzj.com/view/yaoshenji/41917.html

打開第一章的連接,你會發現,連接後面自動添加了#@page=1。

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

你翻頁會發現,第二頁的連接是後面加了#@page=2,第三頁的連接是後面加了#@page=3,以此類推。

可是,這些並非圖片的地址,而是這個展現頁面的地址,要下載圖片,首先要拿到圖片的真實地址。

審查元素找圖片地址,你會發現,這個頁面不能右鍵!

這就是最最最最低級的反爬蟲手段,這個時候咱們能夠經過鍵盤的F12調出審查元素窗口。

有的網站甚至把F12都禁掉,這種也是很低級的反爬蟲手段,騙騙剛入門的手段而已。

面對這種禁止看頁面源碼的初級手段,一個優雅的通用解決辦法是,在鏈接前加個view-source:。

Shell

view-source:https://www.dmzj.com/view/yaoshenji/41917.html

用這個連接,直接看的就是頁面源碼。

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

更簡單的辦法是,將鼠標焦點放在瀏覽器地址欄,而後按下F12依然能夠調出調試窗口。

這個漫畫網站,仍是能夠經過F12審查元素,調出調試窗口的。

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

咱們能夠在瀏覽器調試窗口中的Network裏找到這個頁面加載的內容,例如一些css文件啊、js文件啊、圖片啊,等等等。

要找圖片的地址,直接在這裏找,別在html頁面裏找,html信息那麼多,一條一條看得找到猴年馬月。

在Network中能夠很輕鬆地找到咱們想要的圖片真實地址,調試工具很強大,Headers能夠看一些請求頭信息,Preview能夠瀏覽返回信息。

搜索功能,過濾功能等等,應有盡有,具體怎麼用,本身動手點一點,就知道了!

好了,拿到了圖片的真實地址,咱們看下連接:

https://images.dmzj.com/img/chapterpic/3059/14237/14395217739069.jpg

這就是圖片的真實地址,拿着這個連接去html頁面中搜索,看下它存放在哪一個img標籤裏了,搜索一下你會發現,瀏覽器中的html頁面是有這個圖片連接的。

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

但你是用view-source:打開這個頁面,你會發現你搜索不到這個圖片連接。

Shell

view-source:https://www.dmzj.com/view/yaoshenji/41917.html

記住,這就說明,這個圖片是動態加載的!

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

使用view-source:方法,就是看頁面源碼,並無論動態加載的內容。這裏面沒有圖片連接,就說明圖片是動態加載的。

是否是判斷起來很簡單?

遇到動態加載不要慌,使用JavaScript動態加載,無外乎兩種方式:

  • 外部加載
  • 內部加載

外部加載就是在html頁面中,以引用的形式,加載一個js,例如這樣:

XHTML

<script type="text/javascript" src="https://cuijiahua.com/call.js"></script>

這段代碼得意思是,引用cuijiahua.com域名下的call.js文件。

內部加載就是Javascript腳本內容寫在html內,例如這個漫畫網站。

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

這時候,就能夠用搜索功能了,教一個搜索小技巧。

https://images.dmzj.com/img/chapterpic/3059/14237/14395217739069.jpg

圖片連接是這個,那就用圖片的名字去掉後綴,也就是14395217739069在瀏覽器的調試頁面搜索,由於通常這種動態加載,連接都是程序合成的,搜它準沒錯!

XHTML

    <script type="text/javascript">
        var arr_img = new Array();
        var page = '';
        eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('g f=\'{"e":"h","i":"0","l":"k\\/3\\/5\\/2\\/j.4\\r\\6\\/3\\/5\\/2\\/d.4\\r\\6\\/3\\/5\\/2\\/7.4\\r\\6\\/3\\/5\\/2\\/8.4\\r\\6\\/3\\/5\\/2\\/c.4\\r\\6\\/3\\/5\\/2\\/b.4\\r\\6\\/3\\/5\\/2\\/a.4\\r\\6\\/3\\/5\\/2\\/9.4\\r\\6\\/3\\/5\\/2\\/m.4\\r\\6\\/3\\/5\\/2\\/v.4\\r\\6\\/3\\/5\\/2\\/A.4\\r\\6\\/3\\/5\\/2\\/n.4\\r\\6\\/3\\/5\\/2\\/B.4\\r\\6\\/3\\/5\\/2\\/x.4\\r\\6\\/3\\/5\\/2\\/y.4","w":"p","o":"1","q":"\\s\\\u \\t\\z"}\';',38,38,'||14237|chapterpic|jpg|3059|nimg|14395217891719|14395217893745|14395217913416|14395217908431|14395217904781|1439521790086|1439521788936|id|pages|var|41917|hidden|14395217739069|img|page_url|14395217918734|14395217931135|chapter_order|15|chapter_name||u7b2c01|u91cd|u8bdd|14395217923415|sum_pages|14395217940216|14395217943921|u751f|14395217926321|1439521793602'.split('|'),0,{}))
    </script>

不出意外,你就能看到這段代碼,14395217739069就混在其中!

看不懂Javascript,怎麼辦啊?

不要緊,說實話,我看着也費勁兒。

那我們就找找規律,分析分析,看看能不能優雅的解決這個動態加載問題,咱們再看這個圖片連接:

https://images.dmzj.com/img/chapterpic/3059/14237/14395217739069.jpg

連接中的數字是否是眼熟?

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

這不就是這幾個數字合成的嗎?

好了,我有個大膽的想法!直接把這些長的數字搞出來,合成下連接試試看。

Python

import requests
from bs4 import BeautifulSoup
import re
 
url = 'https://www.dmzj.com/view/yaoshenji/41917.html'
r = requests.get(url=url)
html = BeautifulSoup(r.text, 'lxml')
script_info = html.script
pics = re.findall('\d{13,14}', str(script_info))
chapterpic_hou = re.findall('\|(\d{5})\|', str(script_info))[0]
chapterpic_qian = re.findall('\|(\d{4})\|', str(script_info))[0]
for pic in pics:
    url = 'https://images.dmzj.com/img/chapterpic/' + chapterpic_qian + '/' + chapterpic_hou + '/' + pic + '.jpg'
    print(url)

運行代碼,你能夠獲得以下結果:

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

踏破鐵鞋無覓處,得來全不費工夫!

比對一下你會發現,這些,還真就是漫畫圖片的連接!

可是有個問題,這麼合成的的圖片連接不是按照漫畫順序的,這下載下來漫畫圖片都是亂的啊!不優雅!

這個網站也是人寫的嘛!是人,就好辦!慣性思惟,要是你,是否是小數放在前面,大數放在後面?這些長的數字裏,有13位的,有14位的,而且都是以14開頭的數字,那我就賭它末位補零後的結果,就是圖片的順序!

Python

import requests
from bs4 import BeautifulSoup
import re
 
url = 'https://www.dmzj.com/view/yaoshenji/41917.html'
r = requests.get(url=url)
html = BeautifulSoup(r.text, 'lxml')
script_info = html.script
pics = re.findall('\d{13,14}', str(script_info))
for idx, pic in enumerate(pics):
    if len(pic) == 13:
        pics[idx] = pic + '0'
pics = sorted(pics, key=lambda x:int(x))
chapterpic_hou = re.findall('\|(\d{5})\|', str(script_info))[0]
chapterpic_qian = re.findall('\|(\d{4})\|', str(script_info))[0]
for pic in pics:
    if pic[-1] == '0':
        url = 'https://images.dmzj.com/img/chapterpic/' + chapterpic_qian + '/' + chapterpic_hou + '/' + pic[:-1] + '.jpg'
    else:
        url = 'https://images.dmzj.com/img/chapterpic/' + chapterpic_qian + '/' + chapterpic_hou + '/' + pic + '.jpg'
    print(url)

程序對13位的數字,末位補零,而後排序。

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

在跟網頁的連接按順序比對,你會發現沒錯!就是這個順序!

不用讀懂Javascript合成連接代碼,直接分析測試,夠不夠優雅?

三、下載圖片

萬事俱備,只欠東風!

使用其中一個圖片連接,用代碼下載試試。

Python

import requests
from urllib.request import urlretrieve
dn_url = 'https://images.dmzj.com/img/chapterpic/3059/14237/14395217739069.jpg'
urlretrieve(dn_url,'1.jpg')

經過urlretrieve方法,就能夠下載,這是最簡單的下載方法。第一個參數是下載連接,第二個參數是下載後的文件保存名。

不出意外,就能夠順利下載這張圖片!

可是,意外發生了!

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

出現了HTTP Error,錯誤代碼是403。

403表示資源不可用,這是又是一種典型的反扒蟲手段。

別慌,咱們再分析一波!

打開這個圖片連接:

URL:https://images.dmzj.com/img/chapterpic/3059/14237/14395217739069.jpg

這個地址就是圖片的真實地址,在瀏覽器中打開,可能直接沒法打開,或者能打開,可是一刷新就又不能打開了!

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

若是再打開章節頁面,再打開這個圖片連接就又能看到圖片了。

章節URL:
https://www.dmzj.com/view/yaoshenji/41917.html

記住,這就是一種典型的經過Referer的反扒爬蟲手段!

Referer能夠理解爲來路,先打開章節URL連接,再打開圖片連接。打開圖片的時候,Referer的信息裏保存的是章節URL。

動漫之家網站的作法就是,站內的用戶訪問這個圖片,我就給他看,從其它地方過來的用戶,我就不給他看。

是否是站內用戶,就是根據Referer進行簡單的判斷。

這就是很典型的,反爬蟲手段!

解決辦法也簡單,它須要啥,咱給它就完了。Python

import requests
from contextlib import closing
 
download_header = {
    'Referer': 'https://www.dmzj.com/view/yaoshenji/41917.html'
}
 
dn_url = 'https://images.dmzj.com/img/chapterpic/3059/14237/14395217739069.jpg'
with closing(requests.get(dn_url, headers=download_header, stream=True)) as response:
    chunk_size = 1024  
    content_size = int(response.headers['content-length'])  
    if response.status_code == 200:
        print('文件大小:%0.2f KB' % (content_size / chunk_size))
        with open('1.jpg', "wb") as file:  
            for data in response.iter_content(chunk_size=chunk_size):  
                file.write(data)  
    else:
        print('連接異常')
print('下載完成!')

使用closing方法能夠設置Headers信息,這個Headers信息裏保存Referer來路,就是第一章的URL,最後以寫文件的形式,保存這個圖片。

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

下載完成!就是這麼簡單!

4、漫畫下載

將代碼整合在一塊兒,下載整部漫畫。編寫代碼以下:Python

import requests
import os
import re
from bs4 import BeautifulSoup
from contextlib import closing
from tqdm import tqdm
import time
 
"""
    Author:
        Jack Cui
    Wechat:
        https://mp.weixin.qq.com/s/OCWwRVDFNslIuKyiCVUoTA
"""
 
# 建立保存目錄
save_dir = '妖神記'
if save_dir not in os.listdir('./'):
    os.mkdir(save_dir)
    
target_url = "https://www.dmzj.com/info/yaoshenji.html"
 
# 獲取動漫章節連接和章節名
r = requests.get(url = target_url)
bs = BeautifulSoup(r.text, 'lxml')
list_con_li = bs.find('ul', class_="list_con_li")
cartoon_list = list_con_li.find_all('a')
chapter_names = []
chapter_urls = []
for cartoon in cartoon_list:
    href = cartoon.get('href')
    name = cartoon.text
    chapter_names.insert(0, name)
    chapter_urls.insert(0, href)
 
# 下載漫畫
for i, url in enumerate(tqdm(chapter_urls)):
    download_header = {
        'Referer': url
    }
    name = chapter_names[i]
    # 去掉.
    while '.' in name:
        name = name.replace('.', '')
    chapter_save_dir = os.path.join(save_dir, name)
    if name not in os.listdir(save_dir):
        os.mkdir(chapter_save_dir)
        r = requests.get(url = url)
        html = BeautifulSoup(r.text, 'lxml')
        script_info = html.script
        pics = re.findall('\d{13,14}', str(script_info))
        for j, pic in enumerate(pics):
            if len(pic) == 13:
                pics[j] = pic + '0'
        pics = sorted(pics, key=lambda x:int(x))
        chapterpic_hou = re.findall('\|(\d{5})\|', str(script_info))[0]
        chapterpic_qian = re.findall('\|(\d{4})\|', str(script_info))[0]
        for idx, pic in enumerate(pics):
            if pic[-1] == '0':
                url = 'https://images.dmzj.com/img/chapterpic/' + chapterpic_qian + '/' + chapterpic_hou + '/' + pic[:-1] + '.jpg'
            else:
                url = 'https://images.dmzj.com/img/chapterpic/' + chapterpic_qian + '/' + chapterpic_hou + '/' + pic + '.jpg'
            pic_name = '%03d.jpg' % (idx + 1)
            pic_save_path = os.path.join(chapter_save_dir, pic_name)
            with closing(requests.get(url, headers = download_header, stream = True)) as response:  
                chunk_size = 1024  
                content_size = int(response.headers['content-length'])  
                if response.status_code == 200:
                    with open(pic_save_path, "wb") as file:  
                        for data in response.iter_content(chunk_size=chunk_size):  
                            file.write(data)  
                else:
                    print('連接異常')
        time.sleep(10)

大約40分鐘,漫畫便可下載完成!

Python3 網絡爬蟲:漫畫下載,動態加載、反爬蟲這都不叫事

 

仍是那句話,咱們要作一個友好的爬蟲。寫爬蟲,要謹慎,勿給服務器增長過多的壓力,知足咱們的獲取數據的需求,這就夠了。

你好,我也好,你們好纔是真的好。

5、總結

  • 本文講解了如何判斷頁面信息是否是動態加載的,如何解決動態加載問題。
  • 本文講解了一些常見的反爬蟲策略以及解決辦法。
相關文章
相關標籤/搜索