1 - کار کردن با APIها

در این فصل می‌آموزید که چگونه برنامه 135 ‌ی خودمتّکی بنویسید که بصری‌سازی‌ها را بر اساس داده‌های دریافتیِ خود تولید می‌کند. برنامه‌ی شما از یک رابط برنامه‌نویسی کاربردی (API) تحت وب استفاده می‌کند که به جای صفحه‌های وب، به طور خودکار اطّلاعاتی مشخّص را از یک وب‌سایت تقاضا می‌کند و سپس از این اطّلاعات برای تولید بصری‌سازی استفاده می‌کند. از آن‌جایی که برنامه‌هایی مانند این همیشه داده‌های فعلی را برای بصری کردن استفاده می‌کنند، حتّی در صورت تغییر سریع داده‌ها همیشه به‌روز خواهند ماند.

2 - استفاده از یک API تحت وب

API تحت وب بخشی از یک وب‌سایت است که برای ارتباط ب136 ا برنامه‌ها طرّاحی شده است. برنامه‌ها از URLهای خاصّی برای درخواست اطّلاعات استفاده می‌کنند. این درخواست‌ها فراخوانی API خوانده می‌شود. داده‌ی درخواستی در قالبی دریافت می‌شود که پردازش آن آسان باشد، مانند JSON یا CSV. بسیاری از برنامه‌هایی که بر منابع داده‌های خارجی متّکی هستند، مانند برنامه‌هایی که با سایت‌های شبکه‌های اجتماعی ادغام می‌شوند، به این فراخوانی‌ها وابسته‌اند.

3 - Git و GitHub

ما بصری‌سازی‌های خود را بر اساس اطّلاعات دریافتی از GitHub درست خواهیم کرد. گیت‌هاب سایتی است که به برنامه‌نویسان امکان مشارکت در پروژه‌های برنامه‌نویسی می‌دهد. از API گیت‌هاب برای دریافت اطّلاعاتی درباره‌ی پروژه‌های پایتون موجود در سایت استفاده می‌کنیم و سپس به کمک پلات‌لی یک بصری‌سازی تعاملی از محبوبیّت نسبی این پروژه‌ها تولید می‌کنیم.

