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

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

04.04.2018, 21:03. Показов 7052. Ответов 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
06.04.2018, 17:22
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Рыжий Лис Посмотреть сообщение
и ничего нигде не лочится.
Потому что выполняется в отдельном потоке.
Многопоточность != асинхронность.
Многопоточность основана на вытесняющей многозадачности.
Асинхронность - на кооперативной многозадачности + event loop.
В многопоточных программах потоками (если это настоящие потоки) управляет планировщик ОС.
В асинхронных программах потоков нет - они однопоточные. Однако есть корутины\сопрограммы, которые как раз выполняют роль нитей.
И управление нитями\корутинами - переключение с одной задачи на другую - задача соответствующей библиотеки (в python это asyncio).
И библиотека ожидает, что мы в своем коде будет использовать только асинхронный код - иначе все усилия - кот под хвост.

Небольшая цитата:
Переключение корутин осуществляется значительно быстрее переключения тредов, поскольку при этом не происходит переключения режима работы процессора.
-------------------------------------------------
Цитата Сообщение от danilshik Посмотреть сообщение
асинхронные запросы нужно только для частей работающих с вводом/выводам и работающих сетью
А что есть граббинг? В основном это ожидание сетевого ввода\вывода и совсем немного какие-то задачи на CPU.
Распараллеливать запросы посредством асинхронного кода более практично, чем использование многопоточности.
-----------------------------
Цитата Сообщение от danilshik Посмотреть сообщение
У вас нет какого нибудь парсера на нем
Парсера нет. Есть простой пример. Остальное нужно смотреть в доках по asyncio и aiohttp.


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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import logging
import asyncio
import aiohttp
import os
import time
import lxml.html
 
urls = [
        'http://www.python.org',
        'http://www.python.org/about/',
        'http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html',
        'http://www.python.org/doc/',
        'http://www.python.org/download/',
        'http://www.python.org/getit/',
        'http://www.python.org/community/',
        'https://wiki.python.org/moin/',
        'http://planet.python.org/',
        'https://wiki.python.org/moin/LocalUserGroups',
        'http://www.python.org/psf/',
        'http://docs.python.org/devguide/',
        'http://www.python.org/community/awards/',
        'http://www.not_exists_url/'  # несуществующий url, чтобы получить ошибку
        ]
 
 
async def fetch(client,url):
    """
    Сопрограмма для загрузки данных по указанному url
    """
    data = dict.fromkeys(['err','title','url','status','text'])
    try:
        async with client.get(url) as r:
            # получение контента асинхронная операция, поэтому ее нужно await'ить\ожидать
            text = await r.text() 
            title = lxml.html.fromstring(text).find(".//head/title").text #cssselect('head title')[0].text
            data.update({
                'title':title,
                'status':r.status,
                'url':url,
                'text':text
                })
            return data
    
    except Exception as err:
        data.update({'err':str(err),'url':url})
        return data
   
        
    
async def main(urls):
    """
    Создает группу сопрограмм и ожидает их завершения
    """
    # создаем экземпляр клиента
    async with aiohttp.ClientSession() as client:
        # создаем корутины
        coroutines = [fetch(client,url) for url in urls]
        # ожидаем выполнения всех корутин
        completed, pending = await asyncio.wait(coroutines)
        # итерация по завершенным результатам
        for item in completed:
            data = item.result()
            
            if not data['err']:
                log.info("%s %s %s",
                    data['title'],
                    data['status'],
                    data['url']
                    )
            else:
                log.error("%s %s",
                    data['url'],
                    data['err']
                    )
                    
 
if __name__ == '__main__':
    
    log = logging.getLogger(__name__)
    format = '%(asctime)s %(levelname)s:%(message)s'
    logging.basicConfig(format=format, level=logging.INFO)
    log.info("Start")
    
    # получаем экзепляр цикла событий
    event_loop = asyncio.get_event_loop()
    
    try:
        t_start = time.time()
        # запуск цикла  обработки событий 
        event_loop.run_until_complete(main(urls))
        t_end = time.time()
        log.info("Time End: %s",t_end - t_start) # 3.042086362838745
    finally:
        # обязательно закрываем
        event_loop.close() 
    #N.B     в python 3.7  все три операции обернули одной: asyncio.run(coro)
