Advertisement
  1. Code
  2. Ruby

بناء اول web scraper خاص بك، الجزء 3

Scroll to top
Read Time: 17 min

Arabic (العربية/عربي) translation by Ayman Amar (you can also view the original English article)

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

المواضيع

  • استخراج التدوينة الخاصة بي
  • النقب
  • المستخرج
  • طرق مساعدة
  • كتابة المشاركات

استخراج التدوينة الخاصة بي

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

أنا استثمرت قدرا كبيرا من الوقت على التصميم الجديد بعد أن استعملت تدوينة Middleman لاحتياجاتي. كل ما بقي هو استيراد المحتوى من قاعدة البيانات الخاصة بي المخزنة على تطبيق Sinatra، لذلك أنا بحاجة إلى استخراج المحتوى الموجود ونقله إلى موقعي الثابت الجديد.

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

فيما يلي لقطتين من التدوينة الخاصة بي.

لقطة شاشة للتدوينة القديمة

A screenshot of an old podcastA screenshot of an old podcastA screenshot of an old podcast

لقطة شاشة للتدوينة الجديدة

A screenshot of a new podcastA screenshot of a new podcastA screenshot of a new podcast

دعونا نقوم بما نريد تحقيقه. نريد استخراج البيانات التالية من 139 حلقة موزعة على 21 موقع فهرس صفحات:

  • العنوان
  • الضيف
  • الرؤوس الفرعية مع قائمة الموضوعات
  • رقم مقطوعة SoundCloud لكل حلقة
  • تاريخ
  • الحلقة رقم
  • النص من ملاحظات العرض
  • الروابط من ملاحظات العرض

نحن أعدنا من خلال ترقيم الصفحات والسماح ل Mechanize بانقر فوق كل رابط للحلقة. في صفحة التفاصيل التالية، سوف نجد جميع المعلومات الواردة أعلاه التي نحتاج إليها. باستخدام هذه البيانات المستخرجة، ونحن نريد لملء المسألة الأمامية و "الجسم" من ملفات تخفيض السعر لكل حلقة.

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

def compose_markdown

1
def compose_markdown(options={})
2
<<-HEREDOC
3
--- 

4
title: #{options[:interviewee]}

5
interviewee: #{options[:interviewee]}

6
topic_list: #{options[:title]}

7
tags: #{options[:tags]}

8
soundcloud_id: #{options[:sc_id]}

9
date: #{options[:date]}

10
episode_number: #{options[:episode_number]}

11
---

12


13
#{options[:text]}

14
HEREDOC

15
end

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

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

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

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

الرمز كامل

1
require 'Mechanize'
2
require 'Pry'
3
require 'date'
4
5
# Helper Methods

6
7
# (Extraction Methods)

8
9
def extract_interviewee(detail_page)
10
  interviewee_selector = '.episode_sub_title span'
11
  detail_page.search(interviewee_selector).text.strip
12
end
13
14
def extract_title(detail_page)
15
  title_selector = ".episode_title"
