Python 指南 · 1 分钟阅读

使用 Python 请求和代理进行网页抓取

一个基于请求和 BeautifulSoup 的实用抓取工作流程,通过经过身份验证的代理路由,以便您的源 IP 保持隐藏,您的爬虫能够存活于速率限制之下。包含完整的可运行代码。

抓取器是一个会话,而不是一系列的获取请求

一个运行十分钟的爬虫和一个运行数小时的爬虫之间的区别几乎从来不是解析——而是你如何发出请求。一个天真的 for url in urls: requests.get(url) 每次都打开一个新的 TCP 连接,发送没有实际意义的头部,不保存 cookies,并且从一个 IP 地址不断攻击目标,直到它得到一个 429 或被封锁。

解决方案是一个 requests.Session 加上一个代理。会话池化连接并持久化 cookies,因此一批页面的请求更快,看起来像一个连贯的客户端。一个 authenticated proxy 用数据中心的出口替换你的真实地址,因此网站永远看不到你的源 IP,单个被封锁的地址不会结束整个工作。s4m 代理在 1080 端口上使用 SOCKS5,在 3128 端口上使用 HTTP,均需通过 USER:PASS 进行身份验证——使用 socks5h:// 以便 DNS 在远程解析,主机名不会泄露。对于一次性实验,free public proxy list 是一个单独的、使用风险自负的池。

一个强大的请求抓取器

会话、真实的头部、带有回退的重试、代理轮换和BeautifulSoup解析在一个文件中。用您的凭据替换USER:PASS。

python
import random
import time
import requests
from bs4 import BeautifulSoup
from requests.exceptions import RequestException

USER, PASS, HOST = "USER", "PASS", "proxy.s4m.online"
POOL = [
    f"socks5h://{USER}:{PASS}@{HOST}:1080",  # needs requests[socks]
    f"http://{USER}:{PASS}@{HOST}:3128",
]

HEADERS = {
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36",
    "Accept-Language": "en-US,en;q=0.9",
}

def make_session(proxy):
    s = requests.Session()
    s.headers.update(HEADERS)
    s.proxies = {"http": proxy, "https": proxy}
    return s

def fetch(url, retries=4):
    for attempt in range(1, retries + 1):
        proxy = random.choice(POOL)
        try:
            s = make_session(proxy)
            r = s.get(url, timeout=(5, 20))
            if r.status_code == 429:
                raise RequestException("rate limited")
            r.raise_for_status()
            return r
        except RequestException as e:
            wait = 2 ** attempt + random.random()  # exponential backoff
            print(f"[{attempt}/{retries}] {proxy} -> {e}; sleeping {wait:.1f}s")
            time.sleep(wait)
    raise SystemExit(f"giving up on {url}")

html = fetch("https://quotes.toscrape.com/").text
soup = BeautifulSoup(html, "html.parser")
for q in soup.select(".quote"):
    text = q.select_one(".text").get_text(strip=True)
    who = q.select_one(".author").get_text(strip=True)
    print(f"{who}: {text}")

保持爬虫存活的因素

四个比你选择的解析器更重要的习惯。

发送真实的用户代理

默认的python-requests UA是一个明显的标志。设置一个正常的浏览器用户代理和一个Accept-Language头,以便请求与普通流量融为一体。

退后,不要盲目重试

在 429 或超时时,使用指数退避和抖动进行休眠,然后再重试。紧密的重试循环只会加深阻塞并消耗代理流量。

旋转出口,而不仅仅是重试

每次尝试从池中选择一个新的代理,以便一个被标记的IP不会停止工作。计量数据中心代理使得扩展成本低廉。

尊重目标

阅读robots.txt,限制并发,并仅抓取您被允许的内容。一个礼貌的爬虫也是一个更难被阻止的爬虫。

从一个脚本到一个真实的管道

一旦获取循环稳定,其他的就是协调。保持一个小的代理 URL 池,并在每次请求时轮换;当目标变得激进时,增加池的大小而不是重试次数。随着进程的进行持久化结果,这样中途失败不会让你失去整个爬取,并在页面之间添加礼貌的延迟,以便你保持在速率限制之下,而不是与之抗争。

当你超越临时脚本时,同样的代理端点可以直接用于 Scrapy 或异步客户端——请参见 Python 中的旋转代理以获取 aiohttp 池。所有内容都建立在相同的 s4m 构建块上:经过身份验证的数据中心 SOCKS5 和 HTTP 代理,按需计费,配有 API 和 工具 来检查出口 IP 和端口。如果你只需要快速验证一个国家或协议,可以从 免费列表 开始。

FAQ

问题,已解答

我需要PySocks来运行SOCKS5示例吗?

是的。请求中的 SOCKS 支持来自 PySocks — 使用 pip install "requests[socks]" 安装它。端口 3128 上的 HTTP 端点与标准请求安装兼容,无需额外依赖。

我如何在抓取时避免被封锁?

发送真实的用户代理,限制请求速率,在429响应时退避,并通过代理池轮换出口IP。阻止通常与行为和IP声誉有关,而不是您使用的解析器。

s4m的代理是住宅代理吗?

不。s4m出售经过身份验证的数据中心SOCKS5和HTTP代理,按需计费。单独的免费公共代理列表是第三方的,使用风险自负,仅适用于快速测试。

为什么使用socks5h而不是socks5?

socks5h要求代理解析DNS,因此您抓取的主机名永远不会从您的机器泄漏。普通的socks5首先在本地解析,这可能会暴露您的目标。

为抓取而构建的代理

将请求指向 proxy.s4m.online,轮换经过身份验证的数据中心 SOCKS5 和 HTTP 出口,仅为您使用的流量付费。每当目标期望一个时,添加一个专用IP。

人们通过搜索找到此页面

此页面回答的真实搜索短语 — 链接的短语打开详细覆盖它们的页面。