from urllib.parse import urljoin
|
|
|
|
import requests
|
|
from lxml.etree import HTML
|
|
|
|
from .exceptions import *
|
|
|
|
|
|
class WebClientForm:
|
|
def __init__(self, form):
|
|
self.action = form.attrib['action']
|
|
self.method = form.attrib.get('method', "GET").upper()
|
|
self.data = {i.attrib['name']: i.attrib.get('value', "")
|
|
for i in form.xpath("//input")
|
|
if 'name' in i.attrib}
|
|
|
|
def update(self, items):
|
|
for k, v in items.items():
|
|
self[k] = v
|
|
|
|
def __getitem__(self, k):
|
|
return self.data[k]
|
|
|
|
def __setitem__(self, k, v):
|
|
self.data.update({k: v})
|
|
|
|
def __contains__(self, k):
|
|
return k in self.data
|
|
|
|
def __iter__(self):
|
|
return self.data.items()
|
|
|
|
def __repr__(self):
|
|
return str((self.method, self.action, self.data))
|
|
|
|
|
|
class WebClient(requests.Session):
|
|
def __init__(self, headers={}, **kwargs):
|
|
self.url = ""
|
|
self.res = None
|
|
self.form = None
|
|
super().__init__(**kwargs)
|
|
self.headers.update(headers)
|
|
|
|
def send(self, *args, **kwargs):
|
|
r = super().send(*args, **kwargs)
|
|
self.res = r
|
|
self.url = r.url
|
|
self.status_code = r.status_code
|
|
self.form = None
|
|
return r
|
|
|
|
def find_forms(self, **filters):
|
|
if not self.res:
|
|
return []
|
|
tree = HTML(self.res.content)
|
|
filters_xpath = [f"[@{k}='{v}']" for k, v in filters.items()]
|
|
filters_xpath = "".join(filters_xpath)
|
|
return tree.xpath("//form" + filters_xpath)
|
|
|
|
def select_form(self, **filters):
|
|
forms = self.find_forms(**filters)
|
|
if len(forms) != 1:
|
|
if forms:
|
|
raise TooManyFormsError(self.res, filters)
|
|
else:
|
|
raise FormNotFoundError(self.res, filters)
|
|
self.form = WebClientForm(forms[0])
|
|
|
|
def update_form(self, **arguments):
|
|
if not self.form:
|
|
raise NoFormSelectedError('update_form')
|
|
|
|
def submit_form(self):
|
|
if not self.form:
|
|
raise NoFormSelectedError('submit_form')
|
|
if self.form.method not in ["POST"]:
|
|
raise NotImplementedError(
|
|
f"Submit method '{self.form.method}' not supported.")
|
|
self.request(
|
|
self.form.method,
|
|
urljoin(self.url, self.form.action),
|
|
data=self.form.data
|
|
)
|