Let's create a script that registers with Ideone.com in Python.

Python으로 Ideone.com에 업로드하는 스크립트를 만들어보자.

-** The post may look strange in immature Japanese. ** ** --I apologize first.

At the beginning (개요)

Code sharing (코드의 공유)

Looking at various programming community sites, To show your code, to apply other syntax highlighting, You can see that you are using various Web IDEs. This is because it is not possible to write fonts and text colors with HTML tags one by one.

여러 프프로그래밍 커뮤니티를 둘러보다 보면, 자신의 소스 코드를 보여주기 위해, 혹은 Syntax Highlighting을 적용시키기 위해, 여러가지 Web IDE를 사용하는 것을 발견할 수 있다. 일일히 HTML 태그를 적어가며 폰트를 지정하고, 글자 색을 입힐 수는 없는 노릇이다.

About various Web IDEs (여러 Web IDE 에 대해)

codepad, Ideone, JSFiddle (HTML / CSS / Javascript only) Anyway), Coding Ground (available here on the Web Shell infrastructure), There are a lot of things like Pastebin.com (just make it look unexecutable), For me, ʻideone.com` was the easiest to use. It supports many languages and can actually execute the code. It's a bonus that the syntax highlighting looks good.

codepad, Ideone, JSFiddle(HTML / CSS / Javascript 밖에 못 쓰지만 아무튼), Coding Ground(여기는 Web Shell 기반으로 이용 가능함), Pastebin.com(실행은 부가능하고, 보여주기만 가능) 등 많이 있는데, 나는 ideone.com 쪽이 제일 쓰기 쉬웠던 것 같다. 지원하는 언어도 많을 뿐더러, 실제로 소스 코드를 실행해 볼 수도 있기 때문이다. Syntax Highlight 기능이 보기 좋게 꾸며주는 것은 덤.

However, this is also troublesome (허나, 이것마 저도 귀찮다)

Find a web browser, connect to something like ʻideone.com`, copy my code, and copy the created URL again. Only then can you share the link. It's a really inefficient move. So, let's upload it one after another and get the URL immediately after creating it.

웹 브라우저로 가서, ideone.com 등으로 접속하고, 내 소스 코드를 복붙하여 실행 후, 만들어진 URL을 또 다시 복붙한다. 이제야 링크를 공유할 수 있다. 실로 비효율적인 움직임이다. 그러므로, 작성하고 바로 업로드해서 바로 URL을 얻을 수 있도록 해보자.

Analysis of data flow (데이터 흐름 의 분석)

Simulation for testing (테스트를 위한 시뮬레이션)

For now, let's actually connect to ʻideone.com` and register the code.

우선 실제로 ideone.com 으로 접속해서 소스 코드를 등록해보자.

0001.png

Simply write only test. If you press the Run button here, a URL that can be shared will appear, but that is not the purpose now. Seeing that the address changes, it is likely to use the Form tag and POST transmission. It is necessary to find out the address and data of the destination to which the data is transmitted. Try using the Chrome developer tools for this.

간단하게 test만 적어둔다. 여기서 Run 버튼을 누르면 공유가 가능한 URL이 나올테지만, 지금 목적은 그게 아니다. 주소가 바뀐다는 사실을 보면 Form 태그로 POST 전송을 이용할 가능성이 높다. 데이터를 전송할 목적지의 주소와 데이터를 알아낼 필요가 있다. 이를 위해 Chrome의 개발자 도구를 사용한다.

0002.png

When all the pages have been loaded in this way, clear the network tab and wait. And Run. After that, stop the work with the ʻESC` button at the right time. When that happens,

위와 같이 페이지 로딩이 완전히 끝난 상태에서 Network 탭을 비워둔다. 그리고 Run. 그 후 타이밍에 맞춰 ESC 버튼을 눌러 동작을 정지시킨다. 그러면,

Result (결과)

0003.png

As shown in the picture, the information of the page transmitted POST and the page created to move later appear. If you click on that submit page and look at the header information, it says:

사진처럼 POST 전송한 페이지의 정보와, 앞으로 이동할 만들어진 페이지가 나타난다. 이 submit 페이지를 클릭하여 헤더 정보를 보면 이렇게 적혀있다.

0004.png

When connecting to / ideone / Index / submit /, it receives HTTP 302 and redirects with the Location header. Let's take a look at the data sent from here.

/ideone/Index/submit/에 접속하면 HTTP 302를 받으며 Location 헤더를 통해 리다이렉션한다. 이쪽에서 보낸 데이터를 보자.

0005.png

Various variables were transmitted together. And you can see that the file variable is the code you created earlier. If you try to connect by directly entering the address of the Location header,

여러 변수가 함께 전송되었다. 그리고 file 변수에 아까 적은 소스 코드가 적혀있는 것을 확인할 수 있다. Location 헤더의 주소를 직접 입력하여 접속해보면,

0006.png

Certainly the correct page is created.

제대로 된 페이지가 확실히 만들어져있다.

Organize (정리)

Let's organize it here.

여기까지 일단 정리해보자.

--Run will send the data to the/ ideone / Index / submit /page with POST.

It's simple. Using this information, let's actually write the script code.

매우 심플하다. 이번에는 이 정보를 이용해서 실제로 스크립트 코드를 적어보자.

Creating a script (스크립트 의 작성)

The test environment uses Python 3.5 in ʻUbuntu. You have already installed the requests and lxmllibraries withpip. However, since I originally used Windows 10, I started using Developer Tools of Chrome from Windows. The point is that it can be anywhere.

테스트 환경은 Ubuntu 에서 Python 3.5를 사용한다. 이미 pip로 requests와 lxml 라이브러리를 설치해둔 상태이다. 그러나 원래 Windows 10을 사용하고 있기 때문에 Chrome의 Developer Tool은 Windows에서 사용했다. 말하자면, 어디든 상관 없다는 이야기다.

Test code # 1 (테스트 코드 # 1)

First, let's embody it as it appears in the screenshot.

우선, 스크린 샷에 나와있는 대로 구현해보자.

ideone-test-1.py


from requests import get, post

from pprint import pprint

url = "http://ideone.com/ideone/Index/submit/"

headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" }

data = {
    "p1": "69fc2d31721791fd3912a444035b0ba6",
    "p2": 9,
    "p3": 20,
    "p4": 1710,
    "clone_link": "/",
    "file": "Lorem ipsum dolor sit amet",
    "input": '',
    "syntax": 1,
    "timelimit": 0,
    "note": '',
    "_lang": 116,
    "public": 1,
    "run": 1,
    "Submit": '',
}

rs = post(url, headers=headers, data=data)
print(rs)
pprint(dict(rs.headers))

The content of the code was changed to Lorem ipsum dolor sit amet. I changed the header ʻUser-Agent to that of Chrome` because I don't know if it will be banned from the server. Now run.

소스 코드의 내용은 'Lorem ipsum dolor sit amet' 으로 변경했다. 혹시 서버에서 밴당할까 싶어 User-Agent 헤더도 Chrome의 것으로 바꿨다. 이대로 실행.

<Response [200]>
{'Cache-Control': 'no-store, no-cache, must-revalidate, post-check=0, '
                  'pre-check=0',
 'Connection': 'Keep-Alive',
 'Content-Encoding': 'gzip',
 'Content-Length': '11067',
 'Content-Type': 'text/html',
 'Date': 'Tue, 02 Aug 2016 05:23:03 GMT',
 'Expires': 'Thu, 19 Nov 1981 08:52:00 GMT',
 'Keep-Alive': 'timeout=1, max=90',
 'Pragma': 'no-cache',
 'Server': 'Apache/2.2.22 (Debian)',
 'Set-Cookie': 'JIDEONE=d6e51e6c3ec5a653d7e1be24b87cf050; expires=Thu, '
               '02-Aug-2018 05:23:04 GMT; path=/',
 'Vary': 'Accept-Encoding',
 'X-Powered-By': 'PHP/5.4.15-1'}

No. 200 is coming. Oh, I missed the redirect. Add ʻallow_redirects = True` to the argument of the post function and re-execute.

200번이 온다. 아, 리다이렉트한다는 것을 깜빡했다. post 함수의 인자에 allow_redirects=True 를 추가해서 재실행한다.

<Response [302]>
{'Cache-Control': 'no-store, no-cache, must-revalidate, post-check=0, '
                  'pre-check=0',
 'Connection': 'Keep-Alive',
 'Content-Encoding': 'gzip',
 'Content-Length': '20',
 'Content-Type': 'text/html',
 'Date': 'Tue, 02 Aug 2016 05:54:25 GMT',
 'Expires': 'Thu, 19 Nov 1981 08:52:00 GMT',
 'Keep-Alive': 'timeout=1, max=85',
 'Location': '/',
 'Pragma': 'no-cache',
 'Server': 'Apache/2.2.22 (Debian)',
 'Set-Cookie': 'PHPSESSID=2gg67ij2vmh97d3aapqia8sgt4; path=/',
 'Vary': 'Accept-Encoding',
 'X-Powered-By': 'PHP/5.4.15-1'}

It's finally 302, but the Location header is wrong. Well, it's not unexpected. Probably a cookie or p1 hash issue. This time, let's imitate the behavior of the web browser as it is.

드디어 302가 되었지만, Location 헤더가 맞지 않다. 뭐, 예상 못한 것은 아니다. 아마도 Cookie나 p1 의 해쉬코드가 문제일 것이다. 이번에는 웹 브라우저의 동작을 그대로 따라해보자.

Test code # 2 (테스트 코드 # 2)

Restoration of variable roots (변수 값의 복원)

Before writing the code, there are a few things to check. Variables that are non-zero, such as syntax, public, and run, can be guessed with Boolean variables, but I don't understand the meaning of p1, p2, p3, p4. Let's connect to view-source: http://ideone.com/ and check it out.

소스 코드를 적기 전에 몇 가지 확인해야 할 것이 있다. syntax나 public, run 같이 0 혹은 1로 되어있는 변수들은 Boolean 변수로 추측이 가능하지만, p1, p2, p3, p4 의 의미를 알 수가 없다. 당장 view-source:http://ideone.com/ 에 접속해 조사해보자.

0011.png

You can see it as soon as you scroll down a little. The hashes are directly in the p1 of the ʻinput tags, and the numbers are also entered in the p2andp3. By the way ... Is there anything in p4? ?? ?? You don't have to panic. You should definitely give it somewhere. In such cases, it is often Javascript. Let's search using <script or .js` as a keyword.

스크롤을 조금 내리니 보인다. input 태그 중 p1 에 해쉬코드가 그대로 들어가있고, p2와 p3에는 숫자가 입력되어있다. 그런데... p4에는 아무것도 없어??? 당황할 필요 없다. 분명히 어딘가에서 값을 줄 것이다. 이런 경우에는 대체로 Javascript로 되어 있는 경우가 많다. '<script' 나 '.js' 로 검색해보자.

0013.png

I looked around a lot, but when I was most interested in that ʻideone-common.js`, I opened it.

여기저기 둘러봤는데, 이 'ideone-common.js'가 가장 의심스러워 열어봤더니,

0014.png

I found it.

찾았다.

Variable calculation (변수의 계산)

It's a little easier to see because it's made up of jQuery. It seems that the function protection is used for p4 to factor p1, p2, p3 and substitute the result. However, there seems to be a problem. I can't see the definition of the protection function. And you can see protection in the ʻevalpart that you dragged. According to the factors ofp, a, c, k, e, d`, it seems to be random reading.

jQuery로 되어 있어 조금 보기 쉽다. p4에 protection이라는 함수로 p1, p2, p3를 인자로 넣어 그 결과를 대입하는 것으로 보인다. 그러나 문제가 있어 보인다. protection 함수의 정의가 보이지 않는다. 그리고 위의 드래그한 eval 부분에 protection이 보인다. p,a,c,k,e,d 라는 인자를 보아하니 난독화된 것 같다.

0015.png

When you actually run it with a developer tool, a protection function is created. It's a little interesting, but it's not the purpose now. Let's analyze the protection function.

실제로 개발자 도구에서 실행시켜보니 protection 함수가 만들어진다. 살짝 흥미가 동하지만 지금 목적은 이게 아니다. protection 함수를 분석해보자.

protection.js


