microsoft / playwright-python

Python version of the Playwright testing and automation library.
https://playwright.dev/python/
Apache License 2.0
11.88k stars 906 forks source link

flask and playwright #390

Closed ywlfff closed 3 years ago

ywlfff commented 3 years ago

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()

ywlfff commented 3 years ago

The same can be said of my question:how to create a playwright browser instance using a singleton pattern?

dongfangtianyu commented 3 years ago

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()
ywlfff commented 3 years ago

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 -

gospider007 commented 3 years ago

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

kumaraditya303 commented 3 years ago

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)
ywlfff commented 3 years ago

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!!!

yuanzhiyu commented 2 years ago

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?

smart-xing666 commented 1 year ago

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())

https://docs.python.org/3/library/asyncio.html

ElieTaillard commented 10 months ago
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

Hamza5 commented 4 months ago
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.