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

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

04.04.2018, 21:03. Показов 7100. Ответов 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
Ответ Создать тему
Новые блоги и статьи
изучаю kubernetes
lagorue 13.01.2026
А пригодятся-ли мне знания kubernetes в России?
сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
Модель микоризы: классовый агентный подход 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 считается внутри мицелия. кстати, обьем тоже должен там считаться. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru