دور زدن کپچای سایت سایپا بدون پردازش تصویر

‌‌دور زدن کپچای saipa.irancar.com

یک پیام داشتم که دوستی میگفت دنبال یک اسکریپت ساده هست تا بتونه فیلدهای یک سری فرم، مثل فرم سامانه فروش سایپا رو اتوماتیک پر کنه.

خب! راه حل، سرراست و ساده به نظر میرسه، به جز کپچای پایین صفحه که همیشه دردسر ساز بوده!

توی این پست قصد دارم قدم به قدم، مراحلی که برای دور زدن کپچای سایت https://saipa.iranecar.com انجام دادم رو توضیح بدم.

DON’T TRY THIS AT HOME!!! :))

تلاش اول

برای شروع کنجکاوی روی کپچا راست کلیک میکنم و inspect element رو میزنم تا ببینم اون پشت چه خبره.

You can use Inspect Element to have fun!

اتفاق عجیب و شوک کننده اینکه دیتاهای کپچا به صورت inline svg داخل متن html ذخیره شدند!! با کمی دقت متوجه میشیم که کل تصویر کپچا به صورت یک سری داده داخل تگهای path هستند.

The Path Is Everywhere

در قدم اول، همه داده های مربوط به کد کپچا رو با چند خط کد استخراج میکنم. انتخاب من برای اینکار، پایتون و سلنیوم:

from selenium import webdriver
driver = webdriver.Chrome('/path/to/your/webdriver')
driver.get('https://saipa.iranecar.com/registration')
paths = driver.find_elements_by_tag_name('path')

مشخصه که در خط دوم سایت سایپا رو باز میکنم و بعد تمام المنتهایی که تگ path دارند رو داخل یک لیست نگه میدارم.

خب خب خب، یک مشکل! با رفتن به لینک https://saipa.iranecar.com مستقیم صفحه ای که کپچا وجود داره باز نمیشه و نیازه تا یک سری کلیک و انتخاب داشته باشیم تا به صفحه مورد نظرمون برسیم.

برای اینکه مشکل ساز نشه یک ورودی به کد بالا اضافه میکنم تا بعد از اینکه داخل کنسول ENTER زدم برنامه ادامه پیدا کنه:

from selenium import webdriver
driver = webdriver.Chrome('/path/to/your/webdriver')
driver.get('https://saipa.iranecar.com/registration')

# wait for input -> after captcha loaded we press enter to continue
input('press "ENTER" to see the result')

paths = driver.find_elements_by_tag_name('path')

یک نگاه به مقادیر attribute d از هر عنصر paths بندازیم:

from selenium import webdriver
driver = webdriver.Chrome('/path/to/your/webdriver')
driver.get('https://saipa.iranecar.com/registration')

# wait for input -> after captcha loaded we press enter to continue
input('press "ENTER" to see the result')

paths = driver.find_elements_by_tag_name('path')

for path in paths:
    print(path.get_attribute('d'))

اوه اوه! شد این:

WoOw

این عددهای عجیب و غریب، تصویر svg که به عنوان کپچا میبینیم رو می‌سازند.

هنوز بهم ریخته و بدرد نخوره و لیستی که داریم خط خطی های اضافی روی کد کپچا رو هم شامل میشه؛ قدم بعدی اینه که اونها رو هم حذف کنم تا فقط لیستی از چهار رقم اصلی رو داشته باشم. برای اینکار کافیه element هایی که fill=none است رو پردازش نکنم!

from selenium import webdriver
driver = webdriver.Chrome('/path/to/your/webdriver')
driver.get('https://saipa.iranecar.com/registration')

# wait for input -> after captcha loaded we press enter to continue
input('press "ENTER" to see the result')

paths = driver.find_elements_by_tag_name('path')

for path in paths:
    if path.get_attribute('fill') != 'none':
        print(path.get_attribute('d'))

ظاهرا همه چیز روبراهه و فقط باید از لیستی که به دست آوردیم، رقم ها رو یکی یکی تشخیص بدیم؛ ایده من اینه که ظاهرا تمام ۵ ها داده های مشابه دارند، تمام ۲ ها داده های مشابه دارند و …

یعنی اگه اطلاعات اتریبیوت d از هر رقم (از ۰ تا ۹) رو یک جا ذخیر داشته باشم، با مقایسه اتریبیوت d عنصرهای داخل لیستم می‌تونم عدد مربوط بهش رو تشخیص بدم.

برای اینکه مطمئن بشم، دریافت تصویر جدید رو اونقدر میزنم تا به تصویری برسم که رقم تکراری داشته باشه:

7317

اینجا دو تا هفت داریم و انتظار من اینه که داده های اتریبیوت d شون یکسان باشه؛ دیتای مربوط به اولین هفت:

و دیتای مربوط به دومین هفت:

با یک مقایسه چشمی متوجه میشیم که دو مقدار بالا با هم فرق دارند و مقادیر یکسان نیستند؛ شکست خوردیم!!!!

تلاش دوم

بعد از کمی ور رفتن با اعداد، نکته عجیبی خودنمایی میکنه! با کد زیر، اتریبیوت d از تمام رقم های کپچایی که واسم اومده رو میخونم و اعدادش که بر اساس اسپیس از هم جدا میکنم، داخل یک لیست میریزم و طول هر لیست رو محاسبه میکنم ( خودم هم نفهمیدم چی گفتم! (: )

from selenium import webdriver
driver = webdriver.Chrome('/path/to/your/webdriver')
driver.get('https://saipa.iranecar.com/registration')

# wait for input -> after captcha loaded we press enter to continue
input('press "ENTER" to see the result')

paths = driver.find_elements_by_tag_name('path')

for path in paths:
    if path.get_attribute('fill') != 'none':
        data = path.get_attribute('d').split(' ')
        print(len(data))

برای کپچای بالا (7317) نتیجه میشه:

210
401
104
210

حله (: ظاهرا با وجود اینکه داده های ۱ ها، ۲ ها یا ۳ های مختلف با هم فرق دارند اما همه رقم های مشابه طول یکسان دارند. (توی خروجی بالا برای هر دو تا ۷ طول ۲۱۰ به دست اومد)

با چند خط کد مشابه و آزمون و خطا یک دیکشنری از ارقام ۰ تا ۹ میسازم که بر اساس len(data) بهم رقم مربوط رو بده:

digit = {
    246: 0,
    104: 1,
    264: 2,
    401: 3,
    221: 4,
    269: 5,
    273: 6,
    210: 7,
    352: 8,
    290: 9
}

حالا کافیه لیست paths رو پیمایش کنم، اتریبیتوت d رو براساس اسپیس از هم جدا کنم و سایزش رو به دست بیارم؛ سایز رو به دیکشنری بالا می‌دم و تمام!

برای تصویر پایین نتیجه میشه:

8927
290
352
210
264

خب خب خب (: رقم ها رو درست تشخیص دادیم ولی جای هر رقم رو نه! (ترتیب اعداد بالا میشه: ۹۸۷۲)

تلاش سوم

عبارت اول داخل اتریبیوت d از هر رقم، شامل یک M هست به اضافه یک عدد اعشاری! (مثلا: M89.65)

M10

نکته بامزه اینه که هر چی این عدد کوچکتر باشه نشون دهنده اینه که اون رقم زودتر توی کپچا ظاهر شده! به عبارت دیگه اگه لیست رو بر اساس این عدد مرتب کنم ترتیب رقم های کپچا درست میشه:

from selenium import webdriver

digit = {
    246: 0,
    104: 1,
    264: 2,
    401: 3,
    221: 4,
    269: 5,
    273: 6,
    210: 7,
    352: 8,
    290: 9
}

driver = webdriver.Chrome('/path/to/your/webdriver')
driver.get('https://saipa.iranecar.com/registration')

# wait for input -> after captcha loaded we press enter to continue
input('press "ENTER" to see the result')

paths = driver.find_elements_by_tag_name('path')

code = []
for path in paths:
    if path.get_attribute('fill') != 'none':
        data = path.get_attribute('d').split(' ')
        
        location = float(data[0][1:]) # get the number after M
        d = digit[len(data)]
        
        code.append([d, location])
        
real_code = sorted(code, key=lambda x: x[1])
real_code = ''.join(str(x[0]) for x in real_code)
print(real_code)

نتیجه کد با کمی تغییر:

https://aparat.com/v/J2v8T

هدف از این مطلب، نشون دادن یک مشکل در کپچای سایت سایپا ست که انتظار می‌ره بتونند با یک روش بهتر جایگزینش کنند.

نوشته دور زدن کپچای سایت سایپا بدون پردازش تصویر اولین بار در ویرگول پدیدار شد.

گردآوری توسط ایده طلایی

پاسخی بگذارید