Парсер листа персонажей Dungeons and Dragons через Python

Суть этого проекта проста, но некоторые советы от тех, кто считает, что им есть что добавить, будут оценены по достоинству.

Назначение: Приложение предназначено для входа в аккаунт на Myth-Weavers (https://www.myth-weavers.com/) и вернуть имена всех листов Dungeons and Dragons, которые были созданы для учетной записи. Этот

Приложение также должно иметь возможность использовать прямую ссылку (https://www.myth-weavers.com/sheet.html#id=2311944). Теоретически это возможно, потому что вы можете получить доступ к ссылке и соответствующему листу, не входя в Myth-Weavers.

ЧАСТЬ ПЕРВАЯ: Мне нужно, чтобы приложение могло войти на сайт и использовать мои учетные данные для входа в мою учетную запись. Когда я захожу на сайт, по сети отправляются следующие данные формы:

vb_login_username: Testbug Jones
vb_login_password: 
s: 
securitytoken: guest
do: login
vb_login_md5password: fea5ff2cf4764d2e76ea81e68bb458d1
vb_login_md5password_utf: fea5ff2cf4764d2e76ea81e68bb458d1

Я использую следующий код, чтобы проверить мой прогресс через вход в систему:

import requests

headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) 
Chrome/85.0.4183.121 Safari/537.36'
  }

login_data = {
    's' : '',
    'securitytoken' : 'guest',
    'vb_login_username' : 'Testbug Jones',
    'vb_login_password' : 'TeStBuG',
    'redirect' : 'index.php',
    'login' : 'Login',
    'vb_login_md5password' : 'fea5ff2cf4764d2e76ea81e68bb458d1',
    'vb_login_md5password_utf' : 'fea5ff2cf4764d2e76ea81e68bb458d1'
}


#get page
url = 'https://www.myth-weavers.com/'
source = requests.get(url)

#isolates login form, along with an sid
print('\n\n***CURRENT LOGIN STATUS***')
login_status = source.text
login_status = login_status.split("<!-- login form -->")[1]
login_status = login_status.split("<!-- / login form -->")[0]
print(login_status)

#nab sid and update library
sid  = login_status.split('<input type="hidden" name="s" value="')[1]
sid = sid.split('" /')[0]
login_data['s'] = sid

#create session and attempt to log in
with requests.Session() as s:
  print('\n\n***ATTEMPTING TO LOGIN***')
  r = s.post(url, data = login_data, headers = headers)
  login_status = r.text
  login_status = login_status.split("<!-- login form -->")[1]
  login_status = login_status.split("<!-- / login form -->")[0]
  print(login_status)

Что касается самой формы входа, то обычно она выглядит так:

<li class="smallfont" id="login" style="width: auto; float: right; text-align: right; padding-right: 6px;">
        <span id="login_register"><a href="#" onclick="fetch_object('login_register').style.display = 'none'; fetch_object('login_form').style.display = ''; return false;" tabindex="0">Log In</a> / <a href="https://www.myth-weavers.com/register.php?s=f4cde1e552e96a9a2b4c4479559e6510">Register</a> <a href="//www.myth-weavers.com/login.php?do=lostpw" style="font-size:smaller">forgot password?</a></span>
        <form id="login_form" style="display: none;" action="https://www.myth-weavers.com/login.php?do=login" method="post" onsubmit="md5hash(vb_login_password, vb_login_md5password, vb_login_md5password_utf, 0)">
        <script type="text/javascript" src="//static.myth-weavers.com/clientscript/vbulletin_md5.js?v=388"></script>

        <input type="text" class="bginput" style="font-size: 10px" name="vb_login_username" id="navbar_username" size="10" accesskey="u" tabindex="0" value="User Name" onfocus="if (this.value == 'User Name') this.value = '';" onblur="if (this.value == '') this.value = 'User Name';" />

        <input type="password" class="bginput" style="font-size: 10px" name="vb_login_password" id="navbar_password" size="10" tabindex="0" value="Password" onfocus="if (this.value == 'Password') this.value = '';" onblur="if (this.value == '') this.value = 'Password';" />

    <label for="cb_cookieuser_navbar"><input type="checkbox" name="cookieuser" value="1" tabindex="0" id="cb_cookieuser_navbar" accesskey="c" />Remember Me?</label>

        <input type="submit" class="button" value="Log in" tabindex="0" title="Enter your username and password in the boxes provided to login, or click the 'register' button to create a profile for yourself." accesskey="s" />

        <input type="hidden" name="s" value="f4cde1e552e96a9a2b4c4479559e6510" />
        <input type="hidden" name="securitytoken" value="guest" />
        <input type="hidden" name="do" value="login" />
        <input type="hidden" name="vb_login_md5password" />
        <input type="hidden" name="vb_login_md5password_utf" />
        </form>
