بايثون9 دقائق قراءة

كيفية ترميز الروابط (URL Encoding) في بايثون (دليل urllib.parse الكامل)

يعتمد ترميز الروابط في بايثون على الدالة urllib.parse.quote() لتطبيق percent-encoding على النصوص، وعلى الدالة urllib.parse.urlencode() لترميز القواميس إلى سلاسل استعلام. يغطي هذا الدليل الدوال quote() و unquote() و urlencode() و parse_qs() مع أمثلة عملية.

ترميز الروابط باستخدام quote()

تُعد الدالة urllib.parse.quote() الأداة الأساسية في بايثون لتطبيق percent-encoding على النصوص. فهي تحوّل المحارف غير الآمنة للاستخدام في الروابط إلى ما يقابلها من صيغ percent-encoded. وبشكل افتراضي، تعتبر الشرطات المائلة الأمامية (/) محارف آمنة، لكن يمكنك تخصيص هذا السلوك.

from urllib.parse import quote

# الترميز الأساسي
print(quote('hello world'))
# Output: hello%20world

# ترميز المحارف الخاصة
print(quote('price=10&qty=2'))
# Output: price%3D10%26qty%3D2

# بشكل افتراضي، لا يتم ترميز /
print(quote('path/to/file'))
# Output: path/to/file

# لترميز الشرطات المائلة أيضًا، اضبط safe=''
print(quote('path/to/file', safe=''))
# Output: path%2Fto%2Ffile

# ترميز محارف اليونيكود
print(quote('cafe'))
# Output: caf%C3%A9

# تحديد محارف آمنة إضافية
print(quote('key=value&foo=bar', safe='=&'))
# Output: key=value&foo=bar

يُعد المعامل safe هو المفتاح للتحكم في ما يتم ترميزه. وبشكل افتراضي تكون قيمته safe='/'. وإذا أردت ترميز كل شيء باستثناء المحارف الأبجدية الرقمية والمحارف _.-~، فاضبط safe=''. وهذا يعادل الدالة encodeURIComponent() في جافاسكريبت.

توجد أيضًا الدالة quote_plus() التي تعمل مثل quote() لكنها ترمّز المسافات كعلامة + بدلًا من %20. وهذه هي الصيغة المستخدمة في بيانات نماذج HTML (application/x-www-form-urlencoded).

from urllib.parse import quote_plus

print(quote_plus('hello world'))
# Output: hello+world

print(quote_plus('key=value&name=John Doe'))
# Output: key%3Dvalue%26name%3DJohn+Doe

فك ترميز الروابط باستخدام unquote()

تعكس الدالة urllib.parse.unquote() عملية percent-encoding، إذ تحوّل تسلسلات %XX إلى محارفها الأصلية. وتوجد أيضًا الدالة unquote_plus() التي تحوّل بالإضافة إلى ذلك علامات + إلى مسافات.

from urllib.parse import unquote, unquote_plus

# فك الترميز الأساسي
print(unquote('hello%20world'))
# Output: hello world

print(unquote('caf%C3%A9'))
# Output: cafe (with accent)

# unquote لا تحوّل + إلى مسافة
print(unquote('hello+world'))
# Output: hello+world

# unquote_plus تحوّل + إلى مسافة
print(unquote_plus('hello+world'))
# Output: hello world

# فك ترميز رابط كامل
url = 'https://example.com/search?q=C%2B%2B%20programming'
print(unquote(url))
# Output: https://example.com/search?q=C++ programming

استخدم دائمًا unquote_plus() عند فك ترميز بيانات النماذج، لأن نماذج HTML ترمّز المسافات كعلامة +. واستخدم unquote() لفك ترميز الروابط العامة حيث تُرمّز المسافات كـ %20.

ترميز سلاسل الاستعلام باستخدام urlencode()

تأخذ الدالة urllib.parse.urlencode() قاموسًا أو قائمة من الصفوف (tuples) وتحوّلها إلى سلسلة استعلام منسّقة بشكل صحيح. وهذه هي الطريقة الأكثر ملاءمة لبناء سلاسل الاستعلام في بايثون.

from urllib.parse import urlencode

# قاموس إلى سلسلة استعلام
params = {
    'q': 'python programming',
    'page': 1,
    'lang': 'en'
}
print(urlencode(params))
# Output: q=python+programming&page=1&lang=en

# قائمة من الصفوف (تحافظ على الترتيب، وتسمح بتكرار المفاتيح)
params = [
    ('tag', 'python'),
    ('tag', 'web'),
    ('sort', 'date')
]
print(urlencode(params))
# Output: tag=python&tag=web&sort=date

