С Новым годом! Форум программистов, компьютерный форум, киберфорум
Python: Web
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.79/34: Рейтинг темы: голосов - 34, средняя оценка - 4.79
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186

Beautiful Soup 4, сравнение html-парсеров при запуске в потоке

04.04.2018, 21:03. Показов 7090. Ответов 47
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте, спарсил страницу с помощью lxml, встроенного в Beautiful Soup - выдал 30 секунд. Не поверил, начал искать ошибки и оптимизировать. Ничего конкретного не получилось, время парсинга примерна такая же. Вспомнил, что у Beautiful Soup есть еще 2 других парсера html.parser и html5lib, в итоге их результаты:
Запуск в отдельном потоке Запуск в общей программе, без создания отдельного потока
html.parser:2 1.44
html5lib: 14 5.5
lxml: 30 1.24

И собственно вопрос, почему в потоке lxml так теряет в скорости? Хотя он считается самым быстрым
Метод запуска потока:
Python
1
2
3
4
5
6
def starting_threading():
    while q.empty()==False:
        if(threading.active_count()<=threads_count):
            t = threading.Thread(target=all_parsing, args=(q,))
            t.start()
            print("Старт потока",t)
q-очередь, threads_count=1.
Вдруг кому-то захочется спарсить эту же страницу ради спортивного интереса: https://www.mvideo.ru/smartfon... rtfony-205 . Парсить только ссылки на телефоны 1 страницы (этой). Пишите библиотеку и язык программирования для сравнения. Должен быть запрос страницы, без сохранения ее на диск, а потом ее парсинга
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
04.04.2018, 21:03
Ответы с готовыми решениями:

Beautiful Soup Python
Я пишу программу открывающую все ссылки на указанной странице и выписывает те, у которых в url есть id= и т.д. Но по какой-то причине в...

