【问题标题】:Can't get a certain item from a webpage using requests无法使用请求从网页中获取特定项目
【发布时间】:2020-01-28 14:01:15
【问题描述】:

我创建了一个脚本来从网页上抓取nameemail 地址。当我运行我的脚本时,我相应地得到name,但如果是email,这就是我得到的aeccdcd7cfc0eedadcc783cdc1dc80cdc1c3。每次运行脚本时,我得到的字符串而不是 email 都会发生变化。

Website link

到目前为止我已经尝试过:

import requests
from bs4 import BeautifulSoup

url = "https://www.seafoodsource.com/supplier-directory/Tri-Cor-Flexible-Packaging-Inc"

res = requests.get(url,headers={"User-Agent":"Mozilla/5.0"})
soup = BeautifulSoup(res.text,'lxml')
name = soup.select_one("[class$='-supplier-view-main-container'] > h1").text
email = soup.select_one("[class='__cf_email__']").get("data-cfemail")
print(f'{"Name: "}{name}\n{"Email: "}{email}')

当前输出:

Name: Tri-Cor Flexible Packaging Inc
Email: aeccdcd7cfc0eedadcc783cdc1dc80cdc1c3

预期输出:

Name: Tri-Cor Flexible Packaging Inc
Email: bryan@tri-cor.com

PS 我不追求与任何浏览器模拟器相关的任何解决方案,例如 selenium。

如何使用请求从该页面获取该电子邮件?

【问题讨论】:

  • 您是否确认电子邮件确实存在于您获得的数据中?例如,将 res.text 保存到文件并在编辑器中检查。
  • 你试过用例如窥探吗? Telerik fiddler 在浏览器中打开页面以查看如何从服务器检索电子邮件地址时?然后在您的请求 GET 中模拟标头/参数/数据。
  • 电子邮件受保护?
  • 这是不使用js浏览器或驱动程序得到的<b>Contact email: </b> <a href="/cdn-cgi/l/email-protection#b2d0c0cbd3dcf2c6c0db9fd1ddc09cd1dddf"><span class="__cf_email__" data-cfemail="5f3d2d263e311f2b2d36723c302d713c3032">[email protected]</span></a>

标签: python python-3.x web-scraping beautifulsoup


【解决方案1】:

您必须解码电子邮件。

import requests
from bs4 import BeautifulSoup

def cfDecodeEmail(encodedString):
    r = int(encodedString[:2],16)
    email = ''.join([chr(int(encodedString[i:i+2], 16) ^ r) for i in range(2, len(encodedString), 2)])
    return email

url = "https://www.seafoodsource.com/supplier-directory/Tri-Cor-Flexible-Packaging-Inc"

res = requests.get(url,headers={"User-Agent":"Mozilla/5.0"})
soup = BeautifulSoup(res.text,'lxml')
name = soup.select_one("[class$='-supplier-view-main-container'] > h1").text
email = cfDecodeEmail(soup.select_one("[class='__cf_email__']").get("data-cfemail"))
print(f'{"Name: "}{name}\n{"Email: "}{email}')

输出:

Name: Tri-Cor Flexible Packaging Inc
Email: bryan@tri-cor.com

