selenium post
selenium 에서 post 를 사용하기 위해서는 selenium-requests 라는 패키지를 설치해서 사용하는 방식이 있습니다. selenium-requests 를 사용하게 되면 selenium 으로 주로 사용했던 키동작이라던가 그런 부분을 사용을 할 수 없습니다.
그리고 selenium 과 selenium-requests 는 각각의 브라우저로 동작하기 때문에 필요한 부분에서만 처리를 하고자 하는 경우 web session 유지가 되지 않습니다. 예를 들어 selenium 를 이용하여 로그인 처리를 한 후 selenium-requests 를 이용하고자 한다면 로그인이 유지가 안된다는 뜻입니다.
그래서 selenium 으로만 post 할 수 있는 방법에 대해 알아 보았습니다.
아무곳이나 테스트할 수 없기 때문에 예전에 사용하던 flask 를 이용해서 간단하게 post 테스트 할 수 있는 환경을 만들었습니다.
from flask import Flask, make_response, jsonify, request stock = { "fruit": { "apple": 10, "banana": 20 } } app = Flask(__name__) @app.route("/stock") def get_stock(): res = make_response(jsonify(stock), 200) return res @app.route("/stock/<goods>") def get_goods(goods): """ Returns a goods from stock """ if goods in stock: res = make_response(jsonify(stock[goods]), 200) return res res = res = make_response(jsonify({"error": "Not found"}), 404) return res @app.route("/stock/<goods>", methods=["POST"]) def create_goods(goods): """ Creates a new goods if it doesn't exist """ req = request.get_data() print("create_goods") print(req) res = make_response(jsonify({"message": "goods created"}), 201) return res if __name__ == '__main__': app.run(debug=True)
크게 내용은 없고 post가 들어오면 어떤 식으로 들어온 것인지 그대로 출력만 해줍니다.
그리고 다음으로는 selenium 을 래핑한 함수입니다. 이전 예제에서도 주로 사용했어서
이름은 selenium_v1.py 로 지었습니다.
import chromedriver_autoinstaller from selenium import webdriver from selenium.webdriver.common.keys import Keys import time import os class selenium_v: def __init__(self): self.driver = None self.download_path = None return def create_web_driver_chrome(self, headless=True, download_path=None): options = webdriver.ChromeOptions() options.add_argument('disable-gpu') options.add_experimental_option('excludeSwitches',['enable-logging']) if headless: options.add_argument('headless') if download_path!=None: self.download_path = os.path.abspath(download_path) prefs = {"download.default_directory":self.download_path} options.add_experimental_option("prefs",prefs) if self.driver!=None: self.driver.close() self.driver = None chromedriver_autoinstaller.install() try: self.driver = webdriver.Chrome(options=options) self.driver.implicitly_wait(10) except Exception as e: print("exception",e) return self.driver def download_wait(self, timeout_min=1): if self.download_path==None: print("error can not find download path") return -2 path_to_downloads = self.download_path seconds = 0 dl_wait = True sum_after = 0 while dl_wait and seconds < timeout_min*60: time.sleep(5) dl_wait = False sum_before = sum_after sum_after = 0 for fname in os.listdir(path_to_downloads): if fname.endswith('.crdownload'): sum_after += os.stat(path_to_downloads+'/'+fname).st_size dl_wait = True if dl_wait and seconds > 10 and sum_before == sum_after: print("download timeout") dl_wait = False return -1 seconds += 5 return seconds def get(self,url): if self.driver == None: return -1 return self.driver.get(url) def close(self): if self.driver == None: return -1 return self.driver.close() def save_page_source(self, filename): if self.driver == None: return -1 html = self.driver.page_source try: f = open(filename, 'w', encoding = 'utf-8') f.write(html) f.close() except: print("exception",e) return 0 if __name__ == "__main__": sel = selenium_v() sel.create_web_driver_chrome(headless=True,download_path=".") print(sel.get("https://www.daum.net")) print(sel.driver.page_source) print(sel.get("https://www.python.org/ftp/python/3.9.11/python-3.9.11-embed-amd64.zip")) sel.download_wait() print(sel.driver.page_source) sel.save_page_source("test.html")
다음의 코드가 핵심 코드입니다.
import requests import selenium_v1 as selenium_v from bs4 import BeautifulSoup def print_roundtrip(response, *args, **kwargs): format_headers = lambda d: '\n'.join(f'{k}: {v}' for k, v in d.items()) print(("" "---------------- request ----------------\n" "{req.method} {req.url}\n" "{reqhdrs}\n" "\n" "{req.body}\n" "---------------- response ----------------\n" "{res.status_code} {res.reason} {res.url}\n" "{reshdrs}\n" "\n" "{res.text}\n" "").format( req=response.request, res=response, reqhdrs=format_headers(response.request.headers), reshdrs=format_headers(response.headers), )) data1={"cat":1,"dog":2} data2={"cat":3,"lion":4,"bird":2} data3={"cat":3,"dog":4} resp = requests.post('http://127.0.0.1:5000/stock/animal', data=data2, hooks={'response': print_roundtrip}) def get_script(url, params): post_script = ''' function post_to_url(path, params, method) { method = method || "post"; var form = document.createElement("form"); form._submit_function_ = form.submit; form.setAttribute("method", method); form.setAttribute("action", path); for(var key in params) { var hiddenField = document.createElement("input"); hiddenField.setAttribute("type", "hidden"); hiddenField.setAttribute("name", key); hiddenField.setAttribute("value", params[key]); form.appendChild(hiddenField); } document.body.appendChild(form); form._submit_function_(); } ''' run_script = f'post_to_url("{url}",' run_script = run_script + "{" first = True for odata in params: if first == False: run_script = run_script + "," else: first = False run_script = run_script + odata + ':' + str(params[odata]) run_script = run_script + "} )" #print(post_script + run_script) return post_script + run_script sel = selenium_v.selenium_v() sel.create_web_driver_chrome(headless=True, download_path=".") sel.driver.execute_script(get_script("http://127.0.0.1:5000/stock/animal", data1)) print(sel.driver.page_source) # get page #sel.get('http://127.0.0.1:5000/stock/animal') #sel.driver.implicitly_wait(3)
requests 와 selenium 을 비교하기 위해서 requests 예제가 위쪽에 있습니다.
핵심은 selenium 의 execute_script 를 이용하게 됩니다.
스크립트를 실행시키기 위해서 스크립트를 구성하는 함수가 get_script 함수입니다.
get_script( 접속하고자 하는url 정보, 전달하고자 하는 data의 dict 타입 필요합니다. )
script의 내용은 javascript 형태로 form을 만들고 post를 하게 되는 내용입니다. default가 post가되고 get으로 구현할 수 도 있습니다.
실행 결과
서버쪽 실행 켤과를 살펴보면 아래와 같이 나오게 되는데
앞쪽 결과가 requests로 넘긴 결과 입니다.
create_goods b'cat=3&lion=4&bird=2' 127.0.0.1 - - [29/Jan/2023 20:31:41] "POST /stock/animal HTTP/1.1" 201 - create_goods b'cat=1&dog=2' 127.0.0.1 - - [29/Jan/2023 20:31:43] "POST /stock/animal HTTP/1.1" 201 -
차이가 없으며 출력 값이 다른 것은 보낼때 data1,data2 각각 다른 값을 보냈기에 다른것입니다.
보내는쪽 결과
아래는 requests 로 보낸 결과 입니다.
---------------- request ---------------- POST http://127.0.0.1:5000/stock/animal User-Agent: python-requests/2.28.1 Accept-Encoding: gzip, deflate, br Accept: */* Connection: keep-alive Content-Length: 19 Content-Type: application/x-www-form-urlencoded cat=3&lion=4&bird=2 ---------------- response ---------------- 201 CREATED http://127.0.0.1:5000/stock/animal Server: Werkzeug/2.2.2 Python/3.8.8rc1 Date: Sun, 29 Jan 2023 11:31:41 GMT Content-Type: application/json Content-Length: 33 Connection: close { "message": "goods created" }
아래는 다음 코드에 의해서 나오는 값입니다.
print(sel.driver.page_source)
<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">{ "message": "goods created" } </pre></body></html>