Python请求-使用按IP服务器导航站点

Python请求-使用按IP服务器导航站点,python,python-requests,Python,Python Requests,我想抓取一个站点,但是cloudflare挡住了我的去路。我能够获得服务器IP,所以cloudflare不会打扰我 我如何在请求库中使用它 例如,我想直接去 www.example.com/foo.php,但在请求中,它将解析cloudflare网络上的IP,而不是我希望它使用的IP。我怎样才能让它使用我想要的那个 我可能会发送一个请求,以便主机设置为www.example.com的真实IP,但这只会给我主页。如何访问网站上的其他链接?您必须设置一个自定义标题host,其值为example.co

我想抓取一个站点,但是cloudflare挡住了我的去路。我能够获得服务器IP,所以cloudflare不会打扰我

我如何在请求库中使用它

例如,我想直接去
www.example.com/foo.php
,但在请求中,它将解析cloudflare网络上的IP,而不是我希望它使用的IP。我怎样才能让它使用我想要的那个


我可能会发送一个请求,以便主机设置为www.example.com的真实IP,但这只会给我主页。如何访问网站上的其他链接?

您必须设置一个自定义标题
host
,其值为
example.com
,类似于:

requests.get('http://127.0.0.1/foo.php', headers={'host': 'example.com'})
我们应该做到这一点。如果要验证,请键入以下命令(需要netcat):
nc-l-p80
,然后运行上述命令。它将在netcat窗口中生成输出:

GET /foo.php HTTP/1.1
Host: example.com
Connection: keep-alive
Accept-Encoding: gzip, deflate
Accept: */*
User-Agent: python-requests/2.6.2 CPython/3.4.3 Windows/8

您必须告诉
请求
伪造
主机
头,并用IP地址替换URL中的主机名:

requests.get('http://123.45.67.89/foo.php', headers={'Host': 'www.example.com'})
URL“修补”可以通过
URLPRASE
库完成:

parsed = urlparse.urlparse(url)
hostname = parsed.hostname
parsed = parsed._replace(netloc=ipaddress)
ip_url = parsed.geturl()

response = requests.get(ip_url, headers={'Host': hostname})
针对堆栈溢出的演示:

>>> import urlparse
>>> import socket
>>> url = 'http://stackoverflow.com/help/privileges'
>>> parsed = urlparse.urlparse(url)
>>> hostname = parsed.hostname
>>> hostname
'stackoverflow.com'
>>> ipaddress = socket.gethostbyname(hostname)
>>> ipaddress
'198.252.206.16'
>>> parsed = parsed._replace(netloc=ipaddress)
>>> ip_url = parsed.geturl()
>>> ip_url
'http://198.252.206.16/help/privileges'
>>> response = requests.get(ip_url, headers={'Host': hostname})
>>> response
<Response [200]>
导入URL解析 >>>导入套接字 >>>url='1〕http://stackoverflow.com/help/privileges' >>>parsed=urlparse.urlparse(url) >>>hostname=parsed.hostname >>>主机名 'stackoverflow.com' >>>ipaddress=socket.gethostbyname(主机名) >>>IP地址 '198.252.206.16' >>>已解析=已解析。\替换(netloc=ipaddress) >>>ip_url=parsed.geturl() >>>ip地址 'http://198.252.206.16/help/privileges' >>>response=requests.get(ip_url,headers={'Host':hostname}) >>>回应 在本例中,我动态查找了ip地址。

回答HTTPS/SNI支持:使用以下目录中的
HostHeaderSSLAdapter
: 上述解决方案适用于非加密HTTP连接的VirtualHost。对于HTTPS,您还需要在TLS标头中传递SNI(服务器名称标识),因为某些服务器将根据通过SNI传递的内容提供不同的SSL证书。另外,python ssl库在默认情况下不会查看
Host:
头以匹配连接时的服务器连接

上面提供了一个简单的方法,可以将传输适配器添加到为您处理此问题的请求中

例子
我认为向特定IP发送https请求的最佳方法是添加一个定制的解析器,将域名绑定到您想要访问的IP。这样,SNI和主机头都被正确设置,并且证书验证可以作为web浏览器始终成功

否则,您将看到各种问题,如
unsecurerequestwarning
SSLCertVerificationError
,并且在
客户端Hello
中始终缺少SNI,即使您尝试不同的头和验证参数组合

requests.get('https://1.2.3.4/foo.php,headers={“host”:“example.com”,verify=True)

另外,我试过了

pip安装请求[安全]

这里提到的所有解决方案

他们中没有人在击球时设置SNIhttps://IP 直接的

# mock /etc/hosts
# lock it in multithreading or use multiprocessing if an endpoint is bound to multiple IPs frequently
etc_hosts = {}


# decorate python built-in resolver
def custom_resolver(builtin_resolver):
    def wrapper(*args, **kwargs):
        try:
            return etc_hosts[args[:2]]
        except KeyError:
            # fall back to builtin_resolver for endpoints not in etc_hosts
            return builtin_resolver(*args, **kwargs)

    return wrapper


# monkey patching
socket.getaddrinfo = custom_resolver(socket.getaddrinfo)


def _bind_ip(domain_name, port, ip):
    '''
    resolve (domain_name,port) to a given ip
    '''
    key = (domain_name, port)
    # (family, type, proto, canonname, sockaddr)
    value = (socket.AddressFamily.AF_INET, socket.SocketKind.SOCK_STREAM, 6, '', (ip, port))
    etc_hosts[key] = [value]


_bind_ip('example.com', 443, '1.2.3.4')
# this sends requests to 1.2.3.4
response = requests.get('https://www.example.com/foo.php', verify=True)

如果只使用HTTPS,那么您将得到一个错误,即主机名与证书不匹配。@ TyMeTeSuz保罗可以禁用请求中的SSL CurtFiod验证,这应该允许您访问服务器,但可以打开您的中间攻击:看起来有人创建了一个允许请求的实用程序。为SSL连接指定一个主机头:效果很好。不幸的是,当您使用POST和数据生成器(用于分块编码)时,似乎存在一个错误,您最终会有两个“主机”头,一个是原始的,一个是新的:(@jlh:如果您有一个简单的可复制案例,我可以看看是否可以修复。@jlh:看起来像一个bug;通过HTTP库的一个不同的低级路径被采用,并且没有被告知跳过主机头。我不确定在此期间发生了什么,但截至2020年11月,这似乎没有任何影响。
# mock /etc/hosts
# lock it in multithreading or use multiprocessing if an endpoint is bound to multiple IPs frequently
etc_hosts = {}


# decorate python built-in resolver
def custom_resolver(builtin_resolver):
    def wrapper(*args, **kwargs):
        try:
            return etc_hosts[args[:2]]
        except KeyError:
            # fall back to builtin_resolver for endpoints not in etc_hosts
            return builtin_resolver(*args, **kwargs)

    return wrapper


# monkey patching
socket.getaddrinfo = custom_resolver(socket.getaddrinfo)


def _bind_ip(domain_name, port, ip):
    '''
    resolve (domain_name,port) to a given ip
    '''
    key = (domain_name, port)
    # (family, type, proto, canonname, sockaddr)
    value = (socket.AddressFamily.AF_INET, socket.SocketKind.SOCK_STREAM, 6, '', (ip, port))
    etc_hosts[key] = [value]


_bind_ip('example.com', 443, '1.2.3.4')
# this sends requests to 1.2.3.4
response = requests.get('https://www.example.com/foo.php', verify=True)