Python 页面的链接和该子页面的链接。递归/线程

Python 页面的链接和该子页面的链接。递归/线程,python,python-3.x,multithreading,recursion,threadpool,Python,Python 3.x,Multithreading,Recursion,Threadpool,我在做一个函数,下载一个网站的内容,然后我在网站中寻找链接,我递归调用每个链接,直到第7级。问题是这需要很多时间,所以我想使用线程池来管理这些调用,但我不知道如何将这些任务准确地划分到线程池中 import requests import re url = 'https://masdemx.com/category/creatividad/?fbclid=IwAR0G2AQa7QUzI-fsgRn3VOl5oejXKlC_JlfvUGBJf9xjQ4gcBsyHinYiOt8' def s

我在做一个函数,下载一个网站的内容,然后我在网站中寻找链接,我递归调用每个链接,直到第7级。问题是这需要很多时间,所以我想使用线程池来管理这些调用,但我不知道如何将这些任务准确地划分到线程池中

import requests
import re

url = 'https://masdemx.com/category/creatividad/?fbclid=IwAR0G2AQa7QUzI-fsgRn3VOl5oejXKlC_JlfvUGBJf9xjQ4gcBsyHinYiOt8'


def searchLinks(url,level):
    print("level: "+str(level))
    if(level==3):
        return 0

    response = requests.get(url)
    enlaces = re.findall(r'<a href="(.*?)"',str(response.text))

    for en in enlaces:
        if (en[0] == "/" or en[0]=="#"):
            en= url+en[1:]
        print(en)
        searchLinks(en,level+1)


searchLinks(url,1)
这是我的实际代码,没有线程池

import requests
import re

url = 'https://masdemx.com/category/creatividad/?fbclid=IwAR0G2AQa7QUzI-fsgRn3VOl5oejXKlC_JlfvUGBJf9xjQ4gcBsyHinYiOt8'


def searchLinks(url,level):
    print("level: "+str(level))
    if(level==3):
        return 0

    response = requests.get(url)
    enlaces = re.findall(r'<a href="(.*?)"',str(response.text))

    for en in enlaces:
        if (en[0] == "/" or en[0]=="#"):
            en= url+en[1:]
        print(en)
        searchLinks(en,level+1)


searchLinks(url,1)
导入请求
进口稀土
url='1〕https://masdemx.com/category/creatividad/?fbclid=IwAR0G2AQa7QUzI-FSGRN3Vol5OEJXKLCjlfugbjF9XJQ4GCBSYhinyiot8'
def搜索链接(url,级别):
打印(“级别:+str(级别))
如果(级别==3):
返回0
response=requests.get(url)

enlaces=re.findall(r'对于初学者,请注意这可能是一个很大的操作。例如,如果每个页面平均只有10个唯一的链接,那么如果要递归7层,则需要处理1000多万个请求

另外,我会使用一个HTML解析库,比如代替regex,这是一种很容易刮取HTML的方法。避免打印到stdout,这也会降低工作速度

至于线程,一种方法是使用工作队列。Python是线程安全的,因此您可以创建一个工作线程池,轮询从队列中检索URL。当线程获得URL时,它会查找页面上的所有链接,并将相关URL(或页面数据,如果您愿意)附加到全局列表中(这是CPython上的一个锁——对于其他实现,在共享数据结构上使用锁)。URL在工作队列上排队,该过程继续

线程在级别达到0时退出,因为我们使用的是a而不是堆栈。这里(可能安全)的假设是链接级别多于深度

并行性来自线程阻塞等待请求响应,从而允许CPU运行响应到达的另一个线程来执行HTML解析和队列工作

如果您希望在多个内核上运行,以帮助并行化工作负载中CPU受限的部分,并研究生成进程。但是,由于瓶颈是I/O受限的(等待HTTP请求),单靠线程就可以实现并行化

下面是一些示例代码:

import queue
import requests
import threading
import time
from bs4 import BeautifulSoup

def search_links(q, urls, seen):
    while 1:
        try:
            url, level = q.get()
        except queue.Empty:
            continue

        if level <= 0:
            break

        try:
            soup = BeautifulSoup(requests.get(url).text, "lxml")

            for x in soup.find_all("a", href=True):
                link = x["href"]

                if link and link[0] in "#/":
                    link = url + link[1:]

                if link not in seen:
                    seen.add(link)
                    urls.append(link)
                    q.put((link, level - 1))
        except (requests.exceptions.InvalidSchema, 
                requests.exceptions.ConnectionError):
            pass

if __name__ == "__main__":
    levels = 2
    workers = 10
    start_url = "https://masdemx.com/category/creatividad/?fbclid=IwAR0G2AQa7QUzI-fsgRn3VOl5oejXKlC_JlfvUGBJf9xjQ4gcBsyHinYiOt8"
    seen = set()
    urls = []
    threads = []
    q = queue.Queue()
    q.put((start_url, levels))
    start = time.time()
    
    for _ in range(workers):
        t = threading.Thread(target=search_links, args=(q, urls, seen))
        threads.append(t)
        t.daemon = True
        t.start()
    
    for thread in threads:
        thread.join()
    
    print(f"Found {len(urls)} URLs using {workers} workers "
          f"{levels} levels deep in {time.time() - start}s")
在这个小的运行中,性能提高了3倍。在大的运行中,我遇到了最大的请求错误,所以这只是一个玩具示例