爬蟲實戰:一鍵爬光指定網站所有圖片(一)

前言:

​ 最近自己在做圖片處理工具,最開始的初衷只是爲了做一個圖片深度學習項目,做的時候缺少大量的圖片素材,手動去下載自己又是比較懶,並且操作起來非常的麻煩,於是自己寫了一個單頁面全圖片的爬蟲,等自己實現完功能之後,發現又有很多功能是可以優化的,於是在這個基礎上我又做了一下功能升級,最終出了一個爬取指定網站所有圖片的版本,當然,這個版本還有很多可以優化的點,我會在下面的實際過程中進行說明。本篇着重說明指定頁面的圖片抓取。

項目目標:

​ 指定某一頁面進行圖片資源進行爬取,保存到本地硬盤。

項目分析:

​ 1、本項目我們要實現某一個指定網站的頁面URL,也就是提取href的鏈接。並將所有的內鏈創建到下一個任務當中去。

​ 2、除了頁面中的href鏈接,我們還要讀取頁面中所有圖片元素,通過get方式進行訪問,讀取後保存。

簡單分析了一下,我們開始代碼的實現

首先完成第2項的功能,我們要將頁面圖片元素提取出來,並寫入到一個指定的文件目錄當中,根據url中的文件名進行保存處理,考慮到我們未來功能複用性,所以我單獨爲單頁面文件下載實現了一個類,(當然最終實現之後,發現Python中存在一些問題,這裏我們在尾部再做解釋)

我們先定義一個類,這裏命名叫“DownloadImage.py”,因爲是通過curl方式進行抓取採集圖片列表,我們要定義一個header頭屬性,以及一個保存圖片的本地的地址,定義代碼如下:

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'
}
_downloadDir = './img/'

因爲這個類我們要複用,所以單獨放到一個py文件中,方便後面進行引用。

在類的構造方法中,我們需要進行一個參數的初始化。爲了下載指定頁面的圖片,那麼我們需要指定一個頁面(必要參數),爲了路徑可定義化,我們考慮增加了一個可選的本地存放的路徑參數。 另外,因爲每個頁面其實有些圖片我們是不需要的,比如一些頁面的logo.gif,style.css中的樣式圖片我們並不需要,那我們這裏就定義了一個圖片過濾參數。

定義完之後我們需要對對象中的參數進行賦值,並初始化相關的參數。

def __init__(self,url,download_path=None,filter=[]):
    self.url = url
    self.initUrl()
    self.filter =filter

    # 定義圖片下載圖徑
    if download_path:
        self.downloadPath=self._downloadDir + download_path
    else:
        self.downloadPath=self._downloadDir + self.urlParse.netloc
    self.makeDir()
    self.getImages()

首頁我們在傳入Url之後,將這個url賦給個類,方便對象中直接調用,然後我們要將url進行一個格式化,解析一次。

這個方法名就是initUrl(),方法的主用要途對過 urlparse方法,將url的域名和參數進行分離。整理成我們需要的格式。

原因是因爲在http的頁面當中,我們定義圖片會有幾種格式:

1.絕對路徑,大部分網站的圖片url都是這樣,單獨配置了域名資源進行顯示

2.相對路徑,有很多網站只有一臺服務器,會把靜態資源和html文件放在一起

3.某些站點的域名證書綁定是兼容性的,所以也會有//前綴進行http和https的兼容處理。

處理完url之後,我們將圖片的過濾增加進去, 方法我不再細說,處理方式是通過正則進入搜索匹配來過濾的,比如傳入[‘png’,‘gif’],那麼所有的png和gif都不再被下載。

然後我們再說一下makeDir,初始化時會判斷文件下載目錄是否存在,如果不存在,則新建。

def makeDir(self):
    if not os.path.exists(self.downloadPath):
        os.makedirs(self.downloadPath)

最後,我們通過curl獲取傳參的url頁面中所有的圖片地址!

