Python 页面的链接和该子页面的链接。递归/线程
我在做一个函数,下载一个网站的内容,然后我在网站中寻找链接,我递归调用每个链接,直到第7级。问题是这需要很多时间,所以我想使用线程池来管理这些调用,但我不知道如何将这些任务准确地划分到线程池中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
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倍。在大的运行中,我遇到了最大的请求错误,所以这只是一个玩具示例