Jak vyplnit webový formulář z Pythona

Teorie

Pokud znám políčka a strukturu předem, pouze vytvořím příslušnou HTTP hlavičku následovanou daty s odpověďmi (viz specifikace HTTP protokolu) a pošlu vše přes sokety na příslušnou IP adresu a port a rozeberu vrácenou HTTP odpověď pro případnou kontrolu chyb.

Pokud neznám políčka a strukturu předem musím před to zařadit krok parsování HTML/XHTML stránky - tj. extrakce značky <form>, jeho atributů action a method (případně encrypted) a všech polí <input>, <textarea>, <file> a já nevím co všechno tam může být.

Rozbor HTML

Mějme demonstrační stránku http://spreadsheets.google.com/viewform?key=piYHG7JxUHxstW-8oPftQHQ. Nejdříve si ji skuste vypnit ručně, a podívat se na výsledky http://spreadsheets.google.com/pub?key=piYHG7JxUHxstW-8oPftQHQ (zobrazí se po 5 minutách).

Nyní si rozebereme html kod z demonstrační stránky. Přeformátováno pro lepší čitelnost:

<body><h1>Anonymní dotazník</h1>
<pre class="ss-form-desc">
tento dotazník pomůže lidem na celém světě ;-)
      </pre>
    <p>
    </p>
    <form action="http://spreadsheets.google.com/formResponse?key=piYHG7JxUHxstW-8oPftQHQ" method="POST">
        <div class="ss-form-entry">
            <span class="ss-q-title">Jméno </span>
            <span class="ss-q-help">  </span>
            <input type="text" class="ss-q-short" name="single:7" />
        </div>
        <div class="ss-form-entry">
            <span class="ss-q-title">Město </span>
            <span class="ss-q-help">  </span>
            <input type="text" class="ss-q-short" name="single:8" />
        </div>
        <div class="ss-form-entry">
            <span class="ss-q-title">#  Jak se nazývá typ žloutenky - nemoc špinavých rukou? </span>
            <span class="ss-q-help">chřipka to není ;-) </span>
            <ul class="ss-choices"><li>
                <input type="radio" value=" žloutenka typu A" name="group:2" checked="checked" /> žloutenka typu A</li> <li>
                <input type="radio" value=" žloutenka typu B" name="group:2" /> žloutenka typu B</li> <li>
                <input type="radio" value=" žloutenka typu C" name="group:2" /> žloutenka typu C</li>
            </ul>
        </div>
        <div class="ss-form-entry">
            <span class="ss-q-title">Jaké příznaky se nevyskytují u infarktu myokardu?  </span>
            <span class="ss-q-help">   </span>
            <ul class="ss-choices"><li>
                <input type="checkbox" value="premenstruční syndrom" name="group:3" />premenstruční syndrom</li> <li>
                <input type="checkbox" value="zánět močového měchýře" name="group:3" />zánět močového měchýře</li> <li>
                <input type="checkbox" value="bolest na hrudi" name="group:3" />bolest na hrudi</li>
            </ul>
        </div>
        <div class="ss-form-entry">
            <span class="ss-q-title">Napište něco o sobě  </span>
            <span class="ss-q-help">jen tak ...   </span>
            <textarea class="ss-q-long" name="single:5" rows="8" cols="75"></textarea>
        </div>
        <p>
        </p>
        <input type="submit" value="Odeslat" />
    </form>
    <span class="ss-powered-by">používá technologii Dokumenty Google
    </span>
    <p>
    </p><small>
        <a href="http://www.google.com/accounts/TOS">Smluvní podmínky služby</a>-
        <a href="http://www.google.com/google-d-s/terms.html">Další smluvní podmínky</a></small>
</body>
  • na řádce 6 vidíme, jaká stránka se volá, když se formulář odesílá, ten použijeme i my. Tedy bude to http://spreadsheets.google.com/formResponse?key=piYHG7JxUHxstW-8oPftQHQ
  • je tam 5 polí na vyplnění, každé se nějak jmenuje. Hledejte vždy parametr name= ... :
  • Jméno = single:7
  • Město = single:8
  • Jak se nazývá typ žloutenky - nemoc špinavých rukou? = group:2 (je napsáno u jednotlivých položek)
  • Jaké příznaky se nevyskytují u infarktu myokardu? = group:3
  • Napište něco o sobě = single:5

Možná řešení

urlib

# -*- coding: utf-8 -*-
import urllib, urllib2

adresa= "http://spreadsheets.google.com/formResponse?key=piYHG7JxUHxstW-8oPftQHQ"
parametry= {
    "single:7": "Bystroushaak",
    "single:8": "Litomerice",
    "group:2":  "Žloutenka typu B",
    "group:3":  "Zánět močového měchýře",
    "single:5": "Tento formular byl vyplnen scriptem od Bystroushaaka!"}

params= urllib.urlencode(parametry)  # Prekoduje parametry do tvaru vhodneho pro odeslani
req= urllib2.Request(adresa, params) # Vytvori request, coz je smichanina adresy a parametru, pripadne i hlavicek

spojeni = urllib2.urlopen(req)       # Otevre
spojeni.read()                       # a nacte stranku
spojeni.close()

Program je jistě možno doplnit o načítání jednotlivých parametrů přes raw_input() nebo pomocí jakéhokoliv GUI, ale to si jistě laskavý čtenář udělá sám nebo se zeptá v konferenci.

zope.testbrowser

Stáhněte a nainstalujte modul z http://pypi.python.org/pypi/zope.testbrowser - úplně dole je soubor ke stažení. Kod programu:

# -*- coding: utf-8 -*-

from zope.testbrowser.browser import Browser
browser = Browser('http://spreadsheets.google.com/viewform?key=piYHG7JxUHxstW-8oPftQHQ')

form = browser.getForm()

# ukázka získání možných voleb
c = form.getControl(name='group:2')
print c.options

# vyplnění některých polí
c.getControl(value=' žloutenka typu C').selected = True
form.getControl(name='single:7').value = 'Honza Nový'
form.getControl(name='single:8').value = 'Nová Ves'
form.getControl(name='group:3').value = ['premenstruční syndrom', 'zánět močového měchýře']
form.getControl(name='single:5').value = 'pěkný příklad'

form.submit()
print browser.contents

Možné trable

Je možné, že časem narazíte na problém se souborem robots.txt. Poznáte to tak, že vám nepůjde načíst stránka a jako chybu bude Python hlásit toto:

Traceback (most recent call last):
 File "<stdin>", line 3, in <module>
 File "c:\python25\lib\site-packages\zope.testbrowser-3.4.2-py2.5.egg\zope\test browser\browser.py", line 224, in open
   self.mech_browser.open(url, data)
 File "build\bdist.win32\egg\mechanize\_mechanize.py", line 203, in open
 File "build\bdist.win32\egg\mechanize\_mechanize.py", line 254, in _mech_open
mechanize._response.httperror_seek_wrapper: HTTP Error 403: request disallowed by robots.txt

Existují dva způsoby jak to vyřešit:

  1. dočasná úprava konfigurace objektu Browser:
>>> >>> from zope.testbrowser.browser import Browser
>>> >>> browser= Browser()
>>> >>> browser.mech_browser.set_handle_robots(False)
  1. trvalá úprava:
  • otevřete si soubor Python25\Lib\site-packages\zope.testbrowser-3.4.2-py2.5.egg\zope\testbrowser\browser.py
  • přidejte 160 řádek v tomto tvaru: self.mech_browser.set_handle_robots(False)
  • výsledek by měl vypadat nějak takto: http://img84.imageshack.us/my.php?image=broyu3.jpg