تحليل الروابط في بايثون باستخدام urlparse
40 دقيقة تقريبًا
- Variable
- String
- Integer
- Float
- Boolean
- List
- Dictionary
- type
- key
- index
- append
- len
- if
- else
- elif
- Comparison Operators
- and
- or
- not
- for
- while
- break
- continue
- Function
- def
- Parameter
- Argument
- return
- open
- with
- read
- write
- strip
- set
- startswith
- endswith
- import
- urlparse
- scheme
- hostname
- path
- query
- fragment
- lower
- rstrip
- replace
- split
يحسب العدّاد في الأعلى تمارين الدرس، وعددها 3، أما القراءة وتشغيل الأمثلة فلا تُحتسب.
في الدرس السابق خلينا بايثون تقرأ الروابط من ملف بدل ما نكتبها بإيدنا داخل الكود.
يعني بقينا نقدر ناخد ملف فيه آلاف الروابط ونعمل:
https://example.com/ https://example.com/blog/python-seo/ https://example.com/product/laptop/
لكن لحد هنا بايثون بتتعامل مع الـ URL كنص فقط. يعني بالنسبة لها هو مجرد String.
إحنا بقى كمتخصصين SEO شايفين حاجات أكتر من كده.
شايفين https، وشايفين example.com، وشايفين /blog/python-seo/.
ولو الرابط كان https://example.com/shop/?category=shoes&page=2 هنلاحظ كمان ?category=shoes&page=2.
وده مهم جدًا، لأنك في الشغل الحقيقي ممكن تحتاج تعرف:
- هل الرابط
HTTPولاHTTPS؟ - هل الرابط داخلي ولا خارجي؟
- الرابط تابع لأي Domain؟
- إيه الـ Path بتاع الصفحة؟
- هل الرابط فيه Parameters؟
- هل عندي آلاف الروابط المختلفة بسبب Parameters؟
- هل الرابط Blog ولا Product ولا Category؟
فالنهاردة مش هدفنا نتعلم URLs كنظرية، هدفنا نخلي بايثون تبدأ تفهم مكونات الرابط بدل ما تشوفه كنص طويل فقط.
الأول نفهم الـ URL نفسه
خد الرابط https://example.com/blog/python-seo/. نقدر نقسمه بشكل بسيط إلى ثلاثة أجزاء:
الجزء https بيحدد الـ Protocol أو الـ Scheme. والجزء example.com هو الـ Domain. والجزء /blog/python-seo/ هو الـ Path.
خد مثال أكبر:
عندنا هنا:
- Scheme:
https - Domain:
example.com - Path:
/shop/ - Query:
category=shoes&page=2
الـ Query هو الجزء اللي بيجي بعد ?. وده هنقابله كتير جدًا في الـ SEO. مثلًا ?page=2 أو ?sort=price أو ?filter=color.
وممكن في بعض المواقع تلاقي آلاف URLs بتتولد بسبب الـ Parameters دي، وده بعد كده ممكن يدخلنا في مشاكل زي Duplicate URLs أو Crawl Waste.
لكن الأول محتاجين نخلي بايثون تعرف تفصل الأجزاء دي.
هل نقدر نتعامل مع الرابط كـ String؟
آه. ودي أول حاجة لازم نفهمها.
https://example.com/blog/python-seo/
بما إن url عبارة عن String، نقدر نستخدم عليه حاجات اتعلمناها قبل كده. مثلًا:
Blog URL
وده بالفعل استخدمناه في الدرس السابق. لكن نقدر نعمل أكتر من كده.
مثلًا، عايزين نعرف هل الرابط بيستخدم HTTPS. ممكن نكتب:
HTTPS URL
الكود اشتغل. لكن فيه مشكلة صغيرة في طريقة التفكير.
إحنا مش عايزين نعرف هل https:// موجودة في أي مكان داخل النص. إحنا عايزين نعرف:
وهنا عندنا String Method مناسبة أكتر.
استخدام startswith()
الدالة startswith() بتسألك: هل النص يبدأ بالقيمة دي؟ نجرب:
True
ليه؟ لأن الرابط فعلًا يبدأ بـ https://. نجرب رابط تاني:
False
لأن الرابط بيبدأ بـ http:// مش https://. نقدر نستخدمها مع if:
Not HTTPS
ودي أول حاجة مفيدة جدًا نقدر نعملها على مجموعة URLs.
نفحص مجموعة روابط
https://example.com/ HTTPS http://example.com/old-page/ Not HTTPS https://example.com/blog/seo/ HTTPS
كده إحنا استخدمنا حاجة بسيطة جدًا، لكن بدأنا فعلًا نعمل فحص على URLs.
طيب لو عايزين نعرف نهاية الرابط؟
عندنا Method تانية اسمها endswith(). زي ما startswith() بتفحص بداية النص، فـ endswith() بتفحص نهايته. مثلًا:
True
False
ممكن تستخدم الفكرة دي مثلًا لو عندك مجموعة URLs وعايز تعرف الملفات اللي بتنتهي بـ .pdf أو .xml أو امتداد معين. مثال:
Normal URL: https://example.com/ XML: https://example.com/sitemap.xml PDF: https://example.com/report.pdf
لكن لسه عندنا مشكلة أكبر. لو عايزين نجيب الـ Domain من الرابط، هنجيبه إزاي؟
هل نقطع الرابط بإيدنا؟
ممكن تفكر تستخدم حاجات زي split() أو Slicing. مثلًا الرابط https://example.com/blog/python/ ممكن تحاول تقسمه.
لكن URLs مش كلها بنفس الشكل. ممكن يكون عندك:
فبدل ما نحاول نفك URL يدويًا، بايثون عندها أداة جاهزة معمولة أصلًا علشان تفهم URLs. اسمها urllib.parse.
وقبل ما نستخدمها، محتاجين نفهم حاجة صغيرة اسمها import.
يعني إيه import؟
بايثون فيها أدوات أساسية بنستخدمها مباشرة، زي print() وlen(). لكن فيها كمان أدوات كتير مش بتكون محملة داخل البرنامج من البداية.
فلو محتاج أداة معينة، أقدر أقول لبايثون: أنا محتاج الأداة دي في البرنامج. وده بنعمله باستخدام import.
إحنا محتاجين أداة اسمها urlparse وموجودة داخل urllib.parse. فنكتب:
مش مطلوب منك دلوقتي تحفظ مكتبات بايثون كلها، ولا حتى تدخل في تفاصيل الـ Modules والـ Packages. المهم حاليًا إنك تفهم إن السطر ده بيخلينا نقدر نستخدم urlparse() داخل البرنامج.
أول تجربة مع urlparse()
ParseResult(scheme='https', netloc='example.com', path='/blog/python-seo/', params='', query='', fragment='')
الناتج شكله كبير شوية، لكن هو في الحقيقة عمل حاجة مفيدة جدًا.
أخد https://example.com/blog/python-seo/ وفصله لأجزاء. لاحظ scheme='https'، وعندنا netloc='example.com'، وعندنا path='/blog/python-seo/'. يعني بايثون بدأت تفهم URL فعلًا.
إزاي نجيب جزء واحد فقط؟
إحنا حفظنا النتيجة داخل parsed_url. دلوقتي نقدر نطلب الجزء اللي محتاجينه. مثلًا:
https
ولو كتبنا:
example.com
ولو كتبنا:
/blog/python-seo/
هنا ظهر شكل جديد: parsed_url.scheme أو parsed_url.path.
الفكرة ببساطة إن urlparse() رجعت لنا نتيجة فيها أكتر من معلومة. وإحنا باستخدام النقطة . بنطلب معلومة معينة منها. يعني parsed_url.scheme هات الـ Scheme، وparsed_url.path هات الـ Path.
نجرب URL أكبر
Scheme: https Domain: example.com Path: /shop/ Query: category=shoes&page=2
كده الموضوع بدأ يبقى أوضح.
الرابط اللي كان https://example.com/shop/?category=shoes&page=2 بايثون بقت شايفاه كالتالي:
- Scheme =
https - Domain =
example.com - Path =
/shop/ - Query =
category=shoes&page=2
وده هيفتح لنا استخدامات مهمة جدًا بعد كده.
استخدام hostname
في أغلب شغلنا مش محتاجين netloc نفسها، إحنا محتاجين اسم الـ Host أو الـ Domain. نقدر نستخدم parsed_url.hostname. مثال:
www.example.com
هنستخدم hostname كتير، خصوصًا لما نبدأ نفرق بين الروابط الداخلية والخارجية.
مشكلة SEO حقيقية: الرابط داخلي ولا خارجي؟
افترض إن موقعنا هو example.com، وعندنا الروابط دي:
عايزين بايثون تقولنا كل رابط Internal ولا External. الأول لازم نجيب الـ Hostname.
example.com
إذن نقدر نقارن if parsed_url.hostname == "example.com":. لو الاتنين متساويين، الرابط تابع للموقع. نجرب:
https://example.com/blog/ Internal https://example.com/services/ Internal https://google.com/ External https://python.org/ External
وده أول تحليل URL حقيقي نقدر نستفيد منه في SEO.
لكن عندنا مشكلة مع www
ممكن الموقع يشتغل بالشكل example.com، وفي بعض البيانات تلاقي www.example.com.
False
وطبيعي، لأن النصين مش متساويين. لكن بالنسبة لنا ممكن الاتنين يكونوا تابعين لنفس الموقع.
ففي المثال البسيط بتاعنا نقدر نقبل الحالتين. مثلًا:
https://example.com/blog/ Internal https://www.example.com/services/ Internal https://google.com/ External
إحنا استخدمنا هنا or اللي اتعلمناه قبل كده.
في مراحل متقدمة هنقدر نعمل Normalization أفضل للـ URLs، لكن دلوقتي الطريقة دي واضحة ومناسبة للي بنتعلمه.
نكشف الروابط اللي فيها Parameters
دي من الحاجات المهمة جدًا في SEO.
عايزين نعرف أي روابط فيها Query Parameters. دلوقتي إحنا عارفين إن parsed_url.query بتجيب الجزء اللي بعد ?.
نجرب على رابط مفيهوش Query:
الناتج هيكون فاضي. لكن:
page=2
إذن نقدر نستخدم if.
No Parameters: https://example.com/shop/ Has Parameters: https://example.com/shop/?page=2 Has Parameters: https://example.com/shop/?sort=price No Parameters: https://example.com/blog/
ممكن تكون وقفت عند if parsed_url.query: وقلت: فين المقارنة؟ إحنا معملناش if parsed_url.query == شيء، وده لأن بايثون تقدر تستخدم النص نفسه كشرط.
لو parsed_url.query فيها نص، الشرط يعتبر True. لو فاضية ""، يعتبر False. خلينا نبسطها بعيد عن URLs.
There is a name
Empty
فنفس الفكرة هنا. لو الـ Query موجودة، الشرط يشتغل.
نطلع الروابط اللي فيها Parameters في List
بدل ما نطبعها فقط:
['https://example.com/shop/?page=2', 'https://example.com/shop/?sort=price']
بدأنا هنا نعمل حاجة ممكن تتحول بعد كده إلى Audit فعلي.
تخيل إن الملف مش فيه 4 URLs. تخيل فيه 50,000 URLs، وتقدر تطلع منه في ثواني كل الروابط اللي فيها Parameters علشان تبدأ تراجعها.
نعرف نوع الصفحة من الـ Path
خد الرابط https://example.com/blog/python-seo/. إحنا مش محتاجين نبحث داخل الرابط كله عن /blog/. دلوقتي إحنا نقدر نجيب الـ Path نفسه:
Blog URL
ليه ده أفضل من البحث في URL كله؟ لأن إحنا حددنا بالضبط الجزء اللي عايزين نفحصه. ممكن كلمة blog تظهر مثلًا داخل Query Parameter، لكن إحنا مهتمين بمكان الصفحة داخل الـ URL.
نرجع لمشكلة www: توحيد شكل الرابط
فاكر لما قلنا إننا هنقدر نعمل Normalization أفضل للـ URLs؟ خلينا نبدأ فيها دلوقتي بأبسط شكل، لأنها هتفرق معاك جدًا في أي تحليل.
بص على الرابطين دول:
False
بالنسبة لبايثون دول رابطين مختلفين. واحد فيه Blog بحرف كبير، والتاني مفيهوش / في الآخر.
ولو عندك ملف فيه روابط جاية من أكتر من مصدر، هتلاقي الحالات دي كتير. ولو عدّيت الصفحات من غير ما توحّد شكلها، العدد هيطلع أكبر من الحقيقي.
الحل إننا نوحّد شكل الرابط قبل ما نقارن. وده اللي اسمه Normalization.
أول حاجة: الحروف الكبيرة والصغيرة
عندنا lower()، ودي بتحول النص كله لحروف صغيرة:
https://example.com/blog/python-seo/
/Blog/ و/blog/ ممكن يكونوا صفحتين مختلفتين فعلًا. إحنا هنا بنوحّد الشكل علشان نقارن ونكتشف، مش علشان نحكم إنهم نفس الصفحة. ولو لقيت الشكلين في الـ Crawl، ده في حد ذاته حاجة تستاهل مراجعة.تاني حاجة: الـ / اللي في الآخر
فاكر strip() من درس الملفات؟ كانت بتشيل المسافات وعلامة نزول السطر من أول النص وآخره. عندنا rstrip()، ودي نفس الفكرة بس من الآخر بس، ونقدر نقولها تشيل إيه بالظبط:
https://example.com/blog
يعني: شيل أي / موجودة في آخر النص.
تالت حاجة: www
عندنا replace()، ودي بتدور على نص وتبدله بنص تاني:
https://example.com/blog/
إحنا قلنا لبايثون: لو لقيت ://www. حط مكانها ://. وكتبناها بالشكل ده مش www. لوحدها، علشان منمسحش www لو ظهرت في أي حتة تانية في الرابط.
نجمعهم في Function
بما إننا هنعمل التلات خطوات دول على كل رابط، ده مكان مناسب جدًا لـ Function:
https://example.com/blog https://example.com/blog True
الرابطين اللي كانوا مختلفين في الأول، بعد التوحيد بقوا شكل واحد.
من غير ما تشغّل، توقّع الناتج. وافتكر الـ Set من الدرس الثاني:
إيه اللي هيطبعه السطرين الأخيرين؟
عندنا 3 روابط مكتوبين بشكل مختلف، لكن بعد التوحيد كلهم بقوا رابط واحد. ولو كنت عدّيتهم من غير Normalization، كنت هتفتكر إن عندك 3 صفحات.
نقسم الرابط لأجزاء: split()
قلنا قبل كده إننا مش هنقطع الرابط بإيدنا علشان نجيب الـ Domain، وده لسه صح، urlparse() هي الأداة الصح هناك. لكن فيه استخدام تاني لـ split() مفيد جدًا: إننا نعرف الصفحة تابعة لأي قسم في الموقع.
split() بتقسم النص عند علامة معينة، وترجع List:
['https:', '', 'example.com', 'blog', 'python-seo', '']
إحنا قلنا لبايثون: قسّم الرابط عند كل /.
ممكن يستغربك النص الفاضي '' اللي ظهر. ده لأن بين الـ / الاتنين اللي في https:// مفيش أي حاجة. ونفس الكلام في الآخر، بعد آخر /.
- 1
parts[0]هو https: - 2
parts[1]نص فاضي - 3
parts[2]هو الـ Domain: example.com - 4
parts[3]هو أول فولدر: blog - 5
parts[4]هو python-seo
يعني لو عايز القسم اللي الصفحة تابعة له، هنجيب parts[3]:
blog
دلوقتي نطبقها على مجموعة روابط:
blog https://example.com/blog/python-seo/ product https://example.com/product/laptop/ blog https://example.com/blog/technical-seo/ category https://example.com/category/phones/
كده في ثواني عرفنا كل صفحة تابعة لأي قسم. ولما نوصل لـ Pandas هنستخدم نفس الفكرة علشان نعرف كل قسم في الموقع بيجيب كام Click.
نربط الدرس السابع بالدرس السادس
دلوقتي بدل List مكتوبة بإيدنا، هنرجع لـ urls.txt. افترض إن الملف يحتوي على:
عايزين نقرأ الملف ونطلع منه: الروابط الداخلية، الروابط الخارجية، روابط HTTP، والروابط اللي فيها Parameters.
هنا هنبدأ فعليًا نجمع الدروس مع بعض. نكتب:
Internal URLs: https://example.com/ https://example.com/blog/python-seo/ http://example.com/old-page/ https://example.com/shop/?page=2 https://example.com/product/laptop/ https://example.com/shop/?sort=price External URLs: https://google.com/ HTTP URLs: http://example.com/old-page/ URLs with Parameters: https://example.com/shop/?page=2 https://example.com/shop/?sort=price
خلينا نفهم نقطة مهمة هنا. إحنا استخدمنا أكثر من if.
وبعدها if parsed_url.scheme == "http": وبعدها if parsed_url.query:
ليه معملناش elif؟ لأن URL واحد ممكن يكون في أكتر من مجموعة.
مثلًا http://example.com/shop/?page=2 ممكن يكون Internal وفي نفس الوقت HTTP وفي نفس الوقت Has Parameters.
لو استخدمنا elif، أول شرط يتحقق كان ممكن يمنع الشروط اللي بعده من التنفيذ. لكن إحنا عايزين كل اختبار يحصل بشكل مستقل. ودي نقطة مهمة جدًا في طريقة التفكير.
نحفظ النتائج في ملفات
بما إننا اتعلمنا ده في الدرس السابق، نقدر نحفظ النتائج. مثلًا روابط الـ Parameters:
وروابط HTTP:
كده بايثون مش بس بتكتشف المشاكل أو الأنواع المختلفة، لكنها كمان بتطلع لك ملفات تقدر تراجعها بعد كده. وده بالضبط الاتجاه اللي ماشيين له.
تطبيق كامل
عندك ملف urls.txt ومحتواه:
هنكتب برنامج يقرأ الملف ويحلل كل URL.
Internal URLs: https://example.com/ https://example.com/blog/seo/ http://example.com/blog/old-seo/ https://example.com/product/laptop/ https://example.com/shop/?page=2 https://example.com/shop/?sort=price External URLs: https://google.com/search/ https://python.org/ Blog URLs: https://example.com/blog/seo/ http://example.com/blog/old-seo/ Product URLs: https://example.com/product/laptop/ HTTP URLs: http://example.com/blog/old-seo/ URLs with Parameters: https://example.com/shop/?page=2 https://example.com/shop/?sort=price
لو بصيت على البرنامج ده وقارنته بأول درس، هتلاحظ إننا بدأنا نعمل حاجات أقرب فعلًا لشغل SEO.
بايثون دلوقتي بقت قادرة:
- 1تقرأ ملف.
- 2تقرأ كل URL.
- 3تفك الرابط.
- 4تفهم الـ Domain.
- 5تفهم الـ Path.
- 6تعرف الـ Protocol.
- 7تعرف هل فيه Parameters.
- 8تصنف الروابط.
- 9وتقدر تحفظ النتائج.
ولسه إحنا مستخدمناش أي Crawler ولا دخلنا على الموقع نفسه. إحنا فقط بنتعامل مع قائمة URLs موجودة عندنا بالفعل.
حاجة أخيرة مهمة: Fragment
ممكن تشوف رابط بالشكل https://example.com/guide/#pricing. الجزء #pricing اسمه Fragment.
pricing
إذن الأجزاء الأساسية اللي قابلناها النهاردة: Scheme، Hostname، Path، Query، Fragment.
مش لازم تحفظ الأسماء كأنك داخل امتحان نظري. الأهم إنك لما تشوف https://example.com/shop/?page=2#products تبقى فاهم إن الرابط مش قطعة واحدة. بايثون تقدر تفصله، وإنت تقدر تختار الجزء اللي يخدم المشكلة اللي بتحاول تحلها.
إيه اللي المفروض تكون فاهمه بعد الدرس؟
لو عندك url = "https://example.com/blog/python-seo/" فده String.
تقدر تفحص بدايته url.startswith("https://") وتقدر تفحص نهايته url.endswith(".xml").
ولو محتاج تفهم مكونات الرابط بشكل صحيح، تستخدم from urllib.parse import urlparse ثم parsed_url = urlparse(url).
ومنها تقدر تجيب parsed_url.scheme أو parsed_url.hostname أو parsed_url.path أو parsed_url.query أو parsed_url.fragment.
ولو عايز توحّد شكل الرابط قبل ما تقارن، عندك lower() وrstrip("/") وreplace(). ولو عايز تعرف الصفحة تابعة لأي قسم، عندك url.split("/")[3].
لكن الأهم من الأوامر نفسها إنك تفهم ليه بنعمل ده. إحنا مش بنتعلم urlparse() لمجرد إن فيه Function اسمها urlparse(). إحنا بنتعلمها لأن URL بالنسبة لمتخصص SEO فيه معلومات، وكل جزء فيه ممكن نحتاج نفحصه بشكل مختلف.
الاختبار التطبيقي
urls.txt جاهز ليك بنفس المحتوى اللي هيتقالك. الناتج مخفي لحد ما تحل.أنشئ ملفًا اسمه urls.txt واكتب فيه:
مطلوب منك كتابة برنامج يقوم بالآتي:
- يقرأ الروابط من الملف.
- يستخدم
strip()لتنظيف كل رابط. - يستخدم
urlparse()لتحليل الرابط. - ينشئ قائمة اسمها
internal_urlsويضع فيها روابطexample.comوwww.example.com. - ينشئ قائمة اسمها
external_urlsويضع فيها باقي الروابط. - ينشئ قائمة اسمها
http_urlsويضع فيها الروابط التي تستخدمhttpبدلhttps. - ينشئ قائمة اسمها
blog_urlsويضع فيها أي URL يحتوي الـ Path الخاص به على/blog/. - ينشئ قائمة اسمها
parameter_urlsويضع فيها URLs التي تحتوي على Query Parameters. - يطبع كل مجموعة بشكل واضح.
حلّل ملف الروابط إلى مجموعات
اقرأ urls.txt، ونظّف كل رابط بـ strip()، وحلّله بـ urlparse()، ثم اعمل القوائم internal_urls وexternal_urls وhttp_urls وblog_urls وparameter_urls زي ما اتشرح فوق، واطبع كل قائمة.
بعد ما تعمل الجزء الأول، أضف خطوة أخيرة.
احفظ النتائج في ملفات
بعد ما تعمل نفس التحليل، احفظ http_urls داخل http_urls.txt، واحفظ parameter_urls داخل parameter_urls.txt، بحيث يكون كل URL في سطر مستقل.
لو قدرت تعمل الاختبار ده بنفسك، فأنت دلوقتي مش بس بتقرأ URLs باستخدام بايثون، أنت بدأت تحلل تركيب الرابط نفسه وتطلع منه معلومات تقدر تستخدمها في SEO.
بقيت تمارين لم تحلّها
0/3التمارين المتبقية: 3 من 3. يُحتسب الدرس مكتملًا حين تحلّها كلها، ويمكنك العودة إليها متى شئت.
أحسنت، وصلت إلى نهاية الدرس.