دليل احترافي لاستخراج البيانات من أي موقع باستخدام بايثون

دقائق القراءة: 9

مقدمة في استخراج البيانات من الويب باستخدام Python

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

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

شرح احترافي لاستخراج البيانات من المواقع باستخدام بايثون وأدوات تحليل صفحات الويب

في هذا الدليل ستتعرف على كيفية بناء أداة استخراج بيانات باستخدام Python، بداية من فحص بنية الصفحة، ثم جمع المحتوى الثابت بواسطة BeautifulSoup، والتعامل مع المحتوى الديناميكي عبر Selenium، وصولاً إلى حفظ النتائج في ملفات JSON أو CSV.

تنبيه قانوني قبل البدء في Web Scraping

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

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

الالتزام القانوني ليس مجرد خطوة احترازية، بل هو جزء أساسي من الممارسة المهنية المسؤولة.

ما الذي تحتاج إليه قبل بناء أداة الاستخراج؟

قبل كتابة أي سطر برمجي، تحتاج إلى تجهيز البيئة المناسبة للعمل. الخطوة الأولى هي التأكد من تثبيت Python على جهازك. في العديد من توزيعات Linux مثل Ubuntu 20.04 يأتي Python 3 مثبتاً مسبقاً.

للتحقق من وجوده، نفّذ الأمر التالي:

python3 -v

إذا كان مثبتاً بشكل صحيح، فستظهر نتيجة مشابهة لما يلي:

Python 3.8.2

بعد ذلك، ستحتاج إلى تثبيت حزمتين أساسيتين:

  • BeautifulSoup لاستخراج العناصر من HTML.
  • Selenium للتعامل مع الصفحات التي تعتمد على JavaScript في تحميل المحتوى.

يمكن تثبيتهما بالأوامر التالية:

pip3 install beautifulsoup4
pip3 install selenium

وإذا كنت تنوي استخدام Selenium، فعليك أيضاً تثبيت متصفح Google Chrome وبرنامج ChromeDriver المتوافق مع إصدار المتصفح الموجود لديك.

كيف تفحص الصفحة قبل استخراج البيانات؟

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

https://www.imdb.com/chart/top/

ابدأ بالنقر بزر الفأرة الأيمن على عنوان أول فيلم، ثم اختر Inspect Element لفحص العنصر داخل أدوات المطور في المتصفح.

فحص عنصر عنوان فيلم داخل صفحة IMDb باستخدام أدوات المطور لتحديد محددات CSS

عند البحث داخل الشيفرة باستخدام CTRL+F ستلاحظ أن عناوين الأفلام يمكن الوصول إليها عبر المحدد التالي:

table tbody tr td.titleColumn a

هذا المحدد يعني أننا نستهدف عناصر الروابط a الموجودة داخل خلية جدول تحمل الفئة titleColumn. ويمكنك اختبار ذلك مباشرة في وحدة التحكم Console بالمتصفح باستخدام:

document.querySelectorAll("table tbody tr td.titleColumn a")[0].innerText

اختبار محدد CSS داخل Console لاستخراج عنوان فيلم من صفحة IMDb

بمجرد الوصول إلى المحدد الصحيح، تصبح كتابة الشيفرة البرمجية أكثر دقة وأقل عرضة للأخطاء.

استخدام BeautifulSoup لاستخراج المحتوى الثابت

المحتوى الثابت هو المحتوى الموجود فعلياً داخل استجابة الصفحة الأصلية، أي أنك تستطيع رؤيته في مصدر الصفحة عبر View Page Source. هذا النوع أسهل في الاستخراج لأنه لا يحتاج إلى تنفيذ JavaScript.

في المثال التالي سنجلب أول عشرة عناوين من القائمة باستخدام requests وBeautifulSoup:

import requests
from bs4 import BeautifulSoup

page = requests.get('https://www.imdb.com/chart/top/')
# Getting page HTML through request
soup = BeautifulSoup(page.content, 'html.parser')
# Parsing content using beautifulsoup
links = soup.select("table tbody tr td.titleColumn a")
# Selecting all of the anchors with titles
first10 = links[:10]
# Keep only the first 10 anchors
for anchor in first10:
    print(anchor.text)
    # Display the innerText of each anchor

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

ناتج استخراج أول عشرة عناوين أفلام من IMDb باستخدام BeautifulSoup في بايثون

كيفية التعامل مع المحتوى الديناميكي باستخدام Selenium

ليست كل البيانات موجودة مباشرة في HTML عند أول طلب. كثير من المواقع الحديثة تستخدم JavaScript لتحميل أجزاء من الصفحة بعد فتحها. هنا تظهر أهمية Selenium، لأنه يحاكي تصفح المستخدم الحقيقي داخل المتصفح وينتظر ظهور العناصر بعد اكتمال التحميل.

إرسال الطلب عبر Selenium ثم التحليل باستخدام BeautifulSoup

في هذا المثال سنستخدم Selenium لجلب الصفحة، ثم نمرر محتواها إلى BeautifulSoup للتحليل:

from bs4 import BeautifulSoup
from selenium import webdriver

