scrape images from website

يعني تعلّم كيفية تجريف الصور من موقع ويب حل مشكلتين: معرفة موضع كل مصدر للصورة في HTML، ثم تنزيل الملفات الثنائية الموجودة خلف عناوين URL تلك. يشرح هذا الدليل الخطوتين باستخدام شيفرة Python قابلة للتشغيل، ويتناول الحالات الدقيقة مثل اختيار srcset والتحميل الكسول وخلفيات CSS، ويوضح كيفية الالتزام بحدود حقوق النشر والنطاق الترددي.

الصور أثقل من النص، لذا يتطلب جمعها على نطاق واسع خطة للتخزين وإزالة التكرار والتزامن وتكلفة الشبكة. كل ما يلي قابل للتشغيل باستخدام requests وBeautifulSoup وPlaywright، وتنطبق هذه الأساليب على معرض واحد أو كتالوج كبير.

DataImpulse مزود بروكسي أخلاقي يوفّر أكثر من 90 مليون عنوان IP سكني ومحمول ومن مركز بيانات في 195 دولة. ويعتمد نموذج الدفع حسب الاستخدام بدءاً من 1 دولار لكل GB مع حركة مرور لا تنتهي صلاحيتها، ويُستخدم لتجريف الويب والتحقق من الإعلانات ومراقبة الأسعار وأبحاث السوق وإدارة حسابات متعددة.

حقائق أساسية

  • النقطة الأساسية: لتجريف الصور من موقع ويب، يجب أولاً استخراج كل مصدر، بما في ذلك img src وsrcset وسمات بيانات التحميل الكسول وخلفيات CSS وJSON-LD، ثم تنزيل الملفات الثنائية، لأن الترميز وملفات الصور الفعلية طلبات منفصلة.
  • أفضل نوع بروكسي: بروكسيات سكنية متدوّرة تستخدم عناوين IP حقيقية للمستهلكين وتتجاوز الاكتشاف.
  • السعر: بدءاً من 1 دولار لكل GB، والدفع حسب الاستخدام، مع حركة مرور لا تنتهي صلاحيتها ومن دون اشتراك.
  • التغطية: أكثر من 90 مليون عنوان IP مستمد أخلاقياً في 195 دولة.
  • الموثوقية: معدل نجاح 99.51%، وتقييم 4.8 من 5 على G2.
  • البروتوكولات والاستهداف: HTTP وHTTPS وSOCKS5، مع تضمين الاستهداف حسب الدولة.
كيف يعمل تجريف الصور من البداية إلى النهاية

أين توجد عناوين URL للصور في صفحة ويب؟

تُخزَّن عناوين URL للصور في مواضع متعددة، لا في السمة src وحدها، لذلك يفحصها مجرّف موثوق كلها. وقد تشير الصفحة إلى الصورة نفسها عبر الوسوم القياسية والسمات المتجاوبة وعناصر التحميل الكسول النائبة وأوراق الأنماط والبيانات المنظَّمة.

  • الصور القياسية: تحتوي السمة src في وسم <img> على عنوان URL المباشر.
  • الصور المتجاوبة: تسرد السمة srcset في <img> و<source> عناوين URL متعددة مع واصفات العرض أو الكثافة، ما يتيح للمتصفح اختيار الحجم.
  • الصور المحمّلة كسولاً: غالباً ما تكون src الظاهرة عنصراً نائباً صغيراً، فيما يوجد عنوان URL الحقيقي في data-src أو data-original أو سمة مخصصة مشابهة إلى أن يمرّر المستخدم الصفحة.
  • خلفيات CSS: تُعيَّن الصور الزخرفية بواسطة background-image: url(...) في الأنماط المضمنة أو أوراق الأنماط، ولا تظهر مطلقاً في وسم <img>.
  • البيانات المنظَّمة: غالباً ما تسرد صفحات المنتجات والمقالات صورة أساسية عالية الدقة في كتلة JSON-LD.

بسبب هذا التوزع، تعامل مع الصفحة بوصفها مجموعة مصادر مرشحة واجمع من جميع هذه المواضع قبل التنزيل. يلخص الجدول التالي موضع كل مصدر والمشكلة الشائعة التي تعثر معظم المجرّفات.

