如何防止Python大規(guī)模圖像抓取過程中出現(xiàn)內(nèi)存不足錯誤

億牛云代理

摘要

圖像抓取是一種常見的網(wǎng)絡(luò)爬蟲技術(shù),用于從網(wǎng)頁上下載圖片并保存到本地文件夾中。然而,當(dāng)需要抓取的圖片數(shù)量很大時,可能會出現(xiàn)內(nèi)存不足的錯誤,導(dǎo)致程序崩潰。本文介紹了如何使用Python進(jìn)行大規(guī)模的圖像抓取,并提供了一些優(yōu)化內(nèi)存使用的方法和技巧,以及如何計算和評估圖片的質(zhì)量指標(biāo)。

正文

1. 導(dǎo)入必要的庫和模塊

為了實現(xiàn)圖像抓取的功能,我們需要導(dǎo)入一些必要的庫和模塊,如pickle、logging、datetime等。其中,pickle模塊用于序列化和反序列化對象,方便我們將處理結(jié)果保存到文件中;logging模塊用于記錄程序的運(yùn)行日志,方便我們調(diào)試和監(jiān)控程序的狀態(tài);datetime模塊用于獲取和處理日期和時間相關(guān)的信息,方便我們設(shè)置請求頭部和日志格式等。

import pickle

import logging

from datetime import datetime

from dateutil.parser import parse as parse_date

from brisque import BRISQUE

import os

import cv2

import numpy as np

from PIL import Image

from io import BytesIO

import os

import requests

from skimage import color

from time import sleep

from random import choice

import concurrent.futures

from requests.exceptions import Timeout

from robots import RobotParser

from headers import HEADERS

MAX_RETRIES = 3? ? ? ? # Number of times the crawler should retry a URL

INITIAL_BACKOFF = 2? ? # Initial backoff delay in seconds

DEFAULT_SLEEP = 10? ? ? # Default sleep time in seconds after a 429 error

brisque = BRISQUE(url=False)

2. 設(shè)置日志記錄器

為了方便記錄程序的運(yùn)行日志,我們需要設(shè)置一個日志記錄器,用于將日志信息輸出到文件中。我們可以使用logging模塊提供的方法來創(chuàng)建一個名為“image-scraper”的日志記錄器,并設(shè)置其日志級別為INFO。然后,我們可以創(chuàng)建一個文件處理器,用于將日志信息寫入到指定的文件中,并設(shè)置其日志格式為包含日志級別、線程名、時間和消息內(nèi)容等信息。最后,我們可以將文件處理器添加到日志記錄器中,使其生效。

# --- SETUP LOGGER ---

filename = 'image-scraper.log'

filepath = os.path.dirname(os.path.abspath(__file__))

# create file path for log file

log_file = os.path.join(filepath, filename)

# create a FileHandler to log messages to the log file

handler = logging.FileHandler(log_file)

# set the log message formats

handler.setFormatter(

? ? logging.Formatter(

? ? ? ? '%(levelname)s %(threadName)s (%(asctime)s): %(message)s')

)

# create a logger with the given name and log level

logger = logging.getLogger('image-scraper')

# prevent logging from being send to the upper logger - that includes the console logging

logger.propagate = False

logger.setLevel(logging.INFO)

# add the FileHandler to the logger

logger.addHandler(handler)

3. 定義計算圖片質(zhì)量指標(biāo)的函數(shù)

為了評估圖片的質(zhì)量,我們需要計算一些圖片質(zhì)量指標(biāo),如亮度、清晰度、對比度、色彩度等。我們可以定義一個函數(shù)get_image_quality_metrics,接受一個包含圖片數(shù)據(jù)的響應(yīng)對象作為參數(shù),并返回一個包含各種質(zhì)量指標(biāo)的字典。在這個函數(shù)中,我們首先使用PIL庫和numpy庫將圖片數(shù)據(jù)轉(zhuǎn)換為數(shù)組形式,并使用cv2庫和skimage庫對圖片進(jìn)行處理和計算。具體來說:

計算亮度:我們將圖片轉(zhuǎn)換為灰度圖,并計算其像素值的平均值。

計算清晰度:我們使用拉普拉斯算子對灰度圖進(jìn)行邊緣檢測,并計算其方差值。

計算對比度:我們使用均方根對比度的公式,計算灰度圖像素值與其平均值的差的平方的平均值的平方根。

計算噪聲:我們使用高斯濾波或中值絕對偏差(MAD)的方法,計算圖片的方差值。

計算飽和度:我們將圖片轉(zhuǎn)換為HSV顏色空間,并計算其飽和度通道的平均值。

計算色彩度:我們將圖片轉(zhuǎn)換為LAB顏色空間,并計算其a和b通道的平方和的平方根的平均值。

獲取圖片的尺寸:我們獲取圖片的高度和寬度,并將其添加到字典中。

def get_image_quality_metrics(response):

? ? """

? ? Calculate various image quality metrics for an image.

? ? Args:

? ? ? ? response (requests.Response): The response object containing the image data.

? ? Returns:

? ? ? ? dict: A dict of image quality metrics including brightness, sharpness, contrast, and colorfulness.

? ? """

? ? image_array = np.frombuffer(response.content, np.uint8)

? ? image = cv2.imdecode(image_array, cv2.IMREAD_COLOR)

? ? metrics = dict()

? ? # Calculate brightness

? ? gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

? ? metrics['brightness'] = np.mean(gray)

? ? # Calculate sharpness using variance of Laplacian

? ? metrics['sharpness'] = cv2.Laplacian(gray, cv2.CV_64F).var()

? ? # Calculate contrast using root mean squared contrast

? ? metrics['contrast'] = np.sqrt(np.mean((gray - np.mean(gray)) ** 2))

? ? # Calculate image noise using variance of Gaussian or median absolute deviation (MAD)

? ? metrics['noise'] = np.var(image)

? ? # Calculate saturation using average saturation of pixels or histogram analysis

? ? hsv = color.rgb2hsv(image)

? ? saturation = hsv[:, :, 1]

? ? metrics['saturation'] = np.mean(saturation)

? ? # Calculate colorfulness

? ? lab = color.rgb2lab(image)

? ? a, b = lab[:, :, 1], lab[:, :, 2]

? ? metrics['colorfulness'] = np.sqrt(np.mean(a ** 2 + b ** 2))

? ? # Get dimenstions of the image

? ? height, width, _ = image.shape

? ? metrics['height'] = height

? ? metrics['width'] = width

? ? return metrics

4. 定義發(fā)送請求的函數(shù)

為了從網(wǎng)頁上下載圖片,我們需要發(fā)送GET請求到圖片的URL,并獲取響應(yīng)對象。我們可以定義一個函數(shù)send_request,接受一個URL作為參數(shù),并返回一個響應(yīng)對象。在這個函數(shù)中,我們需要處理一些可能出現(xiàn)的異常和錯誤,如超時、狀態(tài)碼不為200、429等。為了避免被網(wǎng)站屏蔽或限制,我們需要使用代理服務(wù)器和隨機(jī)選擇的請求頭部。具體來說:

我們使用requests庫提供的方法來創(chuàng)建一個代理服務(wù)器對象,使用億牛云提供的代理服務(wù)器信息。

我們使用一個while循環(huán)來重試請求,設(shè)置一個最大重試次數(shù)和一個初始退避延遲時間。

我們從headers模塊中隨機(jī)選擇一個請求頭部,并將其添加到請求中。

我們使用try-except語句來捕獲可能出現(xiàn)的異常和錯誤,并根據(jù)不同的情況進(jìn)行處理:?

如果出現(xiàn)超時錯誤,我們記錄日志信息,并增加重試次數(shù)和退避延遲時間。