option = webdriver.ChromeOptions()
# I use the following options as my machine is a window subsystem linux.
# I recommend to use the headless option at least, out of the 3
option.add_argument('--headless')
option.add_argument('--no-sandbox')
option.add_argument('--disable-dev-sh-usage')

# Replace YOUR-PATH-TO-CHROMEDRIVER with your chromedriver location
driver = webdriver.Chrome('YOUR-PATH-TO-CHROMEDRIVER', options=option)
driver.get('https://www.imdb.com/chart/top/')
# Getting page HTML through request
soup = BeautifulSoup(driver.page_source, 'html.parser')
# Parsing content using beautifulsoup. Notice driver.page_source instead of page.content
links = soup.select("table tbody tr td.titleColumn a")
# Selecting all of the anchors with titles
first10 = links[:10]
# Keep only the first 10 anchors
for anchor in first10:
    print(anchor.text)
    # Display the innerText of each anchor

لاحظ أننا استخدمنا driver.page_source بدلاً من page.content، لأن الصفحة هنا تأتي من المتصفح الذي يديره Selenium وليس من طلب مباشر عبر requests.

الدخول إلى صفحة كل فيلم واستخراج بيانات إضافية

بدلاً من الاعتماد على click() والرجوع في كل مرة، من الأفضل استخدام الروابط المستخرجة وفتحها مباشرة عبر driver.get(). هذا الأسلوب أوضح وأفضل من ناحية الأداء.

في المثال التالي سنستخرج عنوان الفيلم وسنة الإصدار والمدة الزمنية:

from bs4 import BeautifulSoup
from selenium import webdriver

option = webdriver.ChromeOptions()
# I use the following options as my machine is a window subsystem linux.
# I recommend to use the headless option at least, out of the 3
option.add_argument('--headless')
option.add_argument('--no-sandbox')
option.add_argument('--disable-dev-sh-usage')

# Replace YOUR-PATH-TO-CHROMEDRIVER with your chromedriver location
driver = webdriver.Chrome('YOUR-PATH-TO-CHROMEDRIVER', options=option)
page = driver.get('https://www.imdb.com/chart/top/')
# Getting page HTML through request
soup = BeautifulSoup(driver.page_source, 'html.parser')
# Parsing content using beautifulsoup
totalScrapedInfo = []
# In this list we will save all the information we scrape
links = soup.select("table tbody tr td.titleColumn a")
# Selecting all of the anchors with titles
first10 = links[:10]
# Keep only the first 10 anchors
for anchor in first10:
    driver.get('https://www.imdb.com/' + anchor['href'])
    # Access the movie’s page
    infolist = driver.find_elements_by_css_selector('.ipc-inline-list')[0]
    # Find the first element with class ‘ipc-inline-list’
    informations = infolist.find_elements_by_css_selector("[role='presentation']")
    # Find all elements with role=’presentation’ from the first element with class ‘ipc-inline-list’
    scrapedInfo = {
        "title": anchor.text,
        "year": informations[0].text,
        "duration": informations[2].text,
    }
    # Save all the scraped information in a dictionary
    totalScrapedInfo.append(scrapedInfo)
    # Append the dictionary to the totalScrapedInformation list

print(totalScrapedInfo)
# Display the list with all the information we scraped

الفكرة هنا بسيطة: نفتح صفحة كل فيلم، نحدد القائمة التي تحتوي على معلوماته الأساسية، ثم نستخرج العناصر المطلوبة ونحفظها داخل قاموس dictionary.

استخراج المحتوى الديناميكي من صفحات الأفلام

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

في المثال التالي سنضيف أسماء القوائم التحريرية إلى النتائج التي جمعناها:

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

option = webdriver.ChromeOptions()
# I use the following options as my machine is a window subsystem linux.
# I recommend to use the headless option at least, out of the 3
option.add_argument('--headless')
option.add_argument('--no-sandbox')
option.add_argument('--disable-dev-sh-usage')

# Replace YOUR-PATH-TO-CHROMEDRIVER with your chromedriver location
driver = webdriver.Chrome('YOUR-PATH-TO-CHROMEDRIVER', options=option)
page = driver.get('https://www.imdb.com/chart/top/')
# Getting page HTML through request
soup = BeautifulSoup(driver.page_source, 'html.parser')
# Parsing content using beautifulsoup
totalScrapedInfo = []
# In this list we will save all the information we scrape
links = soup.select("table tbody tr td.titleColumn a")
# Selecting all of the anchors with titles
first10 = links[:10]
# Keep only the first 10 anchors
for anchor in first10:
    driver.get('https://www.imdb.com/' + anchor['href'])
    # Access the movie’s page
    infolist = driver.find_elements_by_css_selector('.ipc-inline-list')[0]
    # Find the first element with class ‘ipc-inline-list’
    informations = infolist.find_elements_by_css_selector("[role='presentation']")
    # Find all elements with role=’presentation’ from the first element with class ‘ipc-inline-list’
    scrapedInfo = {
        "title": anchor.text,
        "year": informations[0].text,
        "duration": informations[2].text,
    }
    # Save all the scraped information in a dictionary
    WebDriverWait(driver, 5).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "[data-testid='firstListCardGroup-editorial']")))
    # We are waiting for 5 seconds for our element with the attribute data-testid set as `firstListCardGroup-editorial`
    listElements = driver.find_elements_by_css_selector("[data-testid='firstListCardGroup-editorial'] .listName")
    # Extracting the editorial lists elements
    listNames = []
    # Creating an empty list and then appending only the elements texts
    for el in listElements:
        listNames.append(el.text)

    scrapedInfo['editorial-list'] = listNames
    # Adding the editorial list names to our scrapedInfo dictionary
    totalScrapedInfo.append(scrapedInfo)
    # Append the dictionary to the totalScrapedInformation list