گیت‌هاب (https://github.com/) نام خود را از Git گرفته که یک سیستم کنترل نسخه‌ی توزیع‌شد138 137 ه است. گیت به افراد کمک می‌کند که کار خود را در یک پروژه مدیریّت کنند تا تغییراتی که یک نفر ایجاد می‌کند در کار دیگران اختلالی ایجاد نکند. زمانی که ویژگی جدیدی به یک پروژه اضافه می‌کنید، گیت تغییرات شما را در هر فایل دنبال می‌کند. اگر کد شما کار کند، شما تغییرات اعمالی خود را اجرایی می‌کنید و گیت وضعیّت جدید پروژه را ثبت می‌کند. اگر اشتباهی کرده باشید و بخواهید تغییرات خود را برگردانید، به سادگی می‌توانید به یک وضعّیت درست قبلی برگردید. (برای آشنایی بیشتر با کنترل نسخه با گیت پیوست ت را ببینید) پروژه‌های گیت‌هاب در مخزن‌ها نگهداری می‌شوند که دربردارنده‌ی تمام چیزهای مربوط به یک پروژه است: کد آن، اطّلاعات مشارکت‌کنندگان، مشکلات یا گزارش ایرادات و …

زمانی که کاربران گیت‌هاب پروژه‌ای را می‌پسندند می‌توانند به آن ستاره بدهند تا حمایت خود را نشان دهند و پروژه‌هایی را که احتمالاً می‌خواهند مورد استفاده قرار دهند پی بگیرند. در این فصل برنامه‌ای خواهیم نوشت که به طور خودکار اطّلاعاتی درباره‌ی پُرستاره‌ترین پروژه‌های پایتون در گیت‌هاب دریافت کند و سپس بصری‌سازی‌های پرمعنایی درباره‌ی این پروژه‌ها خواهیم ساخت.

4 - درخواست داده با یک فراخوانی API

API گیت‌هاب به شما امکان می‌دهد که گستره‌ی وسیعی از اطّلاعات را از طریق فراخوانی API درخواست کنید. برای این که ببینید فراخوانی API چه شکلی است آدرس زیر را در نوار آدرس مرورگر خود وارد کنید و ENTER را فشار دهید:

https://api.github.com/search/repositories?q=language:python&sort=stars

این فراخوانی تعداد پروژه‌هایی را که در حال حاضر گیت‌هاب میزبان آن‌هاست به همراه اطّلاعاتی درباره‌ی محبوب‌ترین مخزن‌های پایتون برمی‌گرداند. بیایید فراخوانی را بررسی کنیم. اوّلین بخش، https://api.github.com درخواست را به قسمتی از گیت‌هاب هدایت می‌کند که به فراخوانی‌های API پاسخ می‌دهد. بخش بعدی، search/repositories به API می‌گوید که در تمام مخزن‌های گیت‌هاب جست‌و‌جو انجام بده.

علامت سوال بعد از repositories نشان می‌دهد که می‌خواهیم نشان‌وندی را رد کنیم. q مخفّف query (= پرس و جو) است و علامت تساوی (=) به ما امکان می‌دهد که تعیین مورد پرسش را شروع کنیم (q=). با استفاده از language:python می‌گوییم که تنها به دنبال اطّلاعاتی درباره‌ی مخزن‌هایی هستیم که پایتون زبان اصلی آن‌هاست. بخش پایانی، &sort=stars، پروژه‌ها را بر اساس تعداد ستاره‌هایی که دریافت کرده‌اند مرتّب می‌کند.

تکه‌کد زیر چند خطّ اوّل پاسخ را نشان می‌دهد:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "total_count": 3494012,
  "incomplete_results": false,
  "items": [
    {
      "id": 21289110,
      "node_id": "MDEwOlJlcG9zaXRvcnkyMTI4OTExMA==",
      "name": "awesome-python",
      "full_name": "vinta/awesome-python",
      --snip--

چنان‌که از پاسخ درمی‌یابید این URL برای این طرّاحی نشده که انسان‌ها آن را وارد کنند زیرا خروجی آن به شکلی‌ست که برنامه‌ها آن را پردازش می‌کنند. گیت‌هاب در زمان نگارش این متن ۳،۴۹۴،۰۱۲ پروژه‌ی پایتون یافته است (۱). چون مقدار incomplete_results برابر false است، متوجّه می‌شویم که درخواست موفقیّت‌آمیز بوده و ناقص نیست. (۲) اگر گیت‌هاب قادر به پردازش کامل درخواست API نباشد این مقدار را true برمی‌گرداند. پس از این دو items می‌آید که لیستی شامل جزئیات محبوب‌ترین پروژه‌های پایتون روی گیت‌هاب است. (۳)

5 - نصب کردن Requests

بسته‌ی Requests به برنامه‌های پایتون امکان می‌دهد که به سادگی از یک وب‌سایت درخواست اطّلاعات کرده و پاسخ آن‌ها را بررسی کنند. از pip برای نصب آن استفاده می‌کنیم:

1
$ python -m pip install --user requests

این خط به پایتون می‌گوید که ماژول pip را اجرا و بسته‌ی Requests را به نصب پایتون کاربر فعلی اضافه کند. اگر از python3 یا دستور متفاوتی برای اجرای برنامه‌ها و نصب بسته‌ها استفاده می‌کنید، مطمئن شوید که اینجا هم از آن‌ها استفاده می‌کنید.

نکته
اگر این دستور در macOS کار نکرد آن را بدون پرچم --user امتحان کنید.

6 - پردازش پاسخ API

اکنون نوشتن برنامه‌ای را آغاز می‌کنیم که به طور خودکار API را فراخوانی می‌کند و نتایج آن را با تشخیص پرستاره‌ترین پروژه‌های پایتون در گیت‌هاب پردازش می‌کند:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import requests

# Make an API call and store the response.
url = 'https://api.github.com/search/repositories?q=language:python&sort=stars'
headers = {'Accept': 'application/vnd.github.v3+json'}
r = requests.get(url, headers=headers)  # ③
print(f"Status code: {r.status_code}")

# Store API response in a variable.
response_dict = r.json()  # ④

# Process results.
print(response_dict.keys())

ماژول requests را وارد کرده‌ایم. URL مربوط به API را در متغیّر url ذخیره می‌کنیم. گیت‌هاب در حال حاضر از سومین نسخه‌ی API خود استف139 اده می‌کند پس این سَرپیام‌ها را برای فراخوانی آن تعریف می‌کنیم که صریحاً این نسخه‌ی API را درخواست می‌کند. سپس به کمک requests این API را فرامی‌خوانیم.

تابع get() را فراخوانی کرده و URL و سرپیام‌های تعریف‌شده را به آن می‌دهیم و شیء پاسخ را به متغیّر r نسبت می‌دهیم. شیء پاسخ مشخّصه‌ای به نام status_code دارد که وضعیّت درخواست را نشان می‌دهد. (کد وضعیّت ۲۰۰ بیانگر درخواست موفّق است.) مقدار status_code را چاپ می‌کنیم تا مطمئن باشیم که فراخوانی درست پیش رفته است.

این API اطّلاعات را در قالب JSON برمی‌گرداند پس از متد json() برای تبدیل آن‌ها به دیکشنری پایتون استفاده می‌کنیم. دیکشنری حاصل را در response_dict ذخیره می‌کنیم.

در نهایت keysهای response_dict را چاپ کرده و خروجی را می‌بینیم:

1
2
Status code: 200
dict_keys(['total_count', 'incomplete_results', 'items'])

چون کد وضعیّت ۲۰۰ است می‌دانیم که درخواست موفقّیت‌آمیز بوده است. دیکشنری پاسخ تنها سه کلید دارد: total_count، incomplete_results و items. اجازه دهید نگاهی به محتویّات دیکشنری پاسخ بیندازیم.

نکته
فراخوانی‌های ساده مانند این باید مجموعه‌ی کامل نتایج را برگرداند بنابراین صرف نظر کردن از مقدارهای مرتبط با incomplete_results اشکالی ندارد. امّا اگر فراخوانی‌های پیچیده‌تری انجام می‌دهید برنامه‌ی شما باید این مقدار را بررسی کند.

7 - کار کردن با دیکشنری پاسخ

اکنون که اطّلاعات دریافتی از API به صورت دیکشنری ذخیره شده، می‌توانیم با داده‌های داخل آن کار کنیم. ابتدا خروجی‌ای تولید می‌کنیم که اطّلاعات را خلاصه می‌کند. این راه خوبی است که بفهمیم داده‌های دریافتی مطابق انتظار ماست و بررسی داده‌های مورد نظر خود را شروع کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests

# Make an API call and store the response.
--snip--

# Store API response in a variable.
response_dict = r.json()

print(f"Total repositories: {response_dict['total_count']}")  # ①

# Explore information about the repositories.
repo_dicts = response_dict['items']  # ②

print(f"Repositories returned: {len(repo_dicts)}")

# Examine the first repository.
repo_dict = repo_dicts[0]  # ③

print(f"\nKeys: {len(repo_dict)}")  # ④

for key in sorted(repo_dict.keys()):  # ⑤
    print(key)

مقدار مربوط به total_count را چاپ می‌کنیم که بیانگر تعداد کل مخزن‌های پایتون در گیت‌هاب است.

مقدار مرتبط با items لیستی شامل تعدادی دیکشنری است که هر کدام حاوی داده‌های هر مخزن پایتون است. این لیست را در repo_dicts ذخیره می‌کنیم. سپس طول repo_dicts را چاپ می‌کنیم تا بدانیم اطّلاعات چند مخزن را در اختیار داریم.

برای نگاهی دقیق‌تر به داده‌های دریافتی درباره‌ی هر مخزن، اوّلین مورد در repo_dicts را در repo_dict قرار می‌دهیم. سپس تعداد کلیدهای این دیکشنری را چاپ می‌کنیم تا بفهمیم چه‌قدر اطّلاعات در اختیار داریم. در ادامه تمام کلیدهای دیکشنری را چاپ می‌کنیم تا ببینیم چه نوع اطّلاعاتی در پاسخ گنجانده شده است.

این نتایج تصویر واضح‌تری از داده‌های واقعی می‌دهد:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Status code: 200
Total repositories: 3494030
Repositories returned: 30
Keys: 73
archive_url
archived
assignees_url
--snip--
url
watchers
watchers_count

API گیت‌هاب اطّلاعات بسیاری درباره‌ی هر مخزن برمی‌گرداند: ۷۳ کلید در repo_dict وجود دارد. اگر به این کلیدها نگاهی بیندازید درکی کلّی از نوع اطّلاعاتی که می‌توانید درباره‌ی هر پروژه استخراج کنید کسب می‌کنید. (تنها راه دانستن این که چه اطّلاعاتی از طریق API در دسترس است خواندن مستندات آن یا بررسی اطّلاعات از طریق کد است، همان راهی که ما اینجا پی گرفته‌ایم.)

بیایید مقدار برخی از کلیدهای repo_dict را بیرون بکشیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
--snip--

# Explore information about the repositories.
repo_dicts = response_dict['items']

print(f"Repositories returned: {len(repo_dicts)}")

# Examine the first repository.
repo_dict = repo_dicts[0]

print("\nSelected information about first repository:")
print(f"Name: {repo_dict['name']}")
print(f"Owner: {repo_dict['owner']['login']}")
print(f"Stars: {repo_dict['stargazers_count']}")
print(f"Repository: {repo_dict['html_url']}")
print(f"Created: {repo_dict['created_at']}")
print(f"Updated: {repo_dict['updated_at']}")
print(f"Description: {repo_dict['description']}")

مقدار تعدادی از کلیدهای اوّلین مخزن موجود در دیکشنری را چاپ می‌کنیم. نام پروژه را چاپ می‌کنیم. مالک پروژه را یک دیکشنری کامل نمایندگی می‌کند پس از کلید owner برای دستیابی به این دیکشنری استفاده می‌کنیم و سپس از کلید login برای گرفتن نام کاربری مالک بهره می‌گیریم. تعداد ستاره‌های دریافت‌شده و URL مخزن گیت‌هاب پروژه را چاپ می‌کنیم. سپس نشان می‌دهیم که پروژه کی ایجاد شده و آخرین کی به‌روز شده. در نهایت شرح مخزن را چاپ می‌کنیم؛ خروجی چیزی شبیه به این خواهد بود:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Status code: 200
Total repositories: 3494032
Repositories returned: 30

Selected information about first repository:
Name: awesome-python
Owner: vinta
Stars: 61549
Repository: https://github.com/vinta/awesome-python
Created: 2014-06-27T21:00:06Z
Updated: 2019-02-17T04:30:00Z
Description: A curated list of awesome Python frameworks, libraries, software
and resources

چنان‌که می‌بینیم پرستاره‌ترین پروژه‌ی پایتون در گیت‌هاب در زمان نوشتن این کتاب، awesome-python است، مالک آن vinta است و بیش از ۶۰ هزار ستاره از کاربران گیت‌هاب دریافت کرده است. می‌توانیم URL مخزن پروژه، تاریخ ایجاد آن در ژوئن ۲۰۱۴ و به‌روزرسانی شدن آن را به تازگی ببینیم. علاوه بر این شرح مخزن به ما می‌گوید که این پروژه شامل فهرستی از محبوب‌ترین منابع پایتون است.

8 - خلاصه کردن مخزن‌های برتر

اگر بخواهیم این داده‌ها را بصری کنیم به داده‌های بیش از یک مخزن نیاز داریم. بیایید حلقه‌ای بنویسیم تا اطّلاعات منتخب درباره‌ی هر مخزن را چاپ کند تا بتوانیم آن‌ها را در بصری‌سازی خود بیاوریم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
--snip--

# Explore information about the repositories.
repo_dicts = response_dict['items']

print(f"Repositories returned: {len(repo_dicts)}")

print("\nSelected information about each repository:")
for repo_dict in repo_dicts:  # ②
    print(f"\nName: {repo_dict['name']}")
    print(f"Owner: {repo_dict['owner']['login']}")
    print(f"Stars: {repo_dict['stargazers_count']}")
    print(f"Repository: {repo_dict['html_url']}")
    print(f"Description: {repo_dict['description']}")

پیام معرّفی را چاپ می‌کنیم. داخل دیکشنری‌های repo_dicts حلقه می‌زنیم. داخل حلقه نام هر پروژه، مالک آن، تعداد ستاره‌ها، URL و شرح پروژه را چاپ می‌کنیم، مانند زیر:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Status code: 200
Total repositories: 3494040
Repositories returned: 30

Selected information about each repository:

Name: awesome-python
Owner: vinta
Stars: 61549
Repository: https://github.com/vinta/awesome-python
Description: A curated list of awesome Python frameworks, libraries, software
and resources

Name: system-design-primer
Owner: donnemartin
Stars: 57256
Repository: https://github.com/donnemartin/system-design-primer
Description: Learn how to design large-scale systems. Prep for the system
design interview. Includes Anki flashcards.

--snip--

Name: python-patterns
Owner: faif
Stars: 19058
Repository: https://github.com/faif/python-patterns
Description: A collection of design patterns/idioms in Python

9 - پایش محدودیت نرخ API

نرخ استفاده از بیشتر APIها محدود است، به این معنی که دفعات فراخوانی آن در مدت زمان مشخّص محدود شده است. برای دیدن این که به محدودیّت‌های گیت‌هاب نزدیک شده‌اید یا نه آدرس https://api.github.com/rate_limit را در مرورگر خود وارد کنید. باید پاسخی شبیه به این ببینید:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "resources": {
    "core": {
      "limit": 60,
      "remaining": 58,
      "reset": 1550385312
    },
    "search": {
      "limit": 10,
      "remaining": 8,
      "reset": 1550381772
    },
    --snip--

اطّلاعاتی که در اینجا مورد توجّه ماست محدودیّت نرخ جستجوی API است. در می‌بینیم که حد استفاده ۱۰ درخواست بر دقیقه است و ۸ درخواست این دقیقه را انجام داده‌ایم. مقدار بازنشانی بیانگر زمان به شکل Unix یا epoch time است (تعداد ثانیه‌ها از نیمه‌شب اول ژانویه‌ی ۱۹۷۰) و نشان می‌دهد سهمیّه‌ی ما کی تجدید خواهد شد. اگر به پایان سهمیّه‌ی خود برسید پاسخی کوتاه دریافت می‌کنید که بیان می‌کند شما از محدوده‌ی API گذر کرده‌اید. اگر چنین شود باید تا تجدید سهمیّه‌ی خود صبر کنید.

نکته
بسیاری از APIها از شما می‌خواهند که ثبت نام کنید و هنگام فراخوانی یک کلید API بفرستید. تا زمان نگارش این متن گیت‌هاب چنین درخواستی ندارد امّا اگر یک کلید API دریافت کنید محدودیّت استفاده‌ی شما خیلی کم‌تر خواهد بود.

10 - بصری کردن مخزن‌ها به کمک پلات‌لی

اکنون داده‌هایی را که در اختیار داریم بصری می‌کنیم تا محبوبیّت نسبی پروژه‌های پایتون را در گیت‌هاب نمایش دهیم. یک نمودار میله‌ای تعاملی خواهیم ساخت: ارتفاع هر میله بیانگر تعداد ستاره‌های دریافتیِ پروژه است و با کلیک کردن روی هر میله می‌توانید به صفحه‌ی پروژه در گیت‌هاب بروید. نسخه‌ای از برنامه‌ای را که مشغول کار بر روی آن بودیم به نام python_repos_visual.py ذخیره کنید و آن را تغییر دهید تا مشابه کد زیر شود:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import requests
from plotly.graph_objs import Bar  # ①
from plotly import offline

# Make an API call and store the response.
url = 'https://api.github.com/search/repositories?q=language:python&sort=stars'
headers = {'Accept': 'application/vnd.github.v3+json'}
r = requests.get(url, headers=headers)
print(f"Status code: {r.status_code}")  # ②

# Process results.
response_dict = r.json()
repo_dicts = response_dict['items']

repo_names, stars = [], []  # ③
for repo_dict in repo_dicts:
    repo_names.append(repo_dict['name'])
    stars.append(repo_dict['stargazers_count'])

# Make visualization.
data = [{  # ④
    'type': 'bar',
    'x': repo_names,
    'y': stars,
}]

my_layout = {  # ⑤
    'title': 'Most-Starred Python Projects on GitHub',
    'xaxis': {'title': 'Repository'},
    'yaxis': {'title': 'Stars'},
}

fig = {'data': data, 'layout': my_layout}
offline.plot(fig, filename='python_repos.html')

کلاس Bar و ماژول offline را از plotly وارد می‌کنیم. نیازی به وارد کردن کلاس Layout نیست چون از رویکرد دیکشنری برای تعیین چیدمان استفاده می‌کنیم، مشابه همان کاری که برای لیست data در پروژه‌ی نقشه‌ی زمین‌لرزه در فصل ۱۶ کردیم. وضعیّت پاسخ فراخوانی API را همچنان چاپ می‌کنیم تا در صورت بروز مشکل از آن مطّلع شویم. بخشی از کد پردازش پاسخ API را حذف کرده‌ایم زیرا مرحله‌ی اکتشافی دیگر به پایان رسیده و می‌دانیم چه داده‌ای را نیاز داریم.

سپس دو لیست خالی می‌سازیم تا داده‌های نمودار اوّلیّه را در آن ذخیره کنیم. نام هر پروژه را به عنوان برچسب میله‌ها و تعداد ستاره‌ها را برای تعیین ارتفاع میله نیاز داریم. داخل حلقه نام و تعداد ستاره‌ی هر پروژه را به این دو لیست الحاق می‌کنیم.

در ادامه لیست data را تعریف می‌کنیم. این لیست شامل دیکشنری‌ای مشابه آن‌چه در فصل ۱۶ استفاده کردیم خواهد بود که نوع نمودار و داده‌های مربوط به مقدارهای x و y را شامل می‌شود. مقدارهای x نام پروژه‌ها و مقدارهای y تعداد ستاره‌های دریافتی هر پروژه خواهد بود.

در ادامه چیدمان نمودار را از طریق رویکرد دیکشنری تعریف می‌کنیم. به جای ایجاد نمونه‌ای از کلاس Layout، یک دیکشنری می‌سازیم که مشخّصات چیدمان داخل آن قرار می‌گیرد. عنوانی برای نمودار کلّی و برچسبی برای هر محور تعیین می‌کنیم.

شکل ۱۷-۱ نمودار حاصل را نشان می‌دهد. می‌بینیم که چند پروژه‌ی اوّل به طور چشم‌گیری از باقی پروژه‌ها محبوب‌ترند ولی همه‌ی آن‌ها پروژه‌های مهمّی در زیست‌بوم پایتون به شمار می‌آیند.

شکل ۱۷-۱: محبوب‌ترین پروژه‌های پایتون در گیت‌هاب

11 - بهبود نمودارهای پلات‌لی

بیایید شکل و قیافه‌ی نمودارها را بهبود بخشیم. همان‌طور که در فصل ۱۶ دیدید شما می‌توانید تمام راهنماهای شکل‌دهی را به صورت جفت‌های کلید-مقدار در دیکشنری‌های data و my_layout تعریف کنید. تغییراتی اعمالی در شیء data میله‌ها را تحت تاثیر قرار می‌دهد. این نسخه‌ی تغییریافته‌ی شیء data برای نمودار ماست که رنگی خاص و حاشیه‌ای واضح برای هر میله فراهم می‌کند:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
--snip--
data = [{
    'type': 'bar',
    'x': repo_names,
    'y': stars,
    'marker': {
        'color': 'rgb(60, 100, 150)',
        'line': {'width': 1.5, 'color': 'rgb(25, 25, 25)'}
    },
    'opacity': 0.6,
}]
--snip--

تنظیمات marker نمایش‌داده شده طرح میله‌ها را تغییر می‌دهد. یک رنگ آبی شخصی و حاشیه‌ای به رنگ خاکستری تیره و عرض ۱٫۵ پیکسل برای هر میله تعیین کرده‌ایم. همچنین شفافیّت میله‌ها را برابر ۰٫۶ قرار دادیم تا ظاهر نمودار را کمی ملایم‌تر کنیم.

در ادامه my_layout را تغییر می‌دهیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
--snip--
my_layout = {
    'title': 'Most-Starred Python Projects on GitHub',
    'titlefont': {'size': 28},  # ①
    'xaxis': {
        'title': 'Repository',
        'titlefont': {'size': 24},  # ②
        'tickfont': {'size': 14},
    },
    'yaxis': {
        'title': 'Stars',
        'titlefont': {'size': 24},  # ③
        'tickfont': {'size': 14},
    },
}
--snip--

از کلید titlefont برای تعریف اندازه‌ی قلم در عنوان کلی نمودار استفاده می‌کنیم. در دیکشنری xaxis تنظیماتی برای کنترل اندازه‌ی قلم عنوان محور x (titlefont) و علامت‌گذاری‌های آن (tickfont) اضافه می‌کنیم. چون این دیکشنری‌های تودرتو مستقل‌اند می‌توانید کلیدهای مربوط به رنگ و خانواده‌ی قلم عنوان و علامت‌گذاری‌های هر محور را وارد کنیم. همین تنظیمات را برای محور y انجام می‌دهیم.

شکل ۱۷-۲ نمودار دوباره‌طرّاحی‌شده را نشان می‌دهد:

شکل ۱۷-۲: شکل و سیاق نمودار بازنگری شده است.

12 - افزودن تول‌تیپ‌های شخصی

در پلات‌لی می‌توانید با قرار دادن نشان‌گر ماوس روی هر میله اطّلاعاتی را که آن میله نمایندگی می‌کند نشان دهید. این راهنما معمولاً تول‌تیپ نامیده می‌شود و در این مورد تعداد ستاره‌های هر پروژه را نشان می‌دهد. اجازه دهید یک تول‌تیپ شخصی برای نمایش شرح پروژه و نام مالک آن بسازیم.

باید داده‌های دیگری برای تولید تول‌تیپ‌ها استخراج کنیم و شیء data را تغییر دهیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
--snip--

# Process results.
response_dict = r.json()
repo_dicts = response_dict['items']

repo_names, stars, labels = [], [], []  # ①

for repo_dict in repo_dicts:
    repo_names.append(repo_dict['name'])
    stars.append(repo_dict['stargazers_count'])

    owner = repo_dict['owner']['login']  # ②
    description = repo_dict['description']
    label = f"{owner}<br />{description}"  # ③
    labels.append(label)

# Make visualization.
data = [{
    'type': 'bar',
    'x': repo_names,
    'y': stars,
    'hovertext': labels,  # ④
    'marker': {
        'color': 'rgb(60, 100, 150)',
        'line': {'width': 1.5, 'color': 'rgb(25, 25, 25)'}
    },
    'opacity': 0.6,
}]
--snip--

ابتدا لیستی خالی برای نگهداری متن نمایشی هر پروژه140 درست می‌کنیم. در حلقه‌ی پردازش داده‌ها شرح و مالک هر پروژه را بیرون می‌کشیم. پلات‌لی به‌کارگیری کد HTML داخل عناصر متنی را مجاز می‌داند پس رشته‌ای برای برچسب‌ها درست می‌کنیم و از شکست خط (<br />) برای ایجاد فاصله بین نام کاربری مالک و شرح پروژه استفاده می‌کنیم. سپس این برچسب را در لیست labels ذخیره می‌کنیم.

در دیکشنری data مدخلی به کلید hovertext اضافه می‌کنیم و لیستی را که هم‌اکنون ساخته‌ایم به آن نسبت می‌دهیم. پلات‌لی همچنان‌که هر میله را می‌سازد برچسب متناظرش را از این لیست بیرون می‌کشد و تنها زمانی آن را نمایش می‌دهد که بیننده روی آن میله توقّف کند.

شکل ۱۷-۳ نمودار حاصل را نشان می‌دهد.

شکل ۱۷-۳: قرار دادن نشان‌گر روی یک میله نام مالک و شرح پروژه را نشان می‌دهد.

13 - افزودن پیوندهای قابل کلیک به نمودار

چون پلات‌لی به شما امکان استفاده از HTML را در عنصرهای متنی می‌دهد می‌توانیم به سادگی پیوندها را به نمودار خود اضافه کنیم. اجازه دهید که برچسب‌های محور x را به عنوان راهی برای باز کردن خانه‌ی پروژه در گیت‌هاب قرار دهیم. باید URLها را از داده‌ها بیرون کشیده و به کمک آن‌ها برچسب‌های محور x را تولید کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
--snip--

# Process results.
response_dict = r.json()
repo_dicts = response_dict['items']

repo_links, stars, labels = [], [], []  # ①

for repo_dict in repo_dicts:
    repo_name = repo_dict['name']  # ②
    repo_url = repo_dict['html_url']
    repo_link = f"<a href='{repo_url}'>{repo_name}</a>"  # ③
    repo_links.append(repo_link)

    stars.append(repo_dict['stargazers_count'])

--snip--

# Make visualization.
data = [{
    'type': 'bar',
    'x': repo_links,  # ④
    'y': stars,
    --snip--
}]
--snip--

نام لیست را از repo_names به repo_links تغییر می‌دهیم تا نوع اطّلاعاتی را که در نمودار کنار هم قرار می‌دهیم دقیق‌تر بیان کنیم. سپس URL پروژه را از repo_dict بیرون کشیده و در متغیّر موقّت repo_url قرار می‌دهیم. پیوندی به پروژه درست می‌کنیم. از تگ HTML anchor که به شکل <a href='URL'>link text</a> است برای تولید پیوند استفاده می‌کنیم. سپس این پیوند را به لیست repo_links الحاق می‌کنیم.

در ادامه از این لیست به عنوان مقدارهای x استفاده می‌کنیم. نتیجه باید شبیه به قبل باشد با این تفاوت که الان بیننده می‌تواند روی نام پروژه‌ها در پایین نمودار کلیک کند تا صفحه‌ی خانه‌ی پروژه در گیت‌هاب باز شود. اکنون یک بصری‌سازی پرمفهوم و تعاملی از داده‌هایی داریم که از طریق API دریافت شده‌اند.

14 - اطّلاعات بیشتر درباره‌ی پلات‌لی و API گیت‌هاب

برای مطالعه‌ی بیشتر در مورد کار با نمودارهای پلات‌لی دو محل خوب برای شروع وجود دارد. یکی Plotly User Guide in Python به آدرس https://plot.ly/python/user-guide است. این منبع به شما درک بهتری از چگونگی به‌کارگیری داده‌های شما در پلات‌لی برای ساختن بصری‌سازی و دلیل استفاده از این رویکرد می‌دهد.

Python Figure Reference به آدرس https://plot.ly/python/reference/ تمام تنظیمات موجود را برای پیکربندی بصری‌سازی‌های پلات‌لی فهرست می‌کند. تمام انواع نمودارهای قابل رسم به همراه تمام مشخّصه‌هایی که برای هر پیکربندی قابل انتخاب است هم فهرست شده است.

برای اطّلاعات بیشتر در مورد API گیت‌هاب به مستندات آن در https://developer.github.com/v3/ مراجعه کنید. اینجا خواهید آموخت که چگونه گستره‌ی بزرگی از اطّلاعات تخصّصی را از گیت‌هاب بیرون بکشید. اگر حساب کاربریِ گیت‌هاب دارید می‌توانید با داده‌های مربوط به خود یا داده‌های مخزن‌های دیگر کاربران که در دسترس عموم‌اند کار کنید.

15 - API اخبار هکرها

برای بررسی نحوه‌ی استفاده از فراخوانی APIها در سایت‌های دیگر بیایید نگاهی سریع به سایت Hacker News (http://news.ycombinator.com/) بندازیم. در این سایت افراد مقاله‌هایی درباره‌ی برنامه‌نویسی و فنّآوری به اشتراک می‌گذارند و در بحث‌های زنده پیرامون این مقاله‌ها شرکت می‌کنند. API این سایت دسترسی به داده‌های تمام مطالب ثبت‌شده و نظرات سایت را فراهم می‌آورد و می‌توانید از آن بدون نیاز به ثبت‌نام برای دریافت کلید استفاده کنید.

فراخوانی زیر اطّلاعاتی درباره‌ی بالاترین مقاله‌ی فعلی در زمان نگارش این کتاب برمی‌گرداند:

https://hacker-news.firebaseio.com/v0/item/19155826.json

اگر این URL را در مرورگری وارد کنید متنی داخل آکلاد خواهید دید که نشان می‌دهد این یک دیکشنری است. امّا بررسی این پاسخ بدون شکل‌دهی مناسب به آن کاری مشکل است. بیایید این URL را از طریق متد json.dump() فرابخوانیم همانند آنچه در فصل ۱۶ کردیم، تا ببینیم چه اطّلاعاتی درباره‌ی یک مقاله برگردانده می‌شود:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import requests
import json

# Make an API call, and store the response.
url = 'https://hacker-news.firebaseio.com/v0/item/19155826.json'
r = requests.get(url)
print(f"Status code: {r.status_code}")

# Explore the structure of the data.
response_dict = r.json()
readable_file = 'data/readable_hn_data.json'
with open(readable_file, 'w') as f:
    json.dump(response_dict, f, indent=4)

همه چیز در این برنامه آشنا به نظر می‌آید زیرا آن را در سراسر دو فصل قبل استفاده کرده‌ایم. خروجی یک دیکشنری درباره‌ی اطّلاعات مقاله به شماره‌ی ۱۹۱۵۵۸۲۶ است:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "by": "jimktrains2",
  "descendants": 220,
  "id": 19155826,
  "kids": [
    19156572,
    19158857,
    --snip--
  ],
  "score": 722,
  "time": 1550085414,
  "title": "Nasa's Mars Rover Opportunity Concludes a 15-Year Mission",
  "type": "story",
  "url": "https://www.nytimes.com/.../mars-opportunity-rover-dead.html"
}

این دیکشنری شامل تعدادی کلید است که می‌توان با آن کار کرد. کلید descendants (فرزندان) تعداد نظراتی را نشان می‌دهد که مقاله دریافت کرده است. کلید kids (کودکان) شماره‌ی تمام نظراتی را در برمی‌گیرد که به طور مستقیم برای این مقاله ارسال شده است. این نظرات ممکن است خود نظرات دیگری را هم شامل شوند در نتیجه تعداد فرزندان معمولاً بیشتر از تعداد کودکان است. عنوان مقاله‌ی مورد بحث را می‌توان دید و URL‌ای برای مقاله هم در دسترس است.

URL زیر لیست ساده‌ای از شماره‌ی تمام مقاله‌های برتر فعلی وب‌سایت را برمی‌گرداند:

https://hacker-news.firebaseio.com/v0/topstories.json

از این فراخوانی استفاده می‌کنیم تا مقاله‌های موجود در صفحه‌ی اصلی سایت را بیابیم و سپس دنباله‌ای از فراخوانی‌های API مشابه بالا تولید می‌کنیم. با این رویکرد می‌توانیم خلاصه‌ای از تمام مقالات موجود در صفحه‌ی اصلی سایت را بلافاصله داشته باشیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from operator import itemgetter
import requests

# Make an API call and store the response.
url = 'https://hacker-news.firebaseio.com/v0/topstories.json'
r = requests.get(url)  # ①
print(f"Status code: {r.status_code}")

# Process information about each submission.
submission_ids = r.json()  # ②

submission_dicts = []  # ③
for submission_id in submission_ids[:30]:
    # Make a separate API call for each submission.
    url = f"https://hacker-news.firebaseio.com/v0/item/{submission_id}.json"  # ④
    r = requests.get(url)
    print(f"id: {submission_id}\tstatus: {r.status_code}")

    response_dict = r.json()

    # Build a dictionary for each article.
    submission_dict = {  # ⑤
        'title': response_dict['title'],
        'hn_link': f"http://news.ycombinator.com/item?id={submission_id}",
        'comments': response_dict['descendants'],
    }
    submission_dicts.append(submission_dict)  # ⑥

submission_dicts = sorted(submission_dicts, key=itemgetter('comments'),  # ⑦
                          reverse=True)

for submission_dict in submission_dicts:  # ⑧
    print(f"\nTitle: {submission_dict['title']}")
    print(f"Discussion link: {submission_dict['hn_link']}")
    print(f"Comments: {submission_dict['comments']}")

ابتدا API را فراخوانی و وضعیّت پاسخ را چاپ می‌کنیم. فراخوانی API لیستی شامل شماره‌ی ۵۰۰ مقاله‌ی محبوب این سایت در زمان فراخوانی را برمی‌گرداند. سپس شیء پاسخ را به لیست پایتون تبدیل می‌کنیم و آن را در submission_ids ذخیره می‌کنیم. از این شماره‌ها برای ساختن مجموعه‌ای از دیکشنری‌ها استفاده خواهیم کرد که اطّلاعات هر کدام از مقاله‌های فعلی را ذخیره می‌کنند.

لیستی خالی به نام submission_dicts می‌سازیم تا دیکشنری‌ها را نگه داریم. سپس روی شماره‌ی ۳۰ مقاله‌ی برتر حلقه می‌زنیم. با تولید یک URL که شامل مقدار فعلی submission_id است، برای هر مقاله یک فراخوانی API انجام می‌دهیم. وضعیّت هر درخواست را به همراه شماره‌ی آن چاپ می‌کنیم تا مطمئن شویم که درخواست موفقیّت‌آمیز بوده یا نه.

برای هر مقاله‌ی در حال پردازش یک دیکشنری می‌سازیم که در آن عنوان، پیوندی به صفحه‌ی نظرات آن و تعداد نظراتی را که تا الان دریافت کرده ذخیره می‌کنیم. سپس هر submission_dict را به لیست submission_dicts الحاق می‌کنیم.

هر مقاله در این سایت بر اساس امتیازی کلّی رده‌بندی می‌شود که به عوامل مختلفی مثل تعداد رای‌های مثبت، تعداد نظرات دریافتی و میزان جدید بودن آن بستگی دارد. لیست دیکشنری‌ها را بر اساس تعداد نظرات آن‌ها مرتّب می‌کنیم. بر این کار از تابعی به نام itemgetter() استفاده می‌کنیم که در ماژول operator قرار دارد. کلید comments را به این تابع می‌دهیم تا مقدارهای مرتبط با آن را از هر دیکشنری داخل لیست بیرون بکشد. سپس تابع sorted() لیست را بر اساس این مقدار مرتّب می‌کند. لیست را به ترتیب معکوس مرتّب می‌کنیم تا مقالات با بیشترین نظر را اوّل بیاوریم.

به محض مرتّب شدن لیست داخل آن حلقه می‌زنیم و سه نوع اطّلاعات را درباره‌ی هر کدام چاپ می‌کنیم: عنوان، پیوندی به صفحه‌ی بحث و گفتگوی آن و تعداد نظراتی که در حال حاضر برای مقاله وجود دارد:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Status code: 200
id: 19155826   status: 200
id: 19180181   status: 200
id: 19181473   status: 200
--snip--

Title: Nasa's Mars Rover Opportunity Concludes a 15-Year Mission
Discussion link: http://news.ycombinator.com/item?id=19155826
Comments: 220

Title: Ask HN: Is it practical to create a software-controlled model rocket?
Discussion link: http://news.ycombinator.com/item?id=19180181
Comments: 72

Title: Making My Own USB Keyboard from Scratch
Discussion link: http://news.ycombinator.com/item?id=19181473
Comments: 62

--snip--

شما می‌توانید فرآیندی مشابه را برای دسترسی و تحلیل اطّلاعات هر API پی بگیرید. با این داده‌ها می‌توانید نموداری تولید کنید که نشان دهد اخیراً کدام مقاله‌ها بیشترین بحث‌های فعّال را برانگیخته‌اند. این همچنین پایه‌ی برنامه‌هایی است که تجربه‌ی مطالعه‌ی شخصی را برای سایت‌های اینچنینی فراهم می‌آورند. برای اطّلاع یافتن از انواع اطّلاعاتی که از طریق این API می‌توانید دریافت کنید به صفحه‌ی مستندات آن در https://github.com/HackerNews/API/ مراجعه کنید.

16 - خودآزمایی

۱۷-۱- زبان‌های دیگر: فراخوانی API را در python_repos.py تغییر دهید تا جدولی برای نمایش پروژه‌های محبوب به دیگر143 142 141 زبان‌های برنامه‌نویسی تولید کند. زبان‌هایی مانند جاوااسکریپت، روبی، سی، جاوا، پرل، هسکل و گو را امتحان کنید.

۱۷-۲- گفتگوهای فعّال: با استفاده از داده‌های به‌دست‌آمده از hn_submissions.py نموداری میله‌ای از فعّال‌ترین گفت‌و‌گوهای فعلی در Hacker News تولید کنید. ارتفاع هر میله متناسب با تعداد نظراتی است که مقاله دریافت کرده است. برچسب هر میله باید شامل عنوان مقاله باشد و مانند پیوندی به صفحه‌ی نظرات آن عمل کند.

۱۷-۳- آزمودن python_repos.py: در python_repos.py مقدار status_code را چاپ کردیم تا مطمئن شویم فراخوانی API به درستی انجام می‌پذیرد. برنامه‌ای به نام test_python_repos.py بنویسید که از unittest برای اطمینان یافتن از ۲۰۰ بودن مقدار status_code استفاده کند. به بررسی‌های دیگری که می‌توانید انجام دهید فکر کنید. مثلاً این که ذکر تعداد مواردی که برگردانده می‌شود مورد انتظار است یا تعداد کلّ مخزن‌ها باید از مقدار مشخّصی بیشتر باشد.

۱۷-۴- کاوش‌های بیشتر: مستندات مربوط به پلات‌لی و یکی از دو API این فصل را ببینید. از اطّلاعاتی که پیدا می‌کنید استفاده کنید تا شکل نمودارها را شخصی کنید یا اطّلاعاتی دیگری در آن‌ها قرار دهید و بصری‌سازی‌های خود را بسازید.

17 - خلاصه

در این فصل آموختید که چگونه از APIها برای نوشتن برنامه‌های خودمتّکی استفاده کنید که داده‌های مورد نیاز خود را جمع‌آوری و آن‌ها را بصری می‌کنند. از API گیت‌هاب برای بررسی پرستاره‌ترین پروژه‌های پایتون در گیت‌هاب استفاده کردید و نگاهی گذرا به API سایت Hacker News انداختید. یاد گرفتید که چگونه از بسته‌ی Requests برای ارسال خودکار فراخوانی API به گیت‌هاب و روش پردازش نتایج آن استفاده کنید. برخی از تنظیمات پلات‌لی نیز برای شخصی‌سازی بیشتر نمودارهایی که تولید می‌کنند معرّفی شدند.

در فصل بعد از جانگو برای ساختن یک برنامه‌ی کاربری تحت وب به عنوان آخرین پروژه‌ی خود بهره خواهید گرفت.