مصدر الاستخراج مكان العثور عليه مشكلة شائعة
img src سمة src في وسوم img غالباً ما يكون عنصراً نائباً أو معرّف URI من نوع data: في الصفحات المحمّلة كسولاً
srcset srcset في وسوم img وsource يحتوي عدة عناوين URL مع واصفات عرض؛ يجب تحليله واختيار حجم واحد
سمات التحميل الكسول data-src, data-original, data-lazy-src, data-srcset تختلف أسماء السمات حسب الإطار، لذا افحص الترميز الفعلي
خلفية CSS style المضمن وكتل style، وbackground-image: url() لا تظهر مطلقاً في وسم img؛ وتحتاج إلى تعبير نمطي على نص CSS
JSON-LD وسم script من النوع application/ld+json قد يكون حقل الصورة سلسلة أو قائمة أو كائناً متداخلاً

كيف تُعد مجرّف صور باستخدام Python؟

ثبّت requests لـ HTTP وBeautifulSoup للتحليل وlxml بوصفه محركاً سريعاً للمحلل، ثم اجلب الصفحة واقرأ src لكل وسم صورة. هذا المثال البسيط هو الأساس الذي تُبنى عليه كل خطوة لاحقة.

pip install requests beautifulsoup4 lxml

import os
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin

PAGE = "https://example.com/gallery"

session = requests.Session()
session.headers.update({
    "User-Agent": "Mozilla/5.0 (compatible; image-collector/1.0)",
    "Accept": "text/html,application/xhtml+xml",
})

resp = session.get(PAGE, timeout=30)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "lxml")

sources = []
for img in soup.find_all("img"):
    src = img.get("src")
    if src:
        sources.append(urljoin(PAGE, src))

print(len(sources), "image URLs found")
for u in sources[:10]:
    print(u)

يبقي كائن Session الاتصالات مفتوحة ويعيد استخدام الترويسات، وهذا أسرع وألطف على الخادم المستهدف من فتح اتصال جديد لكل طلب. استخدم urljoin دائماً كي تتحول المسارات النسبية مثل /media/photo.jpg إلى عناوين URL مطلقة يمكن تنزيلها لاحقاً. وإذا كنت جديداً على سير العمل الأوسع، يشرح دليل أفضل ممارسات تجريف الويب الترويسات والمهلات واللباقة بمزيد من التفصيل.

كيف تستخرج img src وsrcset والمصادر المحمّلة كسولاً؟

حلل srcset بتقسيمه على الفواصل وقراءة واصف العرض لتختار أكبر نسخة، واقرأ كل سمة محتملة تبدأ بـ data- للمصادر المحمّلة كسولاً. تفوّت القراءة العادية للسمات معظم الصفحة الحديثة، لذا تحتاج إلى دوال مساعدة صغيرة لكل صيغة.

تجمع قيمة srcset عدة عناوين URL مع واصفات مثل 800w. وللحصول على النسخة الأعلى دقة، قارن أرقام العرض واحتفظ بالأكبر.

def largest_from_srcset(value):
    # "small.jpg 480w, big.jpg 1200w" -> "big.jpg"
    best_url, best_w = None, -1
    for part in value.split(","):
        tokens = part.strip().split()
        if not tokens:
            continue
        url = tokens[0]
        width = 0
        if len(tokens) > 1 and tokens[1].endswith("w"):
            try:
                width = int(tokens[1][:-1])
            except ValueError:
                width = 0
        if width > best_w:
            best_url, best_w = url, width
    return best_url

الآن ادمج المصادر القياسية والمتجاوبة والكسولة في جامع واحد. تستخدم الأطر المختلفة أسماء سمات مختلفة، لذا افحص عدة أسماء. وتقرأ الدالة نفسها أيضاً وسوم <source> داخل عناصر <picture> التي تحمل srcset الخاصة بها.

LAZY_ATTRS = ("data-src", "data-original", "data-lazy-src",
              "data-srcset", "data-image", "data-fallback-src")

def parse_srcset(value):
    urls = []
    for part in value.split(","):
        tokens = part.strip().split()
        if tokens:
            urls.append(tokens[0])
    return urls

