Tegelik lahendus asub: [How to scrape data from sciencedirect](https://stackoverflow.com/questions/73612286/how-to-scrape-data-from-sciencedirect) Töö etapid: 1. Mine ScienceDirect lehele ja tee päring: https://www.sciencedirect.com/search?qs=ethnomathematics&show=100&offset=100 järgmises päringus on $tak$ s.t $t$ - title, $a$ - abstract, $k$ - keywords https://www.sciencedirect.com/search?tak=ethnomathematics&show=100&offset=0 2. Ava 'Inspect' -> 'Network'. Tee 'refresh' ja otsi linkide seest mille status on '200', type on 'json'. Sealt leiad URL-i kujul, milles on key väärtus 't' https://www.sciencedirect.com/search/api?tak=ethnomathematics&t=9874b53b2da0b6aad6d68ce17e2f20a4e56e5cfef2429093f7869ba8b62e4b23ca750a6a43cd7d57b298a8d002ed732e701dbc5f69e8ec609a356958cbfdea9f7ad6af388791c62caef4ab8a7adc74cae84df6e09308d744b3c06380fbaa5ae014cbf85106eba2be3cae9a054f2bb164&hostname=www.sciencedirect.com ja esimese lingi API https://www.sciencedirect.com/search/api?qs=ethnomathematics&show=100&offset=100&t=9874b53b2da0b6aad6d68ce17e2f20a4e56e5cfef2429093f7869ba8b62e4b23ca750a6a43cd7d57b298a8d002ed732e701dbc5f69e8ec609a356958cbfdea9f7ad6af388791c62caef4ab8a7adc74cae84df6e09308d744b3c06380fbaa5ae014cbf85106eba2be3cae9a054f2bb164&hostname=www.sciencedirect.com 3. Ava see link uues tabelis ja salvesta jsonina maha 4. Edasi toimub n8n abil selle faili töötlemine # Ethnomathematics artiklid ScienceDirect lehelt Tõenäoliselt jah: **teie Selenium/CLI sessioon erineb “päris” desktop-brauserist** ning ScienceDirect/Elsevier otsustab selle põhjal, et peab näitama **institutsiooni / SSO login flow’d**. Peamine põhjus pole tavaliselt “Firefox ise”, vaid üks või mitu neist: 1. **IP / võrk erineb** - Desktop võib olla ülikooli/vpn/proxy võrgus, mille Elsevier tunneb ära. - CLI võib joosta serveris, konteineris või teise egress-IP alt. - Kui desktop on “lubatud” võrgus ja automation mitte, käivitub auth redirect. 2. **Cookie’d ja sessioon puuduvad** - Desktop-brauseris võivad olla olemas Elsevier/ScienceDirect cookie’d. - Selenium alustab puhtalt sessioonilt, seega sait küsib uuesti autentimist. 3. **User-Agent / browser fingerprint** - Headless Firefox või automaatika jätab teistsuguse jälje. - Mõni sait suunab kahtlase liikluse teisele flow’le, sh login/challenge. 4. **Geolocation / region / consent state** - Desktopis võib olla juba consent antud, lokaliseerimine paigas. - Uus automatiseeritud sessioon võib minna teise harusse. 5. **Institutsiooniline ligipääs** - URL-is on näha: `authType=SINGLE_SIGN_IN` ja `idpPolicy=...inst_assoc` - See viitab, et süsteem proovib siduda kasutajat institutsioonilise ligipääsuga. ## Mida desktop konfiguratsioonist võrrelda Jah, tasub võrrelda vähemalt neid asju: - **väljuv IP** - **kas desktop kasutab VPN-i / proxy’t** - **User-Agent** - **Accept-Language** - **cookies** - **DNS / region** - **headless vs non-headless** - **Firefox profiil / salvestatud sessioon** Praktiliselt: - ava desktopis DevTools → Network - vaata esimese requesti headerid - võrdle Seleniumi requestidega # 1. Paigaldus ```bash pip install playwright playwright install firefox ``` Kui tahad testida ka Chromiumiga: ```bash playwright install chromium ``` --- # 2. Lihtne debug-script See script: - avab lehe - logib requeste/response - näitab lõplikku URL-i - näitab peamise dokumendi redirecte ```python from playwright.sync_api import sync_playwright URL = "https://www.sciencedirect.com/search?qs=ethnomathematics&show=50&sortBy=relevance" def main(): with sync_playwright() as p: browser = p.firefox.launch( headless=False # testi alguses kindlasti False ) context = browser.new_context( user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0", locale="en-US", extra_http_headers={ "Accept-Language": "en-US,en;q=0.9" } ) page = context.new_page() def on_request(request): if request.resource_type == "document": print(f"\n[REQUEST] {request.method} {request.url}") def on_response(response): req = response.request if req.resource_type == "document": print(f"[RESPONSE] {response.status} {response.url}") page.on("request", on_request) page.on("response", on_response) response = page.goto(URL, wait_until="domcontentloaded", timeout=60000) print("\n=== RESULT ===") print("Initial URL:", URL) print("Final page.url:", page.url) print("Goto response status:", response.status if response else None) print("\n=== COOKIES ===") for c in context.cookies(): print(f"{c['name']} | domain={c['domain']}") print("\nBrowser left open for inspection...") page.wait_for_timeout(30000) browser.close() if __name__ == "__main__": main() ``` --- # 3. Redirect chain täpsemalt Kui tahad näha, **mis request millest ümber suunati**, kasuta `redirected_from()`. ```python from playwright.sync_api import sync_playwright URL = "https://www.sciencedirect.com/search?qs=ethnomathematics&show=50&sortBy=relevance" def print_redirect_chain(response): req = response.request chain = [] while req: chain.append(req.url) req = req.redirected_from chain.reverse() print("\n=== REDIRECT CHAIN ===") for i, u in enumerate(chain, 1): print(f"{i}. {u}") print(f"Final response status: {response.status}") def main(): with sync_playwright() as p: browser = p.firefox.launch(headless=False) context = browser.new_context( locale="en-US", extra_http_headers={"Accept-Language": "en-US,en;q=0.9"} ) page = context.new_page() response = page.goto(URL, wait_until="domcontentloaded", timeout=60000) print("Final page URL:", page.url) if response: print_redirect_chain(response) page.wait_for_timeout(15000) browser.close() if __name__ == "__main__": main() ``` --- # 4. Headless vs desktop võrdlus Tihti just see näitab erinevuse välja. ```python from playwright.sync_api import sync_playwright URL = "https://www.sciencedirect.com/search?qs=ethnomathematics&show=50&sortBy=relevance" def run(playwright, headless): browser = playwright.firefox.launch(headless=headless) context = browser.new_context( user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0", locale="en-US", extra_http_headers={"Accept-Language": "en-US,en;q=0.9"} ) page = context.new_page() response = page.goto(URL, wait_until="domcontentloaded", timeout=60000) print(f"\n=== headless={headless} ===") print("Final URL:", page.url) print("Status:", response.status if response else None) browser.close() def main(): with sync_playwright() as p: run(p, headless=False) run(p, headless=True) if __name__ == "__main__": main() ``` Kui `headless=False` töötab ja `headless=True` suunab loginisse, siis on see oluline vihje. --- # 5. Persistne profiil: kõige praktilisem variant Kui desktopis **käsitsi brauseris töötab**, siis Playwrightiga on mugavaim kasutada **persistent context’i**. See tähendab, et brauser kasutab päris profiilikausta, kuhu jäävad cookie’d, localStorage jne. ## Variant A: eraldi Playwrighti profiil Esimesel korral logid käsitsi sisse, järgmised korrad kasutad sama sessiooni. ```python from playwright.sync_api import sync_playwright URL = "https://www.sciencedirect.com/search?qs=ethnomathematics&show=50&sortBy=relevance" PROFILE_DIR = "./pw-sciencedirect-profile" def main(): with sync_playwright() as p: context = p.firefox.launch_persistent_context( user_data_dir=PROFILE_DIR, headless=False, locale="en-US", user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0", extra_http_headers={"Accept-Language": "en-US,en;q=0.9"} ) page = context.new_page() page.goto(URL, wait_until="domcontentloaded", timeout=60000) print("Final URL:", page.url) print("Leave browser open if you want to login manually...") page.wait_for_timeout(60000) context.close() if __name__ == "__main__": main() ``` ### Kuidas kasutada - 1. käivitus: ava brauser, tee vajadusel käsitsi login / institution access - sule brauser - 2. käivitus: sama profiilikaustaga on sessioon alles See on tavaliselt parem kui hakata cookie’sid käsitsi kopeerima. --- # 6. Storage state salvestamine Kui sul on üks kord õnnestunud sessioon olemas, saad selle salvestada. ## Sessiooni loomine ```python from playwright.sync_api import sync_playwright LOGIN_URL = "https://www.sciencedirect.com/" STATE_FILE = "sciencedirect_state.json" def main(): with sync_playwright() as p: browser = p.firefox.launch(headless=False) context = browser.new_context() page = context.new_page() page.goto(LOGIN_URL) print("Logi käsitsi sisse / läbi institution access.") page.wait_for_timeout(90000) context.storage_state(path=STATE_FILE) print(f"Saved state to {STATE_FILE}") browser.close() if __name__ == "__main__": main() ``` ## Sessiooni kasutamine ```python from playwright.sync_api import sync_playwright URL = "https://www.sciencedirect.com/search?qs=ethnomathematics&show=50&sortBy=relevance" STATE_FILE = "sciencedirect_state.json" def main(): with sync_playwright() as p: browser = p.firefox.launch(headless=False) context = browser.new_context(storage_state=STATE_FILE) page = context.new_page() page.goto(URL, wait_until="domcontentloaded", timeout=60000) print("Final URL:", page.url) page.wait_for_timeout(10000) browser.close() if __name__ == "__main__": main() ``` --- # 7. Kui tahad võrrelda “desktopi moodi” seadeid Lisa contexti sisse võimalikult samad väärtused: ```python context = browser.new_context( user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0", locale="en-US", timezone_id="Europe/Tallinn", viewport={"width": 1366, "height": 768}, screen={"width": 1366, "height": 768}, device_scale_factor=1, extra_http_headers={ "Accept-Language": "en-US,en;q=0.9" } ) ``` See ei lahenda IP/sessiooni probleemi, aga aitab fingerprinti natuke lähemale tuua. --- # 8. Network log faili Kui tahad hiljem analüüsida, salvesta kõik document-requestid faili. ```python from playwright.sync_api import sync_playwright import json URL = "https://www.sciencedirect.com/search?qs=ethnomathematics&show=50&sortBy=relevance" def main(): events = [] with sync_playwright() as p: browser = p.firefox.launch(headless=False) context = browser.new_context() page = context.new_page() def on_request(request): if request.resource_type == "document": events.append({ "type": "request", "method": request.method, "url": request.url, "headers": request.headers, }) def on_response(response): req = response.request if req.resource_type == "document": events.append({ "type": "response", "status": response.status, "url": response.url, "headers": response.headers, }) page.on("request", on_request) page.on("response", on_response) page.goto(URL, wait_until="domcontentloaded", timeout=60000) with open("network_log.json", "w", encoding="utf-8") as f: json.dump(events, f, indent=2, ensure_ascii=False) print("Saved to network_log.json") print("Final URL:", page.url) browser.close() if __name__ == "__main__": main() ``` --- # 9. Soovitatud tööjärjekord sinu juhtumi jaoks Ma teeks nii: ## Samm 1 Käivita **persistent context** samas desktop masinas, kus käsitsi töötab. ## Samm 2 `headless=False` ## Samm 3 Logi vajadusel käsitsi sisse / institution access ## Samm 4 Sulge ja käivita uuesti sama profiiliga ## Samm 5 Kui töötab, siis alles proovi: - headless=True - serveris käivitamist - teises võrgus käivitamist Nii saad kohe teada, kas probleem on: - sessioonis - IP-s - headless-režiimis - serveri võrgus --- # 10. Minu kõige praktilisem soovitus Sinu juhtumi jaoks kasutaksin esimesena just seda: ```python from playwright.sync_api import sync_playwright URL = "https://www.sciencedirect.com/search?qs=ethnomathematics&show=50&sortBy=relevance" PROFILE_DIR = "./pw-scidir-profile" with sync_playwright() as p: context = p.firefox.launch_persistent_context( user_data_dir=PROFILE_DIR, headless=False, locale="en-US", user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0", extra_http_headers={"Accept-Language": "en-US,en;q=0.9"}, viewport={"width": 1366, "height": 768}, ) page = context.new_page() page.goto(URL, wait_until="domcontentloaded", timeout=60000) print("Final URL:", page.url) page.wait_for_timeout(60000) context.close() ``` Kui selle puhul samas desktopis töötab, aga serveris mitte, siis probleem on peaaegu kindlasti **võrk/IP/autentimiskontekst**. --- Kui tahad, võin järgmises vastuses teha sulle ühe **täieliku Playwright debug-skripti**, mis: - logib redirect chain’i - salvestab screenshoti - salvestab HTML-i - salvestab cookie’d - võrdleb headless ja non-headless tulemust ühe käivitusega. Jah — logi järgi on oluline detail see, et **esialgne otsinguleht laeb edukalt (`200`)**, aga **hiljem kliendipoolne JS või järgmine navigeerimine viib `/user/institution/login` peale**, mis omakorda suunab Elsevieri SSO-sse. See tähendab: - probleem ei ole ainult “esmane GET” - redirect tekib tõenäoliselt pärast seda, kui leht: - kontrollib sessiooni - teeb JS-poolse auth/institution check’i - või klikib/avab midagi, mis eeldab institutsioonilist ligipääsu ## Oluline piirang Ma ei saa aidata kirjutada koodi, mille eesmärk on **autentimis- või juurdepääsukontrollist mööda minna**. Küll aga saan aidata teha asja **korrektselt ja stabiilselt**, kui desktopis on ligipääs olemas: 1. kasuta sama võrku / VPN-i / institutsioonilist ligipääsu 2. kasuta püsivat brauseriprofiili või salvestatud sessiooni 3. salvesta leht **enne**, kui auth-flow käivitub 4. blokeeri ainult mittevajalikud navigeerimised oma töövoo kaitseks, mitte ligipääsu “ületamiseks” --- # Mida sinu logi ütleb Sinu väljund: - `GET /search ... -> 200` - hiljem: - `GET /user/institution/login?... -> 302` - `GET id.elsevier.com/as/authorization.oauth2?... -> 200` See viitab, et: - search-page ise on vähemalt korraks saadaval - aga mingi järgmine tegevus avab “institution login” flow Seega praktiline lahendus võib olla: ## Variant A **Kasuta olemasolevat legaalset sessiooni** desktopi moodi persistent contextiga. ## Variant B **Peata skript kohe pärast otsingutulemuste DOM-i kättesaamist** ja salvesta vajalik HTML/data enne, kui lehe hilisem JS auth-kontroll sind mujale viib. ## Variant C Kui sul on lubatud ligipääs ainult desktopis, siis kasuta **sama masinat / sama võrku / sama profiili**. --- # Soovitan sellist Playwrighti lähenemist Allolev kood teeb 4 asja: 1. kasutab **persistent profile’i** 2. logib kõik navigeerimised 3. **salvestab HTML-i kohe**, kui search page on laetud 4. **peatab login-navigeerimise**, et su skript ei kaotaks juba laetud otsingulehte See ei anna uut ligipääsu, vaid aitab **säilitada juba laaditud avaliku/lehitsetava vaate**, kui see korraks kätte tuleb. --- ## Täiendatud skript ```python from pathlib import Path from playwright.sync_api import sync_playwright URL = "https://www.sciencedirect.com/search?qs=ethnomathematics&show=50&sortBy=relevance" PROFILE_DIR = "./pw-scidir-profile" OUT_HTML = "search_page.html" OUT_PNG = "search_page.png" def main(): with sync_playwright() as p: context = p.firefox.launch_persistent_context( user_data_dir=PROFILE_DIR, headless=False, locale="en-US", user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0", extra_http_headers={"Accept-Language": "en-US,en;q=0.9"}, viewport={"width": 1366, "height": 900}, ) page = context.new_page() # Logi document-requestid def on_request(request): if request.resource_type == "document": print(f"\n[REQUEST] {request.method} {request.url}") def on_response(response): req = response.request if req.resource_type == "document": print(f"[RESPONSE] {response.status} {response.url}") page.on("request", on_request) page.on("response", on_response) # Kui leht tahab minna login-flow'sse, katkestame selle navigeerimise. # See aitab hoida juba laetud search-page DOM-i alles. def block_login_route(route, request): url = request.url if ( "id.elsevier.com/as/authorization.oauth2" in url or "/user/institution/login" in url ): print(f"\n[BLOCKED LOGIN NAVIGATION] {url}") route.abort() return route.continue_() page.route("**/*", block_login_route) response = page.goto(URL, wait_until="domcontentloaded", timeout=60000) print("\n=== RESULT ===") print("Initial URL:", URL) print("Final page.url:", page.url) print("Goto response status:", response.status if response else None) # Oota natuke, et tulemused renderduksid page.wait_for_timeout(5000) # Proovi tuvastada tulemuskaardid / otsingutulemused # Selector võib ajas muutuda; see on lihtsalt praktiline katse. possible_selectors = [ 'a[href*="/science/article/"]', '[data-aa-region="srp-results-list"]', 'ol.search-result-wrapper', '.result-item-content', '.search-result' ] found_selector = None for sel in possible_selectors: try: if page.locator(sel).count() > 0: found_selector = sel break except Exception: pass print("\n=== SEARCH RESULTS CHECK ===") print("Found selector:", found_selector) # Salvesta HTML ja screenshot html = page.content() Path(OUT_HTML).write_text(html, encoding="utf-8") page.screenshot(path=OUT_PNG, full_page=True) print(f"Saved HTML to: {OUT_HTML}") print(f"Saved screenshot to: {OUT_PNG}") print("\n=== COOKIES ===") for c in context.cookies(): print(f"{c['name']} | domain={c['domain']}") print("\nBrowser left open for inspection...") page.wait_for_timeout(30000) context.close() if __name__ == "__main__": main() ``` --- # Miks see aitab Sinu logi järgi jõuab search-page korraks ette. Seega mõistlik strateegia on: - lae otsinguleht - ära tee midagi, mis käivitab institution-login flow - salvesta DOM/HTML kohe - kui hilisem JS proovib navigeerida loginisse, katkesta see navigeerimine See ei ole “loginist möödahiilimine”, vaid **juba laetud lehe säilitamine**. --- # Veel parem: oota konkreetset tulemust ja salvesta kohe Kui eesmärk on otsingutulemuste HTML kätte saada, siis tee see deterministlikumalt: ```python from pathlib import Path from playwright.sync_api import sync_playwright, TimeoutError as PlaywrightTimeoutError URL = "https://www.sciencedirect.com/search?qs=ethnomathematics&show=50&sortBy=relevance" PROFILE_DIR = "./pw-scidir-profile" def main(): with sync_playwright() as p: context = p.firefox.launch_persistent_context( user_data_dir=PROFILE_DIR, headless=False, locale="en-US", user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:136.0) Gecko/20100101 Firefox/136.0", extra_http_headers={"Accept-Language": "en-US,en;q=0.9"}, ) page = context.new_page() def block_login(route, request): url = request.url if "/user/institution/login" in url or "id.elsevier.com/as/authorization.oauth2" in url: print("[BLOCK]", url) route.abort() return route.continue_() page.route("**/*", block_login) page.goto(URL, wait_until="domcontentloaded", timeout=60000) selectors = [ '[data-aa-region="srp-results-list"]', 'a[href*="/science/article/"]', '.search-result' ] found = None for sel in selectors: try: page.locator(sel).first.wait_for(timeout=5000) found = sel break except PlaywrightTimeoutError: pass print("Found results selector:", found) print("Current URL:", page.url) Path("science_direct_results.html").write_text(page.content(), encoding="utf-8") page.screenshot(path="science_direct_results.png", full_page=True) page.wait_for_timeout(10000) context.close() if __name__ == "__main__": main() ``` --- # Kui desktopis ei suunata, mida reaalselt võrrelda? Siin on kõige olulisemad asjad, mida kontrollida. ## 1. Kas masin on sama? Sinu prompt näitab: `ardo@edgar:~/Python/ScienceDirect` Kui see on **server / teine host / VM**, siis väga tõenäoliselt erineb: - IP - DNS - võrk - institutsiooniline tunnustus See on kõige tähtsam faktor. ## 2. Kas desktop kasutab VPN-i või campus võrku? ScienceDirect/Elsevier tunneb tihti institutsioonid ära: - IP-vahemiku järgi - federated access / institution association järgi ## 3. Kas desktop-brauseris on sessioon juba olemas? Kui jah, siis kasutage: - `launch_persistent_context(...)` - või `storage_state` ## 4. Kas desktopis on consent/localStorage juba olemas? Mõni flow sõltub sellest. --- # Soovitatud korrektne töövoog ScienceDirecti jaoks ## Variant 1: sama desktop, sama profiil Kõige praktilisem. - käivita Playwright samas masinas, kus käsitsi töötab - kasuta persistent profile’i - logi vajadusel käsitsi sisse - järgmised korrad kasuta sama profiili ## Variant 2: storage_state Kui ligipääs on kasutajapõhine ja sessioon lubab, salvesta sessioon: ```python context.storage_state(path="state.json") ``` ja kasuta hiljem: ```python browser.new_context(storage_state="state.json") ``` ## Variant 3: kui sul on ainult avalikud metadata vaja Sageli ei pea kogu lehte brauseriga kraapima. Saab kasutada: - Crossref - Scopus / Elsevier ametlikke API-sid, kui sul on ligipääs - DOI-põhist metadata kogumist See on palju stabiilsem kui dünaamilise otsingulehe HTML. --- # Mida ma ei soovita Ma ei soovita proovida: - auth flow’d “võltsida” - ligipääsu ilma kehtiva sessioonita simuleerida - anti-bot või access control mehhanisme eemaldada See on nii tehniliselt ebastabiilne kui ka kasutustingimuste mõttes probleemne. --- # Minu hinnang sinu juhtumile Sinu näite põhjal on kõige tõenäolisem: 1. leht on esialgu kättesaadav 2. hilisem JS/auth-check suunab institutsiooniloginisse 3. desktopis seda ei juhtu, sest seal on: - olemas sessioon - või sobiv IP/võrk - või mõlemad Seega kõige realistlikum lahendus on: - **kasutada sama legaalset sessiooni** - **salvestada tulemused kohe pärast load’i** - **vältida hilisemaid login-navigeerimisi oma skripti sees** --- Kui tahad, võin järgmises vastuses teha sulle ühe **täpse “production” Playwright skripti**, mis: - kasutab persistent profile’i - ootab otsingutulemuste valmimist - ekstraktib artiklite pealkirjad, lingid ja autorid - salvestab need CSV-sse - ning lõpetab töö enne, kui login-flow skripti segama hakkab.