>백엔드 개발 >파이썬 튜토리얼 >Python으로 Gmail POPerver와 대화하기

Python으로 Gmail POPerver와 대화하기

PHPz
PHPz원래의
2024-09-08 06:31:431244검색

Talking to a Gmail POPerver with Python

POP는 비교적 오래된 프로토콜입니다. 첫 번째 버전은 1984년에 지정되었습니다. 현재 사용되는 버전인 POP3는 1996년에 지정되었습니다. 이를 시험해 보기 위해 Gmail POP3 서버에 연결하려고 했습니다.

첫 번째 단계는 POP3 설정(어떤 서버에 연결할 것인지, 어떤 포트에 연결할 것인지)을 찾는 것이었습니다. Google이 나를 여기로 이끌었고 그곳에서 다음 정보를 찾았습니다.

pop.gmail.com

SSL 필요: 예

포트: 995

SSL이 필요하다고 언급되어 있습니다. 이것은 내가 25년 전 마지막으로 POP를 다루었을 때 다루었던 것이 아니었습니다. 머리가 아프지 않을까 두려웠지만 결과적으로 전혀 문제가 되지 않았습니다. Python 문서의 도움을 받아 이 코드에 도달했습니다.

import socket
import ssl

hostname = 'pop.gmail.com'
context = ssl.create_default_context()

with socket.create_connection((hostname, 995)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as s:
        print(s.version())

연결되어 사용 중인 SSL 버전 등을 알려줍니다. 대성공! 서버와 대화를 시작할 시간입니다.

POP3에 대한 공식 RFC에서 빌려온 클라이언트와 서버 간의 POP3 대화 예는 다음과 같습니다.

C: <open connection>
S:    +OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>
C:    USER mrose
S:    +OK mrose is a real hoopy frood
C:    PASS secret
S:    +OK mrose's maildrop has 2 messages (320 octets)
C:    STAT
S:    +OK 2 320
C:    LIST
S:    +OK 2 messages (320 octets)
S:    1 120
S:    2 200
S:    .
C:    RETR 1
S:    +OK 120 octets
S:    <the POP3 server sends message 1>
S:    .
C:    QUIT
S:    +OK dewey POP3 server signing off (maildrop empty)
C:  <close connection>

가장 먼저 일어나는 일은 서버가 클라이언트에게 인사말을 보내는 것입니다. 친숙한. 그래서 서버로부터 메시지를 받는 코드를 추가하겠습니다.

소켓에서 데이터 수신을 요청하는 경우 버퍼 크기를 지정해야 합니다. 문서에서는 4096과 같이 2의 거듭제곱을 권장합니다. 서버의 많은 응답이 한꺼번에 전달됩니다. 일부는 그렇지 않습니다. 때로는 서버 읽기 중에 서버의 메시지가 깨질 수 있으며, 앞으로 더 많은 내용이 있어도 버퍼가 최대치까지 채워지지 않을 수 있습니다.

POP3의 경우 메시지가 완전히 들어왔는지 확인하는 방법은 어떤 메시지가 들어오느냐에 따라 다릅니다. 대부분의 경우 서버는 한 줄의 텍스트를 보냅니다. (나중에 다시 살펴보겠지만 각 줄 끝에는 캐리지 리턴과 줄 바꿈 문자가 있습니다.) 훨씬 더 긴 응답을 가질 수 있는 특정 메시지는 완료되었음을 표시하는 다른 방법을 사용합니다. 즉, 한 줄에 마침표를 표시하는 것입니다. 그 자체로.

import socket
import ssl

hostname = 'pop.gmail.com'
context = ssl.create_default_context()

with socket.create_connection((hostname, 995)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as s:
        print(s.version())
        data = s.read(4096)
        print(data)

다시 달리고, 인사를 받습니다. 또 다른 대성공! 줄은 캐리지 리턴 및 줄 바꿈 문자인 "rn"으로 끝납니다.

읽기 메소드에 버퍼 크기를 전달해야 합니다. 그런 다음 서버에서 데이터를 읽을 수 있는 크기의 버퍼를 가지게 됩니다. 하지만 한 번에 얼마나 많은 데이터가 버퍼에 들어올지는 보장할 수 없습니다. 이는 프로토콜이 메시지가 완료되는 시기를 지정하는 방법이 필요하다는 것을 의미합니다. 수많은 전략이 가능합니다. POP는 두 가지를 사용합니다. 모든 메시지에 대해 줄은 rn으로 끝납니다. 짧은(한 줄) 메시지의 경우 이것이 전부입니다. 여러 줄로 구성된 응답의 경우 한 줄에 마침표만 있으면 메시지가 완료되었음을 나타냅니다.

TLSv1.3
b'+OK Gpop ready for requests from 2601:1c0:8301:b590:f408:d66a:3029:16ad dq2mb54750689ivb\r\n'

이제 서버와 대화를 시작해야 합니다. I/O(또는 O/I) 루프를 생성할 시간입니다. 사용자 입력을 받아 서버로 보냅니다. 이런! 문자열을 직접 보낼 수는 없습니다. TypeError가 발생합니다. 메시지를 바이트로 변환해야 합니다. 문자열 encode() 메소드가 이를 수행합니다(utf-8의 기본 인코딩은 잘 작동합니다).

실행할 때만 -- 이런! 내 메시지가 서버로 전송되면 아무 일도 일어나지 않습니다. 클라이언트에서 오는 메시지도 rn으로 끝나야 한다는 사실을 잊어버렸기 때문입니다. 또 다른 작은 조정을 통해 다음과 같은 이점을 얻을 수 있습니다.

import socket
import ssl

hostname = 'pop.gmail.com'
context = ssl.create_default_context()

with socket.create_connection((hostname, 995)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as s:
        print(s.version())
        while True:
            data = s.read(4096)
            print(data)
            msg = input() + "\r\n"
            s.send(msg.encode())

좋습니다. 이제 실제로 로그인을 시도할 수 있습니다!

TLSv1.3
b'+OK Gpop ready for requests from 2601:1c0:8301:b590:f408:d66a:3029:16ad g4mb5147337iow\r\n'
USER grokprogramming
b'+OK send PASS\r\n'
PASS trustno1
b'-ERR [AUTH] Application-specific password required: https://support.google.com/accounts/answer/185833\r\n'

알겠습니다. 해당 링크를 따라가면 애플리케이션 비밀번호를 설정할 수 있는 페이지로 이동합니다. 제가 발견한 잠재적인 걸림돌 중 하나는 제가 아는 한 애플리케이션별 비밀번호를 만들 수 있으려면 귀하의 계정에 2단계 인증이 켜져 있어야 한다는 것입니다. 왜 2024년 경자년에 이중 인증을 켜지 않았을까요? 나는 말할 수 없다. 지금은 그렇습니다.

애플리케이션 비밀번호(공백을 빼주세요)만 있으면 로그인할 수 있어요! 그런 다음 내가 가지고 있는 메시지 수와 결합된 크기를 알려주는 STAT 명령을 실행합니다. 그런 다음 LIST 명령을 실행하면 각 메시지의 ID와 크기가 포함된 메시지 목록이 반환됩니다.

TLSv1.3
b'+OK Gpop ready for requests from 2601:1c0:8301:b590:f408:d66a:3029:16ad e18mb76868856iow\r\n'
USER grokprogramming
b'+OK send PASS\r\n'
PASS baygdsgkmihkckrb
b'+OK Welcome.\r\n'
STAT
b'+OK 263 14191565\r\n'
LIST
b'+OK 263 messages (14191565 bytes)\r\n1 2778\r\n2 2947\r\n3 6558\r\n4 9864\r\n5 35997\r\n6 45462\r\n7 45462\r\n8 63894\r\n9 11487\r\n10 74936\r\n11 74925\r\n12 11632\r\n13 32392\r\n14 74997\r\n15 51961\r\n16 15375\r\n17 46513\r\n18 21519\r\n19 15966\r\n20 27258\r\n21 28503\r\n22 35615\r\n23 86353\r\n24 280'

코드에 버그가 생겼습니다. LIST에 대한 응답은 여러 줄에 걸쳐 있으며 이 경우 여러 버퍼 읽기가 필요합니다. 전체 메시지는 한 줄에 마침표로 끝납니다. 여기서는 메시지에 해당하는 버퍼 하나를 받았습니다. 이제 코드가 루프의 다음 반복으로 진행되고 버퍼에서 다시 읽혀지도록 Return 키를 누르고 빈 메시지를 서버에 보내야 합니다.

사용자가 항상 버퍼에서 다시 읽을지 여부를 선택할 수 있도록 코드를 수정하겠습니다. 또한 최종적으로 서버에서 들어오는 바이트를 디코딩하여 텍스트가 더 보기 좋게 렌더링되도록 하겠습니다.

import socket
import ssl

hostname = 'pop.gmail.com'
context = ssl.create_default_context()

with socket.create_connection((hostname, 995)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as s:
        print(s.version())
        while True:
            data = s.read(4096)
            print(data.decode())
            while input("more? y/[n]: ") == "y":
                data = s.read(4096)
                print(data.decode())
            msg = input("> ") + "\r\n"
            s.send(msg.encode())

이메일 검색 및 연결 해제 메시지 보내기를 포함한 전체 세션은 다음과 같습니다.

> USER grokprogramming
+OK send PASS

more? y/[n]: 
> PASS trustno1
+OK Welcome.

more? y/[n]: 
> STAT
+OK 263 14191565

more? y/[n]: 
> LIST
+OK 263 messages (14191565 bytes)
1 2778
2 2947
3 6558
<...>
260 41300
261 114059
262 174321
263 39206
.

more? y/[n]: 
> RETR 1
+OK message follows
MIME-Version: 1.0
Received: by 10.76.81.230; Thu, 28 Jun 2012 20:21:50 -0700 (PDT)
Date: Thu, 28 Jun 2012 20:21:50 -0700
Message-ID: <CADBp03TWFOKcTOaK_0P7VV2GB+TZsoSd_W4G5nZKKs7pdk6cWQ@mail.gmail.com>
Subject: Customize Gmail with colors and themes
From: Gmail Team <mail-noreply@google.com>
To: Grok Programming <grokprogramming@gmail.com>
Content-Type: multipart/alternative; boundary=e0cb4e385592f8025004c393f2b4

--e0cb4e385592f8025004c393f2b4
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

To spice up your inbox with colors and themes, check out the Themes tab
under Settings.
       Customize Gmail =BB <https://mail.google.com/mail/#settings/themes>

Enjoy!

- The Gmail Team
[image: Themes thumbnails]

Please note that Themes are not available if you're using Internet Explorer
6.0. To take advantage of the latest Gmail features, please upgrade to a
fully supported
browser<http://support.google.com/mail/bin/answer.py?answer=3D6557&hl=3Den&=
utm_source=3Dwel-eml&utm_medium=3Deml&utm_campaign=3Den>
..

--e0cb4e385592f8025004c393f2b4
Content-Type: text/html; charset=ISO-8859-1

more? y/[n]: y

<html>
<font face="Arial, Helvetica, sans-serif">
<p>To spice up your inbox with colors and themes, check out the Themes tab
under Settings.</p>

<table cellpadding="0" cellspacing="0">
  <col style="width: 1px;"/>
  <col/>
  <col style="width: 1px;"/>
  <tr>
    <td></td>
    <td height="1px" style="background-color: #ddd"></td>
    <td></td>
  </tr>
  <tr>
    <td style="background-color: #ddd"></td>
    <td background="https://mail.google.com/mail/images/welcome-button-background.png"
        style="background-color: #ddd; background-repeat: repeat-x;
            padding: 10px; font-size: larger">
          <a href="https://mail.google.com/mail/#settings/themes"
            style="font-weight: bold; color: #000; text-decoration: none;
            display: block;">
      Customize Gmail &#187;</a>
    </td>
    <td style="ba
more? y/[n]: y
ckground-color: #ddd"></td>
  </tr>
 <tr>
    <td></td>
    <td height="1px" style="background-color: #ddd"></td>
    <td></td>
  </tr>
</table>

<p>Enjoy!</p>

<p>- The Gmail Team</p>

<img width="398" height="256" src="https://mail.google.com/mail/images/gmail_themes_2.png" alt="Themes thumbnails" />

<p><font size="-2" color="#999">Please note that Themes are not available if
you're using Internet Explorer 6.0. To take advantage of the latest Gmail
features, please
<a href="http://support.google.com/mail/bin/answer.py?answer=6557&hl=en&utm_source=wel-eml&utm_medium=eml&utm_campaign=en"><font color="#999">
upgrade to a fully supported browser</font></a>.</font></p>

</font>
</html>

--e0cb4e385592f8025004c393f2b4--
.

more? y/[n]: 
> QUIT
+OK Farewell.

more? y/[n]: 
> 

Yet another great success! I was able to log in to the POP3 server and retrieve a message. The script in its current state is pretty flexible, but it requires a lot of work from the user. I'll make a few final tweaks to make interacting with the POP3 server a little easier: if the user starts a message to the server with a "!" it will be stripped out, but the script will read in data from the server until it gets to a period on a line by itself -- in other words, for commands with long responses. No "!" and the script will read in a single line, looking for the \r\n characters.

import socket
import ssl

hostname = 'pop.gmail.com'
context = ssl.create_default_context()

def read_until(s, eom):
    # read into the buffer at least once
    data = s.read(4096)
    # continue reading until end of message
    while data[-len(eom):] != eom:
        data += s.read(4096)
    # return incoming bytes decoded to a string
    return data.decode()

def read_single_line(s):
    return read_until(s, b"\r\n")

def read_muli_line(s):
    return read_until(s, b"\r\n.\r\n")

with socket.create_connection((hostname, 995)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as s:
        print(s.version())
        print(read_single_line(s))
        msg = input("> ")
        # empty msg will close connection
        while msg != "":
            if msg[0] == "!":
                msg = msg[1:]
                long = True
            else:
                long = False
            msg += "\r\n"
            s.send(msg.encode())
            if long:
                print(read_muli_line(s))
            else:
                print(read_single_line(s))
            msg = input("> ")
        s.close()

위 내용은 Python으로 Gmail POPerver와 대화하기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.