def collect_img_sources(soup, base):
    found = set()
    for img in soup.find_all("img"):
        src = img.get("src")
        if src and not src.startswith("data:"):
            found.add(urljoin(base, src))
        if img.get("srcset"):
            biggest = largest_from_srcset(img["srcset"])
            if biggest:
                found.add(urljoin(base, biggest))
        for attr in LAZY_ATTRS:
            val = img.get(attr)
            if not val:
                continue
            if "srcset" in attr:
                for u in parse_srcset(val):
                    found.add(urljoin(base, u))
            else:
                found.add(urljoin(base, val))
    # source tags inside picture elements also carry srcset
    for source in soup.find_all("source"):
        if source.get("srcset"):
            biggest = largest_from_srcset(source["srcset"])
            if biggest:
                found.add(urljoin(base, biggest))
    return found

يمنع تخطي أي src يبدأ بـ data: حفظ العناصر النائبة المضمنة base64، وهي عادةً صور مصغرة منخفضة الجودة يعرضها التحميل الكسول قبل وصول الصورة الحقيقية.

How do you find خلفية CSS and JSON-LD images?

استخدم تعبيراً نمطياً لاستخراج عناوين URL من تعريفات background-image، ومرّ على أي كتلة JSON-LD لقراءة حقل image. لا يظهر هذان المصدران مطلقاً في وسم <img>، لذا سيفوتهما تماماً أي مجرّف يقرأ وسوم الصور فقط.

تُعيَّن الصور الزخرفية وصور الواجهة البارزة عادةً في CSS. افحص كلاً من سمات style المضمنة وكتل <style> بحثاً عن قيم url(...).

import re
from urllib.parse import urljoin

BG_RE = re.compile(
    r"background(?:-image)?\s*:\s*url\(\s*['\"]?(.*?)['\"]?\s*\)",
    re.IGNORECASE,
)

def collect_css_backgrounds(soup, base):
    found = set()
    # inline style attributes
    for el in soup.find_all(style=True):
        for match in BG_RE.findall(el["style"]):
            if match and not match.startswith("data:"):
                found.add(urljoin(base, match))
    # style blocks
    for style in soup.find_all("style"):
        text = style.string or ""
        for match in BG_RE.findall(text):
            if match and not match.startswith("data:"):
                found.add(urljoin(base, match))
    return found

البيانات المنظَّمة هي الموضع الأكثر موثوقية للعثور على صورة أساسية كاملة الدقة في صفحات المنتجات والمقالات. يمكن أن تكون قيمة image سلسلة أو قائمة أو كائناً متداخلاً، لذا مرّ على الشجرة كاملة.

import json

def collect_jsonld_images(soup, base):
    found = set()
    for tag in soup.find_all("script", type="application/ld+json"):
        try:
            data = json.loads(tag.string or "")
        except (ValueError, TypeError):
            continue
        stack = [data]
        while stack:
            node = stack.pop()
            if isinstance(node, dict):
                img = node.get("image")
                if isinstance(img, str):
                    found.add(urljoin(base, img))
                elif isinstance(img, list):
                    for item in img:
                        if isinstance(item, str):
                            found.add(urljoin(base, item))
                stack.extend(node.values())
            elif isinstance(node, list):
                stack.extend(node)
    return found

ادمج المجمعات الثلاثة باتحاد مجموعات، مثل collect_img_sources(soup, base) | collect_css_backgrounds(soup, base) | collect_jsonld_images(soup, base)، وستحصل على مجموعة غير مكررة من عناوين URL المرشحة الجاهزة للتنزيل.

كيف تنزّل ملفات الصور بأمان؟

مرّر كل استجابة على دفعات، وتأكد من أن ترويسة Content-Type تشير فعلاً إلى صورة، وحدد سقفاً للحجم كي لا يستنزف ملف واحد الذاكرة أو القرص. فتنزيل عنوان URL من دون تحقق قد يحفظ صفحات أخطاء HTML أو عمليات إعادة توجيه أو ملفات ضخمة، لذا تحقق قبل الاحتفاظ بالبايتات.

