├── requirements.txt ├── README.md ├── LICENSE ├── submit_record.py ├── extensions.py ├── .gitignore ├── main.py └── network_tools.py /requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.6.0 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Baekjoon Online Judge](http://www.acmicpc.net/) Submitted Code Download Tool 2 | 3 | ## 왜 만들었는가? 4 | 5 | - 시험기간이다. 6 | 7 | ## 설명 8 | 9 | - BOJ에 제출한 코드 중, 맞은 코드를 한번에 다운받는다. 10 | 11 | ## 결과물 12 | 13 | - 문제 별 폴더가 만들어지고, 문제의 번호를 이름으로 파일이 생성됨. (ex. 1000\1000.py) 14 | 15 | - 한 문제를 같은 언어로 여러번 맞았을 경우 가장 좋은(시간, 공간 복잡도가 작은) 코드를 다운로드함. 16 | 17 | - 프로그램을 실행한 폴더에 생성함. 18 | 19 | ## 종속성 20 | 21 | - [python3](https://www.python.org/downloads/) 22 | - [beautifulsoup4](https://www.crummy.com/software/BeautifulSoup/) 23 | 24 | #### Ubuntu 25 | 26 | ```bash 27 | # 패키지 설치 28 | sudo apt install python3-pip 29 | 30 | # beautifulsoup4 설치 31 | pip3 install -r requirements.txt 32 | ``` 33 | 34 | ## 사용법 35 | 36 | ```bash 37 | python3 main.py 38 | ``` 39 | 40 | ### Issue 41 | 42 | - 맞은 코드가 여러 페이지에 걸쳐 있을경우 첫 페이지의 코드만 받음 43 | 44 | ## Update Log 45 | 46 | - 2016/9/6 : 가장 '좋은'코드가 아니라 가장 '안좋은'코드를 선택하던 **치명적인** 문제 해결 47 | - 2016/9/7 : 프로그램이 종료되지 않던 문제 해결, 결과화면 정리 48 | - 2019/1/2 : 페이지 URL 변경으로인한 에러 수정 (thanks to @MilkClouds) 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015 Byeonghun Yoo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /submit_record.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import sys 4 | 5 | __author__ = 'isac322' 6 | 7 | 8 | class SubmitRecord: 9 | def __init__(self, judge_id, mem, time, code_len=None): 10 | try: 11 | self.judge_id = int(judge_id) 12 | self.memory_size = int(mem) 13 | self.time = int(time) 14 | if code_len: 15 | code_len = int(code_len) 16 | self.code_length = code_len 17 | 18 | except ValueError: 19 | print('Problem record error on judge id : {}. Please re-run. If you have same error in same id, ' 20 | 'check BOJ online.'.format(judge_id), 21 | file=sys.stderr) 22 | exit(1) 23 | 24 | def __lt__(self, other): 25 | if self.time == other.time: 26 | if self.memory_size == other.memory_size: 27 | if self.code_length and other.code_length: 28 | return self.code_length < other.code_length 29 | else: 30 | return self.judge_id < other.judge_id 31 | else: 32 | return self.memory_size < other.memory_size 33 | else: 34 | return self.time < other.time 35 | 36 | def __str__(self): 37 | if self.code_length: 38 | return 'ID:{:>8}\tMem:{:>6}KB\tTime:{:>5}MS\tSize:{:>5}B' \ 39 | .format(self.judge_id, self.memory_size, self.time, self.code_length) 40 | else: 41 | return 'ID:{:>8}\tMem:{:>6}KB\tTime:{:>5}MS'.format(self.judge_id, self.memory_size, self.time) 42 | 43 | def __repr__(self): 44 | return self.__str__() 45 | 46 | 47 | if __name__ == '__main__': 48 | print('please run main.py') 49 | -------------------------------------------------------------------------------- /extensions.py: -------------------------------------------------------------------------------- 1 | # coding: UTF-8 2 | 3 | extension_id = { 4 | "Ada": 19, 5 | "Algol 68": 70, 6 | "Assembly (32bit)": 27, 7 | "Assembly (64bit)": 87, 8 | "awk": 21, 9 | "Bash": 5, 10 | "bc": 48, 11 | "Befunge": 71, 12 | "Boo": 46, 13 | "Brainfuck": 23, 14 | "C (Clang)": 59, 15 | "C": 0, 16 | "C# 6.0": 62, 17 | "C++ (Clang)": 60, 18 | "C++": 1, 19 | "C++11 (Clang)": 66, 20 | "C++11": 49, 21 | "C++14 (Clang)": 67, 22 | "C++14": 88, 23 | "C++17 (Clang)": 85, 24 | "C++17": 84, 25 | "C11 (Clang)": 77, 26 | "C11": 75, 27 | "Cobol": 35, 28 | "Cobra": 54, 29 | "D": 29, 30 | "F#": 37, 31 | "Fortran": 13, 32 | "Go": 12, 33 | "Golfscript": 79, 34 | "Haxe": 81, 35 | "Intercal": 47, 36 | "Java (OpenJDK)": 91, 37 | "Java 11": 93, 38 | "Java": 3, 39 | "Kotlin (JVM)": 69, 40 | "Kotlin (Native)": 92, 41 | "LOLCODE": 82, 42 | "Lua": 16, 43 | "Nemerle": 53, 44 | "node.js": 17, 45 | "Objective-C": 10, 46 | "Objective-C++": 64, 47 | "OCaml": 22, 48 | "Pascal": 2, 49 | "Perl": 8, 50 | "PHP": 7, 51 | "Pike": 41, 52 | "PyPy2": 32, 53 | "PyPy3": 73, 54 | "Python 2": 6, 55 | "Python 3": 28, 56 | "R": 72, 57 | "Rhino": 34, 58 | "Ruby 2.5": 68, 59 | "Rust": 44, 60 | "Scheme": 14, 61 | "sed": 43, 62 | "Swift": 74, 63 | "Tcl": 26, 64 | "Text": 58, 65 | "VB.NET 4.0": 63, 66 | "Whitespace": 24, 67 | "아희": 83 68 | } 69 | 70 | extension_name = { 71 | "Ada": 'ada', 72 | "Algol 68": 'a68', 73 | "Assembly (32bit)": '32b.asm', 74 | "Assembly (64bit)": '64b.asm', 75 | "awk": 'awk', 76 | "Bash": 'sh', 77 | "bc": 'bc', 78 | "Befunge": 'befunge.bf', 79 | "Boo": 'boo', 80 | "Brainfuck": 'bf', 81 | "C (Clang)": 'clang.c', 82 | "C": 'c', 83 | "C# 6.0": 'cs', 84 | "C++ (Clang)": 'clang.cpp', 85 | "C++": 'cpp', 86 | "C++11 (Clang)": 'clang_pp11.cpp', 87 | "C++11": 'cpp11.cpp', 88 | "C++14 (Clang)": 'clang_pp14.cpp', 89 | "C++14": 'cpp14.cpp', 90 | "C++17 (Clang)": 'clang_pp17.cpp', 91 | "C++17": 'cpp17.cpp', 92 | "C11 (Clang)": 'clang11.c', 93 | "C11": 'c11.c', 94 | "Clojure": 'clj', 95 | "Cobol": 'cob', 96 | "Cobra": 'cobra', 97 | "D": 'd', 98 | "F#": 'fs', 99 | "Fortran": 'f95', 100 | "FreeBASIC": 'bas', 101 | "Go": 'go', 102 | "Golfscript": 'gs', 103 | "Gosu": 'gsp', 104 | "Haxe": 'haxe.py', 105 | "Intercal": 'i', 106 | "Java (OpenJDK)": 'openjdk.java', 107 | "Java 11": 'j11.java', 108 | "Java": 'java', 109 | "Kotlin (JVM)": 'jvm.kt', 110 | "Kotlin (Native)": 'nat.kt', 111 | "LOLCODE": 'lol', 112 | "Lua": 'lua', 113 | "Nemerle": 'n', 114 | "Nimrod": 'nim', 115 | "node.js": 'js', 116 | "Objective-C": 'm', 117 | "Objective-C++": 'mm', 118 | "OCaml": 'ml', 119 | "Pascal": 'pas', 120 | "Perl": 'pl', 121 | "PHP": 'php', 122 | "Pike": 'pike', 123 | "PyPy2": 'pypy.py', 124 | "PyPy3": 'pypy3.py', 125 | "Python 2": 'py2.py', 126 | "Python 3": 'py3.py', 127 | "R": 'R', 128 | "Rhino": 'rhino.js', 129 | "Ruby 2.5": 'rb', 130 | "Rust": 'rs', 131 | "Scheme": 'scm', 132 | "sed": 'sed', 133 | "SpiderMonkey": 'spider.js', 134 | "Swift": 'swift', 135 | "Tcl": 'tcl', 136 | "Text": 'txt', 137 | "VB.NET 4.0": 'vb', 138 | "Whitespace": 'ws', 139 | "아희": 'aheui' 140 | } 141 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | .static_storage/ 58 | .media/ 59 | local_settings.py 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # Environments 87 | .env 88 | .venv 89 | env/ 90 | venv/ 91 | ENV/ 92 | env.bak/ 93 | venv.bak/ 94 | 95 | # Spyder project settings 96 | .spyderproject 97 | .spyproject 98 | 99 | # Rope project settings 100 | .ropeproject 101 | 102 | # mkdocs documentation 103 | /site 104 | 105 | # mypy 106 | .mypy_cache/ 107 | ### JetBrains template 108 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 109 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 110 | 111 | # User-specific stuff: 112 | .idea/**/workspace.xml 113 | .idea/**/tasks.xml 114 | .idea/dictionaries 115 | 116 | # Sensitive or high-churn files: 117 | .idea/**/dataSources/ 118 | .idea/**/dataSources.ids 119 | .idea/**/dataSources.xml 120 | .idea/**/dataSources.local.xml 121 | .idea/**/sqlDataSources.xml 122 | .idea/**/dynamic.xml 123 | .idea/**/uiDesigner.xml 124 | 125 | # Gradle: 126 | .idea/**/gradle.xml 127 | .idea/**/libraries 128 | 129 | # CMake 130 | cmake-build-debug/ 131 | cmake-build-release/ 132 | 133 | # Mongo Explorer plugin: 134 | .idea/**/mongoSettings.xml 135 | 136 | ## File-based project format: 137 | *.iws 138 | 139 | ## Plugin-specific files: 140 | 141 | # IntelliJ 142 | out/ 143 | 144 | # mpeltonen/sbt-idea plugin 145 | .idea_modules/ 146 | 147 | # JIRA plugin 148 | atlassian-ide-plugin.xml 149 | 150 | # Cursive Clojure plugin 151 | .idea/replstate.xml 152 | 153 | # Crashlytics plugin (for Android Studio and IntelliJ) 154 | com_crashlytics_export_strings.xml 155 | crashlytics.properties 156 | crashlytics-build.properties 157 | fabric.properties 158 | ### Linux template 159 | *~ 160 | 161 | # temporary files which can be created if a process still has a handle open of a deleted file 162 | .fuse_hidden* 163 | 164 | # KDE directory preferences 165 | .directory 166 | 167 | # Linux trash folder which might appear on any partition or disk 168 | .Trash-* 169 | 170 | # .nfs files are created when an open file is removed but is still being accessed 171 | .nfs* 172 | ### Windows template 173 | # Windows thumbnail cache files 174 | Thumbs.db 175 | ehthumbs.db 176 | ehthumbs_vista.db 177 | 178 | # Dump file 179 | *.stackdump 180 | 181 | # Folder config file 182 | [Dd]esktop.ini 183 | 184 | # Recycle Bin used on file shares 185 | $RECYCLE.BIN/ 186 | 187 | # Windows Installer files 188 | *.cab 189 | *.msi 190 | *.msm 191 | *.msp 192 | 193 | # Windows shortcuts 194 | *.lnk 195 | ### VirtualEnv template 196 | # Virtualenv 197 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 198 | [Bb]in 199 | [Ii]nclude 200 | [Ll]ib 201 | [Ll]ib64 202 | [Ll]ocal 203 | [Ss]cripts 204 | pyvenv.cfg 205 | pip-selfcheck.json 206 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # coding: utf-8 3 | 4 | import os 5 | import threading 6 | from getpass import getpass 7 | 8 | import extensions 9 | from network_tools import down_file, get_soup, login 10 | from submit_record import SubmitRecord 11 | 12 | __author__ = 'isac322' 13 | 14 | 15 | def get_solved_problems(sp): 16 | problems = set() 17 | """ 18 | 2021-03-02 아래 DOM 읽어오는 2줄 수정 19 | """ 20 | for tag in sp.find(class_='panel-body').findAll('a'): 21 | problems.add(tag.text) 22 | 23 | return problems 24 | 25 | 26 | working_dir = '' 27 | 28 | 29 | def make_code_file(problem_num, language): 30 | extension = extensions.extension_name[language] 31 | 32 | file_name = problem_num + '.' + extension 33 | directory = os.path.join(working_dir, "{:05d}".format(int(problem_num))) 34 | 35 | return open(os.path.join(directory, file_name), 'w+', encoding='utf-8') 36 | 37 | 38 | screenLock = threading.Lock() 39 | 40 | 41 | def analyze_problem(problem_num): 42 | url = 'https://www.acmicpc.net/status?' 43 | query = {'problem_id': problem_num, 'user_id': user_id, 'result_id': '4', 'language_id': '-1', 'from_mine': '1'} 44 | 45 | for k, v in query.items(): 46 | url += k + '=' + v + '&' 47 | 48 | table = dict() 49 | 50 | page = get_soup(url, query) 51 | rows = page.find('tbody').find_all('tr') 52 | 53 | if len(rows) is 0: 54 | return table 55 | 56 | for row in rows: 57 | column = row.find_all('td') 58 | 59 | language = column[6].text.strip() 60 | 61 | length = None 62 | length_text = column[7].text.strip() 63 | if len(length_text) != 0: 64 | length = length_text.split()[0] 65 | 66 | element = SubmitRecord(judge_id=column[0].text, 67 | mem=column[4].contents[0], 68 | time=column[5].contents[0], 69 | code_len=length) 70 | 71 | if table.get(language) is None: 72 | table[language] = element 73 | else: 74 | table[language] = min(table[language], element) 75 | 76 | problem_name = rows[0].find_all('td')[2].a['title'] 77 | 78 | screenLock.acquire() 79 | print('{:5s} {}'.format(problem_num, problem_name)) 80 | for l, e in table.items(): 81 | print('\t{:10s} {}'.format(l, e)) 82 | screenLock.release() 83 | 84 | return table 85 | 86 | 87 | def analyze_and_make(problem_num): 88 | submitted_codes = analyze_problem(problem_num) 89 | 90 | for language, source_code in submitted_codes.items(): 91 | with make_code_file(problem_num, language) as file: 92 | downloaded = down_file(source_code.judge_id, full_cookie) 93 | file.write(downloaded.decode()) 94 | 95 | """ 96 | Unrated 문제 추가 요망 97 | """ 98 | ignore_list = frozenset(('10947', '9999', '13757')) 99 | 100 | 101 | def get_submitted_files(problems): 102 | for problem_num in problems: 103 | directory = os.path.join(working_dir, "{:05d}".format(int(problem_num))) 104 | 105 | if not os.path.exists(directory): 106 | os.makedirs(directory) 107 | 108 | thread_file_maker = threading.Thread(target=analyze_and_make, args=(problem_num,), daemon=False) 109 | thread_file_maker.start() 110 | 111 | 112 | if __name__ == '__main__': 113 | user_id = input('enter nickname : ') 114 | """ 115 | 2021-03-02 아래 5줄 수정 116 | """ 117 | # user_pw = getpass('enter password : ') 118 | # full_cookie = login(user_id, user_pw) 119 | # print(full_cookie) 120 | full_cookie = input("Please Copy&Paste BOJ Cookie Here. Example: 'a=1; b=2; c=3; d=4;' : ") 121 | print("오래된 언어의 경우 extensions.py를 적절히 수정해 주지 않으면 다운로드 도중 오류가 날 수도 있습니다. Java 8, C++98에서 오류 나는 것을 확인했습니다. 해당 소스코드 외에는 정상적으로 다운로드 잘 됩니다.") 122 | 123 | soup = get_soup('https://acmicpc.net/user/' + user_id) 124 | 125 | working_dir = os.path.join(os.getcwd(), user_id) 126 | 127 | if not os.path.exists(working_dir): 128 | os.makedirs(working_dir) 129 | 130 | problem_set = get_solved_problems(soup) 131 | 132 | get_submitted_files(problem_set) 133 | -------------------------------------------------------------------------------- /network_tools.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import http.client 4 | import re 5 | import urllib.error 6 | from urllib.parse import urlencode 7 | from urllib.request import Request, urlopen 8 | 9 | import sys 10 | from bs4 import BeautifulSoup as Bs 11 | 12 | __author__ = 'isac322' 13 | 14 | 15 | def down_file(judge_id, cookie): 16 | header = { 17 | 'Host': 'www.acmicpc.net', 18 | 'Connection': 'keep-alive', 19 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 20 | 'Origin': 'https://www.acmicpc.net,.', 21 | 'Upgrade-Insecure-Requests': '1', 22 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 23 | 'Chrome/45.0.2454.101 Safari/537.36', 24 | 'Content-Type': 'application/x-www-form-urlencoded', 25 | 'Referer': 'https://www.acmicpc.net/source/{}'.format(judge_id), 26 | 'Accept-Encoding': 'gzip, deflate', 27 | 'Accept-Language': 'ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4', 28 | 'Cookie': cookie + ' _gauges_unique_day=1; _gauges_unique_month=1; _gauges_unique_year=1; _gauges_unique=1; ' 29 | '_ga=GA1.2.1918118912.1444272664' 30 | } 31 | 32 | conn = http.client.HTTPSConnection('www.acmicpc.net') 33 | conn.request('POST', '/source/download/{}'.format(judge_id), None, header) 34 | 35 | response = conn.getresponse() 36 | code = response.read() 37 | 38 | response.close() 39 | 40 | return code 41 | 42 | 43 | def login(user_name, pw): 44 | query = {'login_user_id': user_name, 'login_password': pw} 45 | data = urlencode(query) 46 | header = { 47 | 'Host': 'www.acmicpc.net', 48 | 'Connection': 'keep-alive', 49 | 'Cache-Control': 'max-age=0', 50 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 51 | 'Origin': 'https://www.acmicpc.net', 52 | 'Upgrade-Insecure-Requests': '1', 53 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 54 | 'Chrome/45.0.2454.101 Safari/537.36', 55 | 'Content-Type': 'application/x-www-form-urlencoded', 56 | 'Referer': 'https://www.acmicpc.net/login/?next=/', 57 | 'Accept-Encoding': 'gzip, deflate', 58 | 'Accept-Language': 'ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4', 59 | } 60 | 61 | conn = http.client.HTTPSConnection('www.acmicpc.net') 62 | conn.request('POST', '/signin', data, header) 63 | response = conn.getresponse() 64 | 65 | if 'error' in response.info()['Location']: 66 | print('invalid login') 67 | exit(1) 68 | 69 | cookie = '' 70 | patt = re.compile('^.*((__cfduid|OnlineJudge|acmicpcautologin)[=][\w\d]+;).*$', re.MULTILINE) 71 | 72 | for line in patt.finditer(str(response.info())): 73 | cookie += line.group(1) + ' ' 74 | 75 | response.close() 76 | # print(response.msg) 77 | 78 | return cookie 79 | 80 | 81 | def get_soup(url, query=None): 82 | response = get_response(url, query) 83 | ret = Bs(response, 'html.parser') 84 | response.close() 85 | return ret 86 | 87 | 88 | def get_response(url, query=None, header=None): 89 | if header is None: 90 | user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' \ 91 | 'Chrome/45.0.2454.101 Safari/537.36' 92 | header = { 93 | 'Connection': 'keep-alive', 94 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 95 | 'Upgrade-Insecure-Requests': '1', 96 | 'User-Agent': user_agent, 97 | 'Content-Type': 'application/x-www-form-urlencoded' 98 | } 99 | 100 | data = None 101 | if query is not None: 102 | data = urlencode(query).encode('utf-8') 103 | 104 | req = Request(url, data, header) 105 | 106 | try: 107 | response = urlopen(req) 108 | return response 109 | 110 | except urllib.error.HTTPError: 111 | print('invalid username') 112 | sys.exit(1) 113 | 114 | 115 | if __name__ == '__main__': 116 | print('please run main.py') 117 | --------------------------------------------------------------------------------