如果出現(xiàn)狀態(tài)碼不為200的錯誤,我們記錄日志信息,并根據(jù)狀態(tài)碼進(jìn)行處理:?

如果狀態(tài)碼為429,表示請求過于頻繁,我們需要等待一段時間后再重試,我們可以使用time模塊提供的sleep方法來暫停程序運(yùn)行,并設(shè)置一個默認(rèn)的睡眠時間。

如果狀態(tài)碼為403或404,表示請求被拒絕或資源不存在,我們可以直接跳出

如果狀態(tài)碼為其他值,表示請求出現(xiàn)其他錯誤,我們可以直接拋出異常,并記錄日志信息。?

如果沒有出現(xiàn)異常或錯誤,我們返回響應(yīng)對象,并記錄日志信息。

def send_request(url: str) -> requests.Response:

? ? """

? ? Sends a GET request to the specified URL, checks whether the link is valid,

? ? and returns a response object.

? ? Args:

? ? ? ? url (str): The URL to send the GET request to

? ? """

? ? retry_count = 0

? ? backoff = INITIAL_BACKOFF

? ? header = choice(HEADERS)

? ? # 億牛云 爬蟲代理加強(qiáng)版

? ? proxyHost = "www.16yun.cn"

? ? proxyPort = "31111"

? ? # 代理驗證信息

? ? proxyUser = "16YUN"

? ? proxyPass = "16IP"

? ? # create a proxy server object using the proxy information? ?

? ? proxyMeta = "http://%(user)s:%(pass)s@%(host)s:%(port)s" % {

? ? ? ? "host": proxyHost,

? ? ? ? "port": proxyPort,

? ? ? ? "user": proxyUser,

? ? ? ? "pass": proxyPass,

? ? }

? ? proxies = {

? ? ? ? "http": proxyMeta,

? ? ? ? "https": proxyMeta,

? ? }

? ? while retry_count < MAX_RETRIES:

? ? ? ? try:

? ? ? ? ? ? # Send a GET request to the website and return the response object

? ? ? ? ? ? req = requests.get(url, headers=header, proxies=proxies, timeout=20)

? ? ? ? ? ? req.raise_for_status()

? ? ? ? ? ? logger.info(f"Successfully fetched {url}")

? ? ? ? ? ? return req

? ? ? ? except Timeout:

? ? ? ? ? ? # Handle timeout error: log the error and increase the retry count and backoff delay

? ? ? ? ? ? logger.error(f"Timeout error for {url}")

? ? ? ? ? ? retry_count += 1

? ? ? ? ? ? backoff *= 2

? ? ? ? except requests.exceptions.HTTPError as e:

? ? ? ? ? ? # Handle HTTP error: log the error and check the status code

? ? ? ? ? ? logger.error(f"HTTP error for {url}: {e}")

? ? ? ? ? ? status_code = e.response.status_code

? ? ? ? ? ? if status_code == 429:

? ? ? ? ? ? ? ? # Handle 429 error: wait for some time and retry

? ? ? ? ? ? ? ? logger.info(f"Waiting for {DEFAULT_SLEEP} seconds after 429 error")

? ? ? ? ? ? ? ? sleep(DEFAULT_SLEEP)

? ? ? ? ? ? ? ? retry_count += 1

? ? ? ? ? ? elif status_code == 403 or status_code == 404:

? ? ? ? ? ? ? ? # Handle 403 or 404 error: break the loop and return None

? ? ? ? ? ? ? ? logger.info(f"Skipping {url} due to {status_code} error")

? ? ? ? ? ? ? ? break

? ? ? ? ? ? else:

? ? ? ? ? ? ? ? # Handle other errors: raise the exception and log the error

? ? ? ? ? ? ? ? logger.error(f"Other HTTP error for {url}: {e}")

? ? ? ? ? ? ? ? raise e

? ? # Return None if the loop ends without returning a response object

? ? return None

