Галоўная / Артыкулы

Адаптаванне закрытых цэн па адваротным аналізе запытак API у фоне

Знайце цены, які ніколі не показуюцца ў HTML, стежачы за DevTools за запытамі XHR, REST і GraphQL, які відбуваюцца на адзінцы стороніцы, а потым перзапускаючы гэтыя запыты безпосередня.

Выдавец: REACTAPP.TOP5 хв чытання1213 слоў

Крыніцы: Talking to the API Instead of the Page

Другі частка серыі "Як на самай працоўвае скрапінг"

У паканальный стацыяні мы зішлі на перакананне: інодзе цена, якая нам патрэбна, проста не існуе ў жадным з фрагментаў HTML, якія запрашвае requests. Яна не ўкладзена ў JSON-LD, не знаходзіцца ў таге meta, яе проста няма. Але гэта не значыць, што мы застряглі — зазвычай гэта проста означае, што стораніца запрашвае гэтыя даны пасля пачатковага завантажэння, чераз аднальны фонавы запыт. Мацель тут — знайсці гэты запыт і адрабатваць яго самім.

Крок 1: Ачыніце вкладку "Сеть"

Мы будзем працаваць на конкрэтным прыкладзе: стораніцы товара на сайте Target, target.com/p/dyson-ball-animal-3-upright-vacuum/-/A-85269291 (гэта толькі для деманстрацыі — той самы падход працюе на практычна будзь-якай стораніцы товара).

  1. Завантажыце будзь-якую стораніцу товара.
  • Клацніце правай кнопкай мышы на старонцы і выберыце «Inspect» (альбо нажміце F12).
  • Перайдзіце на панель Network.
  • Перазавантажыце старонку.
  • Застосавіце фільтр Fetch/XHR — гэта сховае запиты на з’явленне адпраўк, CSS і шрыфтов, залишыўшы толькі тыя запиты, якія фактычна перадаюць данні.
  • Шаг 2: Знайсці запит, які вам действительна трэба

    Пройдзіце па спісе аднае ентры за іншай, клацаючы на кожную і пераканаліваючыся ў панелі Response або Preview справа. Вы шукаеце JSON-данні, якія маюць цену, колькасць адзінакоў або будзь-яе іншае значэнне, якое вам трэба.

    Є шырэйшы спосаб: выкорыстайце падзелку пошуку у виглядзе лупы, якая знаходзіцца ў вікні «Сеть», і увайсце значэнне, якое ўжо відразу видна на самай сторанцы. У прыкладзе з Target, якщо увайсці «449.99», то будзе адразу выкананы запит типу POST пад назвай deferred_enrichment/modules. Його тэла-адказ ёсць большым об’ектам JSON, і ў яго ўнутрошчы вы знайдзеце "formatted_current_price":"$449.99.

    Шаг 3: Адтворыце цей запит самі

    Выберыце гэты запит і зафіксавайце ўсю його URL. У гэтым случае це запит типу POST, але всё неабходнае ўжо є ў строе запита:

    https://www.target.com/cdui_orchestrations/v1/pages/pdp/deferred_enrichment/modules
        ?tcin=85269291
        &key=9f36aeafbe60771e321a7cc95a78140772ab3e96
        &channel=WEB
        &device_type=DESKTOP
    

    Ось як гэта выглядае, калі адтворваць на Python:

    import requests
    
    url = "https://www.target.com/cdui_orchestrations/v1/pages/pdp/deferred_enrichment/modules"
    params = {
        "tcin": "85269291",       # the product ID, from the page URL
        "key": "9f36aeafbe60771e321a7cc95a78140772ab3e96",
        "channel": "WEB",
        "device_type": "DESKTOP",
    }
    
    resp = requests.post(url, params=params, headers={"User-Agent": "Mozilla/5.0"})
    data = resp.json()
    
    price = data["modules"][0]["module_data"]["data"]["product"]["price"]["formatted_current_price"]
    print(price)  # $449.99
    

    Гэта і є весь секрет. Няма ніякіх бібліятэкаў для парсінгу, няма CSS-селектараў — вы проста адправляеце той самы запит, які ў тылу адправляе JavaScript сторанцы.

    Парадакт: у DevTools правайм клікніце на запит і выберыце Copy → Copy as cURL. Чыгульна гэта дае вам точныя заголовкі і кукі, якія дадзены браузерам, і якія можна перакласты ў такі кліент, як Postman, або пераканваліць у Python, каб з’ясаваць, які саме элементы насправды неабходны.

    Шаг 4: Калі API ў формате GraphQL

    Дзеяныя сайты направляюць усё через GraphQL замест традыцыйнага REST-рэштрыктування. Гэты формат можна распазнакаць таму, што кожны запыт падходзіць да та самага канцачнага пункту (часта /graphql), а фактычны запыт належыць у тэле запыту, а не ў URL:

    {
      "operationName": "GetPrice",
      "variables": { "productId": "12345" },
      "query": "query GetPrice($productId: ID!) { product(id: $productId) { price } }"
    }
    

    Дзеяныя сайты ідуць даўжэй, выкарыстоўваючы persisted query — у такім случае кліент адправляе не весь текст запыту, а толькі короткі хэш, який яго заменяе:

    /graphql/GetPrice/a1b2c3d4e5f6...?variables={"productId":"12345"}
    

    Этот хэш нельзя вырахаваць самастае — яго трэба заўсёды браць безпасова з DevTools. А так как ён прыязначаны для фронтэнд-часткі сайту, будь-яе апдэйт на ўсунуці можа зменіць хэш і тыха паспрабаваць зламаць ваш скрыпт. Гэта справжня постаяла цена адвароту на гэты метод.

    Шаг 5: Перакананне, чы рэальна ключ API є прыватным

    Зноў паглядзіце на URL-адрэсу з Шага 3 — заўважыце, што key=9f36aeafbe60771e321a7cc95a78140772ab3e96 відкрыта належыць у строке запиту. Чы гэта тое, да чаго вам не павінна быць даступнае?

    Зазвычай ні. Найшырэйшы спосаб працавання — адкрыць View Page Source (Ctrl+U), а потым знайсці apiKey за дапамою Ctrl+F. Вы пабачыце той самы ключ у чыстам тексте, ў блоке налашчэння, які Target адправляе кожнаму візітару.

    Якщо значэнне вже знаходзіцца ў сабеўскай маркапэйнг-структуре стораніцы — без якога-лібо айхоўвання і без участі аутэнтыфікацыі — яно з самага пачатку не было конфідэнцыйным. Це проста публічны ключ, які мае працаваць у браузеры. (Інодзе ён знаходзіцца ў окремым файле .js; якщо раздел «Паказаць код» нічога не прадстаўляе, спробуйце панель «Ўтвары коду» у інструментах разработчыка і абшукуйце за дапамогою Ctrl+Shift+F).

    Адна хорашая практыка: перш чым працаваць над зворачным аналізам канца-тэпу, пераканайцеся, чы не праставляе компанія ўжо задокументаваны публічны чы сапраўднікавы API. Такія зазвычай ўсё бол стабільныя, чым тые, якія вы знайшлі, працюючы з інструментарамі разработчыка.

    Шаг 6: Калі даны ўжо не надаюцца ад самага продавца

    Інодзе запит, які вы шукаеце, нават не падае на домэн самага продавца — у замен ён вядзе на сервіс трэцьей стороны, такі як bazaarvoice.com або judge.me. Гэта ўскладненне, што продавец не выкорыстоўвае саёню інфраструктуру атрыбутаў; ён арендуе систему у постачальніка і проста вбудоввае яго віджет на сторанцы.

    Этыя API віджэтаў, якімі керуе постачальнік, зазвычай захоўваюцца менш строго, чым галоўны сайт продавца, адтолькі якія ўзначаны для вбудоввання ў тыячы некаляжных інтерфейсоў, а не для спецыяльнага выкрытва і блакавання збірачаў данных.

    Што ў гэтым артыкуле навмысна не паказвана

    Якщо вы выполнилі всі пункты вышэй, а запытак, які вам трэба, яшчэ і дае код 403, порожній тэла або ўсё, што нагадвае пункт контроля безпекі, а не рэальныя даны, то гэта не баг у вашам коде. Это значыць, што сайт актыўна выявіў, што вы не ў звычайнай сесіі браузера. Гэта завданне настолькі значныя, што заслуговае на самастоятнюю дыскусію, і самэ гэта є тама, дзе продовжыцца наступны артыкул.

    Глосарый

    Вікнага карточка сеті — панель DevTools, якая фіксуе кожны запыт, які выдае сторанка, а таксама тое, што было адправлена і што вернулася.

    XHR / Fetch — два механізмы, якія викорыстоўвае JavaScript для выканання запытак у фонавым режыме без повнага перзапуску сторанкі; вікнагу карточку сеті можна фільтрацыяваць, каб паказаць толькі іх.

    REST API — это распаўсюджаны шаблон API, пры яком кожны ресурс мае свой сабэтны URL, напрыклад /products/123/price.

    GraphQL — это стыль API, пабудаваны навакол аднаго канцэнтраба, пры яком тэла запиту точна паказвае, калікі данней запрашваюцца.

    Persisted query — это спрыткалка GraphQL, пры якой цэлы текст запиту заменяецца на компактны хеш, які сервер вядома распазнае.

    API key — это значэнне, прыкладзеная да запиту для яго ідентыфікаціі; дзеяныя з іх ёсць сапраўды прыватныя, але большасць — грамадскія, назначаныя для кліентаў, і можа быць видныя ў браузеры будзь-каго.

    Copy as cURL — это апцыя ў DevTools, якая ператварае зафіксаваны запит у выконвальны камандны запыв, што стаўкаеся для перагляду яго точных заголовакоў і кукі.

    Віджет трэцьяго сторонньага — галузь функцыянальнасці, такая як раздзел з атрыбутамі, яку сайт не стварае сам, а ўбудоввае з продукту іншай компаніі.