小白學 Python 爬蟲(16):urllib 實戰之爬取妹子圖

人生苦短,我用 Pythonhtml

前文傳送門:python

小白學 Python 爬蟲(1):開篇git

小白學 Python 爬蟲(2):前置準備(一)基本類庫的安裝github

小白學 Python 爬蟲(3):前置準備(二)Linux基礎入門數據庫

小白學 Python 爬蟲(4):前置準備(三)Docker基礎入門瀏覽器

小白學 Python 爬蟲(5):前置準備(四)數據庫基礎框架

小白學 Python 爬蟲(6):前置準備(五)爬蟲框架的安裝ide

小白學 Python 爬蟲(7):HTTP 基礎工具

小白學 Python 爬蟲(8):網頁基礎學習

小白學 Python 爬蟲(9):爬蟲基礎

小白學 Python 爬蟲(10):Session 和 Cookies

小白學 Python 爬蟲(11):urllib 基礎使用(一)

小白學 Python 爬蟲(12):urllib 基礎使用(二)

小白學 Python 爬蟲(13):urllib 基礎使用(三)

小白學 Python 爬蟲(14):urllib 基礎使用(四)

小白學 Python 爬蟲(15):urllib 基礎使用(五)

引言

最近小編工做比較忙,天天能拿來寫內容的時間都比較短。

剛更新完成的 urllib 基礎內容太乾了,跟牛肉乾同樣,咬着牙疼。

今天稍微空一點,決定好好放浪一下形骸,寫點不正經的內容。

目標準備

看了標題各位同窗應該知道小編今天要幹啥了,沒毛病,小編要掏出本身多年的珍藏分享給你們。

首先,咱們要肯定本身的目標,咱們本次實戰的目標網站是:https://www.mzitu.com/ 。

隨便找張圖給你們感覺下:

小編要是上班看這個,估計是要被老闆吊起來打的。

若是是進來找網址的,能夠直接出去了,下面的內容已經可有可無。

抓取分析

先使用 Chrome 瀏覽器打開網站 https://www.mzitu.com/ ,按 F12 打開開發者工具,查看網頁的源代碼:

能夠看到,這裏是一個 a 標籤包裹了一個 img 標籤,這裏圖片的顯示是來源於 img 標籤,而點擊後的跳轉則是來源於 a 標籤。

將頁面滾動到最下方,看到分頁組件,點擊下一頁,咱們觀察頁面 URL 的變化。

能夠發現,頁面 URL 變化成爲 https://www.mzitu.com/xinggan/page/2/ ,多翻幾頁,發現只是後面的數字在增長。

當前頁面上的圖片只是縮略圖,明顯不是咱們想要的圖片,隨便點擊一個圖片,進入內層頁面:https://www.mzitu.com/214337/ ,能夠發現,這裏圖片纔是咱們須要的圖片:

和前面的套路同樣,咱們往下翻幾頁,能夠發現 URL 的變化規律。

URL 由第一頁的 https://www.mzitu.com/214337/ 變化成爲 https://www.mzitu.com/214337/2 ,一樣是最後一位的數字增長,嘗試當前頁面最大值 66 ,發現依然能夠正常打開,若是變爲 67 ,則能夠看到當前頁面的標題已經顯示爲 404 (頁面沒法找到)。

爬取首頁

上面咱們找到了頁面變化的規律,那麼咱們確定是要從首頁開始爬取數據。

接下來咱們開始用代碼實現這個過程,使用的請求類庫爲 Python 自帶的 urllib ,頁面信息提取採用 xpath 方式,類庫使用 lxml 。

關於 xpath 和 lxml 是什麼東西,小編後續會慢慢講,感興趣的同窗能夠先行百度學習。

下面咱們開始寫代碼。

首先是構建請求頭:

# 請求頭添加 UA
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
    'referer': 'https://www.mzitu.com/'
}
# 保存路徑
save_path = 'D:\\spider_file'複製代碼

若是不加請求頭 UA,會被拒絕訪問,直接返回 403 狀態碼,在獲取圖片的時候,若是不加 referer ,則會被認爲是盜鏈,一樣也沒法獲取圖片。

