نوشتن یک Scraper با Telethon

نوشتن یک Scraper با Telethon

چندین سال پیش (حدود سال ۲۰۱۸) یکی از اساتید دانشگاه قصد جمع آوری آیدی های دانشجویان دانشگاه را از یک گروه بزرگ تلگرامی داشت تا بر اساس آن یک جمع بندی کلی از تمام دانشجویان حاضر در کلاسشان داشته و هر فرد بتواند پروژه ی خودش را برای استاد ارسال کند. در آن زمان استاد قصد داشت مقایسه ای بین تعداد دانشجویان خودش و دانشجویان حاضر در گروهشان داشته باشند و به دلیل مشغله ای که داشتند انجام این کار به صورت دستی ممکن نبود. این مسئله منجر به ساخت یک اسکریپت کوتاه پایتون شد که در سورس کد آن در گیت هاب قابل مشاهده است. این اسکریپت به API حساب شخصی شما در تلگرام متصل شده و از بین گروه هایی که در آن ها عضو هستید تمام کاربران را استخراج می کند.

 

تلگرام مانند هر پلتفرم دیگری به شما اجازه ی data harvesting نمی دهد و جلوی برداشت اطلاعات کاربرانش را می گیرد اما در عین حال (جدا از API ربات ها) به هر حساب کاربری شخصی یک API برای تعامل با حساب خود را می دهد (همانطور که می دانید کلاینت تلگرام برای وب و برای اندروید و آی او اس کاملا اوپن سورس است. با این حساب تنها کاری که باقی می ماند استفاده از کتابخانه ای است که به ما اجازه بدهد به API کلاینت تلگرام دسترسی داشته باشیم. اسکریپت طی این سال ها به دلیل تغییرات کتابخانه ی Telethon دچار تغییراتی شد و حدود دو سال پیش آن را ویرایش کردم و در صفحه ی گیت هاب خودم قرار دادم اما عمرش قدیمی تر از این حرف ها است! کتابخانه ی محبوبی به نام Telethon به زبان پایتون وجود دارد که دقیقا همین کار را برای ما انجام می دهد، یعنی به ما اجازه ی دسترسی به API کلاینت تلگرام را می دهد (البته شما می توانید از آن برای ساخت ربات هم استفاده کنید!).

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

  • App title: عنوان دلخواه شما برای این برنامه.
  • Short name: نام کوتاهی برای برنامه (شبیه به یوزرنیم).
  • URL: آدرس اینترنتی برنامه. این بخش را خالی گذاشته یا آدرس گوگل را وارد کنید.
  • Platform: اسکریپت ما روی سیستم اجرا می شود بنابراین گزینه ی Desktop را انتخاب کنید.
  • Description: توضیح کوتاهی برای کارکرد برنامه. سعی کنید زیاد صادق نباشید تا پر تلگرام به پر شما گیر نکند!

بعد از ساخت برنامه کلید API خاص خودتان را دریافت می کنید (یک api_id و یک api_hash). حالا به صفحه ی گیت هاب اسکریپت رفته و یکی از دو اسکریپت را نسبت به نیاز خود دانلود کنید:

  • AutoExtract.py: این اسکریپت به صورت خودکار تمام کاربرانِ تمام گروه هایی که عوضشان هستید را استخراج می کند.
  • ManualExtraction.py: این اسکریپت تمام گروه ها را به همراه یک ایندکس در ترمینال شما لیست می کند و شما انتخاب می کنید کاربران کدام گروه استخراج شوند. 

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

# ------ اطلاعات شخصی شما
api_id = 0000000 # از تلگرام دریافت می شود
api_hash = 'YOUR HASH HERE' # از تلگرام دریافت می شود
phone = '+00000000000' # شماره تلفن خودتان با فرمت بین المللی
client = TelegramClient(phone, api_id, api_hash)

متغیر های api_id و api_hash را از همان صفحه ی حسابتان در تلگرام دریافت می کنید. متغیر phone شماره ی تلفن حسابتان با فرمت بین المللی است، یعنی با ۹۸+ در ابتدای آن (مثلا ۹۸۹۱۵۱۲۳۴۵۶۷+). متغیر client هم شیء کلاینت شما است که تمام کار ها را برایتان انجام می دهد. اگر به اسکریپت نگاه کنید بخشی بدین شکل می بینید:

print('Saving In file...')
filename = target_group.title
with open(("{}.csv".format(filename)), "w", encoding='UTF-8') as f:
writer = csv.writer(f, delimiter=",", lineterminator="\n")
writer.writerow(['username'])
for user in all_participants:
accept = True

if (user.bot):
accept = False

elif (user.deleted):
accept = False

elif (user.username == 'None'):
accept = False

elif not user.username:
accept = False

elif (user.is_self):
accept = False

elif (isinstance(user.status, types.UserStatusRecently)):
accept = True

elif (isinstance(user.status, types.UserStatusLastWeek)):
accept = True

elif (isinstance(user.status, types.UserStatusLastMonth)):
accept = False

elif (isinstance(user.status, types.UserStatusOnline)):
accept = True

elif (isinstance(user.status, types.UserStatusOffline)):
accept = False
else:
accept = False

همانطور که می بینید اطلاعات کاربران در یک فایل CSV به نام گروه و به صورت خط به خط ذخیره می شود. همچنین من تمام کاربران را استخراج نکرده ام چرا که آن زمان گروه پُر از ربات و کاربران فیک بود بنابراین چند شرط خاص نوشته ایم تا اگر هر شرطی برقرار نبود متغیر accept به false تغییر پیدا کرده و آن کاربر در نتیجه ی نهایی (فایل CSV) حضور نداشته باشد. خصوصیاتی که در هر شرط چک شده اند توسط API تلگرام در اختیار ما قرار می گیرند.

  • user.bot: کاربر نباید ربات باشد.
  • user.deleted: حساب کاربری حذف شده نباشد (حساب های کاربری حذف شده تا چند وقت به عنوان deleted account در گروه باقی می ماندند)
  • user.username: کاربر بدون نام کاربری نباشد.
  • user.is_self: خودمان را استخراج نکنیم!
  • user.status: آخرین زمان آنلاین شدن کاربر بیشتر از UserStatusLastWeek (یک هفته) نباشد.

در نهایت اگر تمام شرط ها برقرار بود شروع به نوشتن اطلاعات هر کاربر می کنیم:

if (accept):
if user.username:
username = "@" + user.username
else:
username = ""
if user.first_name:
first_name = user.first_name
else:
first_name = ""
if user.last_name:
last_name = user.last_name
else:
last_name = ""
name = (first_name + ' ' + last_name).strip()
writer.writerow([name, username])

دلیل چک کردن دوباره برای خالی نبودن username و مقادیر دیگر این بود که در آن زمان کتابخانه ی Telethon باگ های عجیبی داشت و بعضا در مواجهه با کاربرانی که نام هایی با کاراکترهای خاص داشتند دچار مشکل می شد. از زمان نوشتن این اسکریپت زمان زیادی می گذرد و اگر کتابخانه ی Telethon یا API تلگرام تغییر زیادی نکرده باشند اسکریپت هنوز هم کار می کند اما شخصا آن را تست نکرده ام.