Если вы работаете с парсингом, вам, вероятно, знакомы такие концепции, как CURL и FETCH запросы.
В упрощённом виде, CURL и FETCH представляют собой инструменты для выполнения HTTP-запросов. CURL — это утилита командной строки, предназначенная для взаимодействия с веб-сервисами и API. Она позволяет отправлять запросы и получать ответы, что делает её незаменимым инструментом для разработчиков, работающих с сетевыми запросами.
FETCH, в свою очередь, является встроенным методом в JavaScript, который используется для выполнения аналогичных запросов непосредственно из браузера. Этот метод широко используется в веб-приложениях для взаимодействия с сервером, управления данными и выполнения различных сетевых операций.
Обе технологии играют ключевую роль в процессе обмена данными между клиентом и сервером. Они обеспечивают связь между фронтенд- и бэкенд-компонентами, что включает в себя получение данных, отправку запросов, управление токенами и другие задачи. Проще говоря, CURL и FETCH являются основным языком общения в веб-разработке, обеспечивая необходимую интеграцию и взаимодействие между различными частями веб-приложений.
К сожалению для нас python-разработчиков, синтаксис этого обмена не то чтоб родной для нашего языка, несмотря на использование стандартных JSON (dict) в этих самых запросах. Но, теперь наша жизнь станет проще, ведь появился инструмент (библиотека) CurlFetch2Py, которая сможет трансформировать FETCH/CURL запросы в понятный нам синтаксис.
Сегодня я расскажу как работать с этой библиотекой и покажу на конкретных примерах какие проблемы она решает.
CurlFetch2Py
CurlFetch2Py — это библиотека на Python, предназначенная для парсинга команд curl и fetch, преобразуя их в структурированные объекты Python. Это особенно полезно для преобразования сложных HTTP-запросов в формат, который можно легко использовать с библиотекой requests и прочими подобными библиотеками на Python (например httpx).
На данный момент библиотека имеет два метода:
parse_curl_context — метод, принимающий CURL-строку и трансформирующий ее в структурный объект, где через точку можно получать доступ к любому элементу (например к headers, coocies и прочее)
parse_fetch_context — метод, принимающий FETCH запрос в виде строки, трансформирующий его в структурный объект.
Подробнее о том что такое структурный объект и как его использовать мы разберемся далее на конкретных примерах.
Какие проблемы решает CurlFetch2Py?
Преобразование команд в Python-совместимый формат:
CURL и FETCH команды, хотя и используют стандартные форматы данных, часто имеют синтаксис и структуру, которые не совсем удобны для работы непосредственно в Python-коде. CurlFetch2Py облегчает этот процесс, преобразуя команды CURL и FETCH в понятные и легко управляемые Python объекты, которые можно напрямую использовать с библиотеками для HTTP-запросов, такими как requests и httpx.
Упрощение интеграции с API:
Интеграция с различными API часто требует конвертации команд и параметров из формата, используемого в других инструментах или языках программирования, в формат, который понимает Python. CurlFetch2Py позволяет быстро и эффективно преобразовывать запросы, упрощая процесс взаимодействия с API и минимизируя риск ошибок при ручном создании запросов.
Уменьшение количества кода и ошибок:
Ручная обработка и преобразование CURL и FETCH запросов в Python объекты может быть трудоемким процессом и подвержена ошибкам. CurlFetch2Py автоматизирует этот процесс, снижая количество написанного кода и вероятность ошибок, обеспечивая более чистый и поддерживаемый код.
Удобство при отладке и тестировании:
В процессе разработки и тестирования полезно иметь возможность легко преобразовывать запросы и видеть, как они будут выглядеть в Python. С помощью CurlFetch2Py вы можете легко преобразовать существующие CURL или FETCH команды в Python объекты и использовать их в тестах или отладочных скриптах.
Повышение удобства работы с запросами:
CurlFetch2Py позволяет вам легко управлять всеми аспектами HTTP-запросов (методы, заголовки, данные, cookies и другие параметры) в одном структурированном объекте. Это делает код более читаемым и упрощает манипуляцию запросами, особенно когда работа идет с большим количеством параметров и сложными запросами.
Поддержка различных форматов и параметров:
Библиотека поддерживает как базовые, так и расширенные параметры запросов, включая аутентификацию, параметры кэширования, политику перенаправлений и другие настройки. Это позволяет вам легко работать с широким спектром запросов и настроек, не заботясь о подробной ручной конвертации параметров.
В общем, CurlFetch2Py упрощает процесс работы с HTTP-запросами, делая его более удобным и менее подверженным ошибкам.
Установим библиотеку
Библиотеку можно установить через pip используя команду:
pip install --upgrade curl_fetch2py
Страница проекта на GitHub: https://github.com/Yakvenalex/curl_fetch2py
Начнем писать код
Для простоты синтаксиса я буду использовать библиотеку Requests, но вы, так-же, сможете использовать любую асинхронную библиотеку. Например, httpx. Все будет работать так-же корректно.
pip install requests
Для работы с запросами я предлагаю использовать браузер Firefox. Этот браузер славится своей мощной и удобной панелью разработчика, которая предоставляет все необходимые инструменты для эффективного анализа и отладки веб-запросов.
Используя Firefox, вы можете быть уверены, что все данные будут скопированы точно и без искажений, что значительно облегчает работу с CURL и FETCH запросами и их последующее преобразование в Python-код с помощью библиотеки CurlFetch2Py.
Для понимания смысла этой библиотеки я предлагаю сразу поработать с «крепким орешком», а именно с сайтом интернет-магазина DNS (dns-shop.ru). Мой выбор выпал на данный ресурс, так как там, без корректной передачи куки и правильного заголовка — вы не получите никакого результата или тот результат что вы получите вас не удовлетворит.
Выполним импорты
from curl_fetch2py import CurlFetch2Py
import requests
requests — мы импортировали для выполнения GET и POST запросов
CurlFetch2Pu — мы импортировали для трансформации CURL и FETCH в объект Python
Теперь давайте «добудем» CURL и FETCH. Для этого перейдем на любую страницу каталога сайта dns‑shop.ru в Firefox и откроем панель разработчика (F12).
В моем случае это будет страница с WiFi-роутерами.
![](https://habrastorage.org/getpro/habr/upload_files/30c/a02/e06/30ca02e06468de35e7599d365ed76077.png)
После того как вы открыли панель разработчика необходимо перейти во вкладку «Network» (сеть) и перезагрузить страницу (обязательно с открытой панелью разработчика).
![](https://habrastorage.org/getpro/habr/upload_files/d9f/10b/4a3/d9f10b4a349858aa11bb497117a52ad7.png)
Обратите внимание на количество запросов которые выполнились когда вы просто обновили страницу.
На скрине выше я обозначил стрелками вкладки с которыми мы будем сегодня работать:
HTML (документ) — тут будут подгружаться HTML шаблоны, которые, в последствии, можно будет парсить
XHR — тут подгружаются отдельно запросы к внутреннему апи сайта и прочие запросы к бэкенду, которые позволяют получить данные (скрытое апи, подробнее далее).
Для начала заберем FETCH запрос, который был отправлен сайтом для получения HTML шаблона. Для этого кликаем на нужный документ правой кнопкой мыши и далее как на скрине ниже.
![](https://habrastorage.org/getpro/habr/upload_files/77c/93f/876/77c93f876ccec71349217797911024cd.png)
После копирования я получил такой результат (многострочная строка):
fetch_str = '''
await fetch("https://www.dns-shop.ru/catalog/17a8aa1c16404e77/wi-fi-routery/", {
"credentials": "include",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
"Priority": "u=0, i"
},
"method": "GET",
"mode": "cors"
});
'''
И, те кто немного ориентируется в парсинге понимает, что тут нет куки, а это значит, что, скорее всего, позитивного ответа мы не получим (забегая наперед скажу что так и есть), но, мы все равно выполним этот запрос.
Для начала нам необходимо получить контекст (объект с которым мы сможем дальше работать).
context_fetch = CurlFetch2Py.parse_fetch_context(fetch_str)
print(context_fetch)
Для этого мы используем метод parse_fetch_context в моем случае он имеет такой вид:
ParsedContext(method='get',
url='https://www.dns-shop.ru/catalog/17a8aa1c16404e77/wi-fi-routery/',
data=None,
headers=OrderedDict({'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', 'Upgrade-Insecure-Requests': '1', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-User': '?1', 'Priority': 'u=0, i'}), cookies=OrderedDict(), verify=True, auth=None, mode='cors',
cache=None,
redirect=None,
referrer=None,
referrerPolicy=None,
integrity=None,
keepalive=None,
signal=None,
window=None)
Для доступа к любому элементу вы можете воспользоваться точкой.
Пример получения ссылки для запроса:
print(context_fetch.url)
# https://www.dns-shop.ru/catalog/17a8aa1c16404e77/wi-fi-routery/
print(context_fetch.headers)
# OrderedDict({'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', 'Upgrade-Insecure-Requests': '1', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-User': '?1', 'Priority': 'u=0, i'})
OrderedDict
обладает всеми функциональными возможностями стандартного словаря в Python. Это означает, что вы можете получать доступ к любому значению, изменять его или удалять, точно так же, как и с обычным словарем.
Достанем user_agent:
print(context_fetch.headers['User-Agent'])
# Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0
Операции по изменению, удалению и перезаписыванию ключей ни чем не будет отличаться от аналогичной работы со стандартным питоновским словариком (dict).
Теперь, когда вы знаете как доставать нужные нам объекты — выполним GET запрос с этими данными.
request = requests.get(url=context_fetch.url, headers=context_fetch.headers, cookies=context_fetch.cookies)
print(request.text)
Так как наш FETCH не содержал никаких куки, мы не получили того результата который ожидали.
Теперь давайте выполним заход через CURL.
![](https://habrastorage.org/getpro/habr/upload_files/f94/016/c4b/f94016c4b1fb362316efc7149cc78f80.png)
Тут обратите внимание, что нам нужна именно CURL (windows/bash).
В моем случае получилась такая строка:
curl_str = ('curl "https://www.dns-shop.ru/catalog/17a8aa1c16404e77/wi-fi-routery/" --compressed -H "User-Agent: '
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0" -H "Accept: text/html,'
'application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8" -H '
'"Accept-Language: en-US,en;q=0.5" -H "Accept-Encoding: gzip, deflate, br, zstd" -H "Connection: '
'keep-alive" -H "Cookie: lang=ru; city_path=moscow; '
'_ab_=^%^7B^%^22catalog-filter-title-test^%^22^%^3A^%^22GROUP_3^%^22^%^2C^%^22save-sort^%^22^%^3A'
'^%^22CLASSIC_CHOICE^%^22^%^7D; PHPSESSID=c8c86477f2505c5c57508adfbf513a80; '
'current_path=9565a5103f36ecea17597b8bfe0de40efdc12ecd83502fc6a8abccb573ee963ba^%^3A2^%^3A^%^7Bi^%^3A0'
'^%^3Bs^%^3A12^%^3A^%^22current_path^%^22^%^3Bi^%^3A1^%^3Bs^%^3A116^%^3A^%^22^%^7B^%^22city^%^22^%^3A'
'^%^2230b7c1f3-03fb-11dc-95ee-00151716f9f5^%^22^%^2C^%^22cityName^%^22^%^3A^%^22^%^5Cu041c^%^5Cu043e'
'^%^5Cu0441^%^5Cu043a^%^5Cu0432^%^5Cu0430^%^22^%^2C^%^22method^%^22^%^3A^%^22default^%^22^%^7D^%^22^%^3B'
'^%^7D; _csrf=5dc4c4dfd57c848bdf804ea9264482e11c7c7d9cdecf3d1164437fb2552cf3fca^%^3A2^%^3A^%^7Bi^%^3A0'
'^%^3Bs^%^3A5^%^3A^%^22_csrf^%^22^%^3Bi^%^3A1^%^3Bs^%^3A32^%^3A^%^22bDrcoNL3hH458S2xVPhCfHRrwgQl47TZ^%^22'
'^%^3B^%^7D; phonesIdentV2=3151a8e8-a63e-453c-9ee4-e6358dea7fed; rrpvid=387668902930816; '
'cartUserCookieIdent_v3=b5a1cea60b799a4c921e33a79ac90dc741e151a167eadeb15aab282623ec2ce6a^%^3A2^%^3A'
'^%^7Bi^%^3A0^%^3Bs^%^3A22^%^3A^%^22cartUserCookieIdent_v3^%^22^%^3Bi^%^3A1^%^3Bs^%^3A36^%^3A'
'^%^22414082e4-3938-368c-adcb-19aca0ea6a59^%^22^%^3B^%^7D; rcuid=66a8ae5a1c7c78f0804f76fd; '
'_ga_FLS4JETDHW=GS1.1.1722339377.3.0.1722339377.60.0.2014933491; _ga=GA1.1.546989399.1722330715; '
'_gcl_au=1.1.1611987054.1722330715; tmr_lvid=cf3a29d82b50e7215d2c294af1298633; tmr_lvidTS=1722330715545; '
'_ym_uid=1722330716916450973; _ym_d=1722330716; _ym_isad=2; '
'domain_sid=I9FlTxAB0sxuVgTA9_yUh^%^3A1722330716258; tmr_detect=0^%^7C1722336778365; '
'qrator_jsr=1722339377.645.8lWsYTYjSsxKvHln-gmertr4pjkrqo6124t73u49heduq5e8d-00; '
'qrator_ssid=1722339378.020.IasjCPeZkwbjh8B8-sujhkbk8uoc6aov0lu9dkmbmsd09mkcr; '
'qrator_jsid=1722339377.645.8lWsYTYjSsxKvHln-mhridg0gh66967rc23bf0r70obsdgo4s" -H '
'"Upgrade-Insecure-Requests: 1" -H "Sec-Fetch-Dest: document" -H "Sec-Fetch-Mode: navigate" -H '
'"Sec-Fetch-Site: cross-site" -H "Priority: u=0, i" -H "Pragma: no-cache" -H "Cache-Control: no-cache"')
И тут, даже не нужно быть профи по анализу сетевых данных чтоб понять, что наша строка содержит больше данных, в частности, данные куки.
И теперь мы не просто выполним GET запрос с печатью его в консоли, а сохраним результат в html файл.
context_curl = CurlFetch2Py.parse_curl_context(curl_str)
request_curl = requests.get(url=context_curl.url,
headers=context_curl.headers,
cookies=context_curl.cookies)
with open('result_curl.html', 'w', encoding='utf-8') as result:
result.write(request_curl.text)
Выполним запрос и посмотрим на полученный результат (открываем полученный html файл в любом браузере).
![](https://habrastorage.org/getpro/habr/upload_files/31e/fd8/1b9/31efd81b9ac70e64b72b4fd13f68e681.png)
Видим, что данные получены, но, что интересно, мы не получили цен. Это не случайно, ведь конкретно для получения цен на сайте DNS используется POST запрос. Перейдем во вкладку XHR и поищем запрос, содержащий цены.
![](https://habrastorage.org/getpro/habr/upload_files/9db/957/48b/9db95748bbd01e24623f868b86e286c0.png)
И, на будущее, советую вам всегда проверять не только GET, но и POST запросы для получения данных.
Далее, так как мы с вами будем выполнять POST запрос для получения данных, нам необходимо будет особым образом подготовить ту информацию, которую мы будем отправлять.
Для примера я возьму FETCH, но удалю с этой строки значение BODY. Тут дело в том, что синтаксис данных, к сожалению, нам не подходит. Внутри есть символы, которые Requests не сможет корректно обработать, но это не проблема, так как подходящие нам данные мы, без труда, сможем достать с панели разработчика в браузере Firefox.
Вот эту строку уберем:
"body": "data={\"type\":\"product-buy\",\"containers\":[{\"id\":\"as-Qqejh_\",\"data\":{\"id\":\"1016495\"}},{\"id\":\"as-15QD1G\",\"data\":{\"id\":\"1357007\"}},{\"id\":\"as-uU83W7\",\"data\":{\"id\":\"1287741\"}},{\"id\":\"as-oF7xUS\",\"data\":{\"id\":\"1037598\"}},{\"id\":\"as-saRC60\",\"data\":{\"id\":\"1342280\"}},{\"id\":\"as-pKIDmW\",\"data\":{\"id\":\"4764006\"}},{\"id\":\"as-9bOQ9j\",\"data\":{\"id\":\"1037104\"}},{\"id\":\"as-rYMbZv\",\"data\":{\"id\":\"4803824\"}},{\"id\":\"as-NUztog\",\"data\":{\"id\":\"1680400\"}},{\"id\":\"as-WjgieU\",\"data\":{\"id\":\"5052396\"}},{\"id\":\"as-c4I8op\",\"data\":{\"id\":\"5438135\"}},{\"id\":\"as-WwHnmb\",\"data\":{\"id\":\"5334577\"}},{\"id\":\"as-Kn0LG0\",\"data\":{\"id\":\"5099460\"}},{\"id\":\"as-nfC7de\",\"data\":{\"id\":\"1197610\"}},{\"id\":\"as-mo3-8p\",\"data\":{\"id\":\"4722801\"}},{\"id\":\"as-U9WbyJ\",\"data\":{\"id\":\"1315878\"}},{\"id\":\"as-F30goS\",\"data\":{\"id\":\"1083093\"}},{\"id\":\"as-2SrqDR\",\"data\":{\"id\":\"4884836\"}}]}",
Получится такой результат
fetch_post_str = '''
await fetch("https://www.dns-shop.ru/ajax-state/product-buy/", {
"credentials": "include",
"headers": {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-Token": "YkLFatXY0xrJo3Bhc_VhTjQhmDKNKCHI-60Um-BE9CMABrcJupafKaHrRFRLplM2YnHwcetgc7qMykX31HOgeQ==",
"content-type": "application/x-www-form-urlencoded",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"Priority": "u=4",
"Pragma": "no-cache",
"Cache-Control": "no-cache"
},
"referrer": "https://www.dns-shop.ru/catalog/17a8aa1c16404e77/wi-fi-routery/no-referrer",
"method": "POST",
"mode": "cors"
});
'''
Данные для POST запроса мы можем забрать отдельно через панель разработчика.
![](https://habrastorage.org/getpro/habr/upload_files/5d5/36d/fff/5d536dfff9f1180a3b9aafcbd0496843.png)
В моем случае информация выглядит так:
data = {"type": "product-buy",
"containers": [{"id": "as-Qqejh_", "data": {"id": "1016495"}},
{"id": "as-15QD1G", "data": {"id": "1357007"}},
{"id": "as-uU83W7", "data": {"id": "1287741"}},
{"id": "as-oF7xUS", "data": {"id": "1037598"}},
{"id": "as-saRC60", "data": {"id": "1342280"}},
{"id": "as-pKIDmW", "data": {"id": "4764006"}},
{"id": "as-9bOQ9j", "data": {"id": "1037104"}},
{"id": "as-rYMbZv", "data": {"id": "4803824"}},
{"id": "as-NUztog", "data": {"id": "1680400"}},
{"id": "as-WjgieU", "data": {"id": "5052396"}},
{"id": "as-c4I8op", "data": {"id": "5438135"}},
{"id": "as-WwHnmb", "data": {"id": "5334577"}},
{"id": "as-Kn0LG0", "data": {"id": "5099460"}},
{"id": "as-nfC7de", "data": {"id": "1197610"}},
{"id": "as-mo3-8p", "data": {"id": "4722801"}},
{"id": "as-U9WbyJ", "data": {"id": "1315878"}},
{"id": "as-F30goS", "data": {"id": "1083093"}},
{"id": "as-2SrqDR", "data": {"id": "4884836"}}]
}
В дальнейшем, изучив сайт, можно формировать подобие своего собственного API для получения тех или иных данных. Например в этом случае, если вы научитесь получать два параметра: id записи и id самого товара, то вы сможете массово запрашивать цены и прочие данные на неограниченное количество товаров.
Кроме того. Обратите внимание на следующую строку в fetch-запросе:
"content-type": "application/x-www-form-urlencoded"
К сожалению, мы видим что сайт ждет не привычного нам JSON (dict), а x-www-form-urlencoded. Это значит что перед отправкой нам необходимо будет данные преобразовать в в строку в формате URL-кодирования.
Для этого нам необходимо импортировать встроенную в Python библиотеку json. Преобразуем данные в нужный нам формат, используя json.dumps().
data = {"type": "product-buy",
"containers": [{"id": "as-Qqejh_", "data": {"id": "1016495"}},
{"id": "as-15QD1G", "data": {"id": "1357007"}},
{"id": "as-uU83W7", "data": {"id": "1287741"}},
{"id": "as-oF7xUS", "data": {"id": "1037598"}},
{"id": "as-saRC60", "data": {"id": "1342280"}},
{"id": "as-pKIDmW", "data": {"id": "4764006"}},
{"id": "as-9bOQ9j", "data": {"id": "1037104"}},
{"id": "as-rYMbZv", "data": {"id": "4803824"}},
{"id": "as-NUztog", "data": {"id": "1680400"}},
{"id": "as-WjgieU", "data": {"id": "5052396"}},
{"id": "as-c4I8op", "data": {"id": "5438135"}},
{"id": "as-WwHnmb", "data": {"id": "5334577"}},
{"id": "as-Kn0LG0", "data": {"id": "5099460"}},
{"id": "as-nfC7de", "data": {"id": "1197610"}},
{"id": "as-mo3-8p", "data": {"id": "4722801"}},
{"id": "as-U9WbyJ", "data": {"id": "1315878"}},
{"id": "as-F30goS", "data": {"id": "1083093"}},
{"id": "as-2SrqDR", "data": {"id": "4884836"}}]
}
data_json = json.dumps(data)
body = f"data={data_json}"
print(body)
Получим такую строку:
data={"type": "product-buy", "containers": [{"id": "as-Qqejh_", "data": {"id": "1016495"}}, {"id": "as-15QD1G", "data": {"id": "1357007"}}, {"id": "as-uU83W7", "data": {"id": "1287741"}}, {"id": "as-oF7xUS", "data": {"id": "1037598"}}, {"id": "as-saRC60", "data": {"id": "1342280"}}, {"id": "as-pKIDmW", "data": {"id": "4764006"}}, {"id": "as-9bOQ9j", "data": {"id": "1037104"}}, {"id": "as-rYMbZv", "data": {"id": "4803824"}}, {"id": "as-NUztog", "data": {"id": "1680400"}}, {"id": "as-WjgieU", "data": {"id": "5052396"}}, {"id": "as-c4I8op", "data": {"id": "5438135"}}, {"id": "as-WwHnmb", "data": {"id": "5334577"}}, {"id": "as-Kn0LG0", "data": {"id": "5099460"}}, {"id": "as-nfC7de", "data": {"id": "1197610"}}, {"id": "as-mo3-8p", "data": {"id": "4722801"}}, {"id": "as-U9WbyJ", "data": {"id": "1315878"}}, {"id": "as-F30goS", "data": {"id": "1083093"}}, {"id": "as-2SrqDR", "data": {"id": "4884836"}}]}
Эту же строку вы видели под флагом BODY, но изначально она содержала символы, которые не поддерживал requests. Выполнив это преобразование мы получили данные в нужном нам виде.
Теперь попробуем выполнить POST запрос с полученными данными:
data_json = json.dumps(data)
body = f"data={data_json}"
context = CurlFetch2Py.parse_fetch_context(fetch_post_str)
r = requests.post(url=context.url,
headers=context.headers,
data=body,
cookies=context.cookies)
print(r.json())
Так как мы знаем, что ответ необходимо ждать в JSON – сразу ответ трансформируем в нужный нам формат (r.json()
).
![](https://habrastorage.org/getpro/habr/upload_files/ead/836/a2e/ead836a2e7effa4762048b5829ff0984.png)
Данных очень много. Посмотрим с какими ключами мы можем работать.
print(r.json().keys())
# dict_keys(['assets', 'result', 'data', 'message', 'errors'])
![](https://habrastorage.org/getpro/habr/upload_files/708/5d6/877/7085d68772dc9ca8e7a03de30b9e38b6.png)
Тут мы видим, что нужная нам информация хранится в data → states.
Пробежимся циклом for по этим данным.
for product in r.json()['data']['states']:
print(product)
Данные по каждому товару имеют такой вид:
{'id': 'as-Qqejh_', 'data': {'id': 'd520fe01-e3ef-11e4-9470-00155d03361b', 'name': 'Wi-Fi роутер Tenda N301', 'price': {'current': 1150}, 'primaryButton': '<span id="as-v4kRUC"></span>', 'wishlist': '<span id="as-nBVAyg" class="wishlist-button-placeholder wishlist-button-placeholder_btn"></span>', 'hasHistory': True, 'maxPeriodPrice': None}}
Давайте теперь распечатаем только те данные, которые нам будут действительно полезны.
for product in r.json()['data']['states']:
product_data = {'product_id': product['data'].get('id'),
'product_name': product['data'].get('name'),
'price': product['data'].get('price', {}).get('current')}
print(product_data)
![](https://habrastorage.org/getpro/habr/upload_files/23d/541/b8e/23d541b8e12ff418405716f4a0a64961.png)
Вы видите, что в данном случае нам пришлось сделать некоторые преобразования для выполнения корректного post запроса. В других же случаях достаточно будет просто преобразовать данные через CurlFetch2Py.
Заключение
Как вы могли убедиться, библиотека CurlFetch2Py, несмотря на свою лаконичность, оказалась достаточно функциональной и гибкой для обработки данных.
Безусловно, она не охватывает все возможные случаи парсинга, особенно учитывая, что многие сайты применяют Cloudflare и другие технологии защиты. Тем не менее, даже такие крупные интернет-магазины, как DNS, можно достаточно легко обойти при корректном формировании GET/POST запросов.
Экспериментируйте с библиотекой, и я уверен, что результаты вас приятно удивят.
Кстати, в процессе работы над библиотекой я задумался о создании полноценного веб-приложения на FastAPI, которое включало бы фронтенд и интеграцию с логикой данной библиотеки. Если вы хотите увидеть реализацию такого проекта, дайте знать в комментариях и не забудьте принять участие в голосовании, которое находится под этой статьей.
Если вам интересны мои работы и эксклюзивный контент, который я не публикую на Хабре, приглашаю вас в мой Telegram-канал «Легкий путь в Python». В нем уже более 200 единомышленников, и там вы сможете бесплатно обсудить свои проблемы в написании кода на Python или поделиться своим опытом в дружеской и поддерживающей атмосфере.
На этом у меня всё. Благодарю за внимание!