无法使用请求从网页上获取某项内容

时间:2019-09-28 16:18:30

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

我创建了一个脚本来从网页中抓取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我不喜欢任何与浏览器模拟器相关的解决方案,如硒。

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

2 个答案:

答案 0 :(得分:9)

您必须对电子邮件进行解码。

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

答案 1 :(得分:7)

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

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

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

我通过ping Seafoodsource.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。该字节是一个密钥,用于通过将密钥与每个后续字节按位进行XOR来加密和解密其余字节。

     

例如:

     

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 ,这是已解码电子邮件地址的第一部分。

     

此代码中正在发生按位XORing:

     

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

让我进一步解释:

  

编码字符串的第一个字节是密码密钥,在这种情况下为ae或0xae。

     

如果将0xae转换为十进制,则为174。

     

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

     

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

     

^按位异或Xp

     

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

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

让我们将这些十进制转换为十六进制(以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

如果我们将混淆字符串中的 ae 十进制174放在十进制电子邮件地址的开头,则为:

  

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

     

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

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

  

如果我不提到二进制数和XOR操作,我将被忽略。

首字节转换:

  • 十六进制数字:ae
  • 十进制数:174
  • 十六进制(以16为基):98
  • 二进制数:10101110
  • ASCII文本:®

第二字节转换:

  • 十六进制数字:cc
  • 十进制数:204
  • 十六进制(以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项目。