def getImages(self):
    response = requests.get(self.url, headers=self.headers)
    if response.status_code == 200:
        html = et.HTML(response.text)
        images = html.xpath('//img/@src')
        if self.filter:
            match = '|'.join(self.filter)
            self.Imageurls = []
            for value in images:
                if not re.search(match,value):
                    self.Imageurls.append(value)

        else:
            self.Imageurls=images
    else:
        return None

最終類代碼如下:

# 抓取指定網頁所有圖片保存到本地
import requests
import os
from urllib.parse import *
from lxml import etree as et
import re
import sys
# 請求頭
class DownloadImage(object):
    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'
    }
    _downloadDir = './img/'

    def __init__(self,url,download_path=None,filter=[]):
        self.url = url
        self.initUrl()
        self.filter =filter

        # 定義圖片下載圖徑
        if download_path:
            self.downloadPath=self._downloadDir + download_path
        else:
            self.downloadPath=self._downloadDir + self.urlParse.netloc
        self.makeDir()
        self.getImages()

    #通用圖片路徑方法格式化
    def initUrl(self):
        self.urlParse=urlparse(self.url)

    def getImages(self):
        response = requests.get(self.url, headers=self.headers)
        if response.status_code == 200:
            html = et.HTML(response.text)
            images = html.xpath('//img/@src')
            if self.filter:
                match = '|'.join(self.filter)
                self.Imageurls = []
                for value in images:
                    if not re.search(match,value):
                        self.Imageurls.append(value)

            else:
                self.Imageurls=images
        else:
            return None

    #格式化圖片URL
    def formatImageUrls(self,url):
        imgParase = urlparse(url)
        if not imgParase.netloc:
            imgpath = "%s://%s/%s" %(self.urlParse.scheme,self.urlParse.netloc,imgParase.path)
        else:
            imgpath = urljoin(self.url,url)
        return imgpath
    # 保存圖片
    def downloadImage(self,url):
        print("download :" + url)
        arr = url.split('/')
        file_name = self.downloadPath +'/' + arr[-1]
        # file_name = self.downloadPath +'/' + arr[-2] +'/' + arr[-1]
        try:
            response = requests.get(url, headers=self.headers)
            with open(file_name, 'wb') as fp:
                for data in response.iter_content(128):
                    fp.write(data)
            self.start = self.start+1
            return file_name
        except:
            print("download error")

    def makeDir(self):
        if not os.path.exists(self.downloadPath):
            os.makedirs(self.downloadPath)

    def run(self):
        for img in self.Imageurls:
            self.downloadImage(self.formatImageUrls(img))

相關的頭文件引用,大家可以參考python手冊,這裏不再細說。

新建一個單頁的download_image_page.py文件。

import argparse
from DownloadImage import DownloadImage
def getArgv():
    parser = argparse.ArgumentParser()
    parser.add_argument('-i', '--uri', dest='Url', type=str, default='root', help='target Url')
    args= parser.parse_args()
    return args.Url

if __name__ == '__main__':
    url = getArgv()
    obj=DownloadImage(url,None)
    obj.run()

在控制檯中運行:python3 download_image_page.py -i https://www.baidu.com

可以看到執行結果。

這裏大家注意了,因爲我最開始要做的是單頁面採集,最開始設計的時候並未考慮圖片的採集控制,這裏算是一個優化點。

第一階段結束,因爲篇幅原因,整站部分的說明我將在下一篇中進行講解說明。當然,代碼已經上傳,感興趣的朋友可以先行clone。

代碼地址:https://gitee.com/python_play/download_image

本文是“明哥陪你學Python”系列章節之一,如果你對Python有更多興趣,或有問題,可以私信與明哥聯繫,我會陪你一起解決,其它相關章節可以從首頁中的“明哥陪你學Python”列表進行查看。

本系列教程及源碼地址:點擊訪問

最後:如果你正在學習Python的路上,或者準備打算學習Python、明哥會陪着你陪你一起共同進步!

手打不易,有用的話,請記得關注轉發。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章