Helpers to authenticate and programmatically use external websites.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

84 lines
2.4 KiB

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
)