IMAGE_TYPES = ("image/jpeg", "image/png", "image/gif",
               "image/webp", "image/avif", "image/svg+xml")

def download(session, url, proxies=None):
    with session.get(url, proxies=proxies, timeout=30,
                     stream=True) as r:
        r.raise_for_status()
        ctype = r.headers.get("Content-Type", "").split(";")[0].strip()
        if ctype not in IMAGE_TYPES:
            raise ValueError("not an image: " + (ctype or "unknown"))
        chunks = []
        total = 0
        for chunk in r.iter_content(8192):
            chunks.append(chunk)
            total += len(chunk)
            if total > 25 * 1024 * 1024:   # 25 MB safety cap
                raise ValueError("file too large")
        return b"".join(chunks), ctype

يعني البث باستخدام stream=True وiter_content أن فحص الترويسة يجري قبل تنزيل المحتوى كاملاً، لذا يُرفض الرابط ذي التسمية الخاطئة مبكراً. ويلتقط التحقق من النوع المعلن الحالة الشائعة التي يعيد فيها عنوان URL معطوب صفحة HTML 404 بحالة 200. ولمزيد من الأمان يمكنك أيضاً التحقق من بايتات التوقيع الأولى للحمولة، لكن فحص الترويسة مع سقف الحجم يعالج معظم حالات التجريف الواقعية.

كيف تزيل تكرار الصور المنزّلة وتسمّيها؟

احسب تجزئة SHA-256 للبايتات المنزّلة وتخطَّ أي بصمة حفظتها بالفعل، ثم نظّف اسم الملف ووزّع الملفات في مجلدات فرعية بحسب بادئة التجزئة. كثيراً ما تقدّم المواقع الصورة نفسها من مسارات متعددة أو شبكات CDN أو نسخ بأحجام مختلفة، لذا تكون تجزئة المحتوى أكثر موثوقية من مقارنة عناوين URL.

import os
import re
import hashlib
from urllib.parse import urlparse

EXT_BY_TYPE = {
    "image/jpeg": ".jpg", "image/png": ".png", "image/gif": ".gif",
    "image/webp": ".webp", "image/avif": ".avif", "image/svg+xml": ".svg",
}

def safe_name(url, ctype, digest):
    base = os.path.basename(urlparse(url).path)
    base = re.sub(r"[^A-Za-z0-9._-]", "_", base)
    if not base or "." not in base:
        base = digest[:16] + EXT_BY_TYPE.get(ctype, ".img")
    return base

def save_image(data, url, ctype, out_dir, seen):
    digest = hashlib.sha256(data).hexdigest()
    if digest in seen:
        return None                      # exact duplicate, skip
    seen.add(digest)
    # shard into subfolders by hash prefix to avoid one huge directory
    sub = os.path.join(out_dir, digest[:2])
    os.makedirs(sub, exist_ok=True)
    name = safe_name(url, ctype, digest)
    path = os.path.join(sub, name)
    if os.path.exists(path):
        name = digest[:16] + "_" + name  # avoid overwriting a different file
        path = os.path.join(sub, name)
    with open(path, "wb") as f:
        f.write(data)
    return path

تزيل تنقية اسم الأساس محارف اجتياز المسار وبقايا سلسلة الاستعلام، كي لا يتمكن عنوان URL خبيث أو فوضوي من الكتابة خارج المجلد المستهدف. ويبقي التقسيم وفق أول حرفين من التجزئة المجلدات صغيرة، وهو أمر مهم عند جمع عشرات الآلاف من الملفات. ولاكتشاف التكرارات المتقاربة، مثل الصورة ذاتها بدقات مختلفة، تجمع مكتبة تجزئة إدراكية مثل imagehash الملفات المتشابهة بصرياً التي تعاملها تجزئات البايتات على أنها مختلفة.

كيف تنزّل صوراً كثيرة بالتزامن؟

استخدم ThreadPoolExecutor لتنزيل عدة صور في وقت واحد، وأضف محدِّد معدل مشتركاً كي تبقى جميع الخيوط مجتمعة تحت سقف للطلبات في الثانية. تنزيل الصور مقيد بالإدخال والإخراج، لذا تحقق الخيوط تسريعاً كبيراً، لكن التزامن غير المحدود سيثقل الهدف ويؤدي إلى حظرك.