# استخدام doseq=True للقيم على شكل قوائم
params = {
    'tag': ['python', 'web', 'api'],
    'sort': 'date'
}
print(urlencode(params, doseq=True))
# Output: tag=python&tag=web&tag=api&sort=date

# استخدام quote_via للتحكم في ترميز المسافات
from urllib.parse import quote
params = {'q': 'hello world'}
print(urlencode(params, quote_via=quote))
# Output: q=hello%20world  (uses %20 instead of +)

بشكل افتراضي، تستخدم urlencode() الدالة quote_plus() داخليًا، مما يعني أن المسافات تتحول إلى +. وإذا كنت بحاجة إلى %20 للمسافات، فمرّر quote_via=quote كما هو موضّح أعلاه.

تحليل سلاسل الاستعلام باستخدام parse_qs()

تحلّل الدالة urllib.parse.parse_qs() سلسلة الاستعلام وتعيدها إلى قاموس. وتكون كل قيمة في القاموس على شكل قائمة، لأن معاملات الاستعلام قد تحمل قيمًا متعددة. وتوجد أيضًا الدالة parse_qsl() التي تُعيد قائمة من الصفوف.

from urllib.parse import parse_qs, parse_qsl

# تحليل سلسلة استعلام إلى قاموس
qs = 'q=python+programming&page=1&lang=en'
result = parse_qs(qs)
print(result)
# Output: {'q': ['python programming'], 'page': ['1'], 'lang': ['en']}

# ملاحظة: القيم دائمًا على شكل قوائم
print(result['q'][0])  # 'python programming'

# التعامل مع قيم متعددة للمفتاح نفسه
qs = 'tag=python&tag=web&tag=api'
result = parse_qs(qs)
print(result)
# Output: {'tag': ['python', 'web', 'api']}

# parse_qsl تُعيد قائمة من الصفوف
result = parse_qsl(qs)
print(result)
# Output: [('tag', 'python'), ('tag', 'web'), ('tag', 'api')]

# الاحتفاظ بالقيم الفارغة (يتم حذفها بشكل افتراضي)
qs = 'name=John&email=&age=30'
print(parse_qs(qs, keep_blank_values=True))
# Output: {'name': ['John'], 'email': [''], 'age': ['30']}

ترميز الروابط الكاملة باستخدام urlparse

عند التعامل مع روابط كاملة، تتيح لك الدالتان urlparse() و urlunparse() في بايثون تفكيك الروابط وإعادة بنائها بأمان. وهذا مفيد بشكل خاص عندما تحتاج إلى تعديل أجزاء محددة من الرابط دون الإخلال ببنيته.

from urllib.parse import urlparse, urlunparse, urlencode, quote

# تحليل رابط إلى مكوّناته
url = 'https://example.com/search?q=hello&page=1#results'
parsed = urlparse(url)
print(parsed.scheme)    # 'https'
print(parsed.netloc)    # 'example.com'
print(parsed.path)      # '/search'
print(parsed.query)     # 'q=hello&page=1'
print(parsed.fragment)  # 'results'

# بناء رابط من مكوّناته
from urllib.parse import ParseResult
new_url = urlunparse(ParseResult(
    scheme='https',
    netloc='api.example.com',
    path='/v2/search',
    params='',
    query=urlencode({'q': 'python & java', 'limit': 10}),
    fragment=''
))
print(new_url)
# Output: https://api.example.com/v2/search?q=python+%26+java&limit=10

# إضافة مقطع مسار يحتوي على محارف خاصة بأمان
base = 'https://example.com/files/'
filename = 'my report (final).pdf'
safe_url = base + quote(filename, safe='')
print(safe_url)
# Output: https://example.com/files/my%20report%20%28final%29.pdf

بالنسبة إلى كود بايثون الحديث، فكّر في استخدام مكتبة requests التي تتولى ترميز الروابط تلقائيًا عندما تمرّر المعاملات على شكل قاموس. كما توفّر مكتبة httpx إمكانات ترميز تلقائي مماثلة.

import requests

# requests تتولى الترميز تلقائيًا
response = requests.get(
    'https://api.example.com/search',
    params={
        'q': 'python & java',
        'page': 1,
        'sort': 'relevance'
    }
)
print(response.url)
# https://api.example.com/search?q=python+%26+java&page=1&sort=relevance

مقالات ذات صلة

جرّب أدواتنا المجانية