use*_*623 4 python python-requests
我想抓取一个网站,但是cloudflare正在阻碍.我能够获得服务器IP,因此cloudflare不会打扰我.
如何在请求库中使用它?
例如,我想直接进入
www.example.com/foo.php,但在请求中它将解析cloudflare网络上的IP而不是我想要它使用的IP.如何让它使用我想要它使用的那个?
我会发送一个请求,所以真正的IP与主机设置为www.example.com,但这只会给我一个主页.如何访问网站上的其他链接?
Tym*_*aul 13
您必须设置一个host值为的自定义标头example.com,例如:
requests.get('http://127.0.0.1/foo.php', headers={'host': 'example.com'})
Run Code Online (Sandbox Code Playgroud)
应该做的伎俩.如果要验证,则键入以下命令(需要netcat):nc -l -p 80然后运行上面的命令.它将在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
Run Code Online (Sandbox Code Playgroud)
您必须告诉requests伪造Host标头,并使用IP地址替换URL中的主机名:
requests.get('http://123.45.67.89/foo.php', headers={'Host': 'www.example.com'})
Run Code Online (Sandbox Code Playgroud)
可以使用urlparse库完成URL"修补" :
parsed = urlparse.urlparse(url)
hostname = parsed.hostname
parsed = parsed._replace(netloc=ipaddress)
ip_url = parsed.geturl()
response = requests.get(ip_url, headers={'Host': hostname})
Run Code Online (Sandbox Code Playgroud)
针对Stack Overflow的演示:
>>> 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]>
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我动态查找IP地址.
HostHeaderSSLAdapter在requests_toolbelt 模块中使用:上述解决方案适用于非加密 HTTP 连接的虚拟主机。对于 HTTPS,您还需要在 TLS 标头中传递 SNI(服务器名称标识),因为某些服务器将根据通过 SNI 传入的内容提供不同的 SSL 证书。此外,默认情况下,python ssl 库Host:在连接时不查看标头以匹配服务器连接。
上面提供了一个简单的方法,可以将传输适配器添加到为您处理此问题的请求中。
import requests
from requests_toolbelt.adapters import host_header_ssl
# Create a new requests session
s = requests.Session()
# Mount the adapter for https URLs
s.mount('https://', host_header_ssl.HostHeaderSSLAdapter())
# Send your request
s.get("https://198.51.100.50", headers={"Host": "example.org"})
Run Code Online (Sandbox Code Playgroud)
小智 5
我认为将 https 请求发送到特定 IP 的最佳方法是添加一个自定义解析器,将域名绑定到您要访问的 IP。这样,SNI 和主机头都正确设置,并且证书验证始终可以作为 Web 浏览器成功。
否则,即使您尝试不同的标头组合和验证参数,您也会看到各种问题,例如InsecureRequestWarning,SSLCertVerificationError和 SNI 始终缺失Client Hello。
requests.get('https://1.2.3.4/foo.php', headers= {"host": "example.com", verify=True)
另外,我试过
pip install requests[security]这里提到的所有使用 TLS 请求的解决方案都不提供 SNI 支持
当直接点击 https://IP 时,他们都没有设置 SNI。
# 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)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
16259 次 |
| 最近记录: |