
本教程旨在解决使用beautifulsoup无法直接爬取动态加载内容中隐藏电话号码的问题。当目标网站通过javascript异步请求(如graphql api)动态更新dom时,传统的html解析器将失效。文章详细介绍了如何利用浏览器开发者工具分析网络请求,识别数据源api,并使用python的`requests`库模拟这些api请求,从而高效准确地获取所需数据。
在现代网页中,许多内容并非直接嵌入在初始HTML文档中,而是通过JavaScript在用户交互(例如点击按钮)后异步加载。对于这类动态内容,仅依赖于BeautifulSoup这样的静态HTML解析库是不足以获取到完整信息的。本文将以从弹出按钮中抓取隐藏电话号码为例,详细讲解如何通过模拟API请求来解决这一挑战。
当你尝试使用requests库获取网页内容并用BeautifulSoup解析时,如果目标数据(如电话号码)只有在点击某个按钮后才显示,那么你获取到的HTML将不包含这些数据。这是因为BeautifulSoup只能处理服务器返回的原始HTML。而按钮点击后触发的数据加载通常是通过浏览器执行JavaScript代码,向后端API发送请求,然后将返回的数据动态插入到网页DOM中。
在我们的例子中,点击“التواصل”按钮后弹出的电话号码,就是通过JavaScript向一个GraphQL API发送POST请求获取的。
要获取动态加载的数据,我们首先需要找出是哪个API提供了这些数据。浏览器开发者工具(通常按F12键打开)的“网络”(Network)选项卡是关键。
立即学习“Python免费学习笔记(深入)”;
通过分析,我们可以发现当点击按钮时,浏览器向 https://graphql.haraj.com.sa 发送了一个POST请求,请求载荷中包含一个GraphQL查询,以及一个postId变量。
在分析请求载荷时,我们注意到一个名为postId的变量是动态的。这个postId通常可以从原始网页的URL中提取。例如,如果页面URL是 https://haraj.com.sa/1194697687,那么postId可能就是URL末尾的数字部分,但可能需要去除某些前缀。
根据观察,URL中的1194697687对应的postId是94697687,这意味着需要去除前两位数字11。
以下是一个提取postId的示例函数:
import re
def extract_post_id_from_url(url):
"""
从Haraj网站的URL中提取postId。
示例: "https://haraj.com.sa/1194697687" -> 94697687
"""
match = re.search(r'/(\d+)$', url)
if match:
full_id_str = match.group(1)
# 根据观察,postId是URL末尾数字去除前两位"11"后的部分
if full_id_str.startswith('11') and len(full_id_str) > 2:
return int(full_id_str[2:])
return int(full_id_str) # 如果没有"11"前缀,则直接返回
return None
# 示例用法
target_url = "https://haraj.com.sa/1194697687"
post_id = extract_post_id_from_url(target_url)
if post_id:
print(f"提取到的 Post ID: {post_id}")
else:
print("无法从URL中提取 Post ID。")一旦我们识别了API请求的URL、方法、头部和载荷,就可以使用Python的requests库来模拟这个请求。
import requests
import re
def extract_post_id_from_url(url):
"""
从Haraj网站的URL中提取postId。
示例: "https://haraj.com.sa/1194697687" -> 94697687
"""
match = re.search(r'/(\d+)$', url)
if match:
full_id_str = match.group(1)
# 根据观察,postId是URL末尾数字去除前两位"11"后的部分
if full_id_str.startswith('11') and len(full_id_str) > 2:
return int(full_id_str[2:])
return int(full_id_str) # 如果没有"11"前缀,则直接返回
return None
def get_hidden_phone_number(page_url):
"""
通过模拟API请求获取隐藏的电话号码。
"""
# 1. 从页面URL中提取 postId
post_id = extract_post_id_from_url(page_url)
if post_id is None:
print(f"错误: 无法从URL '{page_url}' 中提取有效的 postId。")
return None
# 2. 定义API请求参数
api_url = "https://graphql.haraj.com.sa"
# 根据浏览器网络分析,这些参数可能为空或特定值
params = {
"queryName": "postContact",
"token": "",
"clientId": "",
"version": ""
}
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36",
"accept": "*/*",
"accept-language": "en-US,en;q=0.9",
"content-type": "application/json",
"origin": "https://haraj.com.sa",
"referer": page_url # 设置referer以模拟真实浏览器行为
}
# GraphQL请求的Payload
payload = {
"query": "query postContact($postId: Int!) {postContact(postId: $postId){contactText}}",
"variables": {
"postId": post_id # 使用动态获取的postId
}
}
try:
# 3. 发送POST请求
response = requests.post(api_url, params=params, headers=headers, json=payload)
response.raise_for_status() # 检查HTTP请求是否成功
# 4. 解析JSON响应
data = response.json()
# 提取电话号码
contact_text = data.get('data', {}).get('postContact', {}).get('contactText')
return contact_text
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
return None
except KeyError as e:
print(f"解析响应数据失败: 缺少键 {e}")
return None
# 主函数调用示例
if __name__ == "__main__":
target_page_url = "https://haraj.com.sa/1194697687" # 替换为实际的页面URL
phone_number = get_hidden_phone_number(target_page_url)
if phone_number:
print(f"成功获取到电话号码: {phone_number}")
else:
print("未能获取电话号码。")
# 另一个示例URL
another_page_url = "https://haraj.com.sa/1199808969"
phone_number_2 = get_hidden_phone_number(another_page_url)
if phone_number_2:
print(f"成功获取到另一个电话号码: {phone_number_2}")
else:
print("未能获取另一个电话号码。")代码解释:
通过上述方法,我们成功绕过了BeautifulSoup在处理动态加载内容时的局限性,直接与网站的后端API交互,高效准确地获取了隐藏的电话号码。这种模拟API请求的技术是爬取现代动态网站的关键技能之一。
以上就是Python爬取动态加载内容的隐藏电话号码:API请求方法详解的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号