无法使用请求从网页中获取特定项目

Posted

技术标签:

【中文标题】无法使用请求从网页中获取特定项目【英文标题】: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> 阅读Email Protection 【参考方案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 项目。

【讨论】:

以上是关于无法使用请求从网页中获取特定项目的主要内容,如果未能解决你的问题,请参考以下文章

python爬虫使用requests请求无法获取网页元素时终极解决方案

如何从网页中获取此特定内容? [复制]

Nginx无法获取带下划线的请求头数据问题

无法使用 XPath 从网页获取价格

Feign 请求其它服务无法获取数据

无法使用 Express.js 从请求中获取 POST 正文