Парсинг на Python с Beautiful Soup
Напишите 2 парсера. Первый парсер разработайте для скрапинга сайта pleer.ru, где необходимо вытащить текст из колонки категорий(см. скрин...

Beautiful Soup поиск по атрибуту
Добрый вечер. Подскажите пожалуйста как с помощь bs4 получить все li c data-xxxx, xxxx-каждый раз новое

47
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
04.04.2018, 22:05
Вы чего-то не то делаете: 0.09 сек. (через Thread) на парсинг 12 ссылок этой страницы, 0.06 сек - без создания потока (очевидно, что на одну страницу использование потока дает только накладные расходы.)
Использовался только lxml - без всяких бесполезных оберток типа Beautiful Soup.
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
04.04.2018, 22:22  [ТС]
Цитата Сообщение от Garry Galler Посмотреть сообщение
Вы чего-то не то делаете: 0.09 сек. (через Thread) на парсинг 12 ссылок этой страницы, 0.06 сек - без создания потока (очевидно, что на одну страницу использование потока дает только накладные расходы.)
Использовался только lxml - без всяких бесполезных оберток типа Beautiful Soup.
Можете предоставить код?
0
Просто Лис
Эксперт Python
 Аватар для Рыжий Лис
5972 / 3734 / 1099
Регистрация: 17.05.2012
Сообщений: 10,791
Записей в блоге: 9
05.04.2018, 05:08
Что-то вы не так делаете. lxml (даже в Beautiful Soup) работает охрененно быстро (мой пример: 5 Мб html, тысячи селекторов - 20 сек.)

Покажите как вы ищите ссылки на странице.
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
05.04.2018, 10:54  [ТС]
Цитата Сообщение от Рыжий Лис Посмотреть сообщение
Что-то вы не так делаете. lxml (даже в Beautiful Soup) работает охрененно быстро (мой пример: 5 Мб html, тысячи селекторов - 20 сек.)
Покажите как вы ищите ссылки на странице.
Ну там не только ссылки конечно ищутся, но все же
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def get_html(request):
    return BeautifulSoup(request.content, 'lxml')
 
def parsing_list_object(html,city_code):
     div_list=html.find("div",{"class":"o-plp-container__product-listing grid-view"})
     objects_div=div_list.find_all("div",{"class":"c-product-tile sel-product-tile-main "})
     for object_div in objects_div:
         error_div=object_div.find("div",{"class":"c-product-label"})
         if(error_div!=None):
             span_main=error_div.contents[1]
             error=span_main.contents[3].get_text()
             if(error!="Витринный образец"):
                 continue
         object_a=object_div.find("a",{"class":"sel-product-tile-title"})
         object_link=url_main+object_a["href"]+"/shopdirections?cityId="+city_code
         print(object_link)
Проблема в том, что именно в потоке парсер 30 сек ищет, а без потока где то 1.24
0
Просто Лис
Эксперт Python
 Аватар для Рыжий Лис
5972 / 3734 / 1099
Регистрация: 17.05.2012
Сообщений: 10,791
Записей в блоге: 9
05.04.2018, 11:50
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import logging
import requests
from bs4 import BeautifulSoup
 
log = logging.getLogger(__name__)
 
 
def main():
    logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', level=logging.INFO)
    log.info('start')
    html = requests.get('https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205', proxies={
        'https': '11.11.11.1:3128'
    }).text
    log.info('parse')
    soup = BeautifulSoup(html, 'lxml')
    for tag in soup.find('div', attrs={'class': 'product-tiles-list-wrapper'}).find_all('a'):
        log.info(tag['href'])
    log.info('end')
 
 
if __name__ == '__main__':
    main()
Code
1
2
3
4
01,254 INFO:parse
:02,164 INFO:/products/smartfon-samsung-galaxy-s9-64gb-ultrafiolet-30032352
...
:02,167 INFO:end
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
05.04.2018, 12:31  [ТС]
Цитата Сообщение от Рыжий Лис Посмотреть сообщение
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import logging
import requests
from bs4 import BeautifulSoup
log = logging.getLogger(__name__)
def main():
* * logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', level=logging.INFO)
* * log.info('start')
* * html = requests.get('https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205', proxies={
* * * * 'https': '11.11.11.1:3128'
* * }).text
* * log.info('parse')
* * soup = BeautifulSoup(html, 'lxml')
* * for tag in soup.find('div', attrs={'class': 'product-tiles-list-wrapper'}).find_all('a'):
* * * * log.info(tag['href'])
* * log.info('end')
if __name__ == '__main__':
* * main()
Вы ошиблись в строке
Python
1
for tag in soup.find('div', attrs={'class': 'product-tiles-list-wrapper'})
Таких блоков 2. К тому же вы ищите все ссылки
Python
1
2
3
2018-04-05 14:16:03,338 INFO:/products/smartfon-samsung-galaxy-s9-64gb-ultrafiolet-30032352
2018-04-05 14:16:03,338 INFO:/products/smartfon-samsung-galaxy-s9-64gb-ultrafiolet-30032352/reviews
2018-04-05 14:16:03,338 INFO:/products/smartfon-samsung-galaxy-s9-64gb-ultrafiolet-30032352
Что по сути это ссылка на один и тот же товар.
Заменив, ваш код и если искать элемент h4, в котором находится ссылка. То выходит 0.8
Измененный ваш код:
Python
1
2
3
4
5
6
7
8
9
10
11
def main():
    logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', level=logging.INFO)
    log.info('start')
    html = requests.get('https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205').text
    log.info('parse')
    soup = BeautifulSoup(html, 'lxml')
    div=soup.find_all('h4', attrs={"class": "e-h4 u-pb-0"})
    for tag in div:
        a=tag.find("a",{"class":"sel-product-tile-title"})
        log.info(a['href'])
    log.info('end')
Кликните здесь для просмотра всего текста
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2018-04-05 14:28:39,867 INFO:start
2018-04-05 14:28:40,446 INFO:parse
2018-04-05 14:28:40,668 INFO:/products/smartfon-samsung-galaxy-s9-64gb-ultrafiolet-30032352
2018-04-05 14:28:40,668 INFO:/products/smartfon-samsung-galaxy-s9-64gb-titan-30032356
2018-04-05 14:28:40,668 INFO:/products/smartfon-apple-iphone-8-64gb-space-gray-mq6g2ru-a-30030151
2018-04-05 14:28:40,668 INFO:/products/smartfon-apple-iphone-6-32gb-space-gray-mq3d2ru-a-30028155
2018-04-05 14:28:40,668 INFO:/products/smartfon-apple-iphone-x-64gb-space-gray-mqac2ru-a-30030163
2018-04-05 14:28:40,668 INFO:/products/smartfon-apple-iphone-8-plus-64gb-space-gray-mq8l2ru-a-30030157
2018-04-05 14:28:40,669 INFO:/products/smartfon-apple-iphone-x-64gb-silver-mqad2ru-a-30030164
2018-04-05 14:28:40,669 INFO:/products/smartfon-apple-iphone-7-32gb-black-mn8x2ru-a-30026135
2018-04-05 14:28:40,669 INFO:/products/smartfon-apple-iphone-8-64gb-gold-mq6j2ru-a-30030153
2018-04-05 14:28:40,669 INFO:/products/smartfon-apple-iphone-7-32gb-jet-black-mqtx2ru-a-30030301
2018-04-05 14:28:40,669 INFO:/products/smartfon-apple-iphone-8-plus-64gb-gold-mq8n2ru-a-30030159
2018-04-05 14:28:40,669 INFO:/products/smartfon-samsung-galaxy-s8-64gb-chernyi-brilliant-30027818
2018-04-05 14:28:40,669 INFO:end
0
Просто Лис
Эксперт Python
 Аватар для Рыжий Лис
5972 / 3734 / 1099
Регистрация: 17.05.2012
Сообщений: 10,791
Записей в блоге: 9
05.04.2018, 12:41
Цитата Сообщение от danilshik Посмотреть сообщение
То выходит 0.8
Или примерно 0,2 секунды, если не считать время на скачивание страницы.
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
05.04.2018, 13:03  [ТС]
Мне интересно как Garry Galler добился такой скорости

Причина оказалась в том, что я не посчитал главный поток.
0
Просто Лис
Эксперт Python
 Аватар для Рыжий Лис
5972 / 3734 / 1099
Регистрация: 17.05.2012
Сообщений: 10,791
Записей в блоге: 9
05.04.2018, 13:06
На потоки парареллелится не так. Запускаем, например, 16 потоков, все в бесконечном цикле берут задания из очереди и выполняют.

То есть:
1) запускаем потоки
2) запихиваем в очередь задачи.
3) ждём пока очередь не опустеет.
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
05.04.2018, 13:20  [ТС]
Хотя нет, проблема или в очереди, или в алгоритме запуска потоков:

Добавлено через 4 минуты
Цитата Сообщение от Рыжий Лис Посмотреть сообщение
То есть:
1) запускаем потоки
2) запихиваем в очередь задачи.
3) ждём пока очередь не опустеет.
Не совсем понял, т.е мы должны сначала запустить большое количество потоков, потом запихнуть в очередь задачи, и ждем пока не опустеет? Если переделаете пример под ваш вариант для примера, буду благодарен
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
threads_count=5
 
import queue
q=queue.Queue()
def main(q):
 
    log.info('Удаление из очереди и возврат элемента')
    q.get()
 
    html = requests.get('https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205').text
    log.info('Получение ответа')
    soup = BeautifulSoup(html, 'lxml')
    log.info('Построение soup')
    list_div=soup.find_all("div",{"c-product-tile sel-product-tile-main "})
    for div in list_div:
        log.info(div.find("a")["href"])
 
 
if __name__ == '__main__':
    logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', level=logging.INFO)
    log.info("Старт")
    q.put("opa")
    q.put("opa")
    q.put("opa")
    q.put("opa")
    q.put("opa")
    q.put("opa")
 
    print(threading.active_count())
    log.info('Запуск поток')
    while q.empty()==False:
        if(threading.active_count()<threads_count):
            t = threading.Thread(target=main, args=(q,))
            t.start()
            print(threading.active_count())
            print("Старт потока",t)