5. 定義處理圖片的函數(shù)

為了從響應(yīng)對象中提取圖片的數(shù)據(jù),并計算其質(zhì)量指標(biāo)和BRISQUE分?jǐn)?shù),我們可以定義一個函數(shù)process_image,接受一個響應(yīng)對象和一個URL作為參數(shù),并返回一個包含圖片信息的字典。在這個函數(shù)中,我們需要使用“with”語句來管理文件和圖片對象的打開和關(guān)閉,以及使用“del”語句來釋放不再需要的變量,從而優(yōu)化內(nèi)存使用。具體來說:

我們使用PIL庫提供的方法來打開響應(yīng)對象中的圖片數(shù)據(jù),并將其轉(zhuǎn)換為RGBA格式。

我們使用os模塊提供的方法來創(chuàng)建一個名為“images”的文件夾,用于存儲下載的圖片。

我們使用datetime模塊提供的方法來獲取當(dāng)前的日期和時間,并將其轉(zhuǎn)換為字符串格式,作為圖片的文件名。

我們使用“with”語句來打開一個以日期和時間命名的文件,并將圖片數(shù)據(jù)寫入到文件中。

我們使用brisque模塊提供的方法來計算圖片的BRISQUE分?jǐn)?shù),并將其添加到字典中。

我們使用前面定義的get_image_quality_metrics函數(shù)來計算圖片的其他質(zhì)量指標(biāo),并將其添加到字典中。

我們使用“del”語句來刪除不再需要的變量,如響應(yīng)對象、圖片對象等。

我們返回包含圖片信息的字典。

def process_image(response, url):

? ? """

? ? Process an image from a response object and calculate its quality metrics and BRISQUE score.

? ? Args:

? ? ? ? response (requests.Response): The response object containing the image data.

? ? ? ? url (str): The URL of the image.

? ? Returns:

? ? ? ? dict: A dict of image information including quality metrics and BRISQUE score.

? ? """

? ? # Open the image data from the response object and convert it to RGBA format

? ? image = Image.open(BytesIO(response.content)).convert('RGBA')

? ? # Create a folder named "images" to store the downloaded images

? ? os.makedirs('images', exist_ok=True)

? ? # Get the current date and time and convert it to a string format as the image file name

? ? date_time = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')

? ? # Open a file with the date and time as the file name and write the image data to it

? ? with open(f'images/{date_time}.png', 'wb') as f:

? ? ? ? image.save(f, 'PNG')

? ? # Calculate the BRISQUE score of the image and add it to the dict

? ? image_info = dict()

? ? image_info['brisque'] = get_brisque_score(response)

? ? # Calculate the other quality metrics of the image and add them to the dict

? ? image_info.update(get_image_quality_metrics(response))

? ? # Delete the response object and the image object to free up memory

? ? del response

? ? del image

? ? # Return the dict of image information

? ? return image_info

6. 使用線程池來處理多個網(wǎng)站的圖片抓取任務(wù)

為了提高程序的效率和并發(fā)性,我們可以使用線程池來處理多個網(wǎng)站的圖片抓取任務(wù),并將處理結(jié)果保存到文件中。我們可以使用concurrent.futures模塊提供的方法來創(chuàng)建一個線程池對象,并使用submit方法來提交每個網(wǎng)站的圖片抓取任務(wù)。具體來說:

我們創(chuàng)建一個名為“websites”的列表,用于存儲需要抓取圖片的網(wǎng)站的URL。

我們創(chuàng)建一個名為“results”的列表,用于存儲每個網(wǎng)站的圖片抓取結(jié)果。

我們使用“with”語句來創(chuàng)建一個線程池對象,并設(shè)置其最大線程數(shù)為10。

我們遍歷每個網(wǎng)站的URL,并使用submit方法來提交一個圖片抓取任務(wù),傳入send_request函數(shù)和URL作為參數(shù),并將返回的future對象添加到results列表中。

我們遍歷results列表中的每個future對象,并使用result方法來獲取其結(jié)果,即響應(yīng)對象。

我們判斷響應(yīng)對象是否為None,如果不為None,表示請求成功,我們則使用process_image函數(shù)來處理響應(yīng)對象,并將返回的圖片信息字典添加到results列表中;如果為None,表示請求失敗,我們則跳過該網(wǎng)站。

我們使用pickle模塊提供的方法來將results列表序列化并保存到一個名為“results.pkl”的文件中。

# Create a list of websites to scrape images from

websites = [

? ? 'https://unsplash.com/',

? ? 'https://pixabay.com/',

? ? 'https://www.pexels.com/',

? ? 'https://www.freeimages.com/',

? ? 'https://stocksnap.io/',

]

# Create a list to store the results of each website

results = []

# Create a thread pool with 10 threads and submit tasks for each website

with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:

? ? for website in websites:

? ? ? ? # Submit a task to send a request to the website and get a response object

? ? ? ? future = executor.submit(send_request, website)

? ? ? ? # Add the future object to the results list

? ? ? ? results.append(future)

# Iterate over the results list and get the result of each future object

for future in results:

? ? # Get the response object from the future object

? ? response = future.result()

? ? # Check if the response object is None or not

? ? if response is not None:

? ? ? ? # Process the response object and get the image information dict

? ? ? ? image_info = process_image(response, website)

? ? ? ? # Add the image information dict to the results list

? ? ? ? results.append(image_info)

? ? else:

? ? ? ? # Skip the website if the response object is None

? ? ? ? continue

# Serialize and save the results list to a file using pickle module

with open('results.pkl', 'wb') as f:

? ? pickle.dump(results, f)

結(jié)論

本文介紹了如何使用Python進(jìn)行大規(guī)模的圖像抓取,并提供了一些優(yōu)化內(nèi)存使用的方法和技巧,以及如何計算和評估圖片的質(zhì)量指標(biāo)。我們使用requests庫來發(fā)送GET請求到圖片的URL,并使用代理服務(wù)器和隨機(jī)選擇的請求頭部來避免被網(wǎng)站屏蔽或限制。我們使用PIL庫和cv2庫來處理圖片數(shù)據(jù),并使用brisque模塊和自定義的函數(shù)來計算圖片的質(zhì)量指標(biāo)和BRISQUE分?jǐn)?shù)。我們使用logging模塊來記錄程序的運(yùn)行日志,并使用pickle模塊來將處理結(jié)果保存到文件中。我們使用“with”語句來管理文件和圖片對象的打開和關(guān)閉,以及使用“del”語句來釋放不再需要的變量,從而優(yōu)化內(nèi)存使用。我們使用concurrent.futures模塊來創(chuàng)建一個線程池,并使用submit方法來提交每個網(wǎng)站的圖片抓取任務(wù),從而提高程序的效率和并發(fā)性。通過這些方法和技巧,我們可以實現(xiàn)一個高效、穩(wěn)定、可擴(kuò)展的大規(guī)模圖像抓取程序。

?著作權(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)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,552評論 19 139
  • 設(shè)計和部署 API 以服務(wù)于機(jī)器學(xué)習(xí)模型。## Intuition[CLI 應(yīng)用程序](https://franz...
    陶恒franz閱讀 374評論 0 0
  • 安裝Nginx:yum install -y nginxsystemctl start nginxsystemct...
    碧潭飄雪ikaros閱讀 746評論 0 1
  • Awesome Ruby Toolbox Awesome A collection of awesome Ruby...
    debbbbie閱讀 3,086評論 0 3
  • scrapy學(xué)習(xí)筆記(有示例版) 我的博客 scrapy學(xué)習(xí)筆記1.使用scrapy1.1創(chuàng)建工程1.2創(chuàng)建爬蟲模...
    陳思煜閱讀 13,077評論 4 46

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