【讨论】:

    【解决方案2】:

    简短的回答是您必须解码电子邮件字符串,因为它被混淆了。

    以下是您必须解码从seafoodsource.com 获得的电子邮件字符串的原因。

    网站seafoodsource.com正在使用Cloudflare,这是一家为客户提供网站安全、DDoS缓解和其他服务的美国公司。

    我通过 pingseafoodsource.com 确定该站点正在使用 Cloudflare,该网站返回的 IP 地址为 104.24.19.99。根据 ARIN(美国互联网号码注册机构),该 IP 地址属于网络块 104.16.0.0 - 104.31.255.255,该网络块已注册到 Cloudflare

    您的汤中的字符串 cf_email 也表明该电子邮件地址受到 Cloudflare(CF) 的保护。另一个迹象是此警告message,当您在查看页面源时单击受保护的链接时会显示此警告。

    Cloudflare 电子邮件地址混淆功能通过向电子邮件收集器和其他机器人隐藏目标网站上出现的电子邮件地址来帮助预防垃圾邮件,但普通网站访问者可以看到该电子邮件。

    在这种保护下,电子邮件地址会变成一系列十六进制编码的可变长度字节,具体取决于电子邮件地址的长度。

    值得注意的是,这种编码方法并不是为了安全地加密电子邮件地址而设计的,因为它在密码学上很弱,但它只是旨在混淆非正在搜索 mailto 的智能网络爬虫:HTML 代码中的链接。换句话说,这种编码方法用于混淆电子邮件地址,但不能完全强制其保密。

    您问题中的编码电子邮件地址是:

    aeccdcd7cfc0eedadcc783cdc1dc80cdc1c3

    此电子邮件地址的第一个字节是 ae 或十六进制 0xae。该字节是用于加密和解密剩余字节的密钥,方法是将密钥与每个后续字节按位异或。

    例如:

    0xae ^ 0xcc 是十六进制的 62,转换为 ASCII 中的 b

    0xae ^ 0xdc 是十六进制的 72,转换为 ASCII 中的 r

    0xae ^ 0xd7 是十六进制的 79,转换为 ASCII 中的 y

    0xae ^ 0xcf 是十六进制的 61,转换为 ASCII 中的 a

    0xae ^ 0xc0 是十六进制的 6e,转换为 ASCII 中的 n

    这拼写为 bryan,这是解码后的电子邮件地址的第一部分。

    此代码中发生了按位异或:

    chr(int(encoded_string[i:i+2], 16) ^ base_16)

    让我进一步解释:

    编码字符串的第一个字节是密钥,在本例中为 ae 或 0xae。

    如果我们将 0xae 转换为十进制,它会变成 174。

    当我们将下一个字节 0xcc 转换为十进制时,它变成了 204。

    让我们使用按位运算符 ^ 转换这些小数。

    ^ 按位异或

    返回两个整数按位异或的结果。

    first_byte = 174 # ae
    second_byte = 204 # cc
    xor_decimal = first_byte ^ second_byte 
    print (xor_decimal) 
    # outputs 
    98
    

    让我们将这些十进制转换为十六进制(base-16)。我们可以使用 Python 中的内置函数“hex”来完成。

    first_byte = 174 # ae
    second_byte = 204 # cc
    xor_decimal = first_byte ^ second_byte 
    print (hex)xor_decimal)
    # outputs 
    62
    

    正如我之前提到的十六进制 62,转换为 ASCII 中的 b

    让我们看看编码字符串中的下一个字节迭代。

    first_byte = 174 # ae
    next_byte = 220 # dc
    xor_decimal = first_byte ^ next_byte 
    print (hex)xor_decimal)
    # outputs 
    72
    

    正如我之前提到的十六进制 72,转换为 ASCII 中的 r

    我觉得展示如何将十六进制字符串转换为十进制是相关的。

     # without the 0x prefix
     decimal = int('ae', 16)
     print (decimal)
     # outputs
     174 
    
     # with the 0x prefix
     decimal = int('0xae', 0)
     print (decimal)
     # outputs
     174 
    

    混淆电子邮件地址的 ASCII 文本到十六进制转换:

    ASCII 电子邮件地址:bryan@tri-cor.com

    十六进制电子邮件地址:62 72 79 61 6e 40 74 72 69 2d 63 6f 72 2e 63 6f 6d

    我们可以使用 Python 中的内置函数 bytearray 来解码这个十六进制字符串:

    hex_string = '62 72 79 61 6e 40 74 72 69 2d 63 6f 72 2e 63 6f 6d'
    ascii_conversion = bytearray.fromhex(hex_string).decode()
    print (ascii_conversion)
    # outputs
    bryan@tri-cor.com
    

    混淆电子邮件地址的 ASCII 文本到十进制转换:

    ASCII 电子邮件地址:bryan@tri-cor.com

    十进制电子邮件地址:98 114 121 97 110 64 116 114 105 45 99 111 114 46 99 111 109

    如果我们将十进制 174(即混淆字符串中的 ae)添加到十进制电子邮件地址的头部:

    十进制电子邮件地址:174 98 114 121 97 110 64 116 114 105 45 99 111 114 46 99 111 109

    ASCII 电子邮件地址:®bryan@tri-cor.com

    看起来 ® 是用作您问题中混淆字符串的密码密钥的 ASCII 字符。

    如果我没有提到二进制数和异或运算,那我就失职了。

    首字节转换:

    • 十六进制数:ae
    • 十进制数:174
    • 十六进制(base-16):98
    • 二进制数:10101110
    • ascii 文本:®

    第二个字节转换:

    • 十六进制数:cc
    • 十进制数:204
    • 十六进制(base-16):62
    • 二进制数:11001100
    • ascii 文本:b

    我们可以对上面的二进制数执行相同的^位异或运算:

    # the notation 0b in front of the number is used to express that the value is 
    # a binary literal
    first_byte_binary = 0b10101110
    second_byte_binary = 0b11001100
    xor_binary = first_byte_binary ^ second_byte_binary
    print (bin(xor_binary))
    # outputs
    0b1100010
    
    print (xor_binary)
    # outputs 
    98
    
    print (hex(xor_binary))
    # outputs
    0x62
    
    ascii_conversion = bytearray.fromhex(hex(xor_binary)[2:]).decode()
    print (ascii_conversion)
    # outputs
    b
    

    以下是解码 Cloudflare 混淆电子邮件地址的方法。

    import requests
    from bs4 import BeautifulSoup
    
    url = "https://www.seafoodsource.com/supplier-directory/Tri-Cor-Flexible-Packaging-Inc"
    
    raw_html = requests.get(url,headers={"User-Agent":"Mozilla/5.0"})
    soup = BeautifulSoup(raw_html.text,'lxml')
    
    company_information = []
    
    def get_company_name(soup):
      company_name = soup.find('li', {'class': 'active'}).text
      company_information.append(company_name)
      return
    
    def decode_cloudflare_protected_email(encoded_string):
        # converting the encoding string to int base 16
        base_16 = int(encoded_string[:2], 16)
        decoded_email = ''.join([chr(int(encoded_string[i:i+2], 16) ^ base_16) for i in range(2, len(encoded_string), 2)])
        company_information.append(decoded_email)
        return
    
    get_company_name(soup)
    
    encoded_email = soup.select_one("[class='__cf_email__']").get("data-cfemail")
    decode_cloudflare_protected_email(encoded_email)
    
    print (company_information)
    # outputs
    ['Tri-Cor Flexible Packaging Inc', 'bryan@tri-cor.com']
    

    如果您对探索 XOR 加密感兴趣,我建议您查看 xortool,这是 Aleksei Hellman 的 Github 项目。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-29
      • 2020-08-30
      • 2020-09-13
      • 2018-06-01
      • 1970-01-01
      • 2020-02-14
      • 1970-01-01
      相关资源
      最近更新 更多