Добавлено через 2 часа 25 минут
UPD. Другой вариант - без ожидания завершения каждого задания - обработка результатов по мере поступления.
Все то же самое, изменена только функция main, в которую теперь нужно передать именованный параметр loop с экземпляром запущенного цикла событий.
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
async def main(urls,loop=None):
    """
    Создает группу сопрограмм и получает результаты не дожидаясь окончания всех
    """
    # создаем экземпляр клиента
    
    async with aiohttp.ClientSession() as client:
        tasks = []
        for url in urls:
            task = loop.create_task(fetch(client,url))
            tasks.append(task)
        # итерация по заданиям не ожидая завершения всех
        for task in asyncio.as_completed(tasks):
            data = await task
            
            if not data['err']:
                log.info("%s %s %s",
                    data['title'],
                    data['status'],
                    data['url']
                    )
            else:
                log.error("%s %s",
                    data['url'],
                    data['err']
                    )
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
06.04.2018, 17:43  [ТС]
Если нажать на выбор город на https://www.mvideo.ru/ и искать города: то выдает лишние значения None
Кликните здесь для просмотра всего текста
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
2018-04-06 19:26:46,641 INFO:Start
2018-04-06 19:26:47,487 INFO:Выполнен запрос
2018-04-06 19:26:47,488 INFO:Получен html
2018-04-06 19:26:47,489 INFO:CityCZ_975
2018-04-06 19:26:47,489 INFO:CityCZ_1272
2018-04-06 19:26:47,489 INFO:CityCZ_7173
2018-04-06 19:26:47,489 INFO:CityCZ_2030
2018-04-06 19:26:47,489 INFO:CityCZ_1458
2018-04-06 19:26:47,489 INFO:CityCZ_2128
2018-04-06 19:26:47,489 INFO:CityCZ_1854
2018-04-06 19:26:47,489 INFO:CityCZ_974
2018-04-06 19:26:47,489 INFO:CityCZ_2246
2018-04-06 19:26:47,489 INFO:CityCZ_1250
2018-04-06 19:26:47,489 INFO:CityCZ_2446
2018-04-06 19:26:47,489 INFO:CityCZ_1780
2018-04-06 19:26:47,489 INFO:CityCZ_1638
2018-04-06 19:26:47,489 INFO:CityCZ_2534
2018-04-06 19:26:47,489 INFO:CityCZ_1216
2018-04-06 19:26:47,489 INFO:Получены города
2018-04-06 19:26:47,490 INFO:Time End: 0.84794020652771
CityCZ_2534
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_2534
CityCZ_2030
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_2030
CityCZ_1638
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_1638
CityCZ_1216
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_1216
CityCZ_1780
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_1780
CityCZ_2246
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_2246
CityCZ_7173
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_7173
CityCZ_1458
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_1458
CityCZ_2446
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_2446
CityCZ_1272
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_1272
CityCZ_1250
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_1250
CityCZ_1854
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_1854
CityCZ_2128
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_2128
CityCZ_974
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_974
CityCZ_975
https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=1?cityId=CityCZ_975
None
None
None
None
None
None
None
None
None
None
None
None
None
None
None


Добавлено через 53 секунды
применяю
Python
1
for elem in form.cssselect("input[class='input-radio']"):
Добавлено через 22 секунды
Не пойму, из-за чего происходит

Добавлено через 1 минуту
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
async def parse_city(client,url):
    try:
        async with client.get(url) as r:
            request=await r.json()
            log.info('Выполнен запрос')
            html=lxml.fromstring(request["htmlContent"])
            log.info('Получен html')
            cities=[]
            form=html.cssselect('form[id="city-radio-from"]')[0]
            # cssselect('head title')[0].text
            for elem in form.cssselect("input[class='input-radio']"):
                city = elem.get("value")
                log.info(city)
                city_temp=[]
                city_temp.append(city)
                city_temp.append("https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=" + str(1)+"?cityId="+str(city))
                cities.append(city_temp)
            log.info("Получены города")
            return cities
 
    except Exception as err:
        print(err)
 
