把每一行代理当作一个整体来解析。如果把页面上所有匹配的 td 拉平并按固定数量分组,一旦某个单元格缺失或出现另一张表格,某一行的地址就可能和下一行的端口配成一对。

对于应用已经获取的 HTML,若某张表格的前两列是 IP 地址和端口,可以这样解析:

import ipaddress
from bs4 import BeautifulSoup

soup = BeautifulSoup(html, "html.parser")
table = soup.select_one(table_selector)
if table is None:
    raise ValueError("Expected proxy table was not found")

endpoints = set()
for row in table.select("tbody tr"):
    cells = row.find_all("td", recursive=False)
    if len(cells) < 2:
        continue
    try:
        address = ipaddress.ip_address(cells[0].get_text(strip=True))
        port = int(cells[1].get_text(strip=True))
    except ValueError:
        continue
    if 1 <= port <= 65535:
        endpoints.add((str(address), port))

html 和 table_selector 是应用的输入。选择器和列映射应依据来源的实际标记来确定。免费代理列表是端点记录的一个来源,但在选定该映射之前应先检查它的行。无论是一个历史遗留的表格 ID,还是“页面恰好包含 300 条记录”的说法,都不是持久可靠的模式。

该集合会去除重复的地址与端口对,同时保留每一对之间的关联。即便如此,出现意外的零结果导入时仍应排查:表格可能已经变化,或者抓取到的文档可能是验证页面而不是列表。

当这些信息重要时,把声明支持的协议和检查时间字段与端点一并保留。提取出看似合理的 IP 和端口只证明了格式,并不说明该端点当前能提供可用的 HTTP 或 SOCKS 服务。

表头行或筛选行不是端点。只包含 th 单元格的行会被跳过,而空字段或非地址字段则无法通过校验。不要按位置丢弃第一行和最后一行:页面布局变化后,其中任何一行都可能是真正的端点。

相关文章