Python Can';t修改函数使其独立工作,而不是依赖于返回的结果
我已经用python编写了一个脚本,在向某些链接发送请求时使用代理,以便从那里解析产品名称。我目前的尝试完美地完成了这项工作。此函数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__':
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}…”)