保存路徑小編這裏保存在本地的 D 盤的 spider_file 這個目錄中。

接下來增長一個建立文件夾的方法:

import os

# 建立文件夾
def createFile(file_path):
    if os.path.exists(file_path) is False:
        os.makedirs(file_path)
    # 切換路徑至上面建立的文件夾
    os.chdir(file_path)複製代碼

這裏須要引入 os 模塊。

而後是重頭戲,抓取首頁數據並進行解析:

import urllib.request
from lxml import etree

# 抓取外頁數據
def get_outer(outer_url):
    req = urllib.request.Request(url=outer_url, headers=headers, method='GET')
    resp = urllib.request.urlopen(req)

    html = etree.HTML(resp.read().decode('utf-8'))
    # 獲取文件夾名稱列表
    title_list = html.xpath('//*[@id="pins"]/li/a/img/@alt')
    # 獲取跳轉連接列表
    src_list = html.xpath('//*[@id="pins"]/li/a/@href')

    print('當前頁面' + outer_url + ', 共計爬取' + str(len(title_list)) + '個文件夾')

    for i in range(len(title_list)):
        file_path = save_path + '\\' + title_list[i]
        img_url = src_list[i]
        # 建立對應文件夾
        createFile(file_path)
        # 寫入對應文件
        get_inner(img_url, file_path)複製代碼

具體每行代碼是作什麼,小編就不一一詳細介紹了,註釋已經寫得比較清楚了。

爬取內頁

咱們爬取完外頁的信息後,須要經過外頁提供的信息來爬取內頁的數據,這也是咱們真正想要爬取的數據,具體的爬取思路已經在上面講過了,這裏直接貼代碼:

import urllib.request
import os
from lxml import etree
import time

# 抓取內頁數據並寫入文件
def get_inner(url, file_path):
    req = urllib.request.Request(url=url, headers=headers, method='GET')
    resp = urllib.request.urlopen(req)

    html = etree.HTML(resp.read().decode('utf-8'))

    # 獲取當前頁面最大頁數
    max_num = html.xpath('/html/body/div[2]/div[1]/div[4]/a[5]/span/text()')[0]
    print('當前頁面url:', url, ', 最大頁數爲', max_num)

    for i in range(1, int(max_num)):
        # 訪問過快會被限制,增長睡眠時間
        time.sleep(1)

        inner_url = url + '/' + str(i)
        inner_req = urllib.request.Request(url=inner_url, headers=headers, method='GET')
        inner_resp = urllib.request.urlopen(inner_req)

        inner_html = etree.HTML(inner_resp.read().decode('utf-8'))

        # 獲取圖片 url
        img_src = inner_html.xpath('/html/body/div[2]/div[1]/div[3]/p/a/img/@src')[0]
        file_name = str(img_src).split('/')[-1]
        # 下載圖片
        try:
            request = urllib.request.Request(url=img_src, headers=headers, method='GET')
            response = urllib.request.urlopen(request)
            get_img = response.read()
            file_os_path = file_path + '\\' + file_name
            if os.path.isfile(file_os_path):
                print('圖片已存在:', file_os_path)
                pass
            else:
                with open(file_os_path, 'wb') as fp:
                    fp.write(get_img)
                    print('圖片保存成功:', file_os_path)
                    fp.close()
        except Exception as e:
            print('圖片保存失敗')複製代碼

由於該網站對訪問速度有限制,因此小編在這裏增長了程序的睡眠時間,這只是一種解決方案,還可使用代理的方式突破限制,穩定高速代理都挺貴的,小編仍是慢一點慢慢爬比較省錢。

整合代碼

總體的代碼到這一步,主體框架邏輯已經徹底肯定了,只須要對代碼作一個大體簡單的整理便可,因此,完整的代碼以下:

import urllib.request
import os
from lxml import etree
import time

# 請求頭添加 UA
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',
    'referer': 'https://www.mzitu.com/'
}
# 保存路徑
save_path = 'D:\\spider_file'

