Skip to content

Commit c6ece2b

Browse files
committed
Merge branch 'pull/6'
# Conflicts: # .gitignore
2 parents f867fb5 + c8a3314 commit c6ece2b

File tree

3 files changed

+74
-45
lines changed

3 files changed

+74
-45
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,6 @@ target/
6262

6363
.idea
6464
.vscode
65+
*.iml
6566

6667
# Created by .ignore support plugin (hsz.mobi)

README.md

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,36 @@
33

44
## What is it?
55

6-
A Python 3 script to __automate the download of SQL backups via a [phpMyAdmin](https://www.phpmyadmin.net/) web interface__.
6+
A Python 3 script to __automate the download of SQL backups via a
7+
[phpMyAdmin](https://www.phpmyadmin.net/) web interface__.
78

8-
This is useful when your web hosting provider does not grant you access the the console (for `mysqldump`) but you want to automate the backup of your database (without having to manually use the browser).
9+
This is useful when your web hosting provider does not grant you access to a console (for `mysqldump`) but
10+
you want to automate the backup of your database (without having to manually use the browser).
911

10-
It has been tested with Python 3.4+ on Linux and Windows and phpMyAdmin 4.3.6, 4.5.4.1 and 4.7.0-dev
12+
It has been tested with Python 3.4+ on Linux and Windows and the following versions of phpMyAdmin:
13+
`4.3.x - 4.8.x, 5.0.0`
1114

12-
_Note_: The web interface of phpMyAdmin may change in the future and break this script. Please file a bug report (including your version of phpMyAdmin) if you encounter this issue.
15+
_Note_: The web interface of phpMyAdmin may change in the future and break this script. Please file a bug report
16+
(including your version of phpMyAdmin) if you encounter this issue.
1317

1418
## Usage
1519

1620
usage: phpmyadmin_sql_backup.py [-h] [-o OUTPUT_DIRECTORY] [-p]
1721
[-e EXCLUDE_DBS]
18-
[--compression {none,zip,gzip}]
22+
[--compression {none,zip,gzip,bzip2}]
1923
[--basename BASENAME] [--timeout TIMEOUT]
2024
[--overwrite-existing]
2125
[--prefix-format PREFIX_FORMAT] [--dry-run]
26+
[--http-auth HTTP_AUTH]
2227
URL USERNAME PASSWORD
23-
28+
2429
Automates the download of SQL dump backups via a phpMyAdmin web interface.
25-
30+
2631
positional arguments:
2732
URL phpMyAdmin login page url
2833
USERNAME phpMyAdmin login username
2934
PASSWORD phpMyAdmin login password
30-
35+
3136
optional arguments:
3237
-h, --help show this help message and exit
3338
-o OUTPUT_DIRECTORY, --output-directory OUTPUT_DIRECTORY
@@ -38,7 +43,7 @@ _Note_: The web interface of phpMyAdmin may change in the future and break this
3843
-e EXCLUDE_DBS, --exclude-dbs EXCLUDE_DBS
3944
comma-separated list of database names to exclude from
4045
the dump
41-
--compression {none,zip,gzip}
46+
--compression {none,zip,gzip,bzip2}
4247
compression method for the output file - must be
4348
supported by the server (default: none)
4449
--basename BASENAME the desired basename (without extension) of the SQL
@@ -54,11 +59,12 @@ _Note_: The web interface of phpMyAdmin may change in the future and break this
5459
format. Must be used with --prepend-date to be in
5560
effect
5661
--dry-run dry run, do not actually download any file
57-
--http-auth Basic http authentication, using format
62+
--http-auth HTTP_AUTH
63+
Basic HTTP authentication, using format
5864
"username:password"
59-
60-
Written by Christoph Haunschmidt, version: 2016-03-12.3
61-
65+
66+
Written by Christoph Haunschmidt et al., version: 2019-05-07.0
67+
6268
### Examples
6369

6470
phpmyadmin_sql_backup.py "http://www.example.com/phpmyadmin/" your_user your_password
@@ -69,12 +75,17 @@ Downloads a plain text `.sql` backup of all databases to the current working dir
6975

7076
phpmyadmin_sql_backup.py "http://www.example.com/phpmyadmin/" your_user your_password --exclude-dbs mydb2,mydb4 --prepend-date --basename example_dump --output-directory /tmp --compression zip
7177

72-
Downloads a zipped dump with databases `mydb2` & `mydb4` excluded, the base name `example_dump` and a prepended UTC date / time to the directory `/tmp`, e.g. `/tmp/2016-03-11--15-19-04-UTC_example_dump.zip`.
78+
Downloads a zipped dump with databases `mydb2` & `mydb4` excluded, the base name `example_dump` and a prepended
79+
UTC date / time to the directory `/tmp`, e.g. `/tmp/2016-03-11--15-19-04-UTC_example_dump.zip`.
7380

7481
## Requirements
7582

7683
- A [Python 3.4+](https://www.python.org/) installation on your system
77-
- [Grab - python web-scraping framework](http://grablib.org/): Install via `pip install -U Grab` or see the [installation instructions](http://docs.grablib.org/en/latest/usage/installation.html) if you run into problems.
84+
- [Grab - python web-scraping framework](http://grablib.org/): Install via `pip install -U Grab` or see
85+
the [installation instructions](http://docs.grablib.org/en/latest/usage/installation.html) if you run into problems.
86+
87+
__Note for Windows users__: while it is possible to install the requirements natively, it is often easier to use the
88+
[Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10) if you are using Windows 10
7889

7990
## Changelog
8091

@@ -84,6 +95,9 @@ Currently, there is no changelog; the best option at the moment is to read the c
8495

8596
[GNU GPL3](https://www.gnu.org/licenses/gpl-3.0.html)
8697

87-
## Author
98+
## Contributors
8899

89-
- Christoph Haunschmidt
100+
- Christoph Haunschmidt (original author)
101+
- Jonas Bengtsson (older frame-based phpMyAdmin support)
102+
- Benoît Courtine (HTTP Auth)
103+

phpmyadmin_sql_backup.py

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,35 @@
2121
# tested on Python 3.4+
2222
# requires: grab (http://grablib.org/)
2323
#
24-
# Christoph Haunschmidt 2016-03
24+
# Christoph Haunschmidt, started 2016-03
2525

2626
import argparse
27+
import datetime
2728
import os
2829
import re
2930
import sys
30-
import datetime
3131

3232
import grab
3333

34-
35-
__version__ = '2016-03-12.3'
34+
__version__ = '2019-05-07.0'
3635

3736
CONTENT_DISPOSITION_FILENAME_RE = re.compile(r'^.*filename="(?P<filename>[^"]+)".*$')
3837
DEFAULT_PREFIX_FORMAT = r'%Y-%m-%d--%H-%M-%S-UTC_'
3938

4039

40+
def is_login_successful(g):
41+
return g.doc.text_search("frame_content") or g.doc.text_search("server_export.php")
42+
43+
44+
def open_frame_if_phpmyadmin_3(g):
45+
frame_url_selector = g.doc.select("id('frame_content')/@src")
46+
if frame_url_selector.exists():
47+
g.go(frame_url_selector.text())
48+
49+
4150
def download_sql_backup(url, user, password, dry_run=False, overwrite_existing=False, prepend_date=True, basename=None,
4251
output_directory=os.getcwd(), exclude_dbs=None, compression='none', prefix_format=None,
43-
timeout=60, http_auth=None, **kwargs):
52+
timeout=60, http_auth=None):
4453
prefix_format = prefix_format or DEFAULT_PREFIX_FORMAT
4554
exclude_dbs = exclude_dbs.split(',') or []
4655
encoding = '' if compression == 'gzip' else 'gzip'
@@ -52,12 +61,13 @@ def download_sql_backup(url, user, password, dry_run=False, overwrite_existing=F
5261

5362
g.doc.set_input_by_id('input_username', user)
5463
g.doc.set_input_by_id('input_password', password)
55-
g.doc.submit()
64+
g.submit()
65+
66+
if not is_login_successful(g):
67+
raise ValueError('Could not login - did you provide the correct username / password?')
68+
69+
open_frame_if_phpmyadmin_3(g)
5670

57-
try:
58-
g.doc.text_assert('server_export.php')
59-
except Exception as e:
60-
raise ValueError('Could not login - did you provide the correct username / password? ({})'.format(e))
6171
export_url = g.doc.select("id('topmenu')//a[contains(@href,'server_export.php')]/@href").text()
6272
g.go(export_url)
6373

@@ -67,13 +77,13 @@ def download_sql_backup(url, user, password, dry_run=False, overwrite_existing=F
6777
print('Warning: no databases to dump (databases available: "{}")'.format('", "'.join(dbs_available)),
6878
file=sys.stderr)
6979

70-
file_response = g.doc.submit(
80+
file_response = g.submit(
7181
extra_post=[('db_select[]', db_name) for db_name in dbs_to_dump] + [('compression', compression)])
7282

73-
re_match = CONTENT_DISPOSITION_FILENAME_RE.match(g.response.headers['Content-Disposition'])
83+
re_match = CONTENT_DISPOSITION_FILENAME_RE.match(g.doc.headers['Content-Disposition'])
7484
if not re_match:
7585
raise ValueError(
76-
'Could not determine SQL backup filename from {}'.format(g.response.headers['Content-Disposition']))
86+
'Could not determine SQL backup filename from {}'.format(g.doc.headers['Content-Disposition']))
7787

7888
content_filename = re_match.group('filename')
7989
filename = content_filename if basename is None else basename + os.path.splitext(content_filename)[1]
@@ -102,33 +112,37 @@ def download_sql_backup(url, user, password, dry_run=False, overwrite_existing=F
102112
if __name__ == '__main__':
103113
parser = argparse.ArgumentParser(
104114
description='Automates the download of SQL dump backups via a phpMyAdmin web interface.',
105-
epilog='Written by Christoph Haunschmidt, version: {}'.format(__version__))
115+
epilog='Written by Christoph Haunschmidt et al., version: {}'.format(__version__))
106116

107117
parser.add_argument('url', metavar='URL', help='phpMyAdmin login page url')
108118
parser.add_argument('user', metavar='USERNAME', help='phpMyAdmin login username')
109119
parser.add_argument('password', metavar='PASSWORD', help='phpMyAdmin login password')
110120
parser.add_argument('-o', '--output-directory', default=os.getcwd(),
111-
help='output directory for the SQL dump file (default: the current working directory)')
121+
help='output directory for the SQL dump file (default: the current working directory)')
112122
parser.add_argument('-p', '--prepend-date', action='store_true', default=False,
113-
help='prepend current UTC date & time to the filename; see the --prefix-format option for custom formatting')
123+
help='prepend current UTC date & time to the filename; '
124+
'see the --prefix-format option for custom formatting')
114125
parser.add_argument('-e', '--exclude-dbs', default='',
115-
help='comma-separated list of database names to exclude from the dump')
116-
parser.add_argument('--compression', default='none', choices=['none', 'zip', 'gzip'],
117-
help='compression method for the output file - must be supported by the server (default: %(default)s)')
126+
help='comma-separated list of database names to exclude from the dump')
127+
parser.add_argument('--compression', default='none', choices=['none', 'zip', 'gzip', 'bzip2'],
128+
help='compression method for the output file - must be supported by the '
129+
'server (default: %(default)s)')
118130
parser.add_argument('--basename', default=None,
119-
help='the desired basename (without extension) of the SQL dump file (default: the name given by phpMyAdmin); '
120-
'you can also set an empty basename "" in combination with --prepend-date and --prefix-format')
131+
help='the desired basename (without extension) of the SQL dump file (default: the name given '
132+
'by phpMyAdmin); you can also set an empty basename "" in combination with '
133+
'--prepend-date and --prefix-format')
121134
parser.add_argument('--timeout', type=int, default=60,
122-
help='timeout in seconds for the requests (default: %(default)s)')
135+
help='timeout in seconds for the requests (default: %(default)s)')
123136
parser.add_argument('--overwrite-existing', action='store_true', default=False,
124-
help='overwrite existing SQL dump files (instead of appending a number to the name)')
137+
help='overwrite existing SQL dump files (instead of appending a number to the name)')
125138
parser.add_argument('--prefix-format', default='',
126-
help=str('the prefix format for --prepend-date (default: "{}"); in Python\'s strftime format. '
127-
'Must be used with --prepend-date to be in effect'.format(DEFAULT_PREFIX_FORMAT.replace('%', '%%'))))
139+
help=str('the prefix format for --prepend-date (default: "{}"); in Python\'s strftime format. '
140+
'Must be used with --prepend-date to be in effect'.format(
141+
DEFAULT_PREFIX_FORMAT.replace('%', '%%'))))
128142
parser.add_argument('--dry-run', action='store_true', default=False,
129-
help='dry run, do not actually download any file')
143+
help='dry run, do not actually download any file')
130144
parser.add_argument('--http-auth', default=None,
131-
help='Basic http authentication, using format "username:password"')
145+
help='Basic HTTP authentication, using format "username:password"')
132146

133147
args = parser.parse_args()
134148

@@ -143,4 +157,4 @@ def download_sql_backup(url, user, password, dry_run=False, overwrite_existing=F
143157
sys.exit(1)
144158

145159
print('{} saved SQL dump to: {}'.format(('Would have' if args.dry_run else 'Successfully'), dump_fn),
146-
file=sys.stdout)
160+
file=sys.stdout)

0 commit comments

Comments
 (0)