function protection($a, $b, $c) {
    var $r = 0;
    $a = mul($c, 2);
    for (var $i = 0; $i < $c; $i++) {
        $r = add($r, _mul($i, $b))
    }
    return $r;
}

The code written in one line is hard to see, so I sent it from Online JavaScript Beautifier. It wasn't a difficult calculation. I don't need $ a at all. In short,

한 줄로 써 있는 소스 코드는 보기 힘들기 때문에 Online JavaScript Beautifier에서 개행했다. 어려운 계산은 아니었다. 심지어 $a는 쓰이지도 않는다. 요약해보면,

protection-summary.js


function protection(a, b, c) {
    var r = 0;
    for (var i = 0; i < c; ++i) r += i * b;
    return r;
}

It will be like this. If you organize the calculation formula itself,

이렇게 된다. 계산식 자체를 정리해보면,

protection-summary2.js


function protection(a, b, c) {
    return b * ((c - 1) * c) / 2;
}

It will also be like this. This is what I need now. I have all the ingredients. Let's write a Python script.

이렇게도 된다. 지금 필요한 것이 이것. 재료가 모였다. Python 스크립트를 쓰자.

Whole script code (전체 스크립트 코드)

ideone-test-2.py


from requests import get, post
from lxml.etree import HTML
namespaces = dict(re="http://exslt.org/regular-expressions")

from pprint import pprint

''' #1:Get cookies.'''
#Receive the index page first and save cookies and so on.
# (먼저 index 페이지를 받아 Cookie 등을 저장)
url = "http://ideone.com/"
headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" }
rs = get(url, headers=headers)
cookies = rs.cookies
root = HTML(rs.content if rs.ok else "<error/>")

data = {
    "file": "Lorem ipsum dolor sit amet",
    "input": '',
    "syntax": 1,
    "timelimit": 0,
    "note": '',
    "public": 1,
    "run": 1,
    "Submit": '',
}

''' #2: p1, p2,Calculate p4 from p3.(p1, p2, p3을 이용해 p4 계산) '''
#Analyze HTML content and p1 with XPath, p2,Get p3.
# (HTML의 내용을 분석하여 XPath를 이용해 p1, p2, p3을 가져온다.)
#for x in root.xpath('//*[@id="p1" or @id="p2" or @id="p3" or @id="p4"]'):
#You can also do this with a regular expression.
# (정규식으로 이렇게도 가능)
for x in root.xpath('//*[re:test(@id,"p[1-3]")]', namespaces=namespaces):
    data[x.get("id")] = x.get("value")

p2 = int(data["p2"])
p3 = int(data["p3"])
p4 = p2 * sum(range(p3))
data["p4"] = str(p4)


''' #3: _Get the lang code list.(_lang 코드 리스트를 획득) '''
#Get the list of language codes directly and use them.
# (언어 코드 리스트를 직접 획득하여 사용해 보자.)
_langs = {}
#for x in root.xpath('//li/*[starts-with(@id,"menu-lang-")]'):
for x in root.xpath('//li/*[re:test(@id,"menu-lang-[0-9]+")]', namespaces=namespaces):
    _langs[x.text] = int(x.get("data-id"))

data["_lang"] = _langs["Python 3"]


url = "http://ideone.com/ideone/Index/submit/"
rs = post(url, headers=headers, cookies=cookies, data=data, allow_redirects=False)
print(rs)
pprint(dict(rs.headers))

-# 1: Saved Cookie to maintain Session.

What is the result of the execution?

실행한 결과는?

