使用 socket 和 ssl 手动获取网页

Posted

技术标签:

【中文标题】使用 socket 和 ssl 手动获取网页【英文标题】:Fetch web page manually with socket and ssl 【发布时间】:2022-01-19 13:09:47 【问题描述】:

我正在尝试使用低级 socketssl 库来获取网页。

我当前的代码基于ssl library's official documentation 中的第一个示例。创建 SSL 套接字后,我发送一个手写的GET 请求并读取响应:

import socket
import ssl

hostname = 'www.python.org'
context = ssl.create_default_context()

with socket.create_connection((hostname, 443)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as ssock:
        request = f'GET / HTTP/1.1\r\nHost: hostname\r\nConnection: close\r\n\r\n'
        ssock.sendall(request.encode())

        response = b''
        while True:
            chunk = ssock.recv(4096)
            if not chunk:
                break
            response += chunk

        print(response.decode('utf-8'))

www.python.org 工作正常,但大多数时候当我尝试从***的List of most visited websites 获取前 10 个网页之一时出现问题。由于返回的 HTTP 状态代码通常是 302 Found 告诉我类似

HTTP/1.1 302 Found
Location: https://www.facebook.com/unsupportedbrowser

我尝试在GET 请求中将www.facebook.com 替换为https://www.facebook.com

request = f'GET / HTTP/1.1\r\nHost: https://hostname\r\nConnection: close\r\n\r\n'

现在,我得到不同的状态代码,例如:

400 Bad Request
500 Internal Server Error

我想要实现的几乎就是以下代码所做的。

import requests

url = 'https://python.org'
response = requests.get(url)
print(response.text)

注意事项:

我不一定需要安全连接,但没有ssl,我的成功率就更低了。 我不想在生产中使用它。

【问题讨论】:

...但大多数时候都会出现问题... 没有任何问题,只是 HTTP 协议比您准备实现的更复杂。 ssl 文档中的示例只是一个演示。它们绝不会伪装成功能齐全的 HTTP 客户端。 【参考方案1】:

Facebook 的问题显然是它需要一个 User-Agent 标头,浏览器通常在请求中包含该标头。由于您不是浏览器,因此您至少需要通过包含它来假装您是浏览器。

如果您希望仅使用套接字提交请求,但获得与浏览器相同类型的响应,那么最好的选择可能是首先通过浏览器提交请求并查看发送的标头并由您感兴趣的每个站点返回(为此使用浏览器自己的开发工具)。然后,只需将任何和所有看起来像是网站预期的标题添加到您自己的请求中。

【讨论】:

以上是关于使用 socket 和 ssl 手动获取网页的主要内容,如果未能解决你的问题,请参考以下文章

使用SOCKET获取网页的内容

UDP协议编程与获取一个网页内容

UDP协议编程与获取一个网页内容

Flutter webview:ssl_client_socket_impl 和 Uncaught SecurityError 错误

原生socket请求url获取状态码消息报头响应正文

在play framework 2.4中,如何访问ssl socket(或ssl client cert)