16
  detail_page.search(title_selector).text.gsub(/[?#]/, '')
17
end
18
19
def extract_soundcloud_id(detail_page)
20
  sc = detail_page.iframes_with(href: /soundcloud.com/).to_s
21
  sc.scan(/\d{3,}/).first
22
end
23
24
def extract_shownotes_text(detail_page)
25
  shownote_selector = "#shownote_container > p"
26
  detail_page.search(shownote_selector)
27
end
28
29
def extract_subtitle(detail_page)
30
  subheader_selector = ".episode_sub_title"
31
  detail_page.search(subheader_selector).text
32
end
33
34
def extract_episode_number(episode_subtitle)
35
  number = /[#]\d*/.match(episode_subtitle)
36
  clean_episode_number(number)
37
end
38
39
# (Utility Methods)

40
41
def clean_date(episode_subtitle)
42
  string_date = /[^|]*([,])(.....)/.match(episode_subtitle).to_s
43
  Date.parse(string_date)
44
end
45
46
def build_tags(title, interviewee)
47
  extracted_tags = strip_pipes(title)
48
  "#{interviewee}"+ ", #{extracted_tags}"
49
end
50
51
def strip_pipes(text)
52
  tags = text.tr('|', ',')
53
  tags = tags.gsub(/[@?#&]/, '')
54
  tags.gsub(/[w\/]{2}/, 'with')
55
end
56
57
def clean_episode_number(number)
58
  number.to_s.tr('#', '')
59
end
60
61
def dasherize(text)
62
  text.lstrip.rstrip.tr(' ', '-')
63
end
64
65
def extract_data(detail_page)
66
  interviewee = extract_interviewee(detail_page)
67
  title = extract_title(detail_page)
68
  sc_id = extract_soundcloud_id(detail_page)
69
  text = extract_shownotes_text(detail_page)
70
  episode_subtitle = extract_subtitle(detail_page)
71
  episode_number = extract_episode_number(episode_subtitle)
72
  date = clean_date(episode_subtitle)
73
  tags = build_tags(title, interviewee)
74
75
  options = {
76
    interviewee:    interviewee,
77
    title:          title,
78
    sc_id:          sc_id,
79
    text:           text,
80
    tags:           tags,
81
    date:           date,
82
    episode_number: episode_number
83
  }
84
end
85
86
def compose_markdown(options={})
87
<<-HEREDOC
88
--- 

89
title: #{options[:interviewee]}

90
interviewee: #{options[:interviewee]}

91
topic_list: #{options[:title]}

92
tags: #{options[:tags]}

93
soundcloud_id: #{options[:sc_id]}

94
date: #{options[:date]}

95
episode_number: #{options[:episode_number]}

96
---

97


98
#{options[:text]}

99
HEREDOC

100
end
101
102
def write_page(link)
103
  detail_page = link.click
104
105
  extracted_data = extract_data(detail_page)
106
107
  markdown_text = compose_markdown(extracted_data)
108
  date = extracted_data[:date]
109
  interviewee = extracted_data[:interviewee]
110
  episode_number = extracted_data[:episode_number]
111
112
  File.open("#{date}-#{dasherize(interviewee)}-#{episode_number}.html.erb.md", 'w') { |file| file.write(markdown_text) }
113
end
114
115
def scrape
116
  link_range = 1
117
  agent ||= Mechanize.new
118
119
  until link_range == 21
120
    page = agent.get("https://between-screens.herokuapp.com/?page=#{link_range}")
121
    link_range += 1
122
123
    page.links[2..8].map do |link|
124
      write_page(link)
125
    end
126
  end
127
end
128
129
scrape

لماذا لم نحتج إلى "Nokogiri"؟ Mechanize يوفر لنا مع كل احتياجاتنا للإستخراج. كما ناقشنا في المقالة السابقة، Mechanize يبني فوق Nokogiri ويسمح لنا لاستخراج المحتوى كذلك. ومع ذلك، كان من المهم تغطية هذا الجوهر في المقالة الأولى منذ أن كنا بحاجة إلى بناء فوقها.

النقب

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

إذا قمت بوضع Pry.start (ملزمة) في أي مكان في الرمز الخاص بك، يمكنك فحص التطبيق الخاص بك في هذه النقطة بالضبط. يمكنك النقب في الأشياء في نقاط محددة في التطبيق. هذا مفيد حقا لاتخاذ التطبيق الخاص بك خطوة بخطوة دون التعثر. على سبيل المثال، دعنا نضعها مباشرة بعد وظيفة write_page، ونعرف ما إذا كان الرابط هو ما نتوقعه أم لا.

النقب

1
...
2
3
def scrape
4
  link_range = 1
5
  agent ||= Mechanize.new
6
7
  until link_range == 21
8
    page = agent.get("https://between-screens.herokuapp.com/?page=#{link_range}")
9
    link_range += 1
10
11
    page.links[2..8].map do |link|
12
      write_page(link)
13
      Pry.start(binding)
14
    end
15
  end
16
end
17
18
...

إذا قمت بتشغيل البرمجة، سوف تحصل على شيء من هذا القبيل.

الإخراج

1
»$ ruby noko_scraper.rb
2
3
    321: def scrape
4
    322:     link_range = 1
5
    323:     agent ||= Mechanize.new
6
    324: 
7
    326:   until link_range == 21
8
    327:     page = agent.get("https://between-screens.herokuapp.com/?page=#{link_range}")
9
    328:     link_range += 1
10
    329: 
11
    330:     page.links[2..8].map do |link|
12
    331:       write_page(link)
13
 => 332:     Pry.start(binding)
14
    333:     end
15
    334:   end
16
    335: end
17
18
[1] pry(main)>

بعد ذالك عندما نطلب كائن الرابط، يمكننا التحقق مما إذا كنا على الطريق الصحيح قبل أن ننتقل إلى تفاصيل التنفيذ الأخرى.

طرفية

1
[2] pry(main)> link
2
=> #<Mechanize::Page::Link

3
 "Masters @ Work | Subvisual | Deadlines | Design personality | Design problems | Team | Pushing envelopes | Delightful experiences | Perfecting details | Company values"
4
 "/episodes/139">

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

المستخرج

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

podcast_scraper.rb

1
...
2
3
def write_page(link)
4
  detail_page = link.click
5
  
6
  extracted_data = extract_data(detail_page)
7
8
  markdown_text = compose_markdown(extracted_data)
9
  date = extracted_data[:date]
10
  interviewee = extracted_data[:interviewee]
11
  episode_number = extracted_data[:episode_number]
12
13
  file_name = "#{date}-#{dasherize(interviewee)}-#{episode_number}.html.erb.md" 
14
15
  File.open(file_name, 'w') { |file| file.write(markdown_text) }
16
end
17
18
def scrape
19
  link_range = 1
20
  agent ||= Mechanize.new
21
22
  until link_range == 21
23
    page = agent.get("https://between-screens.herokuapp.com/?page=#{link_range}")
24
    link_range += 1
25
26
    page.links[2..8].map do |link|
27
      write_page(link)
28
    end
29
  end
30
end
31
32
...

ماذا يحدث في طريقة الاستخراج؟ أولا وقبل كل شيء، عقدت كل صفحة فهرس في المدونة القديمة. أنا أستخدم عنوان URL القديم من تطبيق Heroku بما أن الموقع الجديد هو بالفعل على الانترنت في betweenscreens.fm. كان لدي 20 صفحة من الحلقات التي كنت بحاجة إلى عقدها.

حددت العقدة عبر المتغير link_range، الذي قمت بتحديثه مع كل عقدة. كانت عملية ترقيم الصفحات بسيطة مثل استخدام هذا المتغير في عنوان URL لكل صفحة. بسيطة وفعالة.

def scrape

1
page = agent.get("https://between-screens.herokuapp.com/?page=#{link_range}")

ثم، كلما حصلت على صفحة جديدة مع ثماني حلقات أخرى للإستخراج، استخدم page.links لتحديد الروابط التي نريد النقر عليها والمتابعة إلى صفحة التفاصيل لكل حلقة. قررت أن أستعمل مجموعة من الروابط (الروابط [2..8]) لأنها كانت متسقة في كل صفحة. وكانت أيضا أسهل طريقة لاستهداف الروابط التي أحتاج إليها من كل صفحة فهرس. لا حاجة للخطأ حولها مع محددات CSS هنا.

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

def write_page

1
extracted_data = extract_data(detail_page)

def extract_data

1
def extract_data(detail_page)
2
  interviewee = extract_interviewee(detail_page)
3
  title = extract_title(detail_page)
4
  sc_id = extract_soundcloud_id(detail_page)
5
  text = extract_shownotes_text(detail_page)
6
  episode_subtitle = extract_subtitle(detail_page)
7
  episode_number = extract_episode_number(episode_subtitle)
8
  date = clean_date(episode_subtitle)
9
  tags = build_tags(title, interviewee)
10
11
  options = {
12
    interviewee:    interviewee,
13
    title:          title,
14
    sc_id:          sc_id,
15
    text:           text,
16
    tags:           tags,
17
    date:           date,
18
    episode_number: episode_number
19
  }
20
end

كما ترون أعلاه، نأخذdetails_page وتطبيق مجموعة من أساليب الاستخراج على ذلك. نستخرج intervieweetitlesc_idtextepisode_title و episode_number. لقد قمت بإعادة بناء التعليمات البرمجية مجموعة من الأساليب التي تركز على المساعد والمسؤولة عن الاستخراج. دعونا نلقي نظرة سريعة عليها:

أساليب المساعد

أساليب الاستخراج

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

1
def extract_interviewee(detail_page)
2
  interviewee_selector = '.episode_sub_title span'
3
  detail_page.search(interviewee_selector).text.strip
4
end

نحن نبحث في الصفحة على محدد خاص وللحصول على النص دون مساحة بيضاء لا حاجة لها.

1
def extract_title(detail_page)
2
  title_selector = ".episode_title"
3
  detail_page.search(title_selector).text.gsub(/[?#]/, '')
4
end

نأخذ العنوان ونزيله ؟ و # هذه لا تشتغل بشكل جيد مع المسألة الأمامية في المشاركات لحلقاتنا. المزيد من المعلومات حول المسألة الأمامية أدناه.

1
def extract_soundcloud_id(detail_page)
2
  sc = detail_page.iframes_with(href: /soundcloud.com/).to_s
3
  sc.scan(/\d{3,}/).first
4
end

هنا نحن بحاجة إلى العمل أصعب قليلا لاستخراج معرف SoundCloud للمقطوعات المستضافة الخاصة بنا. أولا نحن بحاجة إلى جعل iframes ألية مع href  من soundcloud.com وجعلها سلسلة للمسح ...

1
"[#<Mechanize::Page::Frame\n nil\n \"https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/221003494&amp;auto_play=false&amp;hide_related=false&amp;show_comments=false&amp;show_user=true&amp;show_reposts=false&amp;visual=true\">\n]"

ثم تطابق تعبيرا عاديا للأرقام من معرف القطعة الخاصة بنا soundcloud_id "221003494".

1
def extract_shownotes_text(detail_page)
2
  shownote_selector = "#shownote_container > p"
3
  detail_page.search(shownote_selector)
4
end

استخراج ملاحظات العرض مرة أخرى واضح تماما. نحن بحاجة فقط للبحث عن فقرات ملاحذات العرض في صفحة التفاصيل. لا مفاجآت هنا.

1
def extract_subtitle(detail_page)
2
  subheader_selector = ".episode_sub_title"
3
  detail_page.search(subheader_selector).text
4
end

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

1
def extract_episode_number(episode_subtitle)
2
  number = /[#]\d*/.match(episode_subtitle)
3
  clean_episode_number(number)
4
end

هنا نحن بحاجة لجولة أخرى من التعبير العادي. دعونا نلقي نظرة قبل وبعد تطبيقنا للتعبير العادي.

episode_subtitle

1
" João Ferreira  | 12  Minutes |  Aug 26, 2015 | Episode #139  "

العدد

1
"#139"

خطوة واحدة أخرى حتى يصبح لدينا رقم نظيف.

1
def clean_episode_number(number)
2
  number.to_s.tr('#', '')
3
end

نحن نأخذ هذا الرقم مع تجزئة # ونزيلها. فوالا، لدينا أول رقم حلقة 139 مستخرجة كذلك. أقترح أن علينا أن ننظر إلى الوسائل المساعدة الأخرى قبل أن نجمع كل ذلك معا.

الوسائل المساعدة

بعد كل استخراج الأعمال هذا، علينا القيان ببعض التنظيف. يمكننا البدء بالفعل في إعداد البيانات لتكوين عملية التخفيض. على سبيل المثال، أنا قسمت episode_subtitle قليلا أكثر للحصول على تاريخ نظيف وبناء العلامات مع أسلوب build_tags.

def clean_date

1
def clean_date(episode_subtitle)
2
  string_date = /[^|]*([,])(.....)/.match(episode_subtitle).to_s
3
  Date.parse(string_date)
4
end

نشغل تعبير عادي آخر يبحث عن مواعيد مثل هذا: "26 أغسطس 2015". كما ترون، هذا ليس مفيدا جدا بعد. عن طريق string_date نخرج من العنوان الفرعي، نحتاج إلى إنشاء كائن تاريخ حقيقي. وإلا فإنه سيكون عديم الفائدة لإنشاء منشورات Middleman .

string_date

1
"  Aug 26, 2015"

لذلك نأخذ هذه السلسلة للقيام ب Date.parse. النتيجة تبدو واعدة أكثر.

التاريخ

1
2015-08-26

def build_tags

1
def build_tags(title, interviewee)
2
  extracted_tags = strip_pipes(title)
3
  "#{interviewee}"+ ", #{extracted_tags}"
4
end

هذه تأخد العنوان والمقابلة التي تراكمت لدينا داخل أسلوب extract_data وتزيل كل الأحرف الأنابيب والخردة. نحن نستبدل أحرف الأنابيب بفاصلة، @،؟، #، و & بسلسلة فارغة، وأخيرا نهتم بالاختصارات ل with.

def strip_pipes

1
def strip_pipes(text)
2
  tags = text.tr('|', ',')
3
  tags = tags.gsub(/[@?#&]/, '')
4
  tags.gsub(/[w\/]{2}/, 'with')
5
end

وفي النهاية، ندرج اسم الضيف في قائمة العلامات أيضا، والفصل بين العلامات بفاصلة.

قبل

1
"Masters @ Work | Subvisual | Deadlines | Design personality | Design problems | Team | Pushing envelopes | Delightful experiences | Perfecting details | Company values"

بعد

1
"João Ferreira, Masters  Work , Subvisual , Deadlines , Design personality , Design problems , Team , Pushing envelopes , Delightful experiences , Perfecting details , Company values"

ستؤدي كل واحدة من هذه العلامات إلى رابط لمجموعة من المشاركات لهذا الموضوع. يحدث كل هذا ضمن أسلوب extract_data. دعونا نلقي نظرة أخرى أين نحن:

def extract_data

1
def extract_data(detail_page)
2
  interviewee = extract_interviewee(detail_page)
3
  title = extract_title(detail_page)
4
  sc_id = extract_soundcloud_id(detail_page)
5
  text = extract_shownotes_text(detail_page)
6
  episode_subtitle = extract_subtitle(detail_page)
7
  episode_number = extract_episode_number(episode_subtitle)
8
  date = clean_date(episode_subtitle)
9
  tags = build_tags(title, interviewee)
10
11
  options = {
12
    interviewee:    interviewee,
13
    title:          title,
14
    sc_id:          sc_id,
15
    text:           text,
16
    tags:           tags,
17
    date:           date,
18
    episode_number: episode_number
19
  }
20
end

كل ما تبقى للقيام به هو العودة لتجزئة الخيارات مع البيانات التي استخرجناها. يمكننا تغذية هذه التجزئة في أسلوب compose_markdown، والذي يجعل بياناتنا جاهزة لكونها مكتوبة كملف الذي أحتاجه لموقعي الجديد.

كتابة المشاركات

def compose_markdown

1
def compose_markdown(options={})
2
<<-HEREDOC
3
--- 

4
title: #{options[:interviewee]}

5
interviewee: #{options[:interviewee]}

6
topic_list: #{options[:title]}

7
tags: #{options[:tags]}

8
soundcloud_id: #{options[:sc_id]}

9
date: #{options[:date]}

10
episode_number: #{options[:episode_number]}

11
---

12


13
#{options[:text]}

14
HEREDOC

15
end

لنشر حلقات مدونة على موقع Middleman الخاص بي، اخترت إعادة استخدام نظام التدوين. بدلا من إنشاء مشاركات "نقية" في المدونة، أنشئ ملاحظات عرض لحلقاتي التي تعرض بإستضافة SoundCloud  عبر iframes. على مواقع الفهرس، أعرض فقط iframe بالإضافة إلى العنوان والأدوات.

الصيغة التي أحتاجها لهذا العمل تتكون من شيء يسمى المادة الأمامية. هذا في الأساس مخزن ل مفتاح/قيمة لمواقعي الثابتة. هذا استبدال احتياجات قاعدة البيانات الخاصة بي من موقع Sinatra القديم الخاص بي.

البيانات مثل اسم الشخص الذي تمت مقابلته، والتاريخ،معرف SoundCloud للقصعة، ورقم الحلقة وما إلى ذالك يذهب بين ثلاث شرطات (---) على رأس ملفات الحلقة لدينا. في ما يلي محتوى كل حلقة - مثل الأسئلة والروابط والأدوات الراعية وما إلى ذلك.

المسألة الأمامية

1
---
2
key: value
3
key: value
4
key: value
5
key: value
6
---
7
8
Episode content goes here.

في أسلوبcompose_markdown، يمكنني استخدام HEREDOC لإنشاء هذا الملف مع المسألة الأمامية لكل حلقة نلتف حولها. من تجزئة الخيارات سوف نقوم بتغذية هذا الأسلوب، سوف نستخراج كافة البيانات التي جمعناها في أسلوب المساعد extract_data.

def compose_markdown

1
...
2
3
<<-HEREDOC
4
--- 

5
title: #{options[:interviewee]}

6
interviewee: #{options[:interviewee]}

7
topic_list: #{options[:title]}

8
tags: #{options[:tags]}

9
soundcloud_id: #{options[:sc_id]}

10
date: #{options[:date]}

11
episode_number: #{options[:episode_number]}

12
---

13


14
#{options[:text]}

15
HEREDOC

16
17
...

هذا هو المخطط لحلقة التدوينة جديدة هناك. هذا هو ما جئنا لأجله. ربما تتساءل عن هذا التركيب الخاص: #{options[:interviewee]}. أنا أقتحم كالمعتاد مع السلاسل، ولكن بما أنني بالفعل بداخل <<-HEREDOC، يمكن أن أترك مزدوجة اللإقتباس مفتوحة.

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

في هذه الخطوة النهائية، نحن بحاجة فقط لإعداد المكونات التالية: التاريخ، اسم الضيف، ورقم الحلقة. بالإضافة إلى markdown_text بالتأكيد، الذي حصلنا عليه للتو من compose_markdown.

def write_page

1
...
2
3
markdown_text = compose_markdown(extracted_data)
4
date = extracted_data[:date]
5
interviewee = extracted_data[:interviewee]
6
episode_number = extracted_data[:episode_number]
7
8
file_name = "#{date}-#{dasherize(interviewee)}-#{episode_number}.html.erb.md" 
9
10
...

ثم نحن بحاجة فقط إلى أخد file_name و markdown_text وكتابة الملف.

def write_page

1
...
2
3
File.open(file_name, 'w') { |file| file.write(markdown_text) }
4
5
...

دعونا نكسر هذا أسفله أيضا. لكل مشاركة، أحتاج إلى تنسيق معين: شيء مثل 2016-10-25-Avdi-Grimm-120. أريد أن أكتب الملفات التي تبدأ مع التاريخ وتشمل اسم الضيف ورقم الحلقة.

لتتناسب مع صيغة Middleman المتوقع للحصول على مشاركات جديدة، أنا بحاجة إلى أخذ اسم الضيف ووضعه من خلال أسلوب المساعد الخاص بي لتضييق الاسم بالنسبة لي، من Avdi Grimm إلى Avdi-Grimm. لا شيء سحري، ولكن يستحق نظرة:

def dasherize

1
def dasherize(text)
2
  text.lstrip.rstrip.tr(' ', '-')
3
end

هذا يزيل المسافة البيضاء من النص المستخرج لاسم الضيف ويحل محل المسافة البيضاء بين Avdi وGrimm  مع تقطع. أما بقية اسم الملف فهي متقطعة معا في السلسلة نفسها: "date-interviewee-name-episodenumber".

def write_page

1
...
2
3
"#{date}-#{dasherize(interviewee)}-#{episode_number}.html.erb.md"
4
5
...

وبما أن المحتوى المستخرج يأتي مباشرة من موقع HTML، لا يمكنني ببساطة استخدام .md أو .markdown كملحق لاسم الملف. قررت أن أختار .html.erb.md. للحلقات المستقبلية التي أنشئ دون إستخراج، يمكنني ترك جزء .html.erb وأحتاج فقط ل .md.

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

2014-12-01-Avdi-Grimm-1.html.erb.md

1
--- 
2
title: Avdi Grimm
3
interviewee: Avdi Grimm
4
topic_list: What is Rake | Origins | Jim Weirich | Common use cases | Advantages of Rake
5
tags: Avdi Grimm, What is Rake , Origins , Jim Weirich , Common use cases , Advantages of Rake
6
soundcloud_id: 179619755
7
date: 2014-12-01
8
episode_number: 1
9
---
10
11
Questions:
12
- What is Rake?
13
- What can you tell us about the origins of Rake?
14
- What can you tell us about Jim Weihrich?
15
- What are the most common use cases for Rake?
16
- What are the most notable advantages of Rake?
17
18
Links:
19
In">http://www.youtube.com/watch?v=2ZHJSrF52bc">In memory of the great Jim Weirich
20
Rake">https://github.com/jimweirich/rake">Rake on GitHub
21
Jim">https://github.com/jimweirich">Jim Weirich on GitHub
22
Basic">http://www.youtube.com/watch?v=AFPWDzHWjEY">Basic Rake talk by Jim Weirich
23
Power">http://www.youtube.com/watch?v=KaEqZtulOus">Power Rake talk by Jim Weirich
24
Learn">http://devblog.avdi.org/2014/04/30/learn-advanced-rake-in-7-episodes/">Learn advanced Rake in 7 episodes - from Avdi Grimm ( free )
25
Avdi">http://about.avdi.org/">Avdi Grimm
26
Avdi Grimm’s screencasts: Ruby">http://www.rubytapas.com/">Ruby Tapas
27
Ruby">http://devchat.tv/ruby-rogues/">Ruby Rogues podcast with Avdi Grimm
28
Great ebook: Rake">http://www.amazon.com/Rake-Management-Essentials-Andrey-Koleshko/dp/1783280778">Rake Task Management Essentials fromhttps://twitter.com/ka8725"> Andrey Koleshko

هذا المستخرج سيبدأ في الحلقة الأخيرة، بالتأكيد، والحلقات حتى الأولى. لأغراض العرض التوضيحي، الحلقة 01 جيدة على أي حال. يمكنك أن ترى أعلى المسألة الأمامية مع البيانات التي استخرجناها.

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

افكار اخيرة

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

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

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

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

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.