import time
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed

class RateLimiter:
    # allow N requests per second across all worker threads
    def __init__(self, rate_per_sec):
        self.min_gap = 1.0 / rate_per_sec
        self.lock = threading.Lock()
        self.next_time = 0.0

    def wait(self):
        with self.lock:
            now = time.monotonic()
            if now < self.next_time:
                time.sleep(self.next_time - now)
                now = time.monotonic()
            self.next_time = now + self.min_gap

def fetch_all(urls, out_dir, proxies=None, workers=8, rate=5):
    os.makedirs(out_dir, exist_ok=True)
    limiter = RateLimiter(rate)
    seen = set()
    seen_lock = threading.Lock()
    saved = []

    def job(url):
        limiter.wait()
        data, ctype = download(session, url, proxies)
        with seen_lock:
            return save_image(data, url, ctype, out_dir, seen)

    with ThreadPoolExecutor(max_workers=workers) as pool:
        futures = {pool.submit(job, u): u for u in urls}
        for fut in as_completed(futures):
            url = futures[fut]
            try:
                path = fut.result()
                if path:
                    saved.append((url, path))
            except Exception as exc:
                print("failed", url, exc)
    return saved

يجعل القفل حول seen إزالة التكرار آمنة للخيوط، كما أن تغليف كل مهمة بكتلة try يعني أن عنوان URL سيئاً واحداً لا يوقف التشغيل كله. حافظ على معدل متواضع، فبضع طلبات في الثانية إعداد افتراضي معقول، واقرأ دليل تجريف الويب الأخلاقي لمعرفة كيفية اختيار حدود تحترم الموقع.

كيف تجرّف الصور على نطاق واسع باستخدام البروكسيات؟

تحتاج إلى البروكسيات عندما يبدأ الهدف في تقييد المعدل أو حظر عنوان IP الخاص بك، وهو ما يحدث سريعاً عند تنزيل صور كثيرة من عنوان واحد. مرّر قاموس proxies إلى كل طلب كي تمر حلقة التنزيل نفسها عبر عناوين IP سكنية متدوّرة. إن تدفقاً ثابتاً من طلبات الصور من عنوان IP واحد نمط آلي واضح، ولأن الصور ثقيلة يكون النطاق الترددي عادةً القيد الحقيقي.

USER = "your_login"
PASS = "your_password"
GATEWAY = "gw.dataimpulse.com:823"

# Rotating: a new exit IP is assigned per request
proxies = {
    "http":  "http://%s:%s@%s" % (USER, PASS, GATEWAY),
    "https": "http://%s:%s@%s" % (USER, PASS, GATEWAY),
}

# Sticky session: reuse one IP for a sequence of related downloads
def sticky_proxies(session_id):
    login = "%s-sid-%s" % (USER, session_id)
    uri = "http://%s:%s@%s" % (login, PASS, GATEWAY)
    return {"http": uri, "https": uri}

saved = fetch_all(all_urls, "images", proxies=proxies,
                  workers=8, rate=5)

توزع البروكسيات المتدوّرة الطلبات على عناوين IP كثيرة كي لا يجذب أي عنوان منفرد الانتباه، بينما تحتفظ الجلسات الثابتة بعنوان IP واحد لمعرض يربطه الموقع بجلسة. توفّر DataImpulse بروكسيات سكنية وبروكسيات محمولة للأهداف التي تدقق في حركة المرور عن كثب، إضافة إلى بروكسيات مركز بيانات للتنزيلات كبيرة الحجم من مصادر متسامحة. جميع عناوين IP مستمدة أخلاقياً من مستخدمين يوافقون على ذلك ويتلقون مقابلاً. وإذا رُفضت بيانات اعتمادك برمز 407، يشرح دليل مصادقة البروكسي صيغة تسجيل الدخول الدقيقة.

