跳转到内容
Guides 3 分钟阅读 1245 次浏览

使用 Beautiful Soup 和 requests 通过代理抓取数据

本指南详细介绍如何将代理与 Beautiful Soup 和 requests 集成,实现稳定的网页抓取,避免 IP 封禁并确保成功提取数据。

Python Parsing
使用 Beautiful Soup 和 requests 通过代理抓取数据

使用代理通过 Beautiful Soup 和 requests 抓取数据,指的是先把 requests 库配置为将网络流量路由到指定的代理服务器,再用 Beautiful Soup 解析 HTML 内容,从而实现 IP 轮换、绕过地域限制并减少 IP 封禁。

进行网页抓取时,从单一 IP 地址直接发出请求会导致速率限制、临时或永久 IP 封禁,或遇到地域受限内容。代理服务通过不同的 IP 地址转发请求来应对这些问题,使抓取更稳定、更易扩展。

完成该任务的核心库为:
* requests:Python 的 HTTP 库,简化 HTTP 请求的发送。
* Beautiful Soup:用于解析 HTML 和 XML 文档的 Python 库。

使用 requests 配置代理

requests 库通过请求方法中的 proxies 参数支持代理配置。

单个代理配置

要使用单个代理,请提供一个把协议(HTTP、HTTPS)映射到代理 URL 的字典。

import requests

proxy_http = "http://your_proxy_ip:port"
proxy_https = "https://your_proxy_ip:port" # 通常与 HTTP 相同

proxies = {
    "http": proxy_http,
    "https": proxy_https,
}

try:
    response = requests.get("http://httpbin.org/ip", proxies=proxies, timeout=10)
    response.raise_for_status()
    print("External IP:", response.json().get('origin'))
except requests.exceptions.RequestException as e:
    print(f"Request failed: {e}")

代理认证

对于需要认证的代理,请把凭据直接嵌入代理 URL 中。

import requests

proxy_user = "your_username"
proxy_pass = "your_password"
proxy_host = "your_proxy_ip"
proxy_port = "port"

authenticated_proxy = f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"

proxies = {
    "http": authenticated_proxy,
    "https": authenticated_proxy,
}

try:
    response = requests.get("http://httpbin.org/ip", proxies=proxies, timeout=10)
    response.raise_for_status()
    print("External IP (authenticated):", response.json().get('origin'))
except requests.exceptions.RequestException as e:
    print(f"Authenticated request failed: {e}")

轮换代理

在大规模抓取中,在一份代理列表上轮换是分散请求、降低被封风险的必备手段。

import requests
import random
import time

proxy_list = [
    "http://user1:[email protected]:8000",
    "http://user2:[email protected]:8000",
    "http://user3:[email protected]:8000",
]

def get_random_proxy():
    return random.choice(proxy_list)

def make_proxied_request(url, headers=None, attempt_limit=3):
    for attempt in range(attempt_limit):
        proxy_url = get_random_proxy()
        proxies = {"http": proxy_url, "https": proxy_url}
        try:
            print(f"Attempt {attempt + 1}: Using proxy {proxy_url.split('@')[-1]} for {url}")
            response = requests.get(url, proxies=proxies, headers=headers, timeout=15)
            response.raise_for_status()
            return response
        except requests.exceptions.RequestException as e:
            print(f"Proxy request failed (attempt {attempt + 1}): {e}")
            time.sleep(random.uniform(2, 5)) # 换用新代理重试前的延迟
    return None

# 使用示例 URL 的用法演示
try:
    target_url = "http://quotes.toscrape.com/"
    response = make_proxied_request(target_url)
    if response:
        print(f"Successfully fetched {target_url} with status code {response.status_code}")
except Exception as e:
    print(f"Failed to fetch {target_url} after multiple retries: {e}")

使用 Beautiful Soup 解析 HTML

Beautiful Soup 会把 requests 响应中的 HTML 内容转换为可遍历的 Python 对象。

from bs4 import BeautifulSoup

html_doc = """
<html>
<head><title>Example Page</title></head>
<body>
<p class="intro"><b>Welcome!</b></p>
<div id="content">
    <a href="/item1" class="product-link">Product A</a>
    <a href="/item2" class="product-link">Product B</a>
</div>
</body>
</html>
"""

soup = BeautifulSoup(html_doc, 'html.parser')

# 访问元素
print("Page Title:", soup.title.string)

# 查找特定元素
intro_paragraph = soup.find('p', class_='intro')
print("Intro text:", intro_paragraph.get_text(strip=True))

# 按 class 查找所有元素
product_links = soup.find_all('a', class_='product-link')
for link in product_links:
    print(f"Product: {link.get_text()}, URL: {link['href']}")

将代理与 Beautiful Soup 抓取整合

把代理配置与 Beautiful Soup 解析结合起来,即可构成完整的抓取流程。

import requests
from bs4 import BeautifulSoup
import random
import time

# --- 代理配置(同前文定义)---
proxy_list = [
    "http://user1:[email protected]:8000",
    "http://user2:[email protected]:8000",
    # 从您的服务中添加更多代理
]

def get_random_proxy():
    return random.choice(proxy_list)

def make_proxied_request(url, headers=None, attempt_limit=3):
    for attempt in range(attempt_limit):
        proxy_url = get_random_proxy()
        proxies = {"http": proxy_url, "https": proxy_url}
        try:
            print(f"Attempt {attempt + 1} for {url}: Using proxy {proxy_url.split('@')[-1]}")
            response = requests.get(url, proxies=proxies, headers=headers, timeout=15)
            response.raise_for_status()
            return response
        except requests.exceptions.RequestException as e:
            print(f"Request failed (attempt {attempt + 1}): {e}")
            time.sleep(random.uniform(2, 5))
    return None

# --- 抓取逻辑 ---
def scrape_quotes(url):
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
    }

    response = make_proxied_request(url, headers=headers)

    if response:
        soup = BeautifulSoup(response.text, 'html.parser')
        quotes = soup.find_all('div', class_='quote')

        data = []
        for quote in quotes:
            text = quote.find('span', class_='text').get_text(strip=True)
            author = quote.find('small', class_='author').get_text(strip=True)
            tags = [tag.get_text(strip=True) for tag in quote.find_all('a', class_='tag')]
            data.append({"text": text, "author": author, "tags": tags})
        return data
    return []

# --- 执行 ---
if __name__ == "__main__":
    target_url = "http://quotes.toscrape.com/"
    print(f"Starting scrape of {target_url}")
    scraped_data = scrape_quotes(target_url)

    if scraped_data:
        print(f"Scraped {len(scraped_data)} quotes:")
        for i, quote in enumerate(scraped_data[:3]): # 为简洁起见只打印前 3 条
            print(f"  {i+1}. Author: {quote['author']}, Quote: {quote['text'][:50]}...")
    else:
        print("No data scraped.")

最佳实践与注意事项

User-Agent 请求头

Web 服务器常常检查 User-Agent 请求头来识别客户端。requests 的默认 User-Agent 会暴露爬虫身份。模仿常见浏览器的 User-Agent 可降低被检测的概率。

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Connection": "keep-alive",
}
response = requests.get(url, proxies=proxies, headers=headers)

请求延迟与限速

过快的请求会触发速率限制或 IP 封禁。请在请求之间加入延迟,尤其是在轮换代理时。使用带随机区间的 time.sleep() 效果良好。

import time
import random
# ... 在处理多个 URL 的循环内部 ...
time.sleep(random.uniform(2, 7)) # 等待 2 到 7 秒
response = make_proxied_request(next_url, headers=headers)

错误处理

稳定的抓取需要针对网络问题、代理故障和服务器响应做全面的错误处理。
* requests.exceptions.RequestException:捕获所有与 requests 相关的错误(连接、超时、HTTP 错误)。
* HTTP 状态码:检查 response.status_code。403(Forbidden)、404(Not Found)、429(Too Many Requests)或 5xx(Server Error)等状态码表示存在问题。
* 超时:在 requests.get() 中配置 timeout 参数,避免无限期等待。

CAPTCHA 与高级反抓取措施

部分网站采用 CAPTCHA 或 JavaScript 挑战等高级检测机制。代理有助于 IP 轮换,但无法直接解决这些问题。遇到这类情况,可考虑无头浏览器(例如 Selenium、Playwright)或专门的验证码识别服务。

代理类型对比

特性 数据中心代理 住宅代理
IP 来源 商业服务器、云服务商 使用 ISP IP 的真实用户设备(台式机、手机)
匿名性 高,但 IP 常被识别为数据中心 非常高,IP 看起来像正常用户
成本 通常更低 明显更高
速度 通常更快,延迟更低 可能更慢,延迟更高
被检测风险 被检测/封禁的风险更高 风险更低,适合绕过严格的反机器人措施
使用场景 通用抓取、公开数据、防护较弱的站点 高价值目标、电商、社交媒体、地域定向

法律与合规注意事项

  • robots.txt:遵守网站的 robots.txt 文件,其中规定了针对爬虫的规则。可通过 http://example.com/robots.txt 访问。
  • 服务条款:查看网站的服务条款,抓取可能被禁止。
  • 数据使用:确保符合数据保护法规(例如 GDPR、CCPA)。
  • 对服务器的影响:避免用大量请求压垮目标服务器,请设置适当的延迟。
已更新: 04.03.2026
返回分类

试用我们的代理

遍布 100+ 国家的 20,000+ 代理

support_agent
GProxy Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.