Скрап-сайт (marketchameleon) возвращает зашифрованные данные - PullRequest
0 голосов
/ 18 марта 2020

Я учусь очищать сайты с помощью python, сейчас только что использую запросы и BeautifulSoup ...

Я пытаюсь получить доступ к следующей странице: https://marketchameleon.com/Overview/BAX/Earnings/Earnings-Dates

Да, вам нужна подписка для просмотра всех данных, но она предназначена только для изучения, поэтому достаточно нескольких данных, отображаемых в браузере.

Вот как я получаю данные:

import requests
import urllib.request
from bs4 import BeautifulSoup
headers_Get = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.9',
    'Accept-Encoding': 'gzip, deflate, br',
    'DNT': '1',
    'Connection': 'keep-alive',
    'Upgrade-Insecure-Requests': '1'
}
url = 'https://marketchameleon.com/Overview/BAX/Earnings/Earnings-Dates'
response = requests.get(url, headers_Get)
soup = BeautifulSoup(response.text, “html.parser”)

Однако возвращаемые данные html кажутся зашифрованными (просто извлечение, поскольку зашифрованная часть довольно длинная):

<div class="symov_earnings">
<div class="flex_container_between flex_center_vertical">
<div class="dl-tbl-outer"><div class="dis-prem"><button class="_noprem prem-btn" onclick="site_OpenPremium();">Download Now</button><div class="dis-prem-pop"><p>Premium Feature</p><p><a href="/Account/Login">Login</a><span>|</span><a href="/Subscription/Compare">Subscribe</a></p></div></div></div>
</div>
<div cipherxx="OwA+ADwAOQA+ADwABABEAFcAVgBdAFYAEwBfAFwADQAUAEcASABeAGwAUABNAEQAaQBRAFAAQQBdAF8AVgBXAEUAFgARAFAAXwBXAEsAQwALABYAXABDAGwAWgBRAFcAXgBAAFMAXABBAFIAXQBCABQACgA8ADkAEwAWABgAEAAKAEAAWQBWAFIAUgAGAD0APAAUABEAEwATABYAGAAQABYACABFAEEAEwBVAFQAUQBFAEcADAARAF4AVwBRAF4AaQBcAFQAUgBXAF8AVgBXABQACgA8ADkAEwAWABgAEAAWABQAEQATABMAFgAYABAACgBAAFkAEwBQAFkAVABDAEYAVQBfAA4AEQAOABoADgBjAEQAUgBcAF4AXwBWAFcAFgBxAFAAQQBdAF8AVgBXAEUACAAeAEcAWwAIADUAOgAWABQAEQATABMAFgAYABAACgAbAEUAQQANA

Есть ли способ узнать, что происходит (как сайт защищен от скребков?) И получить действительные данные html?

Спасибо

Ответы [ 2 ]

2 голосов
/ 18 марта 2020

Данные действительно зашифрованы. Если вы посмотрите на JS файлы, которые являются частью веб-сайта, вы можете найти этот конкретный файл , который содержит функцию, используемую для дешифрования данных. Все это делается в Javascript, поэтому у вас есть 2 варианта:

  • использовать , чтобы очистить страницу, перекодировать функцию расшифровки javascript в python
  • используйте безголовый браузер, такой как

Используя первый вариант (перекодирование функции шифрования в ), вот как вы может сделать это:

import requests
from bs4 import BeautifulSoup
import base64
import json

url = "https://marketchameleon.com/Overview/BAX/Earnings/Earnings-Dates"

session = requests.Session()

r = session.get(url)
soup = BeautifulSoup(r.text, "html.parser")

key = session.cookies.get_dict()["v1"]
encryptedDivs = [ i["cipherxx"] for i in soup.find_all("div") if i.get("cipherxx")]

unencrypted = []
for div in encryptedDivs:
    encryptedData = base64.b64decode(div)
    cipher = "".join([
        chr(encryptedData[i]) 
        for i in range(0,len(encryptedData),2)
    ])
    data = ""
    for i in range(0, len(cipher)):
        c_num = ord(cipher[i])
        k_num = ord(key[i % len(key)])
        c2 = c_num ^ k_num
        data += chr(c2)

    unencrypted.append(data)

# unencrypted[0] is the header div with some info about stock price etc...
# unencrypted[1] is the first table
# lets parse the second table unencrypted[2]

soup = BeautifulSoup(unencrypted[2], "html.parser")

tbody = soup.find("tbody").findAll("tr", recursive=False)
thead = soup.find("thead").findAll("tr", recursive=False)

table2 = [
    {
        "Date": t[0].text.strip(),
        "Time": t[1].text.strip(),
        "Period": t[2].text.strip(),
        "Conference Call": t[3].text.strip(),
        "Price Effect" : t[4].find("span").text if t[4].find("span") else t[4].text.strip(),
        "Implied Straddle": t[5].text.strip(),
        "Closing Price": t[6].text.strip(),
        "Opening Gap": t[7].text.strip(),
        "Drift Since": t[8].text.strip(),
        "Range Since": t[9].text.strip(),
        "Price Change 1 Week Before":t[10].text.strip(),
        "Price Change 1 Week After": t[11].text.strip()
    }
    for t in (t.findAll('td', recursive=False) for t in tbody)
    if len(t) >= 11
]

print(json.dumps(table2, indent=4, sort_keys=True))

Обратите внимание, что ключ шифрования находится в Cook ie с именем v1 (поэтому вам нужно requests.Session())

Часть шифрования

Это XOR шифрование . Это XOR значения данных с ключом (в этом случае ключ хранится в кулинарии ie). Для расшифровки вам просто нужно XOR шифр с ключом, чтобы получить исходные данные.

Самый эффективный способ объяснить это - использовать пример:

  • data это строка "HELLO"
  • ключ это строка "97523022"
"H"       "E"        "L"        "L"        "O"
 72        69         76         76         79
 01001000  01000101   01001100   01001100   01001111


"9"       "7"        "5"        "2"        "3"
 57        55         53         50         51
 00111001  00110111   00110101   00110010   00110011

     01001000  01000101   01001100   01001100   01001111
XOR  00111001  00110111   00110101   00110010   00110011
==>  01110001  01110010   01111001   01111110   01111100         
       113        114       121        126        124
HEX   \x71       \x72      \x79       \x7E       \x7C


complete with 0s  :
HEX    \x71\x00 \x72\x00 \x79\x00 \x7E\x00 \x7C\x00

encode \x71\x00\x72\x00\x79\x00\x7E\x00\x7C\x00 to base64

which gives : 'cQByAHkAfgB8AA=='

попробуйте этот код для расшифровки (тот же код, что и код в начале вопроса):

key = "97523022"
payload = "cQByAHkAfgB8AA=="

data = base64.b64decode(payload)

cipher = "".join([
    chr(data[i]) 
    for i in range(0,len(data),2)
])
data = ""
for i in range(0, len(cipher)):
    c_num = ord(cipher[i])
    k_num = ord(key[i % len(key)])
    c2 = c_num ^ k_num
    data += chr(c2)

print(data)

Вывод:

HELLO

Вы также можете проверить эту ссылку и эту вики если вы заинтересованы

0 голосов
/ 18 марта 2020
import requests
from bs4 import BeautifulSoup

r = requests.get(
    "https://marketchameleon.com/Overview/BAX/Earnings/Earnings-Dates")


soup = BeautifulSoup(r.content, 'html.parser')

print(soup.prettify())

Вам не нужно передавать заголовки с этим конкретным сайтом. так как веб-сайт отвечает HTTP/1.1 200 OK, который не должен передавать вид headers.

Вы можете проверить ответ curl, чтобы убедиться, что вы находитесь справа дорожка:

curl -D - https://marketchameleon.com/Overview/BAX/Earnings/Earnings-Dates
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...