Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/282.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Can';t修改函数使其独立工作,而不是依赖于返回的结果_Python_Python 3.x_Function_Web Scraping_Multiprocessing - Fatal编程技术网

Python Can';t修改函数使其独立工作,而不是依赖于返回的结果

Python Can';t修改函数使其独立工作,而不是依赖于返回的结果,python,python-3.x,function,web-scraping,multiprocessing,Python,Python 3.x,Function,Web Scraping,Multiprocessing,我已经用python编写了一个脚本,在向某些链接发送请求时使用代理,以便从那里解析产品名称。我目前的尝试完美地完成了这项工作。此函数parse_product()完全依赖于返回的结果(代理),以便以正确的方式重用相同的代理。我试图以这样的方式修改parse_product()函数,使该函数不依赖于以前对同一函数的调用来重用工作代理,直到无效为止。更清楚地说,我希望主函数更像下面这样。但是,在完成解决后,我将使用多处理来加快脚本的运行速度: if __name__ == '__main__':

我已经用python编写了一个脚本,在向某些链接发送请求时使用代理,以便从那里解析产品名称。我目前的尝试完美地完成了这项工作。此函数
parse_product()
完全依赖于返回的结果(代理),以便以正确的方式重用相同的代理。我试图以这样的方式修改
parse_product()
函数,使该函数不依赖于以前对同一函数的调用来重用工作代理,直到无效为止。更清楚地说,我希望主函数更像下面这样。但是,在完成解决后,我将使用多处理来加快脚本的运行速度:

if __name__ == '__main__':
    for url in linklist:
        parse_product(url)
尽管如此,我们还是希望脚本能够像现在一样工作

我已经试过(使用一个):

注意:
parse_product()
函数返回代理和产品名称。但是,函数返回的代理将在同一函数
parse_product()
中重复使用,直到无效为止


顺便说一句,proxyVault中使用的代理只是占位符。

也许您可以将代理处理逻辑放入类中,并将实例传递给
parse_product()
。然后,
parse_product()
将调用实例的必要方法来获取和/或重置代理。该类可以如下所示:

class ProxyHandler:
    proxyVault = [
        "103.110.37.244:36022",
        "180.254.218.229:8080" # and so on
    ]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Initialize proxy
        proxy_url = choice(self.proxyVault)
        self.proxy = {"https": f"http://{proxy_url}"}

    def get_proxy(self):
        return self.proxy

    def renew_proxy(self):
        # Remove current proxy from the vault
        proxy_pattern = self.proxy.get("https").split("//")[-1]
        if proxy_pattern in proxyVault:
            proxyVault.remove(proxy_pattern)

        # Set new proxy
        random.shuffle(proxyVault)
        proxy_url = choice(self.proxyVault)
        self.proxy = {"https": f"http://{proxy_url}"}
def parse_product(link, proxy_handler):
    try:
        if not proxy_handler:
            raise
        proxy = proxy_handler.get_proxy()
        print("checking the proxy:", proxy)
        res = requests.get(link, proxies=proxy, timeout=5)
        soup = BeautifulSoup(res.text, "html5lib")
        try:
            product_name = soup.select_one("#productTitle").get_text(strip=True)
        except Exception:
            product_name = ""

        return product_name

    except Exception:
        """the following line when hit produces new proxy and remove the bad one that passes through process_proxy(proxy)"""
        proxy_handler.renew_proxy()
        return parse_product(link, proxy_handler)
然后,
parse_product()
可能看起来像这样:

class ProxyHandler:
    proxyVault = [
        "103.110.37.244:36022",
        "180.254.218.229:8080" # and so on
    ]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Initialize proxy
        proxy_url = choice(self.proxyVault)
        self.proxy = {"https": f"http://{proxy_url}"}

    def get_proxy(self):
        return self.proxy

    def renew_proxy(self):
        # Remove current proxy from the vault
        proxy_pattern = self.proxy.get("https").split("//")[-1]
        if proxy_pattern in proxyVault:
            proxyVault.remove(proxy_pattern)

        # Set new proxy
        random.shuffle(proxyVault)
        proxy_url = choice(self.proxyVault)
        self.proxy = {"https": f"http://{proxy_url}"}
def parse_product(link, proxy_handler):
    try:
        if not proxy_handler:
            raise
        proxy = proxy_handler.get_proxy()
        print("checking the proxy:", proxy)
        res = requests.get(link, proxies=proxy, timeout=5)
        soup = BeautifulSoup(res.text, "html5lib")
        try:
            product_name = soup.select_one("#productTitle").get_text(strip=True)
        except Exception:
            product_name = ""

        return product_name

    except Exception:
        """the following line when hit produces new proxy and remove the bad one that passes through process_proxy(proxy)"""
        proxy_handler.renew_proxy()
        return parse_product(link, proxy_handler)

我认为您可以将同一个
ProxyHandler
实例传递给所有线程并进行并行化。

如果您不需要多线程支持(您的编辑建议您不需要),您可以通过以下小改动使其工作
proxyVault
在洗牌列表后保留整个代理池,和活动代理(最后一个)(您的代码既有
shuffle
选项,但只有一个就足够了)
pop()
-ing从列表中删除将更改活动代理,直到没有剩余代理

import random
import requests
from random import choice
from urllib.parse import urljoin
from bs4 import BeautifulSoup

linklist = [
    'https://www.amazon.com/dp/B00OI0RGGO',
    'https://www.amazon.com/dp/B00TPKOPWA',
    'https://www.amazon.com/dp/B00TH42HWE'
]

proxyVault = ['103.110.37.244:36022', '180.254.218.229:8080', '110.74.197.207:50632', '1.20.101.95:49001', '200.10.193.90:8080', '173.164.26.117:3128', '103.228.118.66:43002', '178.128.231.201:3128', '1.2.169.54:55312', '181.52.85.249:31487', '97.64.135.4:8080', '190.96.214.123:53251', '52.144.107.142:31923', '45.5.224.145:52035', '89.218.22.178:8080', '192.241.143.186:80', '113.53.29.218:38310', '36.78.131.182:39243']
random.shuffle(proxyVault)


class NoMoreProxies(Exception):
    pass


def skip_proxy():
    global proxyVault
    if len(proxyVault) == 0:
        raise NoMoreProxies()
    proxyVault.pop()


def get_proxy():
    global proxyVault
    if len(proxyVault) == 0:
        raise NoMoreProxies()
    proxy_url = proxyVault[-1]
    proxy = {'https': f'http://{proxy_url}'}
    return proxy


def parse_product(link):
    try:
        proxy = get_proxy()
        print("checking the proxy:", proxy)
        res = requests.get(link, proxies=proxy, timeout=5)
        soup = BeautifulSoup(res.text, "html5lib")
        try:
            product_name = soup.select_one("#productTitle").get_text(strip=True)
        except Exception:
            product_name = ""

        return product_name

    except Exception:
        """the following line when hit produces new proxy and remove the bad one that passes through process_proxy(proxy)"""
        skip_proxy()
        return parse_product(link)


if __name__ == '__main__':
    for url in linklist:
        result = parse_product(url)
        print(result)

我还建议更改最后一个try/except子句以捕获
RequestException
,而不是
Exception
,我可能在这里遗漏了一些关键的东西(因为已经很晚了),但这似乎是一个非常复杂的简单问题。这几乎是一个XY问题。我将发布一些想法、问题(我的漫游)、观察和建议:

  • 最终目标是,对于每个链接,使用每个代理访问它(一次或尽可能多次?如果是后者,则看起来像是拒绝服务尝试,因此我假设是前者:)(当代理失败时,移动到下一个)。如果这是可行的,那就给一些产品(看起来像是某种电动机)取个名字
  • 为什么是递归?它受到堆栈的限制(在Python中为by)
  • 如果不给变量赋值,就不需要将变量声明为全局变量(也有例外,但我认为这里不是这种情况)
  • 当proxyVault变为空时,进程\u代理(问题变量)表现不好
  • global proxy
    (从答案上看)简直太难看了
  • 为什么要随机选择,而不是简单地从列表中选择下一个代理
  • parse_product_info(parse_product)行为不一致,在某些情况下返回某些内容,在另一些情况下不返回
  • 并行化仅在目标URL级别发生。如果也在代理级别工作,则可以对其进行进一步改进(但需要在代码中添加更多逻辑)
下面是一个简化(更简洁)的版本

代码00.py:

#/usr/bin/env蟒蛇3
导入系统
随机输入
导入请求
从bs4导入BeautifulSoup
URL=[
"https://www.amazon.com/dp/B00OI0RGGO",
"https://www.amazon.com/dp/B00TPKOPWA",
"https://www.amazon.com/dp/B00TH42HWE",
"https://www.amazon.com/dp/B00TPKNREM",
]
代理=[
"103.110.37.244:36022",
"180.254.218.229:8080",
"110.74.197.207:50632",
"1.20.101.95:49001",
"200.10.193.90:8080",
"173.164.26.117:3128",
"103.228.118.66:43002",
"178.128.231.201:3128",
"1.2.169.54:55312",
"181.52.85.249:31487",
"97.64.135.4:8080",
"190.96.214.123:53251",
"52.144.107.142:31923",
"45.5.224.145:52035",
"89.218.22.178:8080",
"192.241.143.186:80",
"113.53.29.218:38310",
"36.78.131.182:39243"
]
def parse_product_info(链接):#还可以将代理作为参数传递
local_proxies=proxies[:]#制作全局代理的自己的副本(以防您希望对其进行洗牌而不影响其他并行处理工作人员)
#随机。shuffle(local_proxy)#没有什么区别,但是如果你真的想洗牌,就取消这一行
对于本地_代理中的代理:
尝试:
proxy_dict={“https”:f“http://{proxy}}}http还是https?
打印(f“要使用的代理:{Proxy_dict['https']}”)
response=requests.get(link,proxies=proxy\u dict,timeout=5)
如果没有答复:
打印(f“HTTP请求返回{response.status_code}code”)
继续#移动到下一个代理
soup=BeautifulSoup(response.text,“html5lib”)
尝试:
产品名称=汤。选择一个(“产品名称”)。获取文本(strip=True)
返回产品名称#检索到的信息,返回它。
例外情况除外,如e:#可能需要使用特定的例外情况
打印(f“错误:{e}”)
#URL可访问,但无法分析信息。
#返回,因为使用任何其他代理可能都是相同的。
返回None#如果要尝试其他代理,请替换为“continue”
例外情况除外,如e:
#打印(f“{e}”)
继续#出现一些异常,移动到下一个代理
def main():
对于url中的url:
打印(f“\n诱人的url:{url}…”)