如何以与curl的--resolve标志类似的方式在python的请求库中指定URL解析?

时间:2017-06-05 17:16:14

标签: python python-requests

我正在编写一些python客户端代码,由于一些环境限制,我想指定一个URL并控制它的解析方式。我可以通过使用--resolve标志来完成卷曲。有没有办法用Python的请求库做类似的事情?

理想情况下,这可以在Python 2.7中运行,但我也可以使3.x解决方案正常工作。

4 个答案:

答案 0 :(得分:2)

在进行了一些挖掘之后,我(不出所料)发现Requests通过要求Python执行它来解析主机名(这要求您的操作系统执行此操作)。首先,我找到一些示例代码来劫持DNS解析(Tell urllib2 to use custom DNS),然后我想出了一些有关Python如何解析socket documentation中主机名的详细信息。然后,这只是将所有内容连接在一起的问题:

import socket
import requests

def is_ipv4(s):
    # Feel free to improve this: https://stackoverflow.com/questions/11827961/checking-for-ip-addresses
    return ':' not in s

dns_cache = {}

def add_custom_dns(domain, port, ip):
    key = (domain, port)
    # Strange parameters explained at:
    # https://docs.python.org/2/library/socket.html#socket.getaddrinfo
    # Values were taken from the output of `socket.getaddrinfo(...)`
    if is_ipv4(ip):
        value = (socket.AddressFamily.AF_INET, 0, 0, '', (ip, port))
    else: # ipv6
        value = (socket.AddressFamily.AF_INET6, 0, 0, '', (ip, port, 0, 0))
    dns_cache[key] = [value]

# Inspired by: https://stackoverflow.com/a/15065711/868533
prv_getaddrinfo = socket.getaddrinfo
def new_getaddrinfo(*args):
    # Uncomment to see what calls to `getaddrinfo` look like.
    # print(args)
    try:
        return dns_cache[args[:2]] # hostname and port
    except KeyError:
        return prv_getaddrinfo(*args)

socket.getaddrinfo = new_getaddrinfo

# Redirect example.com to the IP of test.domain.com (completely unrelated).
add_custom_dns('example.com', 80, '66.96.162.92')
res = requests.get('http://example.com')
print(res.text) # Prints out the HTML of test.domain.com.

我写这篇文章时遇到了一些警告:

  • 这对https效果不佳。代码工作正常(只使用https://443代替http://80)。但是,SSL证书与域名绑定,请求将尝试将证书上的名称验证到您尝试连接的原始域。
  • getaddrinfo返回的IPv4和IPv6地址略有不同。我对is_ipv4的实施对我来说很烦人,如果您在实际应用中使用它,我强烈推荐更好的版本。
  • 代码已经在Python 3上进行了测试,但我认为没有理由说它在Python 2上不能正常工作。

答案 1 :(得分:2)

一段时间以来,我一直在努力寻找解决方案,最后偶然发现了这篇文章。 @ supersam654提供的解决方案对我来说不是立即可用(使用https和python 3.8),但是几天的睡眠让我这个解决方案无论版本如何都可以使用(尚未测试太多版本,但是天真地希望情况如此。

它也应该适用于ipv6-尽管我也没有对此进行测试。

该解决方案的关键是对所有调用使用默认的getaddrinfo()(在其输出上不做任何假设)-只需将主机名替换为ip地址,即可将其覆盖!因此,我大胆地声明了它的工作原理;-)

<td>@Html.TextBoxFor(modelItem => item.arrival, "{0:dd/MM/yyyy}")</td>

要使用上述逻辑-只需在发出请求之前像这样调用函数即可(可以使用IP地址或其他FQDN覆盖!)

import socket

dns_cache = {}
# Capture a dict of hostname and their IPs to override with
def override_dns(domain, ip):
    dns_cache[domain] = ip


prv_getaddrinfo = socket.getaddrinfo
# Override default socket.getaddrinfo() and pass ip instead of host
# if override is detected
def new_getaddrinfo(*args):
    if args[0] in dns_cache:
        print("Forcing FQDN: {} to IP: {}".format(args[0], dns_cache[args[0]]))
        return prv_getaddrinfo(dns_cache[args[0]], *args[1:])
    else:
        return prv_getaddrinfo(*args)


socket.getaddrinfo = new_getaddrinfo

我相信这是比我之前使用的ForcedIPHTTPSAdapter更好的解决方案。

答案 2 :(得分:0)

看起来最简单的方法是使用此软件包:https://github.com/requests/requests-kerberos

使用可路由名称并将hostname_override值设置为Kerberos期望的名称。

答案 3 :(得分:0)

迟到的答案,但有一个名为 forcediphttpsadapter 的模块正是这样做的:

安装:

pip3 install forcediphttpsadapter

用法:

import requests
from forcediphttpsadapter.adapters import ForcedIPHTTPSAdapter

url = 'https://domain.tld/path'
session = requests.Session()
session.mount(url, ForcedIPHTTPSAdapter(dest_ip='x.x.x.x')) # type the desired ip
r = session.get(url, verify=False)
print(r.text)
...

来源: