So, I used Microsoft Edge dev tools (F12), to look at the page source and request headers for each step.
This actually looks fairly simple to use standard techniques..
In the initial qrz.com/login response, there is some dynamic code. If you parse that, you will find "loginTicket", which you'll need to store:
if (step == 1) {
jQuery.ajax({
dataType: "json",
url: '/login-handshake',
data: {'loginTicket': TOKENSTRING, 'username': jQuery('.login-container #username').val(), 'step' : 1},
method: 'post',
(here I've replaced the actual string).
Typing user name and pressing submit, results in a request to /login-handshake, which contain these fields:
loginTicket: TOKENSTRING
step: 1
username: YOURUSERNAME
Next, typing password, results in another post to /login-handshake, containing:
loginTicket: TOKENSTRING
password: YOURPASSWORD
step: 2
username: YOURUSERNAME
Finally, if the handshake works, there is a final post to /login:
'2fcode': '' (empty string)
'flush': 1
'login_ref': 'https%3A%2F%2Fwww.qrz.com%2F'
'password': YOURPASSWORD
'target': '%2F'
'username': YOURUSERNAME
After that, you get a cookie, which you keep using.
Turns out, the whole handshake seems unnecessary:
import requests
data = {'2fcode':'', 'flush': 1, 'login_ref':'https%3A%2F%2Fwww.qrz.com%2F', 'password':YOURPASSWORD, 'target':'%2F','username':YOURUSERNAME}
sess=requests.Session()
sess.get('http://qrz.com/login')
sess.post('http://qrz.com/login', data=data)
querydata={'tquery':'VA6BH', 'mode':'callsign'}
r=sess.post('https://qrz.com/lookup',data=querydata)
You can then pass r.content into bs4:
soup = bs4(r.content)
csdata=soup.find_all('td',id='csdata')
You can then parse the resulting table