python - 使用 Python 抓取网站时设置代理

标签 python proxy request web-crawler

我想为我的爬虫设置代理。我正在使用请求模块和美丽的汤。
我找到了一个 API 链接列表,这些链接提供具有 4 种类型协议(protocol)的免费代理。
所有使用 3/4 协议(protocol)的代理(HTTP、SOCKS4、SOCKS5)都可以工作,除了一个,那就是使用 HTTPS 协议(protocol)的代理。
这是我的代码:

from bs4 import BeautifulSoup
import requests
import random
import json

# LIST OF FREE PROXY APIS, THESE PROXIES ARE LAST TIME TESTED 50 MINUTES AGO, PROTOCOLS: HTTP, HTTPS, SOCKS4 AND SOCKS5
list_of_proxy_content = ["https://proxylist.geonode.com/api/proxy-list?limit=150&page=1&sort_by=lastChecked&sort_type=desc&filterLastChecked=50&country=CH&protocols=http%2Chttps%2Csocks4%2Csocks5",
                        "https://proxylist.geonode.com/api/proxy-list?limit=150&page=1&sort_by=lastChecked&sort_type=desc&filterLastChecked=50&country=FR&protocols=http%2Chttps%2Csocks4%2Csocks5",
                        "https://proxylist.geonode.com/api/proxy-list?limit=150&page=1&sort_by=lastChecked&sort_type=desc&filterLastChecked=50&country=DE&protocols=http%2Chttps%2Csocks4%2Csocks5",
                        "https://proxylist.geonode.com/api/proxy-list?limit=1500&page=1&sort_by=lastChecked&sort_type=desc&filterLastChecked=50&country=AT&protocols=http%2Chttps%2Csocks4%2Csocks5",
                        "https://proxylist.geonode.com/api/proxy-list?limit=150&page=1&sort_by=lastChecked&sort_type=desc&filterLastChecked=50&country=IT&protocols=http%2Chttps%2Csocks4%2Csocks5"]


# EXTRACTING JSON DATA FROM THIS LIST OF PROXIES
full_proxy_list = []
for proxy_url in list_of_proxy_content:
    
    proxy_json = requests.get(proxy_url).text
    proxy_json = json.loads(proxy_json)
    proxy_json = proxy_json["data"]

    full_proxy_list.extend(proxy_json)

# CREATING PROXY DICT
final_proxy_list = []
for proxy in full_proxy_list:

    #print(proxy) # JSON VALUE FOR ALL DATA THAT GOES INTO PROXY

    protocol = proxy['protocols'][0]
    ip_ = proxy['ip']
    port = proxy['port']
        
    proxy = {protocol : protocol + '://' + ip_ + ':' + port}

    final_proxy_list.append(proxy)


# TRYING PROXY ON 3 DIFERENT WEBSITES
for proxy in final_proxy_list:

    print(proxy)
    try:
        r0 = requests.get("https://edition.cnn.com/", proxies=proxy, timeout = 15)
        if r0.status_code == 200:
            print("GOOD PROXY")
        else:
            print("BAD PROXY")
    except:
        print("proxy error")
        
    try:        
        r1 = requests.get("https://www.buelach.ch/", proxies=proxy, timeout = 15)
        if r1.status_code == 200:
            print("GOOD PROXY")        
        else:
            print("BAD PROXY")
    except:
        print("proxy error")
        
    try:      
        r2 = requests.get("https://www.blog.police.be.ch/", proxies=proxy, timeout = 15)
        if r2.status_code == 200:
            print("GOOD PROXY")        
        else:
            print("BAD PROXY")
    except:
        print("proxy error")

    print()
我的问题是,为什么 HTTPS 代理不起作用,我做错了什么?
我的代理看起来像这样:
{'socks4': 'socks4://185.168.173.35:5678'}
{'http': 'http://62.171.177.80:3128'}
{'https': 'http://159.89.28.169:3128'}
我看到有时人们会像这样传递代理:
proxies = {"http": "http://10.10.1.10:3128",
           "https": "http://10.10.1.10:1080"}
但是这个 dict 有 2 个协议(protocol),但在链接中它只有 http,为什么?
我可以只传递一个,我可以在这个字典中传递 10 个不同的 IP 地址吗?

最佳答案

您的代码有几处错误。我将首先解决悬而未决的果实。
首先,您的 SOCKS 代理也不起作用。这是为什么。
编写代理字典的正确方法可以在requests documentation中找到。 .

# your way
proxy = {'socks4': 'socks4://ip:port'}

# the correct way
proxy = {'https': 'socks4://ip:port'}   # note the s in https

# or another correct way
proxy = {'http': 'socks4://ip:port'}  # Note the http with no s

# best correct way if your urls are mixed http:// https://
proxies = {
  'http': 'socks4://ip:port',
  'https': 'socks4://ip:port',
}

httphttps在这些条目中,不是代理服务器的协议(protocol),而是您的 url。
例如:https://www.example.com对比 http://www.example.com .
https:// 的请求url 将转到 https条目,而对 http:// 的请求url 将通过 http入口。如果您只提供一个条目 {'http': 'socks4://ip:port'} , 并且 url 请求是针对 https:// url,那个请求 不会被代理,你自己的IP就会暴露出来。因为没有 socks4://www.example.com 这样的东西浏览时,您提出的请求没有被代理。
在通过代理和 VPN 进行任何工作时,我不喜欢测试代码并向我将运行最终代码的服务器发送请求。我喜欢使用 ipinfo.io .他们的 json 响应包括有关连接 ip 的信息。这样,我可以确保连接通过代理而不是发送误报。
注:由于负载均衡器,连接 IP 与代理 IP 不同的情况并不少见。只要确保连接IP不是你自己的。您可以使用浏览器访问以下代码中的 url 来检查自己的 ip。
因为您使用的是 {'socks4': 'socks4://ip:port'}而不是正确的 {'https': 'socks4://ip:port'} ,您仍然收到 200 个状态代码,并且您的代码返回误报。它返回 200 是因为您确实连接了,但是使用您自己的 IP 而不是通过代理。
由于您没有提供实际发生的具体情况,我在您的代码中添加了一些快速而肮脏的错误处理以找出发生了什么。一些错误与服务器端配置有关,因为大多数 https 代理都需要某种身份验证,例如证书或登录名(尽管它们是“免费”和“公共(public)”的。
我的不完美,但工作代码如下。在 Python 3.8.12 上测试.有关代理连接错误的一些信息在其下方。
提示:检查您的网址。 country=CH第一个应该说 country=CNcountry=AT应该说 country=AR .我的代码反射(reflect)了这一点。
from bs4 import BeautifulSoup
import requests
import json
import time

# LIST OF FREE PROXY APIS, THESE PROXIES ARE LAST TIME TESTED 50 MINUTES AGO
# PROTOCOLS: HTTP, HTTPS, SOCKS4 AND SOCKS5
list_of_proxy_content = [
    "https://proxylist.geonode.com/api/proxy-list?limit=150&page=1&sort_by=lastChecked&sort_type=desc&filterLastChecked=50&country=CN&protocols=http%2Chttps%2Csocks4%2Csocks5",
    "https://proxylist.geonode.com/api/proxy-list?limit=150&page=1&sort_by=lastChecked&sort_type=desc&filterLastChecked=50&country=FR&protocols=http%2Chttps%2Csocks4%2Csocks5",
    "https://proxylist.geonode.com/api/proxy-list?limit=150&page=1&sort_by=lastChecked&sort_type=desc&filterLastChecked=50&country=DE&protocols=http%2Chttps%2Csocks4%2Csocks5",
    "https://proxylist.geonode.com/api/proxy-list?limit=1500&page=1&sort_by=lastChecked&sort_type=desc&filterLastChecked=50&country=AR&protocols=http%2Chttps%2Csocks4%2Csocks5",
    "https://proxylist.geonode.com/api/proxy-list?limit=150&page=1&sort_by=lastChecked&sort_type=desc&filterLastChecked=50&country=IT&protocols=http%2Chttps%2Csocks4%2Csocks5",
]


# EXTRACTING JSON DATA FROM THIS LIST OF PROXIES
full_proxy_list = []
for proxy_url in list_of_proxy_content:

    proxy_json = requests.get(proxy_url).text
    proxy_json = json.loads(proxy_json)
    proxy_json = proxy_json["data"]

    full_proxy_list.extend(proxy_json)

    if not full_proxy_list:
        print("No proxies to check. Exiting...")
        exit
    else:
        print(f"Found {len(full_proxy_list)} proxy servers. Checking...\n")

# CREATING PROXY DICT
final_proxy_list = []
for proxy in full_proxy_list:

    # print(proxy)  # JSON VALUE FOR ALL DATA THAT GOES INTO PROXY

    protocol = proxy["protocols"][0]
    ip_ = proxy["ip"]
    port = proxy["port"]

    proxy = {
        "https": protocol + "://" + ip_ + ":" + port,
        "http": protocol + "://" + ip_ + ":" + port,
    }

    final_proxy_list.append(proxy)

# TRYING PROXY ON 3 DIFERENT WEBSITES
for proxy in final_proxy_list:

    print(proxy)
    try:
        # Use ipinfo.io to test proxy ip
        url = "https://ipinfo.io/json?token=67e01402d14101"
        r0 = requests.get(url, proxies=proxy, timeout=15)

        if r0.status_code == 200:
            # The 3-line block below only works on ipinfo.io
            output = r0.json()
            real_ip = output["ip"]
            print(f"GOOD PROXY [IP = {real_ip}] {proxy}\n")

            # Do something with the response
            html_page = r0.text
            soup = BeautifulSoup(r0.text, "html.parser")
            print(soup, "\n")

            r0.close()  # close the connection so it can be reused

            # Break out of the proxy loop so we do not send multiple successful
            # requests to the same url. Info needed was already obtained.
            # Comment out to check all possible proxies during testing.
            break
        else:
            # If the response code is something other than 200,
            # it means the proxy worked, but the website did not.
            print(f"BAD URL: [status code: {r0.status_code}]\n{r0.headers}\n")
            r0.close()

        time.sleep(5)  # Don't overload the server

    except Exception as error:
        print(f"BAD PROXY: Reason: {str(error)}\n")
您看到的大多数错误都是超时错误,这应该是不言自明的。
其他错误是服务器端错误,因为它们的配置阻止您连接。
没有太技术性的简短列表:
  • Remote end closed connection without response是服务器端
    尽管连接到它,但只是断然拒绝发送您的请求。
  • 407 Proxy Authentication Required是我的错误之一
    上文提到的。这要么希望您提供用户/通行证或
    证书。
  • [Errno 111] Connection refused是我的错误之一
    上文提到的。

  • 重要提示:如果您看到以下任一错误 check_hostname requires server_hostname , EOF occurred in violation of protocol , 或 SSL: WRONG_VERSION_NUMBER运行上述代码后,降级您的 urllib3 库。在某些最新版本中存在代理错误以及其他一些错误。您可以使用命令 pip install -U urllib3==1.25.11 来执行此操作。或 python3 -m pip install -U urllib3==1.25.11 .

    关于python - 使用 Python 抓取网站时设置代理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69064792/

    相关文章:

    java - 来自另一个 servlet 的 request.getContext?

    Python使用数组轴来选择切片

    Python - 迭代嵌套对象

    python - 从列表元组中正确赋值

    带有组织模式文件的 Python 多行正则表达式

    java - Proxy Pac - 使用 "if(Math.random() < 0.5)"进行负载平衡

    node.js - npm 无法通过 Socks5 代理安装

    带有用于本地安装的监视器 UI 的 HTTP 代理

    python-2.7 - 请求中缺少表单数据

    java - 如何设置InputStreamReader的超时时间?