Добавлено через 1 минуту
В очередь будут заносится параметры городов, которые спарсятся в самом начале (в этом коде нету). Этот параметр будет добавляться к ссылке, чтобы парсить телефоны с конкретного города
0
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
05.04.2018, 14:46
Цитата Сообщение от danilshik Посмотреть сообщение
Можете предоставить код?
Все банально - практически копипаст с документации. Интерфейс threading крайне неудобный - его уже давно никто не использует.
Тем более, что для web больше практична не многопоточность, а асинхронность. Но дело ваше - что использовать.
Я вообще не вижу никакого смысла в распараллеливании парсинга html (на одном сайте) - он и так выполняется настолько быстро, что мы скорей получим задержку связанную с созданием потоков и переключением контекста , чем реальное ускорение.
И хорошо, если сервер нас не забанит за сто запросов в секунду. Обычно сайты не разрешают делать более 1-2 запросов в сек и банят всех, кто исчерпывает определенный лимит запросов за час. Лично мне в моих грабберах чаще приходилось вводить искусственные задержки\таймауты, чтобы скрипт как можно дольше работал без бана и выкачал как можно больше.

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def parse_html(resp):    
    result = {}
    root = lxml.html.fromstring(resp.text)
    for elem in root.xpath(xpath):
        result[elem.get('href')] = elem.get('data-track-label')
    return result
 
def task():
    
    while not q.empty():
        try:
            url = q.get()
            if url is None:
                break
            resp = requests.get(url)
            result = ''
            if resp:
                start = time.time()
                result = parse_html(resp)
                end = time.time() 
                log.info(
                    'parse: %s| %s | %s',
                     len(result), 
                     end - start,
                     url
                     )  
        except Exception as err:
            print(err)       
    
        finally:
            q.task_done()
            if result:
               LINKS.append(result)    
                 
def milti_queue(urls,workers):
    
    for url in range(workers):
        t = Thread(target=task)
        t.start()
        threads.append(t)
    
if __name__ == "__main__":
    log = logging.getLogger(__name__)
    logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', level=logging.INFO)
    log.info('start')
    
    xpath = "//a[@data-product-info]"
    url = 'https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=%s'
    urls = []
    LINKS = []
    
    for i in range(1,11):
        urls.append(url % i)
    
    threads = []
    q = queue.Queue()
    for url in urls:
        q.put(url)
    
    milti_queue(urls,workers=len(urls))
    
    q.join()
    log.info('end')
    #print(q.empty())
    #pprint(LINKS)
    print(threading.enumerate())
Code
1
2
3
4
5
6
7
8
9
10
11
12
2018-04-05 14:33:58,631 INFO:start
2018-04-05 14:33:59,487 INFO:parse: 12| 0.07000017166137695 | https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=7
2018-04-05 14:33:59,547 INFO:parse: 12| 0.059999942779541016 | https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=2
2018-04-05 14:33:59,617 INFO:parse: 12| 0.13000011444091797 | https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=4
2018-04-05 14:33:59,677 INFO:parse: 12| 0.12000012397766113 | https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1
2018-04-05 14:33:59,747 INFO:parse: 12| 0.059999942779541016 | https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=10
2018-04-05 14:33:59,901 INFO:parse: 12| 0.07400035858154297 | https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=6
2018-04-05 14:34:00,001 INFO:parse: 12| 0.06000018119812012 | https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=3
2018-04-05 14:34:00,591 INFO:parse: 12| 0.07000017166137695 | https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=9
2018-04-05 14:34:00,641 INFO:parse: 12| 0.09000015258789062 | https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=8
2018-04-05 14:34:01,513 INFO:parse: 12| 0.07000017166137695 | https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=5
2018-04-05 14:34:01,513 INFO:end
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
05.04.2018, 16:33  [ТС]
Цитата Сообщение от Garry Galler Посмотреть сообщение
for i in range(1,11):
* * * * urls.append(url % i)
threads = []
* * q = queue.Queue()
* * for url in urls:
* * * * q.put(url)
Мне интересно, а почему сразу нельзя в очередь ссылки добавить. А то вы сначала добавляете в список, а потом из списка в очередь. Это с чем-то связано?
0
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
05.04.2018, 16:52
Цитата Сообщение от danilshik Посмотреть сообщение
Это с чем-то связано
Нет. Просто сначала была одна ссылка - потом несколько. А так, конечно, можно и сразу в очередь.
Код - просто демо, и не имеет никакого отношения к реальным задачам.
0
Эксперт С++
 Аватар для Avazart
8484 / 6151 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
05.04.2018, 17:05
Ну парсить в многопотоке нет смысла, а вот делать запросы - есть.
А может быть даже есть смысл делать в несколько процессов (например если через прокси)
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
05.04.2018, 20:46  [ТС]
Цитата Сообщение от Garry Galler Посмотреть сообщение
Нет. Просто сначала была одна ссылка - потом несколько. А так, конечно, можно и сразу в очередь.
Код - просто демо, и не имеет никакого отношения к реальным задачам.
Вот вы под каждую ссылку создали поток, и каждый поток парсит только 1 ссылку. А как сделать, чтобы работало одновременно 10 потоков, и парсили ссылки из очереди

Добавлено через 5 минут
Цитата Сообщение от Garry Galler Посмотреть сообщение
Интерфейс threading крайне неудобный - его уже давно никто не использует.
А каким сейчас пользуются?

Добавлено через 3 минуты
Цитата Сообщение от Garry Galler Посмотреть сообщение
t = Thread(target=task)
это from threading import Thread?
0
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
05.04.2018, 23:37
Цитата Сообщение от danilshik Посмотреть сообщение
А каким сейчас пользуются?
Не сейчас, а вместо - multiprocessing.dummy. Это подмодуль multiprocessing для потоков.
Там не требуется создавать потоки вручную - нужно лишь передать список заданий одному из методов пула потоков.
Или можно использовать класс из concurent.futures - ThreadPoolExecutor.
А вообще в тренде использовать асинхронные возможности python: asyncio и синтаксис async await.
Но обычная requests тут не годится - она синхронная.

Цитата Сообщение от danilshik Посмотреть сообщение
это from threading import Thread?
Да.
Цитата Сообщение от danilshik Посмотреть сообщение
А как сделать, чтобы работало одновременно 10 потоков, и парсили ссылки из очереди
Забыл написать, что поскольку в python есть GIL - теоретически число одновременно работающих (активных) потоков ограничено число ядер процессора. Хотя, в случае с I\O это и не совсем так. Погуглите на тему "python многопоточность GIL Дэвид Бизли". В общем, с потоками все довольно непросто.

Впрочем, в multiprocessing.dummy эффективное число потоков можно подобрать эмпирически путем, задавая разное число воркеров и сравнивая результаты.
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
06.04.2018, 02:38  [ТС]
Цитата Сообщение от Garry Galler Посмотреть сообщение
Не сейчас, а вместо - multiprocessing.dummy. Это подмодуль multiprocessing для потоков.
Там не требуется создавать потоки вручную - нужно лишь передать список заданий одному из методов пула потоков.
Или можно использовать класс из concurent.futures - ThreadPoolExecutor.
Цитата Сообщение от Garry Galler Посмотреть сообщение
Впрочем, в multiprocessing.dummy эффективное число потоков можно подобрать эмпирически путем, задавая разное число воркеров и сравнивая результаты.
Использовал как то просто модуль multiprocessing, но мне он показался довольно долгим, так как на создание процессов уделялось время, и к тому же большая нагрузка на CPU и ОЗУ. Из курса дисциплины "Операционные системы" знаю что лучше создать n-потоков, чем n-количество процессов
Цитата Сообщение от Garry Galler Посмотреть сообщение
А вообще в тренде использовать асинхронные возможности python: asyncio и синтаксис async await.
Но обычная requests тут не годится - она синхронная.
Где-то видел, что есть асинхронная версия request, вроде называется grequest

Добавлено через 45 минут
Решил сравнить все приведенные библиотеки, результаты такие:
concurent.futures - ThreadPoolExecutor: 248
Multiprocessing.Dumpy:253
Multiprocesssing - вообще не запустился, процессы создались, и потом программа завершилась. Почему, не знаю, хотя я его когда-то уже использовал. Также пришлось отказаться от очереди и заменить списком
Код
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
import requests
#from multiprocessing.dummy import Pool as ThreadPool
from multiprocessing import Pool
import logging
from bs4 import BeautifulSoup
import threading
import queue
import lxml.html
log = logging.getLogger(__name__)
count_thread=10
q_list =[]
q_url=queue.Queue()
cities=[]
url_main="https://www.mvideo.ru"
 
 
def parse_list_object(q_list):
    start = time.time()
    url=q_list[0]
    city=q_list[1]
    log.info("Парсинг list: %s | %s",url,city)
    r=requests.get(url)
    html=get_html(r)
    for elem in html.xpath(".//div[@class='c-product-tile sel-product-tile-main ']"):
        error_div=elem.xpath(".//span[@class='c-product-label__text']")
        if(error_div!=[]):
            error_div=error_div[0]
            error_text=error_div.xpath("span[2]/text()")[0]
            if(error_text!="Витринный образец"):
                continue
        link_object=url_main+elem.xpath(".//h4[@class='e-h4 u-pb-0']/a")[0].get("href")+"/shopdirections?cityId="+city
        q_url.put(link_object)
        log.info(link_object)
    end = time.time()
    log.info("Спарсен за %s", end-start)
def parse_object(url):
    r=requests.get(url)
    log.info("Запрос")
    html=get_html(r)
    log.info("Парсинг")
    print("Парсинг")
def start_threads():
    # with ThreadPoolExecutor(count_thread) as executor:
    #     for _ in executor.map(parse_list_object, q_list):
    #         pass
 
    with Pool(count_thread) as p:
        p.map(parse_list_object, q_list)
 
    # pool = ThreadPool(count_thread)
    # results = pool.map(parse_list_object, q_list)
    # pool.close()
    # pool.join()
 
 
def get_html(request):
    return lxml.html.fromstring(request.text)
def parse_city():
    r = requests.get("https://www.mvideo.ru/sitebuilder/blocks/regionSelection.json.jsp?pageId=homepage&pageUrl=/&").json()
    log.info('Запрос страницы городов')
    html = lxml.html.fromstring(r["htmlContent"])
    log.info('Получение html')
    for elem in html.xpath("//input[@name='/com/mvideo/domain/RegionSelectionFormHandler.cityId']"):
        city=elem.get("value")
        cities.append(city)
        log.info(city)
 
def all_parsing():
    for city in cities:
        r = requests.get("https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=" + str(1)+"?cityId="+str(city))
        log.info("Переход на 1 страницу города:"+city)
        html =get_html(r)
        count_list=html.xpath("//a[@class='c-pagination__num c-btn c-btn_white ']/text()")[0]
        log.info("Парсинг")
        log.info(count_list)
        for i in range(1,int(count_list)+1):
            parameters=[]
            parameters.append("https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=" + str(i)+"?cityId="+city)
            parameters.append(city)
            q_list.append(parameters)


Добавлено через 3 минуты
Количество потоков/процессов - 10
0
Просто Лис
Эксперт Python
 Аватар для Рыжий Лис
5972 / 3734 / 1099
Регистрация: 17.05.2012
Сообщений: 10,791
Записей в блоге: 9
06.04.2018, 04:56
Цитата Сообщение от Garry Galler Посмотреть сообщение
Но обычная requests тут не годится - она синхронная.
Небольшой вопрос. В каждом потоке я создаю так:
Python
1
2
s = requests.Session()
r = s.get(url, ...)
При этом, судя по логам, в каждом потоке устанавливаются по соединению (INFO:Starting new HTTP connection (1): 11.11.11.1) и ничего нигде не лочится.

Или я не понял, что она синхронная, и requests продолжает использовать общий пул соединений для всех потоков?
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
06.04.2018, 11:06  [ТС]
Цитата Сообщение от Garry Galler Посмотреть сообщение
А вообще в тренде использовать асинхронные возможности python: asyncio и синтаксис async await.
У вас нет какого нибудь парсера на нем, чисто ради примера

Добавлено через 29 минут
судя по этой статье https://maxpoletaev.ru/blog/async-python, асинхронные запросы нужно только для частей работающих с вводом/выводам и работающих сетью.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
06.04.2018, 11:06
Помогаю со студенческими работами здесь

В чем проблема? (Beautiful Soup)
from bs4 import BeautifulSoup import requests headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...

Парсинг Beautiful Soup 4 и Selenium.Webdriver
Здравствуйте, пишу парсер под сайт https://www.kimovil.com/ru/ и возникает проблема: товары динамически подгружаются. Сначала были...

Beautiful Soup - существует ли ограничения на длину получаемых данных?
Здравствуйте! Почему при парсинге html достается только часть данных, которые все лежат в одном теге. данные - это 1 000 000 цифр. Получаю...

Извлечь атрибуты a href и img src одновременно, используя Beautiful Soup
Знаю как извлечь и показать a href urls = soup.findAll('a') for url in urls: print(url) Но как можно найти...

Как убрать soup = BeautifulSoup (html) из кода?
Приветствую всех, только недавно начал изучать Python, дали задание: сделать парс расписания нашей группы в универе. Пользовался Beautiful...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Модель микоризы: классовый агентный подход 3
anaschu 06.01.2026
aa0a7f55b50dd51c5ec569d2d10c54f6/ O1rJuneU_ls https:/ / vkvideo. ru/ video-115721503_456239114
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR
ФедосеевПавел 06.01.2026
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR ВВЕДЕНИЕ Введу сокращения: аналоговый ПИД — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до. . .
Модель микоризы: классовый агентный подход 2
anaschu 06.01.2026
репозиторий https:/ / github. com/ shumilovas/ fungi ветка по-частям. коммит Create переделка под биомассу. txt вход sc, но sm считается внутри мицелия. кстати, обьем тоже должен там считаться. . . .
Расчёт токов в цепи постоянного тока
igorrr37 05.01.2026
/ * Дана цепь постоянного тока с сопротивлениями и напряжениями. Надо найти токи в ветвях. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа и решает её. Последовательность действий:. . .
Новый CodeBlocs. Версия 25.03
palva 04.01.2026
Оказывается, недавно вышла новая версия CodeBlocks за номером 25. 03. Когда-то давно я возился с только что вышедшей тогда версией 20. 03. С тех пор я давно снёс всё с компьютера и забыл. Теперь. . .
Модель микоризы: классовый агентный подход
anaschu 02.01.2026
Раньше это было два гриба и бактерия. Теперь три гриба, растение. И на уровне агентов добавится между грибами или бактериями взаимодействий. До того я пробовал подход через многомерные массивы,. . .
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост.
Programma_Boinc 28.12.2025
Советы по крайней бережливости. Внимание, это ОЧЕНЬ длинный пост. Налог на собак: https:/ / **********/ gallery/ V06K53e Финансовый отчет в Excel: https:/ / **********/ gallery/ bKBkQFf Пост отсюда. . .
Кто-нибудь знает, где можно бесплатно получить настольный компьютер или ноутбук? США.
Programma_Boinc 26.12.2025
Нашел на реддите интересную статью под названием Anyone know where to get a free Desktop or Laptop? Ниже её машинный перевод. После долгих разбирательств я наконец-то вернула себе. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru