Следующий код представляет собой асинхронное исследование для начинающих асинхронных загрузок файлов, чтобы попытаться улучшить время загрузки файлов с определенного веб-сайта.
Задания:
Задачи следующие:
Извлеките ссылку на последнюю публикацию. Это верхняя ссылка под Последняя статистика и является первым совпадением, возвращаемым селектором css
.cta__button.
NB Каждый месяц эта ссылка обновляется, поэтому в следующей ежемесячной публикации, например, 8 апреля 2021 года, ссылка будет обновлена до той, которая связана с Ежемесячная статистика служб охраны психического здоровья Январь, предварительный февраль 2021 г..
Посетите извлеченную ссылку: https://digital.nhs.uk/data-and-information/publications/statistical/mental-health-services-monthly-statistics/performance-de December-2020-provisional-january-2021. и оттуда извлеките ссылки для скачивания для всех файлов, перечисленных в Ресурсы; на данный момент 17 файлов.
Наконец, загрузите все эти файлы, используя полученные URL-адреса, и сохраните их в месте, указанном
folderПеременная.
Настраивать:
Python 3.9.0 64-битная Windows 10
Запрос:
Я был бы признателен за любые предлагаемые улучшения этого кода, например, если бы я реорганизовал сопрограмму fetch_download_links на две совместные процедуры, каждая с ClientSession, где одна сопрограмма получает начальную ссылку на то, где можно получить ресурсы, а вторая сопрограмма для получения фактических ссылок на ресурсы?
mhsmsAsynDownloads.py
import time
import os
from bs4 import BeautifulSoup as bs
import aiohttp
import aiofiles
import asyncio
import urllib
async def fetch_download_links(url:str) -> list:
async with aiohttp.ClientSession() as session:
r = await session.get(url, ssl = False)
html = await r.text()
soup = bs(html, 'lxml')
link = 'https://digital.nhs.uk' + soup.select_one('.cta__button')['href']
r = await session.get(link, ssl = False)
html = await r.text()
soup = bs(html, 'lxml')
files = [i['href'] for i in soup.select('.attachment a')]
return files
async def place_file(source: str) -> None:
async with aiohttp.ClientSession() as session:
file_name = source.split("https://codereview.stackexchange.com/")[-1]
file_name = urllib.parse.unquote(file_name)
r = await session.get(source, ssl = False)
content = await r.read()
async with aiofiles.open(folder + file_name, 'wb') as f:
await f.write(content)
async def main():
tasks = []
urls = await fetch_download_links('https://digital.nhs.uk/data-and-information/publications/statistical/mental-health-services-monthly-statistics')
for url in urls:
tasks.append(place_file(url))
await asyncio.gather(*tasks)
folder="C:/Users/<User>/OneDrive/Desktop/testing/"
if __name__ == '__main__':
t1 = time.perf_counter()
print("process started...")
asyncio.get_event_loop().run_until_complete(main())
os.startfile(folder[:-1])
t2 = time.perf_counter()
print(f'Completed in {t2-t1} seconds.')
Ссылки / Примечания:
- Я написал это после просмотра асинхронный учебник на YouTube Андрея Думитреску. Пример выше мой собственный
- https://docs.aiohttp.org/en/v0.20.0/client.html
- https://docs.aiohttp.org/en/stable/client_advanced.html
- Данные общедоступны
1 ответ
Мне кажется, вы могли бы избавиться от некоторых повторений, но это не так уж важно для небольшого скрипта.
Тем не менее, я бы повторно использовал сеанс, встроил несколько переменных, исправил именование (константы должны быть в верхнем регистре) и использовать некоторые стандартные инструменты библиотеки, такие как os.path чтобы сделать сценарий более защищенным от ошибок. В list Тип возвращаемого значения также может быть более конкретным и указать, например, что это список строк.
Кроме того, что более важно, я бы предпочел, чтобы во всем HTTP-ответе не было такого скрипта! Кто знает, насколько велики файлы, плюс это расточительно … лучше передать ответ прямо на диск! К счастью, API существуют, с использованием .content.iter_any (или же _chunked если вы предпочитаете иметь фиксированный размер блока), вы можете просто перебрать каждый блок данных чтения и записать их в файл следующим образом:
import time
import os
import os.path
from bs4 import BeautifulSoup as bs
import aiohttp
import aiofiles
import asyncio
import urllib
import typing
STATISTICS_URL = 'https://digital.nhs.uk/data-and-information/publications/statistical/mental-health-services-monthly-statistics'
OUTPUT_FOLDER = 'C:/Users/<User>/OneDrive/Desktop/testing/'
async def fetch_download_links(session: aiohttp.ClientSession, url: str) -> typing.List[str]:
r = await session.get(url, ssl=False)
soup = bs(await r.text(), 'lxml')
link = 'https://digital.nhs.uk' + soup.select_one('.cta__button')['href']
r = await session.get(link, ssl=False)
soup = bs(await r.text(), 'lxml')
return [i['href'] for i in soup.select('.attachment a')]
async def place_file(session: aiohttp.ClientSession, source: str) -> None:
r = await session.get(source, ssl=False)
file_name = urllib.parse.unquote(source.split("https://codereview.stackexchange.com/")[-1])
async with aiofiles.open(os.path.join(OUTPUT_FOLDER, file_name), 'wb') as f:
async for data in r.content.iter_any():
await f.write(data)
async def main():
async with aiohttp.ClientSession() as session:
urls = await fetch_download_links(session, STATISTICS_URL)
await asyncio.gather(*[place_file(session, url) for url in urls])
if __name__ == '__main__':
t1 = time.perf_counter()
print('process started...')
asyncio.get_event_loop().run_until_complete(main())
os.startfile(OUTPUT_FOLDER[:-1])
t2 = time.perf_counter()
print(f'Completed in {t2-t1} seconds.')



Большое спасибо. Мне понадобится немного времени, чтобы переварить, но я очень ценю то, что я уже видел +
— QHarr