我正在制作一个下载网站内容的功能,然后我在网站中查找链接,对于每个链接,我都递归调用相同的功能,直到第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)
答案 0 :(得分:0)
您在这里有很多URL,因此这将是一项巨大的操作。例如,如果每个页面平均只有10个链接,则要递归7层,则您正在查看超过1000万个请求。
对于初学者,请使用HTML解析库(例如BeautifulSoup)而不是regex。尽管我尚未测试确切的数量,但这将立即为您带来性能提升。避免打印到标准输出,这也会减慢工作速度。
关于线程,一种方法是使用工作队列。 Python的queue class是线程安全的,因此您可以创建一个工作线程池来轮询以从队列中检索URL。当线程获取URL时,它将在页面上找到所有链接,并将相关的URL(或页面数据,如果需要)附加到全局列表(也是thread-safe operation)上。 URL已排队在工作队列中,过程继续进行。当指定级别达到0时,线程退出。
另一种方法可能是为其所有链接抓取第一个URL,然后在线程池中创建尽可能多的线程,并让它们每个都在单独的链接树上运行。这样可以消除队列争用并减少开销。
无论哪种方式,想法都是线程将阻止等待请求响应,并允许CPU运行其他线程来完成工作,这将弥补线程开销(上下文切换,锁争用)。如果您想在多个内核上运行,请read this blog post about the GIL并查看生成过程。
这是第一种方法的一些示例代码:
import queue
import requests
import threading
import time
from bs4 import BeautifulSoup
def search_links(q, result):
while 1:
try:
url, level = q.get()
except queue.Empty:
continue
if not level:
break
try:
for x in BeautifulSoup(requests.get(url).text, "lxml").find_all("a", href=True):
link = x["href"]
if link and link[0] in "#/":
link = url + link[1:]
result.append(link)
q.put((link, level - 1))
except requests.exceptions.InvalidSchema:
pass
levels = 2
workers = 10
start_url = "https://masdemx.com/category/creatividad/?fbclid=IwAR0G2AQa7QUzI-fsgRn3VOl5oejXKlC_JlfvUGBJf9xjQ4gcBsyHinYiOt8"
urls = []
threads = []
q = queue.Queue()
q.put((start_url, levels))
start = time.time()
for i in range(workers):
threads.append(threading.Thread(target=search_links, args=(q, urls)))
threads[-1].daemon = True
threads[-1].start()
for thread in threads:
thread.join()
print("Found %d URLs using %d workers %d levels deep in %ds" % (len(urls), workers, levels, time.time() - start))
#for url in urls:
# print(url)
一些不是特别快的机器上运行的示例:
> python thread_req.py
Found 7733 URLs using 1 workers 2 levels deep in 112s
> python thread_req.py
Found 7729 URLs using 10 workers 2 levels deep in 27s
> python thread_req.py
Found 7731 URLs using 20 workers 2 levels deep in 25s
在这种小批量生产中,性能提高了4倍。在较大的运行量中,我遇到了最大的请求错误,因此这只是一个玩具示例。另外值得注意的是,对于队列,您将使用递归或堆栈执行BFS而不是DFS。