</li>

На данный момент я думаю, что меня останавливает 1) синтаксис, поскольку я, очевидно, новичок, 2) файлы cookie не обрабатываются правильно или 3) securitytoken / sid не обрабатываются правильно, но я достигаю точки, когда я могу видеть мои ошибки, но не способ их преодоления. Любая помощь или понимание в преодолении этого было бы очень полезно!

ЧАСТЬ ВТОРАЯ: Это позволит мне получить доступ к странице на сайте, в частности к странице Листов, и распечатать список всех найденных там Листов Персонажей. Он также сможет получить файлы JSON, хранящиеся в строках таблицы, в которых найдены имена персонажей.


person Desi Dao    schedule 30.09.2020    source источник


Ответы (1)


Вы должны сделать первый запрос, используя requests.Session(), чтобы получить файлы cookie и отправить их обратно, когда вы делаете сообщение /login.php. Кроме того, вы можете использовать BeautifulSoup, чтобы получить все входное имя/значение в форме входа, поэтому вы просто добавляете свое имя пользователя/пароль (так что вы не жестко кодируете что-либо, кроме имени пользователя/пароля)

Пароль хэшируется md5, поэтому вы можете использовать hashlib для его кодирования.

Вызов входа в систему выполняется следующим образом:

import requests
from bs4 import BeautifulSoup
import hashlib

url = "https://www.myth-weavers.com"
username = "Testbug Jones"
password = "TeStBuG"

s = requests.Session()
r = s.get(url)

soup = BeautifulSoup(r.text, "html.parser")
form = soup.find("form",{"id":"login_form"})
payload = dict([(t.get("name"),t.get("value","")) 
    for t in form.findAll("input")
    if t.get("name")
])

md5 = hashlib.md5(password.encode('utf-8')).hexdigest()
payload["vb_login_username"] = username
payload["vb_login_password"] = password
payload["vb_login_md5password"] = md5
payload["vb_login_md5password_utf"] = md5

r = s.post(f"{url}/login.php", 
    params= {"do": "login"},
    data = payload
)

Затем вы можете использовать s.get(".....") для получения данных листов следующим образом:

r = s.get(f"{url}/sheets")
soup = BeautifulSoup(r.text, "html.parser")
rows = soup.find("table").find_all("tr")[1:]
sheet_data = []
for row in rows:
    tds = row.find_all("td")
    download_link = f'{url}{tds[5].find("a")["href"]}'
    json = s.get(download_link)
    sheet_data.append({
        "name": tds[1].text.strip(),
        "template": tds[2].text.strip(),
        "game": tds[3].text.strip(),
        "download_link": download_link,
        "json": json.json()
    })

print(sheet_data)

запустите это на repl.it

person Bertrand Martel    schedule 30.09.2020
comment
Спасибо! Это решило проблему, но, что более важно, дало мне полезный указатель на литературу по серверной части, которую мне нужно прочитать (например, кодирование и BeautifulSoup). Однако у меня есть дополнительный вопрос: как вы узнали, что нужно игнорировать требование пользовательского агента? Каждое видео, которое я просматривал до сих пор, обязательно подчеркивало важность его получения. - person Desi Dao; 03.10.2020
comment
Может случиться так, что какой-то сервер/веб-сайт проверяет, чтобы заголовок пользовательского агента был из действительного браузера. В этом случае он не проверяет никаких заголовков. На практике, когда я копирую http-вызов из консоли разработчика Chrome. Я щелкаю правой кнопкой мыши «копировать как curl» и удаляю все заголовки, чтобы увидеть, работает ли он без всего. - person Bertrand Martel; 03.10.2020