You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何不受阻碍地爬取受限网页的标题?(含反爬及地区限制)

解决403封禁,稳定爬取目标网页标题的方案

我看了你的代码和遇到的问题,403错误主要是因为目标网站的反爬机制识别出了爬虫请求,再加上你用的免费公开代理质量参差不齐,才会出现偶尔成功但大部分时候失败的情况。下面我拆解问题并给出可行的改进方案:

1. 代理质量与有效性是核心问题

你现在从sslproxies.org获取的代理大多是公开免费的,很多已经失效或者被目标网站拉黑了,而且你没有提前验证代理是否能正常访问目标网站,直接拿来用自然成功率低。另外,目标网站仅允许特定国家地区访问,你还需要筛选对应地区的代理(比如网站只允许美国IP,就优先选US地区的代理)。

2. 单薄的请求头容易被识别为爬虫

你的请求头只有User-Agent,这和真实浏览器的请求差异太大,很容易被反爬机制拦截。需要补充更多浏览器会发送的请求头,模拟真实访问行为。

3. 重试逻辑需要优化

当前的重试逻辑没有控制请求频率,频繁换代理请求会触发网站的反爬阈值;而且当代理列表耗尽时,代码会陷入死循环,没有 fallback 机制。


改进后的完整代码

import random
import time
import requests
from bs4 import BeautifulSoup

link = 'https://www.veteranownedbusiness.com/business/25150/bm-wendling-real-estate'
# 模拟真实浏览器的请求头
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.5",
    "Accept-Encoding": "gzip, deflate, br",
    "Referer": "https://www.veteranownedbusiness.com/",
    "Connection": "keep-alive",
    "Upgrade-Insecure-Requests": "1"
}

def get_proxy_list(target_country="US"):
    """获取指定国家的HTTPS代理(目标网站允许的地区)"""
    proxies = []
    try:
        r = requests.get('https://www.sslproxies.org/', headers=headers, timeout=10)
        r.raise_for_status()
        soup = BeautifulSoup(r.text, "html.parser")
        # 跳过表头,筛选指定国家、支持HTTPS的代理
        for row in soup.select("table.table tr")[1:]:
            cols = row.select("td")
            if len(cols) < 7:
                continue
            ip = cols[0].text.strip()
            port = cols[1].text.strip()
            country = cols[2].text.strip()
            https_support = cols[6].text.strip()
            if country == target_country and https_support == "yes":
                proxies.append(f"{ip}:{port}")
    except Exception as e:
        print(f"获取代理列表失败: {str(e)}")
    return proxies

def validate_proxy(proxy, target_url):
    """验证代理是否能正常访问目标网站(避免403)"""
    try:
        proxy_dict = {'https': f'http://{proxy}', 'http': f'http://{proxy}'}
        r = requests.get(target_url, headers=headers, proxies=proxy_dict, timeout=8)
        # 只要不是403就认为代理可用
        return r.status_code != 403
    except:
        return False

def get_valid_proxies(proxy_list, target_url):
    """过滤出有效的代理,减少无效尝试"""
    valid_proxies = []
    for proxy in proxy_list:
        if validate_proxy(proxy, target_url):
            valid_proxies.append(proxy)
            # 不用验证太多,取5个能用的就足够
            if len(valid_proxies) >= 5:
                break
    return valid_proxies

def scrape(target_url):
    while True:
        # 获取代理列表并验证有效性
        proxy_list = get_proxy_list()
        if not proxy_list:
            print("未获取到代理列表,等待5秒后重试...")
            time.sleep(5)
            continue
        valid_proxies = get_valid_proxies(proxy_list, target_url)
        if not valid_proxies:
            print("没有可用的代理,等待5秒后重试...")
            time.sleep(5)
            continue
        
        # 随机选择可用代理
        proxy = random.choice(valid_proxies)
        proxy_dict = {'https': f'http://{proxy}', 'http': f'http://{proxy}'}
        print(f"正在使用代理: {proxy}")
        
        try:
            # 添加随机延迟,模拟人类访问节奏
            time.sleep(random.uniform(2, 5))
            r = requests.get(target_url, headers=headers, proxies=proxy_dict, timeout=10)
            r.raise_for_status()  # 抛出HTTP错误异常
            soup = BeautifulSoup(r.text, "html.parser")
            title = soup.select_one(".bizname_hdr > h1").get_text(strip=True)
            return title
        except requests.exceptions.HTTPError as e:
            print(f"请求失败,状态码: {r.status_code},更换代理...")
            valid_proxies.remove(proxy)
        except Exception as e:
            print(f"请求出错: {str(e)},更换代理...")
            if proxy in valid_proxies:
                valid_proxies.remove(proxy)

if __name__ == "__main__":
    try:
        title = scrape(link)
        print(f"爬取到的标题: {title}")
    except KeyboardInterrupt:
        print("程序被用户终止")

额外优化建议

  • 如果免费代理还是不稳定,可以考虑使用付费代理服务(比如BrightData、Oxylabs),这类代理质量更高,支持精准地区选择,能大幅提升成功率。
  • 可以使用requests.Session()保持会话状态,模拟浏览器的持续连接,进一步降低被识别的概率。
  • 避免频繁重复爬取同一页面,尽量缓存爬取结果,减少对目标网站的请求压力。

内容的提问来源于stack exchange,提问作者SMTH

火山引擎 最新活动