대부분의 웹은 편안한 API를 사용하여 서버에서 데이터를 이동합니다. 쉬다 수면과 관련이 없는 단순한 개념입니다. 쉬다 수단 대표 상태 이전 , 그러나이 기사는 쉬다 . 이 문서는 사이트에서 일부 데이터를 검색해야 할 때 나머지 API가 없는 문제를 해결하기 위한 복잡한 메커니즘에 관한 것입니다.
웹 사이트는 데이터 대역폭 비용, 데이터 라이선스 비용, 광고를 보지 않는 것 역시 비용이 들 수 있는 여러 가지 이유로 사용자가 이를 수행하지 않기를 원할 수 있습니다. 이 도구는 셀렌 는 명목상 웹 사이트를 테스트하기 위한 것이지 과속 방지턱을 넘기 위한 것이 아닙니다. 대부분의 사이트에는 이용 약관이 있습니다. 이 페이지 하단에서 JMP 용어를 찾을 수 있습니다.
전체 JSL 파일이 첨부되어 있습니다. 2022년 5월 8일에 표시된 대로 Firefox, Windows 및 JMP 웹 사이트용으로 작성되었습니다. Firefox 부분은 아마도 다른 많은 브라우저로 변경될 수 있습니다. Mac에서도 작동할 수 있으며 테스트는 수행되지 않았습니다. JMP 웹 사이트는 시간이 지남에 따라 변경되며 JSL은 조정이 필요합니다. 이것이 공식 API를 사용하지 않는 단점입니다.
시작하기 전에 아래 설명과 같이 드라이버를 다운로드하고 셀레늄을 설치하십시오. Firefox도 필요하거나 선호하는 브라우저용 드라이버에 대해 조사하십시오.
// JMP + Python + Selenium + Firefox (probably works with Chrome or Edge with a few tweaks)
// https://firefox-source-docs.mozilla.org/testing/geckodriver/index.html
// https://selenium-python.readthedocs.io/
// downloaded https://github.com/mozilla/geckodriver/releases -- geckodriver-...-win64.zip (pick latest)
// expand on desktop geckodriver.exe
// installed selenium python -m pip install selenium
// the following code is welded into the JMP.COM's HTML of 8May2022. It could change at anytime.
// the functions are sprinkled through the JSL below, near where I first needed them, reused later.
// you'll need similar functions, probably, and want to consult the Python Selenium bindings to write them.
// I only wrote enough to make this simple example work. There is no actual need to be logged in to JMP.
//
// Using try...except on snips of python code makes debugging much easier. JMP loses the exception message without it.
//
// the JSL functions are thin wrappers around the python calls to selenium which (I believe) is
// a thin wrapper to generate an API call to the gecko (etc) driver. I *think* it might be possible to
// remove the python layer and load/call the driver directly from JSL. But Selenium is documented.
Python Init(); // one-time startup...
// 0: startup
xrc = Python Execute( {}, {By_ID, By_XPATH, rc},
"\[
try:
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.firefox.options import Options
#
options = webdriver.FirefoxOptions()
#options.add_argument("--private") # example. you most likely don't want private, some things don't work.
#
service=Service(r'C:\Users\v1\Desktop\geckodriver.exe')
# sometimes people use "browser" rather than "driver". It will be used below.
driver = webdriver.Firefox(service=service,options=options)
# return two magic values. you may need some others, just add them in the same way...
By_ID = By.ID
By_XPATH = By.XPATH
rc = "ok"
except Exception as e:
rc = repr(e)
]\"
);
If( xrc != 0 | rc != "ok", Throw( "start up Selenium failed" || Try( ": " || Char( rc ), "" ) ) );
PythonInit()는 한 번만 수행하면 됩니다. JMP를 Python에 연결하고 처음에는 몇 초가 걸립니다. 페널티 없이 다시 호출할 수 있습니다. PythonExecute(...)는 변수를 보내지 않지만 실행하는 코드에서 3개를 다시 가져옵니다. 모든 것을 로드하고 브라우저를 시작하는 데 약간의 시간이 걸립니다.
You are looking at an empty browser controlled by JMPJMP에서 제어하는 빈 브라우저를 보고 있습니다.
다음에 JMP.COM 페이지를 엽니다. 일반적으로 눈에 띄지 않는 리디렉션이 표시될 수 있습니다.
// 1: navigate to jmp.com
nav = Function( {url}, {rc},
Python Execute( {url}, {rc},
"\[
try:
driver.get(url)
rc = "ok"
except Exception as e:
rc = repr(e)
]\" );
return(rc);
);
rc = nav( "https://www.jmp.com/" );
if( rc != "ok", throw("nav: "||char(rc)));
nav 함수는 "ok" 또는 오류 메시지를 반환합니다. JMP 웹 페이지가 브라우저에 로드됩니다. 스크린샷에 있는 사람들은 무시하세요.
The icon means the browser is remote controlled.아이콘은 브라우저가 원격 제어됨을 의미합니다.
JMP 사이트에 대한 로그인을 스크립팅합니다. 로그인 버튼을 마우스 오른쪽 버튼으로 클릭하여 버튼의 HTML id 값을 찾습니다. 이를 수행하는 방법을 기억하십시오. 이 설명은 마지막에 생략하겠습니다...
F12 might bring you to the next screen, but this way the control will already be selected.F12 키를 누르면 다음 화면으로 이동할 수 있지만 이렇게 하면 컨트롤이 이미 선택되어 있습니다.
컨트롤의 ID가 표시된 상태로 개발자 콘솔이 열립니다. 더 아래에는 클래스가 있지만 ID가 없는 컨트롤이 있습니다. Selenium의 XPATH가 처리할 수 있습니다. 트릭은 displaybox 탐색과 비슷합니다. 너무 깨지지 않고 여전히 충분히 구체적인 경로를 찾는 것입니다.
Use the button id in the JSL that follows.다음 JSL에서 버튼 ID를 사용하십시오.
ID는 페이지에서 고유하기 때문에 일반적으로 사용 가능한 경우 최상의 선택입니다. WaitID는 로그인 버튼이 나타날 때까지 최대 10초 동안 기다립니다. 기다릴 필요가 없을 수도 있습니다. 이미 있으면 시간이 걸리지 않습니다.
// 2: login with userid/password credentials
// wait for an id to be available
waitID = Function( {id, timeout = 10, BYformat=By_ID}, {rc},
Python Execute( {id, timeout, BYformat}, {rc},
"\[
try:
myElem = WebDriverWait(driver, timeout).until(EC.presence_of_element_located((BYformat, id)))
rc = "ok"
except TimeoutException:
rc = "timeout"
except Exception as e:
rc = repr(e)
]\"
);
Return( rc );
);
rc = waitID( "loginButton", 5 );
If( rc != "ok", Throw( "no login button: " || char(rc) ) );
// click a button ID
clickID = Function( {id, BYformat=By_ID}, {rc},
Python Execute( {id, BYformat}, {rc},
"\[
try:
driver.find_element(BYformat, id).click()
rc = "ok"
except Exception as e:
rc = repr(e)
]\"
);
return(rc);
);
rc = clickID( "loginButton" );
if( rc != "ok", throw("login button: "||char(rc)));
시원한! 로그인 화면이 나타납니다. 다음으로 사용자 이름 필드를 찾으십시오.
Now get the id for the user name field by right-click...이제 마우스 오른쪽 버튼을 클릭하여 사용자 이름 필드의 ID를 가져옵니다...
다시, 예상 필드를 기다립니다. 이제 키스트로크 기능이 필요합니다...
rc = waitID( "idp-discovery-username", 10 );
If( rc != "ok", Throw( "no username field: " || char(rc) ) );
// type a value into a field
keysToID = Function( {id, txt}, {rc},
Python Execute( {id, txt}, {rc},
"\[
try:
driver.find_element(By.ID, id).send_keys(txt)
rc = "ok"
except Exception as e:
rc = repr(e)
]\"
);
return(rc);
);
rc = keysToID( "idp-discovery-username", Include( "$documents/UserID.jsl" ) ); // file contains "AliBaba@1000&OneNights.com", in quotation marks, possibly encrypted
if( rc != "ok", throw("keysToID username: "||char(rc)));
Fake user name for fake password.가짜 비밀번호를 위한 가짜 사용자 이름.
내 사용자 ID가 화면 밖으로 스크롤되고 다음 버튼이 표시됩니다...이름 찾기...
Click the Next button to get the password prompt.다음 버튼을 클릭하여 암호 프롬프트를 표시합니다.
그것을 클릭한 다음 암호 필드를 기다립니다...
// click Next button
rc = clickID( "idp-discovery-submit" );
if( rc != "ok", throw("click submit user name: "||char(rc)));
// wait for password field
rc = waitID( "okta-signin-password", 5 );
If( rc != "ok", Throw( "no password field: " || char(rc) ) );
그런 다음 비밀번호를 입력하십시오
// enter password
rc = keysToID( "okta-signin-password", Include( "$documents/password.jsl" ) ); // file contains "OpenSesame", in quotation marks, possibly encrypted
if( rc != "ok", throw("keysToID password: "||char(rc)));
그리고 과정을 반복합니다...로그인 버튼을 찾습니다...
After entering the password, click the sign in button.비밀번호 입력 후 로그인 버튼을 클릭합니다.
클릭하세요
// click Sign In
rc = clickID( "okta-signin-submit" );
if( rc != "ok", throw("click signin submit: "||char(rc)));
로그인했습니다.
Must be signed in, there is an edit profile choice.로그인해야 합니다. 프로필 편집 선택 항목이 있습니다.
그림에 검색 필드가 있습니다. JSL을 입력하고 돋보기를 클릭합니다. "searchField"는 ID입니다. 돋보기를 클릭할 수 있지만 셀레늄에는 양식의 입력 필드인 searchField에서 작동하는 제출 양식 메커니즘이 있습니다.
The search field needs a bigger window to be visible, here it is.검색 필드를 보려면 더 큰 창이 필요합니다. 여기 있습니다.
페이지의 일부를 로드하는 비동기 JavaScript가 있습니다. 필드가 페이지의 일부로 로드된 경우 특정 필드를 기다릴 필요가 없습니다.
// 3: query for articles about JSL
rc = waitID( "searchField" );
If( rc != "ok", Throw( "no search field: " || char(rc) ) );
// enter a search string. "jsl" currently retruns 3 pages
rc = keysToID( "searchField", "jsl" );
if( rc != "ok", throw("keysToID search field: "||char(rc)));
submitForm = Function( {id}, {rc},
Python Execute( {id}, {rc},
"\[
try:
driver.find_element(By.ID, id).submit()
rc = "ok"
except Exception as e:
rc = repr(e)
]\"
);
return(rc);
);
rc = submitForm( "searchField" ); // submit form works OK off of this input field.
if( rc != "ok", throw("submitForm searchField: "||char(rc)));
이제 결과를 페이지로 이동할 준비를 하십시오. 다중 페이지 목록 요소는 다음과 같습니다.
The pink outer element holds three inner parts: title, description, link.분홍색 외부 요소에는 제목, 설명, 링크의 세 가지 내부 부분이 있습니다.
여러 페이지에 걸쳐 있는 분홍색 원 데이터 항목 목록이 있습니다.
// 4: page through the results to capture them
getElements = Function( {id, BYformat=By_ID}, {rc},
Python Execute( {id, BYformat}, {rc},
"\[
try:
list = driver.find_elements(BYformat,id)
rc = "ok"
except Exception as e:
rc = repr(e)
]\"
);
Return( rc );
);
getNElements = function({},{n},
Python Execute( {}, {n},
"\[
try:
n = len(list)
except Exception as e:
print(repr(e))
n = -1
]\"
);
return(n);
);
getElementItext = function({i,id, BYformat=By_ID},{txt},
Python Execute( {i, id, BYformat}, {txt},
"\[
try:
txt = list[int(i)].find_element(BYformat, id).text
except Exception as e:
txt = "Error: getElementItext: " + repr(e)
]\"
);
return(txt);
);
getElementIattribute = function({i,id, BYformat=By_ID, attr},{txt},
Python Execute( {i, id, BYformat, attr}, {txt},
"\[
try:
txt = list[int(i)].find_element(BYformat, id).get_attribute(attr)
except Exception as e:
txt = "Error: getElementIattribute: " + repr(e)
]\"
);
return(txt);
);
위: 아래 루프에서 사용할 일부 함수. 페이지 하단에 다음 페이지로 이동하는 버튼이 있습니다. 항목 목록을 파괴하고 다시 만드는 일부 JavaScript를 실행합니다. 새 목록을 다시 캡처하기 위해 함수가 다시 호출됩니다. JSL과 Python은 이 예제에 적합합니다. 동시에 추적할 목록이 두 개 이상 있으면 고장납니다. Python을 참조하세요. 목록 변하기 쉬운. 다음 페이지마다 데이터가 로드될 때까지 기다려야 한다고 확신합니다...
dt = New Table( "articles",
New Column( "link", character,
Set Property("Event Handler",
Event Handler(
Click(JSL Quote(Function( {thisTable, thisColumn, iRow}, Web( Char( thisTable:thisColumn[ iRow ] ) ); );)),
Tip(JSL Quote(Function( {thisTable, thisColumn, iRow}, "Open " || Char( thisTable:thisColumn[ iRow ] ) || " in your browser."; );)),
Color(JSL Quote(Function( {thisTable, thisColumn, iRow}, RGBColor("link"); );))
)
)
),
New Column( "title", character ),
New Column( "description", character )
);
// this while loop will grab screens of answers and break() when the NextScreen button goes dim
while(1,
// not sure, yet, what to wait for. maybe the card(s)...
// this html is updated in the background by a json ajax mechanism that
// is hard to see.
rc = waitID("//div[@id='searchresults']//div[@class='result-card']",10,By_XPATH);
If( rc != "ok", Throw( "no search results" ) );
// get the current set
rc = getElements("//div[@id='searchresults']//div[@class='result-card']",By_XPATH);
if( rc != "ok", throw("getElements: "||char(rc)));
n = getNElements();
for(i=0,i<N></N>
이 시점에서 브라우저가 열리고 이 테이블이 화면에 나타납니다.
Today there were 57 entries spanning three pages.오늘은 3페이지에 걸쳐 57개의 항목이 있었습니다.
브라우저를 종료할 시간입니다.
// 5: quit
Python Submit(
"\[
driver.quit() # close the browser
]\" );
Python Term();
마지막에는 XPATH가 있습니다.
rc = clickID("//ul[@id='pager']//a[@class='pager-next']",By_XPATH);
그것의 의미는
// - somewhere below the root of the document find a
ul - a <ul> tag (some sort of HTML list)
[@id='pager'] - the list has this id
// - more nested tags, followed by...
a = a <a> tag (link)
[@class='pager-next']
그렇게 복잡할 필요가 없을 수도 있습니다. 클래스만 고려하면 고유하지 않을 수 있는 항목을 찾기 위해 고유 ID를 사용하는 예입니다.