<Response [302]>
{'Cache-Control': 'no-store, no-cache, must-revalidate, post-check=0, '
                  'pre-check=0',
 'Connection': 'Keep-Alive',
 'Content-Encoding': 'gzip',
 'Content-Length': '20',
 'Content-Type': 'text/html',
 'Date': 'Fri, 05 Aug 2016 06:40:25 GMT',
 'Expires': 'Thu, 19 Nov 1981 08:52:00 GMT',
 'Keep-Alive': 'timeout=1, max=79',
 'Location': '/UH3fgT',
 'Pragma': 'no-cache',
 'Server': 'Apache/2.2.22 (Debian)',
 'Set-Cookie': 'settings=%7B%22run_lang%22%3A%22116%22%7D; expires=Wed, '
               '01-Feb-2017 06:40:25 GMT; path=/, '
               'settings=%7B%22run_lang%22%3A%22116%22%2C%22run_public%22%3A%221%22%7D; '
               'expires=Wed, 01-Feb-2017 06:40:25 GMT; path=/, '
               'settings=%7B%22run_lang%22%3A%22116%22%2C%22run_public%22%3A%221%22%2C%22run_run%22%3A%221%22%7D; '
               'expires=Wed, 01-Feb-2017 06:40:25 GMT; path=/, '
               'settings=%7B%22run_lang%22%3A%22116%22%2C%22run_public%22%3A%221%22%2C%22run_run%22%3A%221%22%2C%22run_syntax%22%3A%221%22%7D; '
               'expires=Wed, 01-Feb-2017 06:40:25 GMT; path=/, '
               'settings=%7B%22run_lang%22%3A%22116%22%2C%22run_public%22%3A%221%22%2C%22run_run%22%3A%221%22%2C%22run_syntax%22%3A%221%22%2C%22run_timelimit%22%3A%220%22%7D; '
               'expires=Wed, 01-Feb-2017 06:40:25 GMT; path=/',
 'Vary': 'Accept-Encoding',
 'X-Powered-By': 'PHP/5.4.15-1'}

The address is often included in the Location header.

Location 헤더에 주소가 잘 들어가있다.

0016.png

The page also appears normally. Well, it can be used as it is, and it can be said that it is almost completed, but let's organize it so that it can be used with bash.

페이지도 정상적으로 나온다. 뭐, 이대로도 쓸 수 있고, 거의 완성이라고도 할 수 있지만, bash에서 쓸 수 있도록 정리해보자.

Reference code (정리 소스 코드)

ideone.py


from os.path import exists, split, splitext
from sys import argv

from urllib.parse import urlunparse
from requests import get, post
from lxml.etree import HTML
namespaces = dict(re="http://exslt.org/regular-expressions")

from pprint import pprint


extensions = {
    ".c": "C",
    ".cpp": "C++14",
    ".java": "Java7",
    ".pl": "Perl",
    ".php": "PHP",
    ".py": "Python 3",
    ".ruby": "Ruby",
    ".sql": "SQL",
    ".vb": "VB.NET",
    ".go": "GO",
    ".js": "JavaScript (rhino)",
    ".lua": "Lua",
}

def get_ideone(_lang):
    url = "http://ideone.com/"
    headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" }
    rs = get(url, headers=headers)
    root = HTML(rs.content if rs.ok else "<error/>")

    data = {}
    for x in root.xpath('//*[re:test(@id,"p[1-3]")]', namespaces=namespaces):
        data[x.get("id")] = x.get("value")

    p2 = int(data["p2"])
    p3 = int(data["p3"])
    p4 = p2 * sum(range(p3))
    data["p4"] = str(p4)

    _langs = {}
    for x in root.xpath('//li/*[re:test(@id,"menu-lang-[0-9]+")]', namespaces=namespaces):
        _langs[x.text.upper()] = int(x.get("data-id"))
    data["_lang"] = _langs[_lang]

    return rs.cookies, data

def ideone(path, _lang=''):
    d, f = split(path)
    n, e = splitext(f)

    if len(_lang) <= 0:
        _lang = "Text"
        if e.lower() in extensions.keys():
            _lang = extensions[e.lower()]

    cookies, data = get_ideone(_lang.upper())

    data.update({
        "input": '',
        "syntax": 1,
        "timelimit": 0,
        "note": '',
        "public": 1,
        "run": 1,
        "Submit": '',
    })

    with open(path, 'r') as ro:
        data["file"] = ro.read()

    url = "http://ideone.com/ideone/Index/submit/"
    headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" }
    rs = post(url, headers=headers, cookies=cookies, data=data, allow_redirects=False)

    scheme = "http"
    netloc = "ideone.com"
    path = rs.headers["Location"]
    params = ''
    query = ''
    favorite = ''
    return _lang, urlunparse((scheme, netloc, path, params, query, favorite))

def usage(name):
    print("Usage: ./{} <file1> <file2> ...".format(name))

def main(argc, args):
    if argc == 1:
        d, f = split(__file__)
        usage(f)
    else:
        for i, x in enumerate(args[1:]):
            d, f = split(x)
            _lang, url = ideone(x)
            print(" - #{} ({}): {} - {}".format(i + 1, f, _lang, url))

if __name__ == "__main__":
    main(len(argv), argv)

The method of matching file extensions is a bit disappointing, but if you modify the ʻextensions` part in the code and use it, the extension will be automatically recognized and uploaded.

파일 확장자를 매칭시키는 방법이 조금 아쉽지만, 소스 코드 안의 extensions 부분을 수정해서 사용하면 자동적으로 확장자를 인식하여 업로드 가능하다.

Execution result (실행 결과)

root@q:~# python3 ideone.py ideone.py
 - #1 (ideone.py): Python 3 - http://ideone.com/5QJTRR

It is out properly. Let's connect. http://ideone.com/5QJTRR

잘 나온다. 접속해보자.

0021.png

Alright. The code I wrote is included as it is.

오케이. 내가 쓴 소스 소드가 그대로 들어가 있다.

Conclusion (결론)

So far, I've first posted to Qiita on how to waste time efficiently. I wrote it, but honestly I don't know when to use it. I will try using it someday.

여기까지 시간을 효율적으로 낭비하는 방법에 대해 Qiita에 처음으로 기술하였다. 내가 적어놓고도 언제 쓸지 솔직히 모르겠다. 언젠간 쓰겠지.

Recommended Posts

Let's create a script that registers with Ideone.com in Python.
Let's create a free group with Python
Let's create a program that automatically registers ID/PW from CSV to Bitwarden with Python + Selenium
Create a virtual environment with conda in Python
Create a page that loads infinitely with python
Create a new page in confluence with Python
Let's create a customer database that automatically issues a QR code in Python
Create a function in Python
Create a dictionary in Python
Create a directory with python
What's in that variable (when running a Python script)
In Python, create a decorator that dynamically accepts arguments Create a decorator
Create a fake Minecraft server in Python with Quarry
[Piyopiyokai # 1] Let's play with Lambda: Creating a Python script
Create a list in Python with all followers on twitter
Create a DI Container in Python
Let's make a GUI with python.
Create an exe file that works in a Windows environment without Python with PyInstaller
Create a child account for connect with Stripe in Python
Let's create a PRML diagram with Python, Numpy and matplotlib.
Create a virtual environment with Python!
Create a binary file in Python
Create code that outputs "A and pretending B" in python
Create a Kubernetes Operator in Python
Let's make a graph with python! !!
A set of script files that do wordcloud in Python3
Create a random string in Python
Write a batch script with Python3.5 ~
[Python / Django] Create a web API that responds in JSON format
How to create a heatmap with an arbitrary domain in Python
[LINE Messaging API] Create a BOT that connects with someone with Python
Let's create a Python directory structure that you won't regret later
Spiral book in Python! Python with a spiral book! (Chapter 14 ~)
Create a Python function decorator with Class
[Python] A program that creates stairs with #
Let's make a shiritori game with Python
Build a blockchain with Python ① Create a class
Create a simple GUI app in Python
Let's create a virtual environment for Python
[Python] Create a virtual environment with Anaconda
Create a JSON object mapper in Python
Run the Python interpreter in a script
Let's make a voice slowly with Python
Create a star system with Blender 2.80 script
Create a word frequency counter with Python 3.4
[Python] Create a LineBot that runs regularly
[GPS] Create a kml file in Python
A typed world that begins with Python
Let's make a web framework with Python! (1)
Let's make a combination calculation in Python
Let's run a Bash script in Java
Let's make a Twitter Bot with Python!
Let's make a web framework with Python! (2)
Create a record with attachments in KINTONE using the Python requests module
Create a frame with transparent background with tkinter [Python]
Create a Vim + Python test environment in 1 minute
Create a GIF file using Pillow in Python
[Python] Get the files in a folder with Python
Create test data like that with Python (Part 1)
A memo that I wrote a quicksort in Python
Create a standard normal distribution graph in Python