async def main(urls):
    """
    Создет группу сопрограмм и ожидает их завершения
    """
    # создаем экземпляр клиента
    async with aiohttp.ClientSession() as client:
        # создаем корутины
        coroutines = [parse_city(client, url) for url in urls]
        completed, pending =await asyncio.wait(coroutines)
        for item in completed:
            cities=item.result()
    async with aiohttp.ClientSession() as client:
        coroutines1=[all_parsing(client, city_url) for city_url in cities]
        completed, pending=await asyncio.wait(coroutines1)
        for item in completed:
            data = item.result()
            print(data)
        #
        #     if not data['err']:
        #         log.info("%s %s %s",
        #                  data['title'],
        #                  data['status'],
        #                  data['url']
        #                  )
        #     else:
        #         log.error("%s %s",
        #                   data['url'],
        #                   data['err']
        #                   )
async def all_parsing(client, city):
    city_title=city[0] #Получение кода города
    city_url=city[1]   #Получение ссылки на 1 страницу списка телефонов данного города
    print(city_title)
    print(city_url)
if __name__ == '__main__':
 
    log = logging.getLogger(__name__)
    format = '%(asctime)s %(levelname)s:%(message)s'
    logging.basicConfig(format=format, level=logging.INFO)
    log.info("Start")
 
    # получаем экзепляр цикла событий
    event_loop = asyncio.get_event_loop()
 
    try:
        t_start = time.time()
        # запуск цикла  обработки событий
        event_loop.run_until_complete(main(url_select_city))
        t_end = time.time()
        log.info("Time End: %s", t_end - t_start)  # 3.042086362838745
    finally:
        # обязательно закрываем
        event_loop.close()
        # N.B     в python 3.7  все три операции обернули одной: asyncio.run(coro)
Добавлено через 6 минут
Хотя если проверить длину списка, то он равен 15, но ни как не 30

Добавлено через 2 минуты
И вопрос как можно ограничить количество запросов? Если я правильно понимаю, одновременно делаются запросы, количество которых равно количеству элементов в списке. А если там будет больше 50, меня же забанить могут)

Добавлено через 1 минуту
Это для ссылки https://www.mvideo.ru/sitebuil... pageUrl=/&
0
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
06.04.2018, 18:39
Цитата Сообщение от danilshik Посмотреть сообщение
то выдает лишние значения None
В моем примере из функции возвращающей значения запроса возврат есть и в случае нормального ответа и в случае ошибки. У вас - нет.
И еще - не смешивайте в одной функции логику запроса и логику парсинга. Это все нужно делать в отдельных функциях помеченных как async и дожидаться их выполнения посредством await.
У меня в коде (и уже пожалел, что не сделал этого сразу) - строчка
Python
1
title = lxml.html.fromstring(text).find(".//head/title").text
должна быть в отдельной функции, поскольку это уже другая логика, отличная от логики запроса.
Такая разбивка делает код более читабельным, понятным не только вам и улучшает отладку при ошибках.
Так что ваши траблы мне пока малопонятны.
Цитата Сообщение от danilshik Посмотреть сообщение
И вопрос как можно ограничить количество запросов?
Библиотека к этому отношения не имеет. Логику ограничения должны придумать и написать вы сами. Можно ввести принудительные ограничения в виде таймаутов: для этого есть асинхронный asyncio.sleep.
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
06.04.2018, 22:57  [ТС]
Как можно поймать и ошибку raise asyncio.TimeoutError from None
concurrent.futures._base.TimeoutError
Кликните здесь для просмотра всего текста
Traceback (most recent call last):
File "D:/Учеба/Диплом/parsers/smartphone/test7.py", line 144, in <module>
event_loop.run_until_complete(main(url_s elect_city))
File "C:\ProgramData\Anaconda3\lib\asyncio\ba se_events.py", line 467, in run_until_complete
return future.result()
File "D:/Учеба/Диплом/parsers/smartphone/test7.py", line 116, in main
item= item.result()
File "D:/Учеба/Диплом/parsers/smartphone/test7.py", line 70, in parse_list_object
r=await request(client,url)
File "D:/Учеба/Диплом/parsers/smartphone/test7.py", line 16, in request
async with client.get(url) as r:
File "C:\ProgramData\Anaconda3\lib\site-packages\aiohttp\client.py", line 783, in __aenter__
self._resp = await self._coro
File "C:\ProgramData\Anaconda3\lib\site-packages\aiohttp\client.py", line 333, in _request
await resp.start(conn, read_until_eof)
File "C:\ProgramData\Anaconda3\lib\site-packages\aiohttp\client_reqrep.py", line 708, in start
self._continue = None
File "C:\ProgramData\Anaconda3\lib\site-packages\aiohttp\helpers.py", line 670, in __exit__
raise asyncio.TimeoutError from None
concurrent.futures._base.TimeoutError
2018-04-07 00:47:33,206 ERROR:Task exception was never retrieved
future: <Task finished coro=<parse_list_object() done, defined at D:/Учеба/Диплом/parsers/smartphone/test7.py:66> exception=TimeoutError()>
0
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
07.04.2018, 01:06
Ошибка возникает на строке item= item.result()
Возможно ловить нужно там.
Кстати, незавершенные задания будут находиться во втором списке - pending.

Добавлено через 8 минут
Хотя, не уверен - обработка ошибок в асинхронных корутинах не совсем тривиальная задача
0
Эксперт С++
 Аватар для Avazart
8484 / 6151 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
07.04.2018, 11:02
Цитата Сообщение от danilshik Посмотреть сообщение
Из курса дисциплины "Операционные системы" знаю что лучше создать n-потоков, чем n-количество процессов
Да только для питона это не те самые "потоки" которые дают прирост, в питоне GIL мешает.
Поэтому я и говорю что нет смысла распараллеливать парсинг с помощью этих потоков.

Добавлено через 38 секунд
Цитата Сообщение от Garry Galler Посмотреть сообщение
Потому что выполняется в отдельном потоке.
Многопоточность != асинхронность.
Многопоточность основана на вытесняющей многозадачности.
Асинхронность - на кооперативной многозадачности + event loop.
В многопоточных программах потоками (если это настоящие потоки) управляет планировщик ОС.
В асинхронных программах потоков нет - они однопоточные. Однако есть корутины\сопрограммы, которые как раз выполняют роль нитей.
И управление нитями\корутинами - переключение с одной задачи на другую - задача соответствующей библиотеки (в python это asyncio).
И библиотека ожидает, что мы в своем коде будет использовать только асинхронный код - иначе все усилия - кот под хвост.
Модцом смешали в кучу ...
0
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
07.04.2018, 13:49
Цитата Сообщение от Avazart Посмотреть сообщение
Да только для питона это не те самые "потоки" которые дают прирост, в питоне GIL мешает.
Это не так. В python - те самые потоки ОС. И они могут давать прирост производительности. Посмотрите доклады Бизли для ясности.
P.S. Если не в курсе - даже lxml отпускает GIL - достаточно глянуть исходник или прочитать в документации это:
Since version 1.1, lxml frees the GIL (Python’s global interpreter lock) internally when parsing from disk and memory, as long as you use either the default parser (which is replicated for each thread) or create a parser for each thread yourself
Многие расширения на Си точно также отпускают GIL, что дает возможность эффективно использовать распараллеливание CPU-dound задач на этих библиотеках.
А про задачи IO уже давно было сказано - они ОТПУСКАЮТ GIL, когда погружаются в состояние ожидания.

Цитата Сообщение от Avazart Посмотреть сообщение
смешали в кучу
Что именно? Это краткий ввод без попыток точных формулировок. Но ошибок там нет.
0
Эксперт С++
 Аватар для Avazart
8484 / 6151 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
07.04.2018, 14:02
Цитата Сообщение от Garry Galler Посмотреть сообщение
Это не так. В python - те самые потоки ОС.
Они то те же самые, но вот GIl делает их не те же самыми.
Цитата Сообщение от Garry Galler Посмотреть сообщение
они могут давать прирост производительности.
А могут и не давать, это качается вычислений.

Добавлено через 1 минуту
Цитата Сообщение от Garry Galler Посмотреть сообщение
P.S. Если не в курсе - даже lxml отпускает GIL - достаточно глянуть исходник или прочитать в документации это:
Да морока с тем что каждый раз приходится думать, гадать, читать доку просто убивает.

Какая то либо библиотека(или место в коде) не будет отпускать и это может стать узким горлышком.
0
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
07.04.2018, 19:52
Цитата Сообщение от danilshik Посмотреть сообщение
И вопрос как можно ограничить количество запросов?
В первый раз я ответил, что таких средств, вроде нет в asyncio.
На самом деле есть: Semaphore, который принимает как параметр число "одновременно" выполняющихся корутин.

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# если указать число заданий n=len(urls), то фактически это безлимит
# если указать n=1, то фактически это синхронное выполнение
# таким образом ограничение - это нечто среднее между этими значениями
 
# создаем семафор
SEM = asyncio.Semaphore(n)  
...
# передаем его как параметр в вызове функции запросов
...fetch(client,url,parallel_limit=SEM)...
 
# в самой функции запросов используем контексный менеджер
...
with (await parallel_limit):
    async with client.get(url) as r:
        ...
P.S. Кстати, по моим тестам асинхронная модель при сетевом I/O немного даже выигрывает по скорости у многопоточной, хотя, конечно, есть много неясностей с обработкой исключений.
И Светлов как-то не сильно эту тему освещает.
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
08.04.2018, 21:52  [ТС]
Цитата Сообщение от Garry Galler Посмотреть сообщение
В первый раз я ответил, что таких средств, вроде нет в asyncio.
На самом деле есть: Semaphore, который принимает как параметр число "одновременно" выполняющихся корутин.
PythonВыделить код

# если указать число заданий n=len(urls), то фактически это безлимит
# если указать n=1, то фактически это синхронное выполнение
# таким образом ограничение - это нечто среднее между этими значениями
# создаем семафор
SEM = asyncio.Semaphore(n) *
...
# передаем его как параметр в вызове функции запросов
...fetch(client,url,parallel_limit=SEM). ..
# в самой функции запросов используем контексный менеджер
...
with (await parallel_limit):
* * async with client.get(url) as r:
* * * * ...
Спасибо, посмотрю чуть позже

Добавлено через 1 минуту
Как думаете стоит так сделать?http://skipperkongen.dk/2016/0... d-asyncio/
Судя по опыту внутри Async находится пул, максимум 5 запросов одновременно делает? или я не так понял
0
Эксперт Python
5438 / 3859 / 1215
Регистрация: 28.10.2013
Сообщений: 9,552
Записей в блоге: 1
09.04.2018, 15:08
Цитата Сообщение от danilshik Посмотреть сообщение
стоит так сделать
Там используется run_in_executor, где дефолтный экзекутор - пул потоков. И используется такой вариант из-за синхронной requests. То есть имеет смысл, только если мы используем в асинхронном коде блокирующие долгоиграющие функции.
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
13.04.2018, 12:35  [ТС]
У меня почему то request+threedpoolexecutor в 10 потоков выполняется в 2 раза быстрее чем async с семафором 10

Добавлено через 1 минуту
request
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
83
84
85
86
87
88
89
90
91
92
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]
    print(url, city)
    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()
    print(len(q_url))
    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)
 
 
 
 
 
 
 
if __name__ == "__main__":
    start_main=time.time()
    log = logging.getLogger(__name__)
    logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', level=logging.INFO)
    log.info('start')
    parse_city()
    all_parsing()
    start_threads()
 
    log.info('end')
    log.info("Время работы парсера: %s",time.time()-start_main)

async
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
url_select_city=["https://www.mvideo.ru/sitebuilder/blocks/regionSelection.json.jsp?pageId=homepage&pageUrl=/&"]
url_main="https://www.mvideo.ru"
sem=asyncio.Semaphore(10)
 
async def get_html(request):
    return lxml.html.fromstring(request)
 
async def request(client,url,limit):
    with(await limit):
        async with client.get(url) as r:
            print(r.status)
            return await r.text()
 
 
async def request_json(client,url):
    async with client.get(url) as r:
        return await r.json()
 
async def parse_city(client,url):
    try:
        r= await request_json(client,url)
        log.info('Выполнен запрос')
        html=await get_html(r["htmlContent"])
        log.info('Получен html')
        cities=[]
        form=html.cssselect('form[id="city-radio-from"]')[0]
        for elem in form.cssselect("input[class='input-radio']"):
            city = elem.get("value")
            log.info(city)
            city_temp=[]
            city_temp.append(city)
            city_temp.append("https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=" + str(1)+"?cityId="+str(city))
            cities.append(city_temp)
        log.info("Получены города")
        return cities
 
    except Exception as err:
        print(err)
 
async def parsing_first_list(client, city,limit):
    city_title=city[0] #Получение кода города
    city_url=city[1]   #Получение ссылки на 1 страницу списка телефонов данного города
    #log.info("Парсинг первой страницы: %s - %s",city_title,city_url)
 
    r=await request(client,city_url,limit)
    log.info("Запрос: %s",city_url)
    html = await get_html(r)
    # div_pagination=html.xpath('//div[@class="c-pagination"]')
    # #=div_pagination.cssselect("/a[class='c-pagination__num c-btn c-btn_white']")
    # print(div_pagination)
    count_list=html.xpath(".//a[@class='c-pagination__num c-btn c-btn_white ']/text()")[0]
    log.info("Количество страниц: %s",count_list)
    urls_list=[]
    for i in range(1,int(count_list)+1):
        url_temp=[]
        url_temp.append("https://www.mvideo.ru/smartfony-i-svyaz/smartfony-205/f/page=" + str(i)+"?cityId="+city_title)
        url_temp.append(city_title)
        urls_list.append(url_temp)
    log.info("Парсинг закончен:%s",city_url)
    return urls_list
 
async def parse_list_object(client, list_objects,limit):
        url=list_objects[0]
        city=list_objects[1]
        log.info("Парсинг list_objects: %s | %s",url, city)
        r=await request(client,url,limit)
        log.info("Запрос list: %s", city)
        html=await get_html(r)
        urls_object=[]
        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
            url_temp=[]
            url_temp.append(link_object)
            url_temp.append(city)
            urls_object.append(url_temp)
            log.info("Ссылка телефона: %s - страница List: %s",link_object,url)
        log.info("Спарсен  - %s",url)
        if(len(urls_object)>0):
            print("Количество спарсенных ссылок:",len(urls_object))
            return urls_object
        else:
            return []
    # except Exception as err:
    #     print(err)
 
 
 
async def main(urls):
    """
    Создет группу сопрограмм и ожидает их завершения
    """
    # создаем экземпляр клиента
    async with aiohttp.ClientSession() as client:
        # создаем корутины
        coroutines = [parse_city(client, url) for url in urls]
        completed, pending =await asyncio.wait(coroutines)
        for item in completed:
            cities=item.result()
    async with aiohttp.ClientSession() as client:
        coroutines1=[parsing_first_list(client, city_url,limit=sem) for city_url in cities]
        completed, pending=await asyncio.wait(coroutines1)
        list_objects=[]
        for item in completed:
            list_objects += item.result()
    async with aiohttp.ClientSession() as client:
        coroutines2=[parse_list_object(client, url_object,limit=sem) for url_object in list_objects]
        completed, pending=await asyncio.wait(coroutines2)
        url_object=[]
        for item in completed:
            item= item.result()
            url_object+=item
            print(url_object)
 
if __name__ == '__main__':
 
    log = logging.getLogger(__name__)
    format = '%(asctime)s %(levelname)s:%(message)s'
    logging.basicConfig(format=format, level=logging.INFO)
    log.info("Start")
 
 
    # получаем экзепляр цикла событий
    event_loop = asyncio.get_event_loop()
 
    try:
        t_start = time.time()
        # запуск цикла  обработки событий
        event_loop.run_until_complete(main(url_select_city))
        t_end = time.time()
        log.info("Time End: %s", t_end - t_start)  # 3.042086362838745
    finally:
        # обязательно закрываем
        event_loop.close()
0
 Аватар для IRIP
514 / 146 / 28
Регистрация: 18.04.2015
Сообщений: 1,904
Записей в блоге: 16
13.04.2018, 12:38
Цитата Сообщение от danilshik Посмотреть сообщение
почему то request+threedpoolexecutor в 10 потоков выполняется в 2 раза быстрее чем async с семафором 10
а какую нагрузку на донора это дает?
На некоторых донорах стоит блок на множество запросов
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
13.04.2018, 12:42  [ТС]
async 1134 против request+threedPool 590

Добавлено через 2 минуты
Цитата Сообщение от IRIP Посмотреть сообщение
Сообщение от danilshik
почему то request+threedpoolexecutor в 10 потоков выполняется в 2 раза быстрее чем async с семафором 10
а какую нагрузку на донора это дает?
На некоторых донорах стоит блок на множество запросов
Не совсем понял, что под донором понимается?
0
 Аватар для IRIP
514 / 146 / 28
Регистрация: 18.04.2015
Сообщений: 1,904
Записей в блоге: 16
13.04.2018, 13:00
сайт с которого берут данные
от способа, которым берешь данные: интервалы между запросами, количество каналов, прокси и т.п.
многое зависит

