Веб-скрапинг — это мощный метод извлечения больших объемов данных с веб-сайтов. Однако сбор данных по требованию может оказаться сложной задачей. В этом подробном руководстве мы узнаем, как превратить веб-скребок Python в API данных в реальном времени с помощью FastAPI.
Зачем создавать API для парсинга?
Вот некоторые ключевые преимущества доставки очищенных данных через API:
Данные в режиме реального времени – API-интерфейсы могут собирать данные по требованию, а не полагаться на устаревшие кэшированные пакеты.
Пользовательские запросы – API позволяют настраивать запросы к данным, а не просто выгружать целые веб-сайты.
Масштабируемость – API справляются с всплесками трафика и масштабируются до тысяч пользователей.
Надежность – API повторяют неудачные запросы и реализуют надежную логику очистки.
Трансформируемость – API-интерфейсы могут реализовывать различные методы доставки данных, такие как веб-перехватчики.
абстракция – API скрывают сложную логику и инфраструктуру парсинга от потребителей API.
Парсинг архитектуры API
На высоком уровне API веб-скрапинга следует следующей архитектуре:
- API получает запрос
- Скребок извлекает данные
- Данные кэшируются
- API возвращает очищенные данные
Ключевые компоненты:
API-сервер – Обрабатывает запросы клиентов и доставляет очищенные данные.
Скребок – Извлекает данные по запросу с целевых сайтов.
Кэш – Сохраняет очищенные данные, чтобы избежать избыточного очистки.
База данных – Опционально сохраняет очищенные данные для исторического анализа.
Зачем использовать FastAPI?
Существует множество отличных API-фреймворков. Для скребков рекомендую FastAPI потому что:
FastAPI очень быстрый — идеально подходит для API очистки данных.
Он обеспечивает автоматическое документирование, проверку, сериализацию и т. д.
Поддерживает asyncio для асинхронного парсинга.
Простой в использовании и обучении. Гибкий для малых и больших API.
Имеет отличную экосистему инструментов парсинга, таких как httpx, parsel и т. д.
Настройка API парсинга
Мы будем использовать следующие основные библиотеки:
FastAPI – Наша платформа API
httpx – Асинхронный HTTP-клиент
посылка — Парсер HTML/XML
логуру — Утилита журналирования
Установите пакеты:
pip install fastapi uvicorn httpx parsel loguru
Для этого руководства мы возьмем некоторые основные данные об акциях из Yahoo Finance.
Создайте API
Давайте настроим исходное приложение FastAPI с одной конечной точкой:
from fastapi import FastAPI
app = FastAPI()
@app.get("/stock/{symbol}")
async def get_stock(symbol: str):
return { "stock": symbol }
Этот базовый API просто возвращает предоставленный нами символ акции.
Запустим сервер и протестируем его:
uvicorn main:app --reload
import httpx
print(httpx.get("http://localhost:8000/stock/AAPL").json())
# {‘stock‘: ‘AAPL‘}
Наш API готов принимать запросы. Далее давайте заставим его очистить некоторые данные.
Парсинг данных о запасах
Чтобы получить данные об акции, мы:
- Создайте URL-адрес Yahoo Finance из символа
- Получить HTML-страницу
- Анализ значений с использованием XPath
from parsel import Selector # for xpath parsing
async def scrape_stock(symbol: str):
url = f"https://finance.yahoo.com/quote/{symbol}"
async with httpx.AsyncClient() as client:
response = await client.get(url)
sel = Selector(response.text)
# parse summary values
values = sel.xpath(‘//div[contains(@data-test,"summary-table")]//tr‘)
data = {}
for value in values:
label = value.xpath("./td[1]/text()").get()
val = value.xpath("./td[2]/text()").get()
data[label] = val
# parse price
data["price"] = sel.xpath(‘//fin-streamer[@data-symbol=$symbol]/@value‘, symbol=symbol).get()
return data
Этот парсер возвращает словарь, содержащий проанализированные данные. Давайте подключим его к нашему API:
from fastapi import FastAPI
from yahoo_finance import scrape_stock
app = FastAPI()
@app.get("/stock/{symbol}")
async def get_stock(symbol: str):
data = await scrape_stock(symbol)
return data
Теперь, когда мы вызываем наш API, он получает последние данные:
http http://localhost:8000/stock/AAPL
HTTP/1.1 200 OK
Content-Length: 340
{
"52 Week Range": "142.00 - 182.94",
"Beta (5Y Monthly)": "1.25",
"Diluted EPS (ttm)": "6.05",
"Earnings Date": "Oct 27, 2022",
"Ex-Dividend Date": "Aug 05, 2022",
"Forward Dividend & Yield": "0.92 (0.59%)",
"Market Cap": "2.44T",
"Open": "156.76",
"PE Ratio (ttm)": "25.60",
"Previous Close": "153.72",
"Price": "155.33",
"Volume": "53,978,024"
}
Добавление кэширования
Скрапинг по каждому запросу — расточительство. Давайте добавим кеширование, чтобы очищать стоковые данные только раз в 5 минут.
Мы будем использовать простой dict
для хранения очищенных данных, связанных с биржевым символом:
STOCK_CACHE = {}
async def scrape_stock(symbol):
if symbol in STOCK_CACHE:
return STOCK_CACHE[symbol]
data = ... # scrape
STOCK_CACHE[symbol] = data
return data
Теперь повторные запросы будут возвращать кэшированные данные, а не каждый раз очищать их.
Мы также можем периодически очищать старый кеш:
import time
CACHE_MAX_AGE = 300 # seconds
async def clear_expired_cache():
curr_time = time.time()
for symbol, data in STOCK_CACHE.items():
if curr_time - data["ts"] > CACHE_MAX_AGE:
del STOCK_CACHE[symbol]
# run every 5 minutes
clear_cache_task = asyncio.create_task(clear_expired_cache())
Это гарантирует, что наш кеш не будет неограниченно расти.
Добавление вебхуков
Для длительных заданий парсинга мы можем использовать вебхуки для асинхронного возврата результатов:
@app.get("/stock/{symbol}")
async def get_stock(symbol: str, webhook: str):
if webhook:
task = asyncio.create_task(fetch_stock(symbol, webhook))
return {"msg": "Fetching data"}
data = await scrape_stock(symbol)
return data
async def fetch_stock(symbol, url):
data = await scrape_stock(symbol)
async with httpx.AsyncClient() as client:
await client.post(url, json=data)
Теперь вместо ожидания завершения очистки наш API немедленно вернет статус и асинхронно доставит данные в вебхук обратного вызова.
Мы можем проверить это с помощью такого инструмента, как Вебхук.сайт.
Масштабирование API парсинга
По мере увеличения трафика вот несколько методов масштабирования:
Добавить кэширование API – Используйте кеш, такой как Redis, чтобы уменьшить нагрузку на очистку.
Запуск нескольких процессов – Масштабирование по ядрам/серверам с помощью Gunicorn.
Разгрузка очистки – Переместите скребки в рабочие процессы, такие как Celery или RabbitMQ.
Воспользуйтесь услугами парсинга – Используйте API-интерфейсы очистки, такие как Scrapfly или ScraperAPI.
Оптимизация парсеров – Обеспечьте эффективность парсеров и избегайте запретов.
Добавить базы данных – Храните собранные данные в базах данных для дальнейшего анализа.
Заключение
В этом руководстве мы создали API веб-скрапинга на Python с использованием FastAPI. Ключевые выводы:
FastAPI предоставляет отличную платформу для очистки API.
Мы можем создавать парсеры для получения данных по требованию.
Кэширование и веб-перехватчики помогают преодолеть ограничения очистки.
Существует множество стратегий оптимизации и масштабирования по мере роста трафика.
API-интерфейсы парсинга открывают множество данных на веб-сайтах. Создавая API вместо статического парсинга, мы можем предоставлять персонализированные данные с малой задержкой в любом масштабе.