banner

Заполняем формы используя BeautifulSoup

Думаю не надо объяснять зачем заполнять всевозможные формы на сайтах. В данной статье я покажу простую реализацию автозаполнения форм с использованием модуля BeatifulSoup.
Она лишь покажет примерный план действий и принцип работы многих универсальных постилок на более "Высокоуровневом" коде". Для того, чтобы скрипт знал какие формы заполнять, нам потребуется база с признаками для заполнения. Я разобью её в 3 файла.

  • overlap.txt - не полные совпадения имен полей.
  • fullmatch.txt -полные совпадения имен полей.
  • ignore.txt - фаил с не изменяемыми полями.


Формат у первых двух файлов: поле|значение
Формат у последнего файла: поле
Чтобы знать куда вставлять не статические данные, а генерируемые потребуются макросы.
В примере я использую 3 макроса: [mail],[comm],[name]. Они будут заменять значение в полях.

#coding:utf-8
from BeautifulSoup import BeautifulSoup
import urllib,urllib2,re
from urlparse import *
class Poster():
    def __init__(self):
        self.load()
        self.prepare()
    # Загружаем файлы
    def load(self):
        self.fullmatch = open("fullmatch.txt").readlines()
        self.overlap = open("overlap.txt").readlines()
        self.ignore = open("ingore.txt").readlines()
        self.comment = "Comment"
    # Обрабатываем базы в удобный для чтения формат
    def prepare(self):
        for i in xrange(len(self.fullmatch)):
            temp = {}
            l,r =  self.fullmatch.decode("utf-8","ignore").split("|")
            temp[l]=r
            self.fullmatch = None
            self.fullmatch = temp
        for i in xrange(len(self.overlap)):
            temp = {}
            l,r = self.overlap.decode("utf-8","ignore").split("|")
            self.overlap = None
            self.overlap = temp
    # Обработка макросов
    def replace(self,data):
        data = data.replace('[mail]','email@email.ru')
        data = data.replace('[comm]',self.comment)
        data = data.replace('[name]','Nick')
        return data
    # Определяем полный Url, основываясь на относительном
    def getact(self,act,url):
        return urljoin(url,act)
    # Парсинг страницы и подготовка запроса
    def parse(self,url):
        data = urllib2.urlopen(url,timeout=self.timeout).read() # Скачиваем страницу
        res = re.compile(u"<form .*?>.*?</form>",re.I|re.M|re.S) # Выполняем поиск форм
        forms =  re.findall(res,data)
        # Для правильного определения нужной нам формы нужен другой алгоритм
        # Здесь используется очень простой алгоритм, исле в форме найден тэг textarea, то анализировать будем её 
        for form in forms:
            if form.find('textarea')>=0:
                data = form
                break
        # Так как BeautifulSoup очень чуствителен к ломаному html приходится предварительно обрабатывать его
        # В данном коде эта обработка  пропущена, если код битый то прекращаем обработку
        try:
            soup = BeautifulSoup(data)
        except:
            return False
        post = {} # Словарь с пост данными
        vals = {} # Словарь со всеми параметрами тэга
        # Поиск action поля в форме, для того, чтобы знать куда отправлять данные.
        try:
            act = soup('form')[0]['action']
        except:
            act = url
        # Определяем полный путь 
        act = self.getact(act,url)
        # Определяем метод, GET/POST
        try:
            method = soup('form')[0]['method'].lower()
        except:
            method = 'get'
        method = method.lower()
        # Находим все поля в форме с именами Input,textarea
        listinp = soup('input')
        listinp.extend(soup('textarea'))
        # Проходим их в цикле
        for input in listinp:
            # Если у поля есть имя
            if input.has_key('name'):
                # Получаем все параметры поля
                vals[input["name"]]= dict(input.attrs)
                # Если у поля есть значение записываем его
                # Иначе оставляем пусты
                if input.has_key('value'):
                    post[input['name']]=input['value']
                elif not input.has_key('value'):
                    post[input['name']]=''
        # Обходим все ключи предварительно подготовленного запроса
        for key in post.keys():
            # Если ключ в списке игнорирования, то не изменяем его
            if  key in self.ignore:
                continue
            # Если у поля есть параметр type и его значение hidden, то тоже не изменяем его
            if vals[key].has_key("type"):
                if vals[key]["type"].lower()=="hidden":
                    continue
            # Если в базе полных совпадений есть такое поле, то подменяем его на требуемое значение
            if self.fullmatch.has_key(key):
                post[key]=self.fullmatch[key]
            # Иначе пытаемся найти не полное совпадение
            else:
                for kk in self.overlap.keys():
                    if kk in key:
                        post[key]=self.overlap[kk]
        # Обрабатывавем макросы
        for key in post.keys():
            post[key]=self.replace(post[key])
        # Постинг
        if method=='post':
            data = urllib.urlencode(post)
            resp  = urllib2.urlopen(url, data, timeout=self.timeout).read()
        if method=='get':
            resp  = urllib2.urlopen(url+'?'+urllib.urlencode(post)).read()
        # Проверка постинга
        if resp.find(self.comment)>0:
            return True
        else:
            return False     
P = Poster()
P.parse('http://site.com')

Опять же повторяю здесь многое упущено. Например здесь нет определения кодировки страниц, генерации данных для макросов, нахождения других полей (select,inputbox) и т.д
Если собирайтесь писать что-то серьёзное, то такие библиотеки использовать не стоит. Они сильно влияют на нагрузку и скорость.

2 Ответов на “Заполняем формы используя BeautifulSoup”

  1. mrZaggi сказал:

    Да, BS крэшит от любого чиха =(. Давно отказался от него в пользу lxml.html. Вот где сила. Проглатывает почти все и не давится. Еще как то в интернете наткнулся на бенчмарки для ВS и lxml. Точных цифр не вспомню но разница что то вроде пары порядков в пользу lxml.

  2. rushter сказал:

    Я как-то раз хотел поставить lxml, но сразу не получилось что-то, так и не стал.

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