Closed ywlfff closed 3 years ago
The same can be said of my question:how to create a playwright browser instance using a singleton pattern?
If I understand you correctly, you can consider this way:
from playwright import sync_playwright
from flask import Flask, request
app = Flask(__name__)
playwright = sync_playwright().start() # not use ` with`
browser = playwright.chromium.launch() # now `browser` is global variable, Only once
@app.route('/test1')
def test1():
my_browser = browser.newContext() # create newContext , it isolate from test2
page = my_browser.newPage()
page.goto("https://google.com")
@app.route('/test2')
def test2():
my_browser = browser.newContext() # create newContext , it isolate from test1
page = my_browser.newPage()
page.goto("https://github.com")
if __name__ == '__main__':
app.run()
If I understand you correctly, you can consider this way:
from playwright import sync_playwright from flask import Flask, request app = Flask(__name__) playwright = sync_playwright().start() # not use ` with` browser = playwright.chromium.launch() # now `browser` is global variable, Only once @app.route('/test1') def test1(): my_browser = browser.newContext() # create newContext , it isolate from test2 page = my_browser.newPage() page.goto("https://google.com") @app.route('/test2') def test2(): my_browser = browser.newContext() # create newContext , it isolate from test1 page = my_browser.newPage() page.goto("https://github.com") if __name__ == '__main__': app.run()
If I understand you correctly, you can consider this way:
from playwright import sync_playwright from flask import Flask, request app = Flask(__name__) playwright = sync_playwright().start() # not use ` with` browser = playwright.chromium.launch() # now `browser` is global variable, Only once @app.route('/test1') def test1(): my_browser = browser.newContext() # create newContext , it isolate from test2 page = my_browser.newPage() page.goto("https://google.com") @app.route('/test2') def test2(): my_browser = browser.newContext() # create newContext , it isolate from test1 page = my_browser.newPage() page.goto("https://github.com") if __name__ == '__main__': app.run()
Thank you for your reply, but I got a error: [2020-12-24 15:08:34,193] ERROR in app: Exception on /test1 [GET] Traceback (most recent call last): File "C:\Users\Administrator.virtualenvs\nk_monitor\lib\site-packages\flask\app.py", line 2447, in wsgi_app response = self.full_dispatch_request() File "C:\Users\Administrator.virtualenvs\nk_monitor\lib\site-packages\flask\app.py", line 1952, in full_dispatch_request rv = self.handle_user_exception(e) File "C:\Users\Administrator.virtualenvs\nk_monitor\lib\site-packages\flask\app.py", line 1821, in handle_user_exception reraise(exc_type, exc_value, tb) File "C:\Users\Administrator.virtualenvs\nk_monitor\lib\site-packages\flask_compat.py", line 39, in reraise raise value File "C:\Users\Administrator.virtualenvs\nk_monitor\lib\site-packages\flask\app.py", line 1950, in full_dispatch_request rv = self.dispatch_request() File "C:\Users\Administrator.virtualenvs\nk_monitor\lib\site-packages\flask\app.py", line 1936, in dispatch_request return self.view_functionsrule.endpoint File "F:/MyCodes/nk_monitor/test.py", line 15, in test1 my_browser = browser.newContext() # create newContext , it isolate from test2 File "C:\Users\Administrator.virtualenvs\nk_monitor\lib\site-packages\playwright\sync_api.py", line 6735, in newContext storageState=storageState, File "C:\Users\Administrator.virtualenvs\nk_monitor\lib\site-packages\playwright_sync_base.py", line 95, in _sync self._dispatcher_fiber.switch() greenlet.error: cannot switch to a different thread 127.0.0.1 - - [24/Dec/2020 15:08:34] "GET /test1 HTTP/1.1" 500 -
I very much hope that the asynchronous interface of playwright can run under multithreading. This problem also occurs when I use the web framework fastapi
The root cause is that playwright-python
uses greenlet for async operations under the hood and greenlet is single threaded whereas flask is multithreaded so it crashes.
Checkout this snippet, running single threaded:
from flask import Flask
from playwright import sync_playwright
app = Flask(__name__)
playwright = sync_playwright().start()
browser = playwright.chromium.launch()
@app.route("/test1")
def test1():
my_browser = browser.newContext()
page = my_browser.newPage()
page.goto("https://google.com")
@app.route("/test2")
def test2():
my_browser = browser.newContext()
page = my_browser.newPage()
page.goto("https://github.com")
if __name__ == "__main__":
app.run(threaded=False)
The root cause is that
playwright-python
uses greenlet for async operations under the hood and greenlet is single threaded whereas flask is multithreaded so it crashes.Checkout this snippet, running single threaded:
from flask import Flask from playwright import sync_playwright app = Flask(__name__) playwright = sync_playwright().start() browser = playwright.chromium.launch() @app.route("/test1") def test1(): my_browser = browser.newContext() page = my_browser.newPage() page.goto("https://google.com") @app.route("/test2") def test2(): my_browser = browser.newContext() page = my_browser.newPage() page.goto("https://github.com") if __name__ == "__main__": app.run(threaded=False)
Thank you very much,it is work!!!
If I understand you correctly, you can consider this way:
from playwright import sync_playwright from flask import Flask, request app = Flask(__name__) playwright = sync_playwright().start() # not use ` with` browser = playwright.chromium.launch() # now `browser` is global variable, Only once @app.route('/test1') def test1(): my_browser = browser.newContext() # create newContext , it isolate from test2 page = my_browser.newPage() page.goto("https://google.com") @app.route('/test2') def test2(): my_browser = browser.newContext() # create newContext , it isolate from test1 page = my_browser.newPage() page.goto("https://github.com") if __name__ == '__main__': app.run()
hi, what should I do if I want to use the async playwright?
If I understand you correctly, you can consider this way:
from playwright import sync_playwright from flask import Flask, request app = Flask(__name__) playwright = sync_playwright().start() # not use ` with` browser = playwright.chromium.launch() # now `browser` is global variable, Only once @app.route('/test1') def test1(): my_browser = browser.newContext() # create newContext , it isolate from test2 page = my_browser.newPage() page.goto("https://google.com") @app.route('/test2') def test2(): my_browser = browser.newContext() # create newContext , it isolate from test1 page = my_browser.newPage() page.goto("https://github.com") if __name__ == '__main__': app.run()
hi, what should I do if I want to use the async playwright?
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto("http://playwright.dev/")
print(await page.title())
await browser.close()
asyncio.run(main())
import os
import time
from flask import Flask, jsonify
from playwright.sync_api import sync_playwright
app = Flask(__name__)
playwright = sync_playwright().start()
browser = playwright.chromium.launch(headless=True)
current_directory = os.path.dirname(os.path.abspath(__file__))
html_file_path = f"file:///{current_directory}/token.html"
@app.route("/new_token", methods=["GET"])
def new_token_route():
try:
my_browser = browser.new_context()
page = my_browser.new_page()
page.goto("https://google.com")
return jsonify("Ok test"), 200
except Exception as e:
print(e)
return (
jsonify(
{
"status": "error",
"message": "An error occurred during the token generation : {}".format(
e
),
}
),
500,
)
if __name__ == "__main__":
app.run()
Error: cannot switch to a different thread
import os import time from flask import Flask, jsonify from playwright.sync_api import sync_playwright app = Flask(__name__) playwright = sync_playwright().start() browser = playwright.chromium.launch(headless=True) current_directory = os.path.dirname(os.path.abspath(__file__)) html_file_path = f"file:///{current_directory}/token.html" @app.route("/new_token", methods=["GET"]) def new_token_route(): try: my_browser = browser.new_context() page = my_browser.new_page() page.goto("https://google.com") return jsonify("Ok test"), 200 except Exception as e: print(e) return ( jsonify( { "status": "error", "message": "An error occurred during the token generation : {}".format( e ), } ), 500, ) if __name__ == "__main__": app.run()
Error:
cannot switch to a different thread
Check the answer https://github.com/microsoft/playwright-python/issues/390#issuecomment-762837202. You should run your Flask app with app.run(threaded=False)
. This workaround is not compatible with gunicorn
or other webservers that are going to run your code with threads.
Can the playwright be combined with a flask?How do I save the Browser object? I don't want to create a browser every time I visit it,by use [with sync_playwright() as playwright]. I do not want to save the Browser object each time I visit it, thereby incurring memory loss. Here is my code for how to improve and become a singleton.
from playwright import sync_playwright from flask import Flask, request app = Flask(name)
@app.route('/test1') def test1(): with sync_playwright() as playwright: browser = playwright.chromium.launch()
@app.route('/test2') def test2(): with sync_playwright() as playwright: browser = playwright.chromium.launch() if name == 'main': app.run()