print(totalScrapedInfo)
# Display the list with all the information we scraped

نتيجة استخراج البيانات الديناميكية من صفحات أفلام IMDb باستخدام Selenium وWebDriverWait

الميزة الحقيقية في WebDriverWait أنه يجعلك تتعامل مع الصفحة كما هي فعلياً بعد التحميل، وليس كما بدت في الاستجابة الأولية فقط.

حفظ البيانات المستخرجة في ملفات JSON وCSV

بعد الانتهاء من جمع البيانات، تأتي مرحلة الحفظ والمعالجة اللاحقة. وأكثر الصيغ شيوعاً في هذا السياق هي JSON وCSV.

import csv
import json

...

file = open('movies.json', mode='w', encoding='utf-8')
file.write(json.dumps(totalScrapedInfo))

writer = csv.writer(open("movies.csv", 'w'))
for movie in totalScrapedInfo:
    writer.writerow(movie.values())

تنسيق JSON مناسب للاستهلاك البرمجي والتكامل مع التطبيقات، بينما يُعد CSV عملياً عند فتح البيانات في برامج الجداول مثل Excel أو أدوات التحليل.

نصائح عملية لتحسين أداء أدوات استخراج البيانات

1. امنح الخادم مهلة بين الطلبات

إرسال عدد كبير من الطلبات خلال فترة قصيرة قد يؤدي إلى ظهور Captcha أو حظر عنوان IP. لذلك من الأفضل إضافة فواصل زمنية بين الطلبات حتى يبدو السلوك طبيعياً.

import time
import requests

page = requests.get('https://www.imdb.com/chart/top/')
# Getting page HTML through request
time.sleep(30)
# Wait 30 seconds
page = requests.get('https://www.imdb.com/')
# Getting page HTML through request

2. استخدم معالجة الأخطاء بذكاء

المواقع تتغير باستمرار، وقد يتبدل هيكل الصفحة أو يتأخر تحميل عنصر ما. لذلك من المفيد استخدام try وexcept لحماية الشيفرة من التوقف المفاجئ.

try:
    WebDriverWait(driver, 5).until(EC.presence_of_element_located((By.CSS_SELECTOR, "your selector")))
    break
except TimeoutException:
    # If the loading took too long, print message and try again
    print("Loading took too much time!")

3. التقط لقطات شاشة أثناء التطوير

إذا كنت تحاول فهم سبب فشل تحميل محتوى ديناميكي، فقد تساعدك لقطات الشاشة على رؤية الحالة الفعلية للصفحة داخل المتصفح الذي يديره Selenium.

driver.save_screenshot('screenshot-file-name.png')

4. ارجع دائماً إلى التوثيق الرسمي

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

لماذا يُعد الجمع بين BeautifulSoup وSelenium خياراً ذكياً؟

الاعتماد على أداة واحدة فقط قد لا يكون كافياً في جميع الحالات. فـBeautifulSoup ممتازة في تحليل الصفحات الثابتة بسرعة وباستهلاك أقل للموارد، بينما يتفوق Selenium عند الحاجة إلى محاكاة التصفح الحقيقي وانتظار تحميل البيانات الديناميكية.

لذلك فإن الدمج بينهما يمنحك مرونة أكبر:

  • استخدام requests وBeautifulSoup عندما تكون الصفحة ثابتة.
  • الانتقال إلى Selenium عندما يكون المحتوى معتمداً على JavaScript.
  • تحليل page_source بواسطة BeautifulSoup بعد تحميل الصفحة عبر Selenium عند الحاجة.

هذا الأسلوب يحقق توازناً جيداً بين السرعة والقدرة على التعامل مع الصفحات المعقدة.

أفضل الممارسات لبناء أداة Scraper قابلة للتوسع

  1. ابدأ دائماً بفحص الصفحة وفهم بنية العناصر قبل كتابة الشيفرة.
  2. استخدم محددات CSS واضحة ومستقرة قدر الإمكان.
  3. افصل بين منطق الجلب، والتحليل، والتنظيف، والحفظ في ملفات أو دوال مستقلة.
  4. أضف سجلات logs لتسهيل تتبع الأخطاء.
  5. اختبر الأداة على عدد قليل من الصفحات أولاً قبل توسيع النطاق.
  6. احترم حدود المواقع وسياسات الاستخدام لتجنب الحظر أو المخالفات القانونية.

الخلاصة التقنية

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

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *