суббота, 12 января 2008 г.

Полуавтоматическая обработка каптчи


Время от времени нам приходится автоматизировать разные действия - от рутинных, до довольно мудрённых. Нормально, что в этом нам помогают наши навыки разработки ПО, программирования. В идеале, конечно, к ним ещё приложить математический поход, и тогда такой продукт становится на порядок ценнее.
В этом посте хотел поделится своим решением, для упрощения жизни рядовым автоматизаторам :) Следующий скрипт представляет из себя небольшой модуль, который поможет делегировать пользователю обработку каптчи, тем самым позволив вашим скриптам взять на себя окружающую эту самую каптчу рутину. Интерфейс максимально упрощён - с внутренней стороны представлен функцией с говорящим названием do_stuff(file), принимающей в качестве параметра имя файла или file-like object (например StringIO(image_buffer) ) , в общем то, что поймёт Image.open(). С внешней стороны - пользовательский интерфейс представлен простейшим Tk окошком с вашей картинкой, полем ввода и счётчиком ожидающих в очереди картинок. Так же имеется в окошке многофункциональный переключатель :) В моём случае, многопоточной обработки, он был использован для передачи сигнала об остановке потоку. Да, модуль будет рад обслужить сразу множество ваших потоков.


Например, для следующей картинки:




имеем такой результат:



$ python captcha_dialog.py  
('dvcv', 0)
собственно код - inside


import thread
from time import sleep

from threading import Event
from Queue import Queue
import Tkinter
import Image, ImageTk

CANVAS_W = 220
CANVAS_H = 70


class DialogWindow(object):
    def __init__(self):
        self.q = Queue()
        self.root = None
        self._qsize = 0  # counter
        self.__qsize = None  # Tk.Label
        thread.start_new_thread(self.process_queue, ())

    def get_qsize(self):
        return self._qsize
    def set_qsize(self, qs):
        self._qsize = qs
        if self.__qsize is not None:
            self.__qsize.config(text=str(qs)+' more')
    qsize = property(get_qsize, set_qsize)

    def draw_window(self):
        self.root = Tkinter.Tk()
        self.canvas = Tkinter.Canvas(self.root, height=CANVAS_H, width=CANVAS_W)
        self.canvas.grid(row=0,columnspan=3)

        self.__qsize = Tkinter.Label(self.root,text='0')
        self.__qsize.grid(row=1,column=0)

        self.user_input = Tkinter.StringVar()
        self.user_input.set('')
        self.entry = Tkinter.Entry(self.root,width=10,
                                   textvariable=self.user_input)
        entry = self.entry
        entry.bind("<Return>",func=lambda _event: self.root.quit())
        entry.bind("<KP_Enter>",func=lambda _event: self.root.quit())
        entry.focus()
        entry.grid(row=1,column=1)

        Tkinter.Button(self.root,text='Ok',
                       command=self.root.quit).grid(row=1,column=2)

        self.stop = Tkinter.IntVar(0)
        Tkinter.Checkbutton(self.root,text='Stop current thread',
                            variable=self.stop).grid(row=2,columnspan=3)

    def process_queue(self):
        while True:
            if self.q.empty():
                if self.root is not None:
                    self.root.destroy()
                    self.root = None
                    self.__qsize = None
                sleep(0.5)
            else:
                if self.root is None:
                    self.draw_window()
                self.current = self.q.get(True, 1)
                self.process_item(self.current)
                self.q.task_done()

    def process_item(self, item):
        image = item.image
        photo = ImageTk.PhotoImage(image)
        self.current_im_id = self.canvas.create_image(CANVAS_W/2,
                                                      CANVAS_H/2,image=photo)
        self.user_input.set('')
        self.stop.set(0)
        self.entry.focus()
        self.set_qsize(self.q.qsize())
        self.root.mainloop()
        #self.canvas.delete(self.current_im_id)
        self.current.captcha = self.user_input.get()
        self.current.stop = self.stop.get()
        self.current.callback()

    def exists(self):
        return self.root is not None

    def put(self, item):
        self.q.put(item)
        self.set_qsize(self.q.qsize())

d = DialogWindow()

class QueueItem(object):
    image = None
    callback = None
    __captcha = None
    stop = False
    def __init__(self, image, callback):
        self.image = image
        self.callback = callback

    def set_captcha(self, captcha):
        self.__captcha = captcha
        self.callback()

    def get_captcha(self):
        return self.__captcha
    captcha = property(get_captcha, set_captcha)

def push_image(image):
    done_event = Event()
    q_item = QueueItem(image, done_event.set)
    d.put(q_item)
    done_event.wait()
    return (q_item.captcha, q_item.stop)

def do_stuff(file='aa.gif'):
    im = Image.open(file)
    (captcha, stop) = push_image(im)
    return (captcha, stop)

if __name__ == '__main__':
    print do_stuff()

Как говорится, ни в коем случае не запускайте этот код, он может повредить вашу систему. Код размещён исключительно в ознакомительных целях, используйте на свой страх и риск.

P.S. насчёт математического подхода - это было под впечатлением от лекций по “теории информации и кодирования” о распознавании образов и пр., но это уже следующим этапом.

Комментариев нет:

Отправить комментарий