banner

Разгадываем капчи с помощью Goggles

Ещё в 2009 году у Google появилось мобильное приложение goggles, оно позволяет распознавать текстовую информацию на изображениях. Кроме как на мобильных телефонах этой функцией воспользоваться нельзя, поэтому гугл очень усложнил протокол обмена между телефоном и их сервисом, чтобы не дать другим воспользоваться таким полезным функционалом :) . Сегодня я расскажу как можно использовать этот сервис в своих целях.

Совсем недавно я наткнулся на это сообщение:
http://board.jdownloader.org/showthread.php?p=157746 ( видео)
Автор этого сообщения рассказывает способ разгадывания recaptcha с помощью goggles и выкладываем исходные коды на delphi. Прочитав, я решил получше разобраться в этом способе и переписать код на python.
Автор не особо рассказывает об алгоритме, поэтому мне пришлось немного помучится, чтобы понять почему гугл выдает совсем другие ответы в моём скрипте, чем программа написанная на delphi. Одной из проблем был размер картинки и её формат. Картинка не должна быть маленькой и должна иметь формат JPG.

import random,re
from grab import Grab
from PIL import Image
cap = 'image.bmp' # фаил с капчей
bdata = "\x22\x00\x62\x3C\x0A\x13\x22\x02\x65\x6E\xBA\xD3\xF0\x3B\x0A\x08\x01\x10\x01\x28\x01\x30\x00\x38\x01\x12\x1D\x0A\x09\x69\x50\x68\x6F\x6E\x65\x20\x4F\x53\x12\x03\x34\x2E\x31\x1A\x00\x22\x09\x69\x50\x68\x6F\x6E\x65\x33\x47\x53\x1A\x02\x08\x02\x22\x02\x08\x01"
tdata = "\x18\x4B\x20\x01\x30\x00\x92\xEC\xF4\x3B\x09\x18\x00\x38\xC6\x97\xDC\xDF\xF7\x25\x22\x00"
ua = 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_3 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7E18 Safari/528.16 GoogleMobileApp/0.7.3.5675 GoogleGoggles-iPhone/1.0; gzip'
headers={"Content-Type": "application/x-protobuffer", "Pragma": "no-cache"}
 
def cssid():
    # Фукнция генерации cssid
    chars = 'ABCDEF0123456789'
    res = ''
    for i in xrange(16):
        res += random.choice(chars)
    return res
 
def varint(value):
    # Функция преобразования в varint
    ret = []
    bits = value & 0x7f
    value >>= 7
    while value:
        ret.append(chr(0x80|bits))
        bits = value & 0x7f
        value >>= 7
    ret.append(chr(bits))
    return ''.join(ret)
 
def encodeImage(image):
    # Функция перекодировки картинки
    size = len(image)
    a = varint(size + 32)
    b = varint(size + 14) 
    c = varint(size + 10)
    size = varint(size)
    return "\n%s\n%s\n%s\n%s%s%s" % (a,b,c,size,image,tdata)
 
def createImage(cap,out):
    # Функция наложения капчи на большое изображение с текстом
    img1 = Image.open('pat.jpg')
    img2 = Image.open(cap)
    im = img2.crop((0,0,img2.size[0],img2.size[1]))
    img1.paste(im,(45,65,45+img2.size[0],65+img2.size[1]))
    img1.save(out,"JPEG",quality=100)
 
 
sid = cssid() # Получаем sid
createImage(cap,'out.jpg') # создаем картинку
img = open("out.jpg",'rb').read() # Считываем картинку в бинарном формате
rdata = encodeImage(img) # Кодируем картинку
g = Grab()
g.setup(url='http://www.google.com/goggles/container_proto?cssid='+sid,
        method='POST',user_agent=ua,payload=bdata,unicode_body=False,headers=headers) # Проверяем валидность cssid
g.request() 
g.setup(url='http://www.google.com/goggles/container_proto?cssid='+sid,payload=rdata) # Отправляем картинку
g.request()
try:
    text =  re.findall('STR(.*?)ENDR',g.response.body)[0].strip() # Пытаемся найти текст капчи в разгаданной картинке
    print text
except Exception,err:
    print err

Теперь немного по самому коду.
Как я уже писал, маленькие изображения гугл не принимает, поэтому накладываем нашу капчу на большое изображение, где заранее написано STR и ENDR. Картинка с капчей накалывается между этих слов, чтобы потом можно было спарсить полученный результат.

С помощью хитрых преобразований получаем бинарный набор данных и отправляем их в google. В ответе получаем не менее запутанный набор данных, среди которого можно найти нужный нам текст, если сервис смог его распознать :) .

Автор идеи заявил, что с 10 попытки ему удалось добиться 93% распознавании текста отдельной капчи,. Текст рекапч выдаваемых мне был валиден менее чем на 50%. Возможно в его стране более простая картинка, так как у меня сложность рекапчи зависит чуть ли не от погоды :) .
Вот что получается у меня:

По распознаваемому тексту видно, что гугл всегда пытается найти существующие слова, а не набор символов, именно им он отдает большее предпочтение, поэтому во многих местах он выдает совсем не подходящие слова. Не победили рекапчу, зато победим более простые капчи, они распозновываются на ура .
Для тех кто решит переписать на свой язык, может пригодится вот это не полное описание протокола:
http://notanothercodeblog.blogspot.com/2011_02_01_archive.html

  • http://seo-parser.ru seo-parser

    Это хорошо что вы написали програмульку по распознаванию каптчи гугла, но у меня есть некоторы замечания:

    1. Как вы видите, то каптча состоит из двух слов: одно написано четко, второе – раздвоено. То слово что четко написано – Google не знает что там (т.к. с помощью нас он сам распознает тексты какие сканирует в библиотеке, и поэтому там можно писать что угодно)! То слово что раздвоенное Google его знает и нужно именно его правильно ввести, т.к.в примере по распознаванию, все слова из раздвоенных каптч распознались не правильно, поэтому можно считать что програмулька не работает!

    НА хабре есть еще статья по тому как Google цифрует книжки
    http://habrahabr.ru/blogs/infosecurity/121010/

  • http://klipner.ru rushter

    Зато другие капчи на ура распознаются.

  • E

    Метод больше не работает, гугл отдает Unparseable Container Request.

  • http://klipner.ru rushter

    Всё отлично работает.

  • kardinal

    Не непашет больше.

  • http://klipner.ru rushter

    Работает, как и работало. Проверил только что.
    Если думайте что скопипастив код у вас всё заработает – ошибайтесь.

  • http://jezuz-chrizt.livejournal.com Крайст

    Умную башку не скопипастишь =)

  • ruma

    сейчас работает?

    исходный пример автора «Recaptcha-sample.exe» возвращает None …

  • http://klipner.ru rushter

    Да.
    http://s2.ipicture.ru/uploads/20110811/ADGs8Qmu.png

  • Lost

    А метод по прежнему работает? Это под какую версию питона ?

  • Lost

    Rushter, поясни идиоту че это такое…
    g = Grab()
    g.setup
    grab – функция из ImageGrab ?
    g.setup ….

  • http://klipner.ru rushter

    Работает, grab качать тут:
    https://bitbucket.org/lorien/grab/downloads

  • Lost

    Скачал, опять не работает. Пишет что параметра unicode_body нет. Возможно у тя библиотека постарее. Можешь выложить если не жалко?
    Без него START recognized END TextúÅí;ᅠ

  • http://klipner.ru rushter

    Последняя версия у меня, не может такого быть :)
    PyCurl, lxml установил ? Библиотека требует их.

  • Lost

    А все ступил. Спасибо все вроде работает. Но параметра там действительно нет). Странно почему у тебя с Unicide_body оно работает.

  • http://klipner.ru rushter

    Без этого параметра не правильно по идее будет работать.