Ссылки на страницу и ссылки на этих страницах.Рекурсия / Темы - PullRequest
0 голосов
/ 21 ноября 2018

Я делаю функцию, которая загружает содержимое веб-сайта, затем я ищу ссылки на сайте и для каждой из них, которую я называю рекурсивно, одну и ту же функцию до 7-го уровня.Проблема в том, что это занимает много времени, поэтому я искал возможность использовать пул потоков для управления этими вызовами, но я не знаю, как именно разделить эти задачи на пул потоков.

Это мой настоящий код, безThreadpool.

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)

1 Ответ

0 голосов
/ 21 ноября 2018

У вас здесь много URL, так что это будет огромная операция.Например, если на каждой странице в среднем всего 10 ссылок, вы просматриваете более 10 миллионов запросов, если хотите обработать 7 слоев глубиной.

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

Что касается потоков, одним из подходов является использование рабочей очереди. класс очереди Python является потокобезопасным, поэтому вы можете создать пул рабочих потоков, которые запрашивают URL-адреса из очереди.Когда поток получает URL, он находит все ссылки на странице и добавляет соответствующий URL (или данные страницы, если вы хотите) в глобальный список (который также является поточно-безопасной операцией ).URL-адреса ставятся в очередь в рабочую очередь, и процесс продолжается.Потоки завершаются, когда указанный уровень достигает 0.

Другой подход может заключаться в том, чтобы очистить первый URL-адрес для всех его ссылок, затем создать столько потоков в пуле потоков и разрешить каждому из них работать в отдельном дереве ссылок.,Это устранит конфликт в очереди и уменьшит накладные расходы.

В любом случае идея состоит в том, что потоки будут блокировать ожидание ответов на запросы и позволять ЦП запускать другой поток для выполнения работы, которая компенсирует накладные расходы на потоки (переключение контекста, блокировка конкуренции).Если вы хотите работать на нескольких ядрах, прочтите этот пост в блоге о 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 с рекурсией или стеком.

Попробуйте!

...