# 建立文件夾
def createFile(file_path):
    if os.path.exists(file_path) is False:
        os.makedirs(file_path)
    # 切換路徑至上面建立的文件夾
    os.chdir(file_path)

# 抓取外頁數據
def get_outer(outer_url):
    req = urllib.request.Request(url=outer_url, headers=headers, method='GET')
    resp = urllib.request.urlopen(req)

    html = etree.HTML(resp.read().decode('utf-8'))
    # 獲取文件夾名稱列表
    title_list = html.xpath('//*[@id="pins"]/li/a/img/@alt')
    # 獲取跳轉連接列表
    src_list = html.xpath('//*[@id="pins"]/li/a/@href')

    print('當前頁面' + outer_url + ', 共計爬取' + str(len(title_list)) + '個文件夾')

    for i in range(len(title_list)):
        file_path = save_path + '\\' + title_list[i]
        img_url = src_list[i]
        # 建立對應文件夾
        createFile(file_path)
        # 寫入對應文件
        get_inner(img_url, file_path)


# 抓取內頁數據並寫入文件
def get_inner(url, file_path):
    req = urllib.request.Request(url=url, headers=headers, method='GET')
    resp = urllib.request.urlopen(req)

    html = etree.HTML(resp.read().decode('utf-8'))

    # 獲取當前頁面最大頁數
    max_num = html.xpath('/html/body/div[2]/div[1]/div[4]/a[5]/span/text()')[0]
    print('當前頁面url:', url, ', 最大頁數爲', max_num)

    for i in range(1, int(max_num)):
        # 訪問過快會被限制,增長睡眠時間
        time.sleep(1)

        inner_url = url + '/' + str(i)
        inner_req = urllib.request.Request(url=inner_url, headers=headers, method='GET')
        inner_resp = urllib.request.urlopen(inner_req)

        inner_html = etree.HTML(inner_resp.read().decode('utf-8'))

        # 獲取圖片 url
        img_src = inner_html.xpath('/html/body/div[2]/div[1]/div[3]/p/a/img/@src')[0]
        file_name = str(img_src).split('/')[-1]
        # 下載圖片
        try:
            request = urllib.request.Request(url=img_src, headers=headers, method='GET')
            response = urllib.request.urlopen(request)
            get_img = response.read()
            file_os_path = file_path + '\\' + file_name
            if os.path.isfile(file_os_path):
                print('圖片已存在:', file_os_path)
                pass
            else:
                with open(file_os_path, 'wb') as fp:
                    fp.write(get_img)
                    print('圖片保存成功:', file_os_path)
                    fp.close()
        except Exception as e:
            print('圖片保存失敗')

def main():
    url = 'https://www.mzitu.com/xinggan/page/'
    for i in range(1, 163):
        get_outer(url + str(i))

if __name__ == '__main__':
    main()複製代碼

最後看一下小編大體用 20 分鐘左右爬取的數據:

小結

作爬蟲比較重要的部分是須要在網頁上分析咱們所須要提取的數據的數據來源,須要咱們清晰的瞭解清楚頁面的 URL 的具體變化,而這些變化必定是遵循某種規則的,找到這種規則就能夠達成自動化的目的。

一些網站會對爬蟲作一些防護,這些防護並非徹底無解的,其實就像矛和盾的故事。本次爬取數據的過程當中遇到的防護方式有限制 ip 必定時間的訪問次數,防止非本網站的請求請求圖片(防盜鏈,referer 檢測),以及 UA 頭檢測(防止非瀏覽器訪問)。可是正所謂上有 x 策下有 x 策,防護並非無解的,只是解決代價的大小而已。

小編這隻爬蟲仍是但願能起到拋磚引玉的做用,但願你們能本身親自動手試試看。

多動手才能學好代碼哦~~~

示例代碼

本系列的全部代碼小編都會放在代碼管理倉庫 Github 和 Gitee 上,方便你們取用。

示例代碼-Github

示例代碼-Gitee

若是個人文章對您有幫助,請掃碼關注下做者的公衆號:獲取最新干貨推送:)
相關文章
相關標籤/搜索