Un exemple simple de communication HTTP utilisant le module socket de python3
Pour faire simple, HTTP est une règle (les données sont appelées charge utile TCP) pour la partie des données TCP (appelée charge utile TCP) qui peut être n'importe quelle donnée car les fichiers HTML sont envoyés par communication TCP (pas nécessairement TCP). (Veuillez suivre ce format), afin que vous puissiez envoyer les données selon ce format avec recv, envoyer.
Ici, continuez en lisant RFC7230, RFC7230 Japanese translation.
Les messages HTTP peuvent être des requêtes client-serveur ou des réponses serveur-client. Les deux peuvent être distingués l'un de l'autre sur cette ligne de départ. La ligne de départ est la ligne de demande dans le premier cas et la [ligne d'état](https: //help.amanohina.) Dans le dernier cas. Il prend le format com / RFC7230-ja.html # p.status-line).
Cette fois, nous utiliserons les champs d'en-tête suivants.
En fait, je n'ai utilisé aucune valeur de champ d'en-tête cette fois.
Ici, les valeurs du code et de la clause d'en-tête sont fixes, mais comme vous le savez, elles sont déterminées dynamiquement.
RETURN_PHRASE = '\r\n'
VERSION = 'HTTP/1.1'
def make_request_line(mtd, request_target):
'''Return http_request-line'''
request_line = \
mtd + ' ' + request_target + ' ' + VERSION + RETURN_PHRASE
return request_line
def make_header_section():
'''Return http_header-section'''
field_values = {
'Host': '192.168.1.200',
'Accept': 'html/plain',
'Connection': 'close',
'User-Agent': 'Suzukaze Browser 1.0'
}
header_sc = 'Host:' + field_values['Host'] + RETURN_PHRASE
header_sc += 'Accept:' + field_values['Accept'] + RETURN_PHRASE
header_sc += 'Connection:' + field_values['Connection'] + RETURN_PHRASE
header_sc += 'User-Agent:' + field_values['User-Agent'] + RETURN_PHRASE
header_sc += RETURN_PHRASE
return header_sc
mtd = 'GET'
request_target = '/'
request_line = make_request_line(mtd, request_target)
header_section = make_header_section()
print(header_section)
La fonction make_request_line (méthode, cible de la requête) -> ligne de requête
renvoie:
GET / HTTP/1.1
La fonction make_header_section () -> header section
renvoie:
Host:192.168.1.200
Accept:html/plain
Connection:close
User-Agent:Suzukaze Browser 1.0
Ligne blanche
Le cordon est fixé à 200. Donc, «reason_phrases» est superflu mais ajouté pour l'atmosphère.
RETURN_PHRASE = '\r\n'
VERSION = 'HTTP/1.1'
def make_status_line():
'''Return http_staus_line'''
reason_phrases = {
'200': 'OK',
'403': 'Forbidden',
'404': 'Not Found',
'405': 'Method Not Allowed'
}
status_line = \
VERSION + ' ' + '200' + ' ' + reason_phrases['200'] + RETURN_PHRASE
return status_line
def make_header_section(content_length):
'''Return http_header_section'''
field_values = {
'Server': 'Suzukaze Server 1.0',
'Date': datetime.now(timezone.utc).strftime(r'%a, %d %b %Y %X %Z'),
'Connection': 'close',
'Content-Type': 'html/plain',
'Content-Length': content_length
}
header_sc = 'Date:' + field_values['Date'] + RETURN_PHRASE
header_sc += 'Server:' + field_values['Server'] + RETURN_PHRASE
header_sc += 'Connection' + field_values['Connection'] + RETURN_PHRASE
header_sc += 'Content-Type' + field_values['Content-Type'] + RETURN_PHRASE
header_sc += 'Content-Length' + field_values['Cotent-Length'] + RETURN_PHRASE
header_sc += RETURN_PHRASE
return header_sc
def read_index_html():
'''Return index.html and that length'''
with open('index.html', 'rb') as f:
index_html = f.read()
index_len = len(index_html)
return index_html, index_len
status_line = make_status_line()
index_html, index_len = read_index_html()
content_length = index_len
header_section = make_header_section(content_length)
make_status_line () -> status line
renvoie:HTTP/1.1 200 OK
make_header_section (Content-Length value) -> header section
renvoie:Date:Mon, 00 Nov 2020 00:00:00 UTC
Server:Suzukaze Server 1.0
Connection:close
Content-Type:html/plain
Content-Length:1224
Ligne blanche
read_index_html () -> le contenu de index.html, la longueur de index.html
renvoie:b"<!DOCTYPE html>\n<head>\n <meta charset='utf-8'>\n</head>\n<style>\n*
(Abréviation)
ab\xe3\x81\xa4\xe3\x81\x84\xe3\x81\xa6</li>\n <li>\xe5\x95\x8f\xe3\x81\x84\xe5\x90\x88\xe3\x82\x8f\xe3\x81\x9b</li>\n </ul>\n </footer>\n </div>\n</body>\n"
1224
Dans ce code, nous savons que la requête est un '/', nous n'avons donc pas besoin d'une cible de requête, et nous fermons également la connexion, nous n'avons donc pas les informations dont le serveur a besoin après tout. Ainsi, la ligne de demande et la clause d'en-tête sont transmises.
Même si nous disons que nous allons implémenter le corps du message côté serveur, nous avons déjà lu index.html comme une chaîne d'octets, donc nous l'attachons simplement à la fin de la clause d'en-tête côté serveur. Il n'y a pas de corps de message côté client.
Il convient de noter ici que la ligne de départ et la clause d'en-tête sont des chaînes de caractères Unicode (type str). Puisqu'il est nécessaire de créer une chaîne d'octets (objet de type octet) à envoyer avec une socket, convertissez-la.
mtd = 'GET'
request_target = '/'
request_line = make_request_line(mtd, request_target)
header_section = make_header_section()
http_msg = \
bytes(request_line, 'utf-8') + bytes(request_line, 'utf-8')
status_line = make_status_line()
index_html, index_len = read_index_html()
msg_body = index_html
content_length = str(index_len)
header_section = make_header_section(content_length)
http_msg = \
bytes(status_line, 'utf-8') + bytes(header_section, 'utf-8') + msg_body
Il ne vous reste plus qu'à vous envoyer ce message HTTP.
Le code ici est basé sur la communication TCP utilisant le module Socket-Python3.
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(SERVER_ADDRESS)
sock.listen(0)
try:
conn_sock, client_address = sock.accept()
print_msg('ac', 'The connection accepted.')
print_msg('i', '{}:{} --------> {}:{}'
.format(client_address[0], client_address[1],
SERVER_ADDRESS[0], SERVER_ADDRESS[1]))
# Receiving the request
recd_data = b''
while True:
data = conn_sock.recv(32)
if not data:
break
recd_data += data
print_msg('i', 'Total received: {}'.format(recd_data))
# Sending the response
http_msg = make_http_msg()
remaining = len(http_msg)
res = 0
while remaining > 0:
res = conn_sock.send(http_msg[res:])
remaining -= res
finally:
conn_sock.shutdown(socket.SHUT_WR)
conn_sock.close()
print_msg('cl', 'The connection closed.')
Vous pouvez déclarer que nous n'enverrons plus de données côté serveur en fermant après l'envoi de la demande. (Les octets vides sont envoyés au niveau du socket, de sorte que le côté serveur peut détecter la fin de la demande en recevant un octet vide.)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect(SERVER_ADDRESS)
print_msg('cn', 'The connection accepted')
# Sending the request
http_msg = make_http_msg()
remaining = len(make_http_msg())
res = 0
while remaining > 0:
res = sock.send(http_msg[res:])
remaining -= res
sock.shutdown(socket.SHUT_WR)
# Receiving the response
recd_data = b''
while True:
data = sock.recv(32)
print(data)
if not data:
break
recd_data += data
finally:
sock.close()
print_msg('cl', 'The connection closed.')
Pour le moment, le côté client doit retirer les deux suivants.
STS_LINE_PTN = b'''
(?P<version>HTTP/[0-9][.][0-9])[ ]
(?P<status_code>[0-9][0-9][0-9])[ ]
(?P<reason>([!\"#$%&\'()*+,-./0-9:;<=>?@A-Z[\\]^_`a-z{|}~ ]|\t)+)
([\r][\n])
'''
re.VERBOSE
au moment de la compilation. Cela ignorera les espaces qui ne sont pas dans la classe de caractères []
.Il est retiré comme suit.
{'version': b'HTTP/1.1', 'status_code': b'200', 'reason': b'OK'}
b '\ r \ n \ r \ n'
et utilisez cet index comme début de [début: fin]
. Pour générer une nouvelle chaîne d'octets (uniquement pour le corps du message). Décodez-le ensuite pour obtenir le code html original. Autrement dit, il est égal à la chaîne de caractères suivante (html_source) côté serveur.with open('sample.html', 'r') as f:
html_source = f.read()
Jusqu'à présent, aucune information n'est requise côté serveur.
Ici, nous créons une simple visionneuse HTML en utilisant Pyqt5. L'interface utilisateur est créée à l'aide d'un concepteur, convertie en code Python3 avec PyQt5 UI code generator 5.15.1
, et le code est falsifié. À l'origine, la logique ajoutée par l'utilisateur est écrite dans une classe créée séparément afin qu'il n'y ait pas de problème même si l'interface utilisateur est réécrite, mais cette fois, l'interface utilisateur n'est pas réécrite, alors définissez-la telle quelle. Le navigateur ressemble à ceci: Importez ce module avec le module principal pour la communication par socket.
browser_app.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWebEngineWidgets import QWebEngineView as QWebView
class Ui_dialog(object):
def setupUi(self, dialog, html_source):
dialog.setObjectName('dialog')
dialog.resize(1000, 650)
self.stackedWidget = QtWidgets.QStackedWidget(dialog)
self.stackedWidget.setGeometry(QtCore.QRect(10, 50, 980, 590))
self.stackedWidget.setObjectName('stackedWidget')
self.page_1 = QtWidgets.QWidget()
self.page_1.setObjectName('page_1')
self.webView = QWebView(self.page_1)
self.webView.setGeometry(QtCore.QRect(0, 0, 980, 590))
self.webView.setObjectName('webView')
self.stackedWidget.addWidget(self.page_1)
self.page_2 = QtWidgets.QWidget()
self.page_2.setObjectName('page_2')
self.page_2_title = QtWidgets.QLabel(self.page_2)
self.page_2_title.setGeometry(QtCore.QRect(370, 60, 240, 50))
font = QtGui.QFont()
font.setPointSize(24)
self.page_2_title.setFont(font)
self.page_2_title.setObjectName('page_2_title')
self.page_2_label1 = QtWidgets.QLabel(self.page_2)
self.page_2_label1.setGeometry(QtCore.QRect(435, 230, 110, 22))
font = QtGui.QFont()
font.setPointSize(16)
self.page_2_label1.setFont(font)
self.page_2_label1.setObjectName('page_2_label1')
self.page_2_ip = QtWidgets.QLineEdit(self.page_2)
self.page_2_ip.setGeometry(QtCore.QRect(435, 262, 110, 28))
font = QtGui.QFont()
font.setPointSize(12)
self.page_2_ip.setFont(font)
self.page_2_ip.setObjectName('page_2_ip')
self.page_2_label2 = QtWidgets.QLabel(self.page_2)
self.page_2_label2.setGeometry(QtCore.QRect(468, 310, 42, 16))
font = QtGui.QFont()
font.setPointSize(16)
self.page_2_label2.setFont(font)
self.page_2_label2.setObjectName('page_2_label2')
self.page_2_url = QtWidgets.QLineEdit(self.page_2)
self.page_2_url.setGeometry(QtCore.QRect(335, 336, 310, 28))
font = QtGui.QFont()
font.setPointSize(12)
self.page_2_url.setFont(font)
self.page_2_url.setObjectName('page_2_url')
self.page_2_save = QtWidgets.QPushButton(self.page_2)
self.page_2_save.setGeometry(QtCore.QRect(425, 384, 130, 40))
font = QtGui.QFont()
font.setPointSize(18)
self.page_2_save.setFont(font)
self.page_2_save.setObjectName('page_2_save')
self.stackedWidget.addWidget(self.page_2)
self.url_lineEdit = QtWidgets.QLineEdit(dialog)
self.url_lineEdit.setGeometry(QtCore.QRect(10, 10, 666, 30))
font = QtGui.QFont()
font.setPointSize(12)
self.url_lineEdit.setFont(font)
self.url_lineEdit.setObjectName('url_lineEdit')
self.search_btn = QtWidgets.QPushButton(dialog)
self.search_btn.setGeometry(QtCore.QRect(670, 10, 90, 30))
font = QtGui.QFont()
font.setPointSize(12)
self.search_btn.setFont(font)
self.search_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
self.search_btn.setObjectName('search_btn')
self.config_btn = QtWidgets.QPushButton(dialog)
self.config_btn.setGeometry(QtCore.QRect(780, 10, 60, 30))
font = QtGui.QFont()
font.setPointSize(10)
self.config_btn.setFont(font)
self.config_btn.setObjectName('config_btn')
# ----------------------------------------------
self.url_lineEdit.setText('en préparation')
self.page_2_title.setText('Setting the DNS')
self.page_2_label1.setText('IP Address')
self.page_2_ip.setText('en préparation')
self.page_2_label2.setText('URL')
self.page_2_url.setText('en préparation')
self.webView.setHtml(html_source)
# ----------------------------------------------
QtCore.QMetaObject.connectSlotsByName(dialog)
def mybrowser(html_source):
app = QtWidgets.QApplication(sys.argv)
dialog = QtWidgets.QDialog()
ui = Ui_dialog()
ui.setupUi(dialog, html_source)
dialog.show()
sys.exit(app.exec_())
Placez-le dans le même répertoire, importez-le côté client et appelez-le comme suit.
status_dict, res_msg = status_line_parser(recd_data)
if status_dict['status_code'].decode() == '200':
html_lines = get_msg_body(res_msg)
html_lines = html_lines.decode()
mybrowser(html_lines)
ensuite
Il est affiché comme ceci.
client.py
client.py
import socket
import re
from colored_print import print_msg
from browser_app import mybrowser
# Request
SERVER_ADDRESS = ('192.168.1.201', 8000)
RETURN_PHRASE = '\r\n'
VERSION = 'HTTP/1.1'
def make_request_line(mtd, request_target):
'''Return http_request-line'''
request_line = \
mtd + ' ' + request_target + ' ' + VERSION + RETURN_PHRASE
return request_line
def make_header_section():
'''Return http_header-section'''
field_values = {
'Host': '192.168.1.200',
'Accept': 'html/plain',
'Connection': 'close',
'User-Agent': 'Suzukaze-Browser-1.0'
}
header_sc = 'Host:' + field_values['Host'] + RETURN_PHRASE
header_sc += 'Accept:' + field_values['Accept'] + RETURN_PHRASE
header_sc += 'Connection:' + field_values['Connection'] + RETURN_PHRASE
header_sc += 'User-Agent:' + field_values['User-Agent'] + RETURN_PHRASE
header_sc += RETURN_PHRASE
return header_sc
def make_http_msg():
'''Return http-message'''
mtd = 'GET'
request_target = '/'
request_line = make_request_line(mtd, request_target)
header_section = make_header_section()
http_msg = \
bytes(request_line, 'utf-8') + bytes(header_section, 'utf-8')
return http_msg
# Response
def status_line_parser(msg_lines):
'''Return dict of status line and http_msg except status line'''
STS_LINE_PTN = b'''
(?P<version>HTTP/[0-9][.][0-9])[ ]
(?P<status_code>[0-9][0-9][0-9])[ ]
(?P<reason>([!\"#$%&\'()*+,-./0-9:;<=>?@A-Z[\\]^_`a-z{|}~ ]|\t)+)
([\r][\n])
'''
p_sts_line = re.compile(STS_LINE_PTN, re.VERBOSE)
m_sts_line = p_sts_line.match(msg_lines)
if m_sts_line is not None:
sts_line_dict = m_sts_line.groupdict()
return sts_line_dict, msg_lines[m_sts_line.end():]
def get_msg_body(msg_lines):
'''Return a message body'''
end = msg_lines.rfind(b'\r\n\r\n')
print(end)
if end is not -1:
return msg_lines[end:]
else:
raise ValueError('msg_lines is a invalid format')
########################################################################
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect(SERVER_ADDRESS)
print_msg('cn', 'The connection accepted')
# Sending the request
http_msg = make_http_msg()
remaining = len(make_http_msg())
res = 0
while remaining > 0:
res = sock.send(http_msg[res:])
remaining -= res
sock.shutdown(socket.SHUT_WR)
# Receiving the response
recd_data = b''
while True:
data = sock.recv(32)
print(data)
if not data:
break
recd_data += data
finally:
sock.close()
print_msg('cl', 'The connection closed.')
status_dict, res_msg = status_line_parser(recd_data)
if status_dict['status_code'].decode() == '200':
html_lines = get_msg_body(res_msg)
html_lines = html_lines.decode()
mybrowser(html_lines)
server.py
server.py
import socket
from datetime import datetime, timezone
from colored_print import print_msg
# Response
SERVER_ADDRESS = ('192.168.1.12', 8000)
RETURN_PHRASE = '\r\n'
VERSION = 'HTTP/1.1'
def make_status_line():
'''Return http_staus-line'''
reason_phrases = {
'200': 'OK',
'403': 'Forbidden',
'404': 'Not Found',
'405': 'Method Not Allowed'
}
status_line = \
VERSION + ' ' + '200' + ' ' + reason_phrases['200'] + RETURN_PHRASE
return status_line
def make_header_section(content_length):
'''Return http_header-section'''
field_values = {
'Server': 'Suzukaze Server 1.0',
'Date': datetime.now(timezone.utc).strftime(r'%a, %d %b %Y %X %Z'),
'Connection': 'close',
'Content-Type': 'html/plain',
'Content-Length': content_length
}
header_sc = 'Date:' + field_values['Date'] + RETURN_PHRASE
header_sc += 'Server:' + field_values['Server'] + RETURN_PHRASE
header_sc += 'Connection:' + field_values['Connection'] + RETURN_PHRASE
header_sc += 'Content-Type:' + field_values['Content-Type'] + RETURN_PHRASE
header_sc += 'Content-Length:' + field_values['Content-Length'] + RETURN_PHRASE
header_sc += RETURN_PHRASE
return header_sc
def read_index_html():
'''Return index.html and that length'''
with open('index.html', 'rb') as f:
index_html = f.read()
index_len = len(index_html)
return index_html, index_len
def make_http_msg():
status_line = make_status_line()
index_html, index_len = read_index_html()
msg_body = index_html
content_length = str(index_len)
header_section = make_header_section(content_length)
http_msg = \
bytes(status_line, 'utf-8') + bytes(header_section, 'utf-8') + msg_body
return http_msg
###############################################################
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(SERVER_ADDRESS)
sock.listen(0)
try:
conn_sock, client_address = sock.accept()
print_msg('ac', 'The connection accepted.')
print_msg('i', '{}:{} --------> {}:{}'
.format(client_address[0], client_address[1],
SERVER_ADDRESS[0], SERVER_ADDRESS[1]))
# Receiving the request
recd_data = b''
while True:
data = conn_sock.recv(32)
if not data:
break
recd_data += data
print_msg('i', 'Total received: {}'.format(recd_data))
# Sending the response
http_msg = make_http_msg()
remaining = len(http_msg)
res = 0
while remaining > 0:
res = conn_sock.send(http_msg[res:])
remaining -= res
finally:
conn_sock.shutdown(socket.SHUT_WR)
conn_sock.close()
print_msg('cl', 'The connection closed.')
colored_print.py
colored_print.py
from colorama import Fore, Style
def print_msg(header, msg):
'''header are i that is INFO or e that is ERROR'''
if header == 'i':
print(Fore.GREEN + '[INFO]',
Style.RESET_ALL + msg)
elif header == 'e':
print(Fore.RED + '[ERROR]',
Style.RESET_ALL + msg)
elif header == 'ac':
print(Fore.BLUE + '[ACCEPT]',
Style.RESET_ALL + msg)
elif header == 'cn':
print(Fore.BLUE + '[CONNECT]',
Style.RESET_ALL + msg)
elif header == 'cl':
print(Fore.BLUE + '[CLOSE]',
Style.RESET_ALL + msg)
else:
print(Fore.RED + 'ERROR: header is an invalid value.'
+ Style.RESET_ALL)
browser_app.py
browser_app.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWebEngineWidgets import QWebEngineView as QWebView
class Ui_dialog(object):
def setupUi(self, dialog, html_source):
dialog.setObjectName('dialog')
dialog.resize(1000, 650)
self.stackedWidget = QtWidgets.QStackedWidget(dialog)
self.stackedWidget.setGeometry(QtCore.QRect(10, 50, 980, 590))
self.stackedWidget.setObjectName('stackedWidget')
self.page_1 = QtWidgets.QWidget()
self.page_1.setObjectName('page_1')
self.webView = QWebView(self.page_1)
self.webView.setGeometry(QtCore.QRect(0, 0, 980, 590))
self.webView.setObjectName('webView')
self.stackedWidget.addWidget(self.page_1)
self.page_2 = QtWidgets.QWidget()
self.page_2.setObjectName('page_2')
self.page_2_title = QtWidgets.QLabel(self.page_2)
self.page_2_title.setGeometry(QtCore.QRect(370, 60, 240, 50))
font = QtGui.QFont()
font.setPointSize(24)
self.page_2_title.setFont(font)
self.page_2_title.setObjectName('page_2_title')
self.page_2_label1 = QtWidgets.QLabel(self.page_2)
self.page_2_label1.setGeometry(QtCore.QRect(435, 230, 110, 22))
font = QtGui.QFont()
font.setPointSize(16)
self.page_2_label1.setFont(font)
self.page_2_label1.setObjectName('page_2_label1')
self.page_2_ip = QtWidgets.QLineEdit(self.page_2)
self.page_2_ip.setGeometry(QtCore.QRect(435, 262, 110, 28))
font = QtGui.QFont()
font.setPointSize(12)
self.page_2_ip.setFont(font)
self.page_2_ip.setObjectName('page_2_ip')
self.page_2_label2 = QtWidgets.QLabel(self.page_2)
self.page_2_label2.setGeometry(QtCore.QRect(468, 310, 42, 16))
font = QtGui.QFont()
font.setPointSize(16)
self.page_2_label2.setFont(font)
self.page_2_label2.setObjectName('page_2_label2')
self.page_2_url = QtWidgets.QLineEdit(self.page_2)
self.page_2_url.setGeometry(QtCore.QRect(335, 336, 310, 28))
font = QtGui.QFont()
font.setPointSize(12)
self.page_2_url.setFont(font)
self.page_2_url.setObjectName('page_2_url')
self.page_2_save = QtWidgets.QPushButton(self.page_2)
self.page_2_save.setGeometry(QtCore.QRect(425, 384, 130, 40))
font = QtGui.QFont()
font.setPointSize(18)
self.page_2_save.setFont(font)
self.page_2_save.setObjectName('page_2_save')
self.stackedWidget.addWidget(self.page_2)
self.url_lineEdit = QtWidgets.QLineEdit(dialog)
self.url_lineEdit.setGeometry(QtCore.QRect(10, 10, 666, 30))
font = QtGui.QFont()
font.setPointSize(12)
self.url_lineEdit.setFont(font)
self.url_lineEdit.setObjectName('url_lineEdit')
self.search_btn = QtWidgets.QPushButton(dialog)
self.search_btn.setGeometry(QtCore.QRect(670, 10, 90, 30))
font = QtGui.QFont()
font.setPointSize(12)
self.search_btn.setFont(font)
self.search_btn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
self.search_btn.setObjectName('search_btn')
self.config_btn = QtWidgets.QPushButton(dialog)
self.config_btn.setGeometry(QtCore.QRect(780, 10, 60, 30))
font = QtGui.QFont()
font.setPointSize(10)
self.config_btn.setFont(font)
self.config_btn.setObjectName('config_btn')
# ----------------------------------------------
self.url_lineEdit.setText('en préparation')
self.page_2_title.setText('Setting the DNS')
self.page_2_label1.setText('IP Address')
self.page_2_ip.setText('en préparation')
self.page_2_label2.setText('URL')
self.page_2_url.setText('en préparation')
self.webView.setHtml(html_source)
# ----------------------------------------------
QtCore.QMetaObject.connectSlotsByName(dialog)
def mybrowser(html_source):
app = QtWidgets.QApplication(sys.argv)
dialog = QtWidgets.QDialog()
ui = Ui_dialog()
ui.setupUi(dialog, html_source)
dialog.show()
sys.exit(app.exec_())