в том числе и шанс быть заблокированным
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
13.04.2018, 13:09  [ТС]
Цитата Сообщение от IRIP Посмотреть сообщение
сайт с которого берут данные
от способа, которым берешь данные: интервалы между запросами, количество каналов, прокси и т.п.
многое зависит
в том числе и шанс быть заблокированным
Ну вот смотри в ThreedPool+request мы делаем в каждом потоке по запросу и сразу парсим результат. Так основное время - это отправка запроса и получение ответа, можно предположить, что 10 потоков дают около 10 запросов к серверу единовременно.
Также и async мы отправляем 10 запросов одновременно, и парсим по мере получения запроса. Так что можно уверять что и тут одновременно 10 запросов в серверу. Но способ с потоками в 2 раза быстрее чем, с async, хотя Garry Galler утвержает что использование async быстрее
0
║XLR8║
 Аватар для outoftime
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,360
Записей в блоге: 5
13.04.2018, 15:27
Цитата Сообщение от danilshik Посмотреть сообщение
Но способ с потоками в 2 раза быстрее чем, с async, хотя Garry Galler утвержает что использование async быстрее
IRIP, danilshik, asyncio в разы быстрее 2х поточной работы с медленными устройствами\сетью. Но нужно понимать одно, выигрыишь не с скорости получения данных, а в том есть есть возможность полностью загрузить канал данными открыв 100500 соединение, не дожидаясь пока мы получим ответ он предыдущих 100499. Вот за счет чего получается быстрее. Если отправлять 2 запроса всего - смысла город городить нету.
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
13.04.2018, 15:35  [ТС]
Цитата Сообщение от outoftime Посмотреть сообщение
Сообщение от danilshik
Но способ с потоками в 2 раза быстрее чем, с async, хотя Garry Galler утвержает что использование async быстрее
IRIP, danilshik, asyncio в разы быстрее 2х поточной работы с медленными устройствами\сетью. Но нужно понимать одно, выигрыишь не с скорости получения данных, а в том есть есть возможность полностью загрузить канал данными открыв 100500 соединение, не дожидаясь пока мы получим ответ он предыдущих 100499. Вот за счет чего получается быстрее. Если отправлять 2 запроса всего - смысла город городить нету.
Тут 10 запросов. К тому же мы не можем сделать столько запросов, либо бан получим, либо ошибку TimeError

Добавлено через 1 минуту
А возможно ли сохранить результат запроса? Идея такая: сначала получаем результаты запросов, а потом потоками обрабатываем эти страницы? По идее же быстрее должно быть
0
║XLR8║
 Аватар для outoftime
1212 / 909 / 270
Регистрация: 25.07.2009
Сообщений: 4,360
Записей в блоге: 5
13.04.2018, 15:38
Цитата Сообщение от danilshik Посмотреть сообщение
Тут 10 запросов. К тому же мы не можем сделать столько запросов, либо бан получим, либо ошибку TimeError
Тогда и смысла заморачиваться нету. Только если вычисления очень долгие, но тогда лучше multiprocessing использовать, ибо треды скорости не прибавят.

В противном случае обычное однопоточное приложение - в самый раз. Еще, я бы сделал немного не так. Сначала выкачал данные, а потом замерял время на парсинг. Смысл задержки сети в время парсинга засовывать?
0
4 / 4 / 2
Регистрация: 04.04.2015
Сообщений: 186
13.04.2018, 15:42  [ТС]
Цитата Сообщение от outoftime Посмотреть сообщение
Тогда и смысла заморачиваться нету. Только если вычисления очень долгие, но тогда лучше multiprocessing использовать, ибо треды скорости не прибавят.
Уже тестировали, ThreedPool большую выгоду дает

Добавлено через 1 минуту
Цитата Сообщение от danilshik Посмотреть сообщение
Решил сравнить все приведенные библиотеки, результаты такие:
concurent.futures - ThreadPoolExecutor: 248
Multiprocessing.Dumpy:253
Multiprocesssing - вообще не запустился, процессы создались, и потом программа завершилась. Почему, не знаю, хотя я его когда-то уже использовал. Также пришлось отказаться от очереди и заменить списком
Код
Хотя не значительно

Добавлено через 26 секунд
Можно сказать погрешность
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
13.04.2018, 15:42
Помогаю со студенческими работами здесь

В чем проблема? (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...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru