Как использовать многопроцессорность для извлечения ссылок с веб-страниц с помощью Beautiful Soup?

У меня есть список ссылок, и я создаю объект Beautiful Soup для каждой ссылки и очищаю все ссылки в тегах абзаца со страницы. Поскольку у меня есть сотни ссылок, которые я хотел бы очистить, один процесс займет больше времени, чем мне бы хотелось, поэтому многопроцессорность кажется идеальным решением.

Вот мой код:

import requests
from bs4 import BeautifulSoup
from multiprocessing import Process, Queue

urls = ['https://hbr.org/2011/05/the-case-for-executive-assistants','https://signalvnoise.com/posts/3450-when-culture-turns-into-policy']

def collect_links(urls):

    extracted_urls = []
    bsoup_objects = []
    p_tags = [] #store language between paragraph tags in each beautiful soup object

    workers = 4 
    processes = [] 
    links = Queue() #store links extracted from urls variable
    web_connection = Queue() #store beautiful soup objects that are created for each url in urls variable 

    #dump each url from urls variable into links Queue for all processes to use
    for url in urls:
        links.put(url)

    for w in xrange(workers):
        p = Process(target = create_bsoup_object, args = (links, web_connection)) 
        p.start()
        processes.append(p)
        links.put('STOP')
        for p in processes:
            p.join()
        web_connection.put('STOP')

    for beaut_soup_object in iter(web_connection.get, 'STOP'):
        p_tags.append(beaut_soup_object.find_all('p'))
    for paragraphs in p_tags:
        bsoup_objects.append(BeautifulSoup(str(paragraphs)))
    for beautiful_soup_object in bsoup_objects:
        for link_tag in beautiful_soup_object.find_all('a'):
            extracted_urls.append(link_tag.get('href'))
    return extracted_urls

def create_bsoup_object(links, web_connection):

    for link in iter(links.get, 'STOP'):
        try:
            web_connection.put(BeautifulSoup(requests.get(link, timeout=3.05).content))
        except requests.exceptions.Timeout as e:
            #client couldn't connect to server or return data in time period specified in timeout parameter in requests.get()
            pass  
        except requests.exceptions.ConnectionError as e:
            #in case of faulty url
            pass           
        except Exception, err:
            #catch regular errors
            print(traceback.format_exc())
            pass
        except requests.exceptions.HTTPError as e:
            pass
    return True

И когда я запускаю collect_links(urls), вместо получения списка ссылок я получаю пустой список со следующей ошибкой:

Traceback (most recent call last):
  File "/usr/local/Cellar/python/2.7.8_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/queues.py", line 266, in _feed
    send(obj)
RuntimeError: maximum recursion depth exceeded while calling a Python object
[]

Я не уверен, к чему это относится. Я где-то читал, что очереди лучше всего работают с простыми объектами. Имеет ли к этому какое-либо отношение размер красивых предметов из супа, которые я в них храню? Я был бы признателен за любое понимание.


person Mika Schiller    schedule 26.08.2015    source источник
comment
Избавьтесь от предложений except … pass, которые выбрасывают полезную информацию. Поместите эту информацию в этот вопрос. Никогда не используйте except …: pass, это всегда будет неправильно.   -  person msw    schedule 26.08.2015
comment
@msw что посоветуете, потому что я использую его для извлечения данных из html, иногда данных нет, потому что разные страницы на несколько% отличаются от других. Я закончил с большим количеством исключений и пропусков   -  person CodeGuru    schedule 04.05.2018


Ответы (1)


Объекты, которые вы помещаете в очередь, должны быть доступны для обработки. Например.

import pickle
import requests
from bs4 import BeautifulSoup

soup = BeautifulSoup(requests.get('http://httpbin.org').text)
print type(soup)
p = pickle.dumps(soup)

Этот код вызывает RuntimeError: maximum recursion depth exceeded while calling a Python object.

Вместо этого вы можете поместить фактический HTML-текст в очередь и передать его через BeautifulSoup в основном потоке. Это по-прежнему улучшит производительность, поскольку ваше приложение, вероятно, будет привязано к вводу-выводу из-за своего сетевого компонента.

Сделайте это в create_bsoup_object():

web_connection.put(requests.get(link, timeout=3.05).text)

который добавит HTML в очередь вместо объекта BeautifulSoup. Затем проанализируйте HTML в основном процессе.

В качестве альтернативы проанализируйте и извлеките URL-адреса в дочерних процессах и поместите extracted_urls в очередь.

person mhawke    schedule 26.08.2015