Если вы работаете с парсингом, вам, вероятно, знакомы такие концепции, как 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-роутерами.

После того как вы открыли панель разработчика необходимо перейти во вкладку «Network» (сеть) и перезагрузить страницу (обязательно с открытой панелью разработчика).

Обратите внимание на количество запросов которые выполнились когда вы просто обновили страницу.

На скрине выше я обозначил стрелками вкладки с которыми мы будем сегодня работать:

  • HTML (документ) — тут будут подгружаться HTML шаблоны, которые, в последствии, можно будет парсить

  • XHR — тут подгружаются отдельно запросы к внутреннему апи сайта и прочие запросы к бэкенду, которые позволяют получить данные (скрытое апи, подробнее далее).

Для начала заберем FETCH запрос, который был отправлен сайтом для получения HTML шаблона. Для этого кликаем на нужный документ правой кнопкой мыши и далее как на скрине ниже.

После копирования я получил такой результат (многострочная строка):

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.

Тут обратите внимание, что нам нужна именно 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 файл в любом браузере).

Видим, что данные получены, но, что интересно, мы не получили цен. Это не случайно, ведь конкретно для получения цен на сайте DNS используется POST запрос. Перейдем во вкладку XHR и поищем запрос, содержащий цены.

И, на будущее, советую вам всегда проверять не только 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 запроса мы можем забрать отдельно через панель разработчика.

В моем случае информация выглядит так:

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()).

Данных очень много. Посмотрим с какими ключами мы можем работать.

print(r.json().keys())

# dict_keys(['assets', 'result', 'data', 'message', 'errors'])

Тут мы видим, что нужная нам информация хранится в 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)

Вы видите, что в данном случае нам пришлось сделать некоторые преобразования для выполнения корректного post запроса. В других же случаях достаточно будет просто преобразовать данные через CurlFetch2Py.

Заключение

Как вы могли убедиться, библиотека CurlFetch2Py, несмотря на свою лаконичность, оказалась достаточно функциональной и гибкой для обработки данных.

Безусловно, она не охватывает все возможные случаи парсинга, особенно учитывая, что многие сайты применяют Cloudflare и другие технологии защиты. Тем не менее, даже такие крупные интернет-магазины, как DNS, можно достаточно легко обойти при корректном формировании GET/POST запросов.

Экспериментируйте с библиотекой, и я уверен, что результаты вас приятно удивят.

Кстати, в процессе работы над библиотекой я задумался о создании полноценного веб-приложения на FastAPI, которое включало бы фронтенд и интеграцию с логикой данной библиотеки. Если вы хотите увидеть реализацию такого проекта, дайте знать в комментариях и не забудьте принять участие в голосовании, которое находится под этой статьей.

Если вам интересны мои работы и эксклюзивный контент, который я не публикую на Хабре, приглашаю вас в мой Telegram-канал «Легкий путь в Python». В нем уже более 200 единомышленников, и там вы сможете бесплатно обсудить свои проблемы в написании кода на Python или поделиться своим опытом в дружеской и поддерживающей атмосфере.

На этом у меня всё. Благодарю за внимание!

Комментарии (0)