【问题标题】:Scraping website (marketchameleon) returns encrypted data抓取网站(marketchameleon)返回加密数据
【发布时间】:2020-03-18 16:55:02
【问题描述】:

我正在学习如何使用 python 抓取网站,目前只使用 requests 和 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 数据?

谢谢

【问题讨论】:

  • 你可能被屏蔽了,我已经试过你的代码了。你可以检查状态码打印(response.status_code)
  • @m-zayan 被告知不被视为block 的类型,该站点在页面加载后动态运行JavaScript 代码,因此,cipherxx 是一种@987654327 @ 为HTML 解码器持有value
  • 感谢 @αԋɱҽԃαмєяιcαη 先生,当我运行相同的代码时,一切正常。我正在从不同的服务器运行相同的代码。这就是为什么我猜他可能被阻止了。
  • @m-zayan 这可能是由于request 本身使用的区域或后端dns。您可以通过curlr.status_code 进行验证

标签: python web-scraping beautifulsoup


【解决方案1】:

数据确实是加密的。如果您查看网站中的 JS 文件,您会发现 this particular file 包含用于解密数据的函数。所有这些都是在 Javascript 中完成的,所以你有 2 个选项:

  • 使用抓取页面,在python中重新编码javascript解密函数
  • 使用像这样的无头浏览器

使用第一个选项(在 中重新编码加密函数),您可以这样做:

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))

请注意,加密密钥位于名为 v1 的 cookie 中(这就是您需要 requests.Session() 的原因)

加密部分

这是XOR encryption。它将数据的值与键进行异或(在这种情况下,键存储在 cookie 中)。解密时,只需将密码与密钥进行异或运算,即可取回原始数据。

最有效的解释方式是举个例子:

  • 数据是字符串“HELLO”
  • key是字符串“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)

输出:

你好

有兴趣的也可以查看this linkthis wiki

【讨论】:

  • 如果您不介意,请您解释一下cipherdata这两个块吗?
  • @αԋɱҽԃαмєяιcαη 我添加了一些关于异或加密的解释
【解决方案2】:
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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-14
    • 2020-10-07
    • 2012-07-07
    • 2014-07-06
    • 2022-01-10
    • 1970-01-01
    相关资源
    最近更新 更多