لأن كل بايت يُنزّل يُحتسب من ميزانية حركة المرور، خطط للحجم قبل البدء: تخطَّ الصور دون عتبة معينة إذا كنت تريد المحتوى الرئيسي فقط، واطلب نسخة srcset أصغر عندما لا تحتاج إلى الدقة الكاملة، وخزّن ما لديك مسبقاً كي لا تعيد عملية التشغيل جلب كل شيء. تستخدم DataImpulse فوترة الدفع حسب الاستخدام بدءاً من 1 دولار لكل GB مع حركة مرور لا تنتهي صلاحيتها، لذلك لا تتطلب مهمة صور ثقيلة ولكن متباعدة اشتراكاً شهرياً، وتُرحَّل حركة المرور غير المستخدمة.

كيف تجرّف المعارض التي يعرضها JavaScript؟

إذا كان الموقع ينشئ معرضه بالكامل في JavaScript، فلن تظهر عناوين URL للصور في HTML الخام، لذا اعرض الصفحة أولاً باستخدام Playwright ومرّر النتيجة إلى المجمعات نفسها. لا يستطيع requests وحده تنفيذ البرامج النصية، ولذلك تحتاج المعارض ذات التمرير اللانهائي والصفحة الواحدة إلى متصفح حقيقي لتشغيل التحميل.

pip install playwright
playwright install chromium

from playwright.sync_api import sync_playwright
from bs4 import BeautifulSoup

def render_and_collect(page_url, use_proxy=False):
    launch_args = {}
    if use_proxy:
        launch_args["proxy"] = {
            "server": "http://gw.dataimpulse.com:823",
            "username": "your_login",
            "password": "your_password",
        }
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True, **launch_args)
        page = browser.new_page()
        page.goto(page_url, wait_until="networkidle")
        # trigger lazy-loading by scrolling to the bottom
        for _ in range(10):
            page.mouse.wheel(0, 4000)
            page.wait_for_timeout(400)
        html = page.content()
        browser.close()
    soup = BeautifulSoup(html, "lxml")
    return (collect_img_sources(soup, page_url)
            | collect_css_backgrounds(soup, page_url))

يفرض التمرير في حلقة على المحمل الكسول استبدال عناصره النائبة data-src بعناوين URL حقيقية، فتلتقطها بعد ذلك المحللات نفسها التي كتبتها بالفعل. ولمعالجة أعمق للعرض بلا واجهة، راجع تجريف صفحات الويب الديناميكية.

أخيراً، اكتب بيان CSV لكي يمكن تتبع كل ملف منزّل إلى عنوان URL المصدر والتجزئة والحجم. يحول البيان مجلداً من ملفات مجهولة إلى مجموعة بيانات يمكنك تدقيقها واستئنافها وإزالة تكرارها عبر عمليات التشغيل.

import csv
from datetime import datetime, timezone

def write_manifest(saved, out_dir):
    path = os.path.join(out_dir, "manifest.csv")
    with open(path, "w", newline="", encoding="utf-8") as f:
        writer = csv.writer(f)
        writer.writerow(["source_url", "local_path", "sha256",
                         "bytes", "fetched_at"])
        for url, local_path in saved:
            with open(local_path, "rb") as img:
                data = img.read()
            writer.writerow([
                url,
                local_path,
                hashlib.sha256(data).hexdigest(),
                len(data),
                datetime.now(timezone.utc).isoformat(),
            ])
    return path

بعد وضع البيان في مكانه، يمكنك إعادة تشغيل المهمة وتخطي أي عنوان URL مسجل بالفعل، مما يوفر النطاق الترددي ويحافظ على اتساق مجموعة بياناتك بمرور الوقت.

هل من القانوني تجريف الصور من موقع ويب وإعادة استخدامها؟

تنزيل ملف صورة فعل تقني، ولا يمنحك أي ترخيص لإعادة استخدام تلك الصورة. هذه أكثر جوانب تجريف الصور سوءاً في الفهم، لذا تعامل معها بحذر. تكاد كل صورة فوتوغرافية أو رسم توضيحي أو تصميم رسومي على الويب تكون محمية بحقوق النشر لحظة إنشائها، ويملك من التقطها أو أنشأها تلك الحقوق ما لم يتنازل عنها صراحةً.

لا تعني القدرة على جلب ملف شيئاً عن حقك في إعادة نشره أو بيعه أو التدريب عليه أو إعادة توزيعه. قبل إعادة استخدام الصور المجرّفة، تحقق من شروط الترخيص المرفقة بها، وابحث عن ترخيص معلن مثل المشاع الإبداعي أو الملكية العامة، واحصل على إذن مكتوب عندما يكون الاستخدام تجارياً. جمع الصور للتحليل الخاص أو الفهرسة أو البحث مسألة مختلفة عن نشرها، والافتراض الآمن هو أن الصورة محمية ما لم ينص الترخيص بوضوح على خلاف ذلك.

راجع أيضاً شروط خدمة الموقع المستهدف وrobots.txt قبل البدء. يوضح دليل التحقق مما إذا كان موقع ويب يسمح بالتجريف كيفية قراءة تلك الإشارات، ويتناول دليل التجريف من دون التعرّض للحظر ممارسات الزحف المحترمة. لا تغير أي من تقنيات هذه المقالة القاعدة الأساسية: الوصول ليس ترخيصاً، والواقع أن وجود مجرّف عامل وحق قانوني في إعادة الاستخدام أمران منفصلان يجب استيفاؤهما بشكل مستقل.

أربعة مواضع توجد فيها صور الصفحة فعلياً

الأسئلة الشائعة

هل يمكنني تجريف الصور من أي موقع ويب؟

من الناحية التقنية يمكنك تنزيل الملفات من معظم المواقع، لكن ينبغي أولاً التحقق من شروط خدمة الموقع وrobots.txt، وتذكّر أن تنزيل صورة لا يمنحك حق إعادة استخدامها. الوصول ليس ترخيصاً.

لماذا لا تظهر بعض الصور في HTML الذي أنزّله؟

تُحمّل تلك الصور عادةً بواسطة JavaScript أو التحميل الكسول، لذلك توجد عناوين URL الحقيقية في سمات البيانات أو تُضاف بعد عرض الصفحة. اعرض الصفحة بمتصفح بلا واجهة مثل Playwright ومرّرها لتشغيل التحميل، ثم حلل النتيجة.

كيف أختار النسخة الأعلى دقة من الصورة؟

حلل سمة srcset، واقرأ واصف العرض بعد كل عنوان URL، واحتفظ بعنوان URL ذي أكبر عرض. وغالباً ما تسرد صفحات المنتجات صورة كاملة الدقة في كتلة JSON-LD أيضاً.

كيف أتجنب تنزيل الصورة نفسها مرتين؟

احسب تجزئة SHA-256 لكل ملف منزّل وتخطَّ أي تجزئة حفظتها بالفعل. وللنسخ المتشابهة بصرياً بأحجام مختلفة، استخدم بدلاً من ذلك مكتبة تجزئة إدراكية مثل imagehash.

هل توفّر DataImpulse واجهة API مُدارة لتجريف الصور؟

لا. توفّر DataImpulse بروكسيات تمرر عبرها مجرّفك الخاص؛ وهي ليست واجهة API مُدارة للتجريف ولا خدمة بروكسي ويب مجانية. تكتب منطق التنزيل وتستخدم عناوين IP الخاصة بها للوصول والتدوير.

متى لا تكون DataImpulse الخيار المناسب؟

إذا كنت تحتاج إلى بروكسيات ISP ثابتة أو واجهة API مُدارة بالكامل للتجريف أو الوصول إلى المواقع المصرفية والحكومية، فإن DataImpulse ليست الأداة المناسبة. تركز على البروكسيات السكنية والمحمولة ومن مركز البيانات المتدوّرة لجمع البيانات العامة والوصول إلى المحتوى.

ابدأ تجريف الصور على نطاق واسع

عندما يحتاج مجرّف الصور لديك إلى وصول موثوق وتدوير عناوين IP من دون اشتراك، توفّر DataImpulse بروكسيات سكنية ومحمولة ومن مركز بيانات مستمدة أخلاقياً مع حركة مرور بالدفع حسب الاستخدام بدءاً من 1 دولار لكل GB. أنشئ حساباً ومرّر حلقة التنزيل عبرها في دقائق.


Share article: