嵌套Web请求的Gevent池

4

我尝试组织一个最多有10个并发下载的池。该函数应该下载基本的URL,然后解析该页面上的所有URL,并下载它们中的每一个,但总体上,并发下载的数量不应超过10。

from lxml import etree 
import gevent
from gevent import monkey, pool
import requests

monkey.patch_all()
urls = [
    'http://www.google.com', 
    'http://www.yandex.ru', 
    'http://www.python.org', 
    'http://stackoverflow.com',
    # ... another 100 urls
    ]

LINKS_ON_PAGE=[]
POOL = pool.Pool(10)

def parse_urls(page):
    html = etree.HTML(page)
    if html:
        links = [link for link in html.xpath("//a/@href") if 'http' in link]
    # Download each url that appears in the main URL
    for link in links:
        data = requests.get(link)
        LINKS_ON_PAGE.append('%s: %s bytes: %r' % (link, len(data.content), data.status_code))

def get_base_urls(url):
    # Download the main URL
    data = requests.get(url)
    parse_urls(data.content)

我该如何组织代码以实现并发,但同时保持所有Web请求的总全局池限制?
3个回答

10
我认为以下内容可以满足您的需求。在我的示例中,我使用BeautifulSoup而不是您提供的链接去除工具。
from bs4 import BeautifulSoup
import requests
import gevent
from gevent import monkey, pool
monkey.patch_all()

jobs = []
links = []
p = pool.Pool(10)

urls = [
    'http://www.google.com', 
    # ... another 100 urls
]
    
def get_links(url):
    r = requests.get(url)
    if r.status_code == 200:
        soup = BeautifulSoup(r.text)
        links.extend(soup.find_all('a'))

for url in urls:
    jobs.append(p.spawn(get_links, url))
gevent.joinall(jobs)
 

4

gevent.pool会限制并发的greenlets,而不是连接。

您应该使用sessionHTTPAdapter

connection_limit = 10
adapter = requests.adapters.HTTPAdapter(pool_connections=connection_limit, 
                                        pool_maxsize=connection_limit)
session = requests.session()
session.mount('http://', adapter)
session.get('一些url')
# 或者使用 gevent 进行处理
from gevent.pool import Pool
# 如果处理数据的时间比下载时间长,则此值应大于连接限制,
# 以便运行处理。
pool_size = 15 
pool = Pool(pool_size)
for url in urls:
    pool.spawn(session.get, url)

1
请问为什么您在HTTPAdapter提供的连接池之外还要使用gevent.pool呢?为什么不直接使用gevent.spawn(...)呢?非常感谢。 - ARF

0

你应该使用gevent.queue来正确地完成它。

此外,this(eventlet examples)对于你理解基本思想会很有帮助。

Gevent的解决方案类似于eventlet。

请记住需要有某个地方存储已访问的URL,以免出现循环,为了避免内存错误,你需要引入一些限制。


问题在于我有两种类型的URL,每一种都需要不同的函数来处理它。 - DominiCane
如果您需要为不同的URL使用不同的处理器(消费者),那么可以在生产者中包装逻辑,根据URL类型生成特定的函数。但它们都有一个队列。 - Ellochka Cannibal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接