Невозможно изменить функцию для работы независимо, вместо зависимости от возвращаемого результата. - PullRequest
0 голосов
/ 11 ноября 2019

Я написал скрипт на python, использующий прокси при отправке запросов на некоторые ссылки, чтобы проанализировать название продукта оттуда. Моя текущая попытка делает работу без нареканий. Эта функция parse_product() полностью зависит от возвращаемого результата (прокси), чтобы правильно использовать тот же прокси. Я пытаюсь изменить функцию parse_product() таким образом, чтобы сама функция не зависела от предыдущего вызова той же функции, чтобы повторно использовать работающий прокси до тех пор, пока он не станет недействительным. Чтобы быть понятнее - я ожидаю, что основная функция будет больше, как показано ниже. Тем не менее, когда все будет решено, я буду использовать многопроцессорность, чтобы скрипт работал быстрее:

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

и все же ожидаю, что скрипт будет работать так, как сейчас.

Iпробовал с (работающим):

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']

def process_proxy(proxy):
    global proxyVault
    if not proxy:
        proxy_url = choice(proxyVault)
        proxy = {'https': f'http://{proxy_url}'}
    else:
        proxy_pattern = proxy.get("https").split("//")[-1]
        if proxy_pattern in proxyVault:
            proxyVault.remove(proxy_pattern)
        random.shuffle(proxyVault)
        proxy_url = choice(proxyVault)
        proxy = {'https': f'http://{proxy_url}'}
    return proxy


def parse_product(link,proxy):
    try:
        if not proxy:raise
        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 proxy, product_name

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


if __name__ == '__main__':
    proxy = None
    for url in linklist:
        result = parse_product(url,proxy)
        proxy = result[0]
        print(result)

Примечание: parse_product() функция возвращает прокси и название продукта. Однако прокси, который возвращает функция, повторно используется в той же функции parse_product() до тех пор, пока не станет недействительным.

Кстати, прокси, используемые в proxyVault, являются просто заполнителями.

Ответы [ 3 ]

1 голос
/ 14 ноября 2019

Если вам не нужна поддержка многопоточности (ваши изменения говорят о том, что вы этого не делаете), вы можете заставить ее работать со следующими незначительными изменениями. proxyVault сохраняет как весь пул прокси, так и и активный прокси (последний) после перетасовки списка (в вашем коде было shuffle и choice, но достаточно только одного из них),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 / исключением, чтобы поймать RequestException вместо Exception.

0 голосов
/ 15 ноября 2019

Ладно, похоже, я смог решить проблему, следуя предложению tmadam в комментарии. Теперь это работает так, как я ожидал, если бы каждый раз приходил с одним звонком. Однако, когда я пытаюсь использовать multiprocessing, плохие прокси-серверы делают несколько вызовов process_proxy(), от которых я хотел бы избавиться, чтобы скрипт в конечном итоге стал потокобезопасным.

Рабочий один:

import random
import requests
from bs4 import BeautifulSoup

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

def process_proxy():
    global proxyVault
    if len(proxyVault)!=0:
        random.shuffle(proxyVault)
        proxy_url = proxyVault.pop()
        proxy = {'https': f'http://{proxy_url}'}
    else:
        proxy = None
    return proxy


def parse_product_info(link):
    global proxy
    try:
        if not proxy:raise
        print("proxy to be used:",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 = ""
        print(link,product_name)

    except Exception:
        proxy = process_proxy()
        if proxy!=None:
            return parse_product_info(link)
        else:
            pass


if __name__ == '__main__':
    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']
    for url in linklist:
        parse_product_info(url)

Моя конечная цель - использовать скрипт следующим образом:

with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
    future_to_url = {executor.submit(parse_product_info,url): url for url in linklist}
    concurrent.futures.as_completed(future_to_url)
0 голосов
/ 14 ноября 2019

Возможно, вы можете поместить логику обработки прокси внутри класса и передать экземпляр в 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}"}

Тогда parse_product() может выглядеть примерно так:

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 всемпотоки и распараллелить тоже.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...