For many projects, you need to extract data and images from websites programmatically. This article demonstrates a complete Python solution for web scraping that downloads images and stores them in Azure Blob Storage.
You'll need to install the following Python libraries:
pip install requests beautifulsoup4 lxml azure-storage
The script performs the following operations:
Create a credentials.json file to store your Azure Storage Account details:
{
"DC_LOCATION": "westeurope",
"GROUP_NAME": "my-resource-group",
"storagacc": {
"Accountname": "mystorageaccount",
"AccountKey": "your-account-key-here",
"Container": "images-container",
"AZURE_CLIENT_SECRET": "your-client-secret",
"AZURE_SUBSCRIPTION_ID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
import requests
from bs4 import BeautifulSoup
from os.path import basename
import os
import sys
import urllib
import urllib2
import urlparse
import argparse
import json
import config
import random
import base64
import datetime
import time
import string
from azure.storage import CloudStorageAccount, AccessPolicy
from azure.storage.blob import BlockBlobService, PageBlobService, AppendBlobService
from azure.storage.models import CorsRule, Logging, Metrics, RetentionPolicy, ResourceTypes, AccountPermissions
from azure.storage.blob.models import BlobBlock, ContainerPermissions, ContentSettings
CURRENT_DIR = os.getcwd()
STORING_DIRECTORY_NAME = "storage_scrapped_images"
STORING_DIRECTORY = CURRENT_DIR + "/" + STORING_DIRECTORY_NAME
if not os.path.exists(STORING_DIRECTORY):
os.makedirs(STORING_DIRECTORY)
def randomword(length):
"""Generate a random word of specified length"""
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(length))
def randomword(length):
"""Generate random alphanumeric strings"""
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(length))
# Load credentials from JSON file
startdate = time.clock()
metadata_loaded = {
'Owner': 'Your-Name',
'Date_Of_Upload': startdate,
'VAR_2': 'VAL_VAR_2',
'VAR_3': 'VAL_VAR_3',
'VAR_4': 'VAL_VAR_4'
}
with open("credentials.json", 'r') as f:
data = json.loads(f.read())
StoAcc_var_name = data["storagacc"]["Accountname"]
StoAcc_var_key = data["storagacc"]["AccountKey"]
StoAcc_var_container = data["storagacc"]["Container"]
block_blob_service = BlockBlobService(
account_name=StoAcc_var_name,
account_key=StoAcc_var_key
)
def copy_azure_files(source_url, destination_object, destination_container):
"""Copy files directly from URL to Azure Blob Storage"""
blob_service = BlockBlobService(
account_name=StoAcc_var_name,
account_key=StoAcc_var_key
)
blob_service.copy_blob(
destination_container,
destination_object,
source_url
)
def upload_func(container, blobname, filename):
"""Upload a local file to Azure Blob Storage"""
start = time.clock()
block_blob_service.create_blob_from_path(
container,
blobname,
filename
)
elapsed = time.clock() - start
print("*** DEBUG *** Time spent uploading API %s is: %s in Bucket/container: %s"
% (filename, elapsed, container))
# Target URL for scraping
URL_TARGET = "https://www.cdiscount.com/search/10/telephone.html"
# Alternative: URL_TARGET = "https://mouradcloud.westeurope.cloudapp.azure.com/blog/blog/category/food/"
base_url = URL_TARGET
out_folder = '/tmp'
# Fetch the webpage
r = requests.get(URL_TARGET)
data = r.text
# Parse HTML with BeautifulSoup
soup = BeautifulSoup(data, "lxml")
# Process all images found in the page
for link in soup.find_all('img'):
image_url = link.get("src")
while image_url is not None:
if 'http' in image_url:
blocks = []
# Filter: Only download image files (PNG, JPG, JPEG)
if image_url.endswith(('.png', '.jpg', '.jpeg')):
print("->>>>>>>>>>>>>> THIS IS AN IMAGE ... PROCESSING")
# Download image locally
file_name_downloaded = basename(image_url)
file_name_path_local = STORING_DIRECTORY + "/" + file_name_downloaded
with open(file_name_path_local, "wb") as f:
f.write(requests.get(image_url).content)
# Upload to Azure Blob Storage
filename_in_clouddir = "uploads/" + file_name_downloaded
copy_azure_files(
image_url,
filename_in_clouddir,
StoAcc_var_container
)
break
else:
print("->>>>>>>>>>>>>> THIS NOT AN IMAGE ... SKIPPING")
break
else:
# Skip local images (those without http in URL)
print("->>>>>>>>>>>>>> THIS IS A LOCAL IMAGE ... SKIPPING")
break
continue
Uses BeautifulSoup with lxml parser for robust HTML parsing:
soup = BeautifulSoup(data, "lxml")
for link in soup.find_all('img'):
image_url = link.get("src")
Only processes images with specific extensions:
if image_url.endswith(('.png', '.jpg', '.jpeg')):
# Process image
storage_scrapped_images/ directoryTwo methods for Azure storage:
copy_blob() - copies directly from URL without local downloadcreate_blob_from_path() - uploads local fileAdd metadata to Azure blobs:
metadata_loaded = {
'Owner': 'Your-Name',
'Date_Of_Upload': timestamp,
'Custom_Field': 'Custom_Value'
}
python scraper.py
Modify the URL_TARGET variable:
URL_TARGET = "https://example.com/images"
Update credentials.json with your Azure Storage details.
def scrape_with_error_handling(url):
try:
r = requests.get(url, timeout=10)
r.raise_for_status()
except requests.exceptions.RequestException as e:
print("Error fetching URL: %s" % e)
return None
try:
soup = BeautifulSoup(r.text, "lxml")
images = soup.find_all('img')
print("Found %d images" % len(images))
except Exception as e:
print("Error parsing HTML: %s" % e)
return None
return images
def is_valid_image_url(url):
valid_extensions = ('.png', '.jpg', '.jpeg', '.gif', '.webp')
valid_domains = ['example.com', 'trusted-source.com']
if not url.endswith(valid_extensions):
return False
parsed = urlparse.urlparse(url)
if parsed.netloc not in valid_domains:
return False
return True
def download_with_retry(url, max_retries=3):
for attempt in range(max_retries):
try:
response = requests.get(url, timeout=5)
return response.content
except requests.RequestException as e:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt) # Exponential backoff
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
logger.info("Starting scraping process")
⚠️ Important:
robots.txt and terms of serviceCrawl-Delay and User-Agent directives# Solution: Disable verification (not recommended for production)
r = requests.get(URL_TARGET, verify=False)
# Ensure parser is robust
soup = BeautifulSoup(data, "html.parser") # Use html.parser instead of lxml
# Verify credentials are correct and account key hasn't expired
block_blob_service = BlockBlobService(
account_name=StoAcc_var_name,
account_key=StoAcc_var_key
)
This Python web scraping solution demonstrates:
Use this as a foundation for building more sophisticated web scraping pipelines for your cloud-based data collection needs.
Repository: Original implementation at Mourad's blog
Web scraping is powerful for data collection, but always respect website policies and rate limits to be a good internet citizen.