docx2csv 0.1.1__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {docx2csv-0.1.1 → docx2csv-0.1.4}/LICENSE +0 -0
- docx2csv-0.1.4/PKG-INFO +131 -0
- docx2csv-0.1.4/README.md +99 -0
- docx2csv-0.1.4/docx2csv/__init__.py +10 -0
- docx2csv-0.1.4/docx2csv/__main__.py +18 -0
- docx2csv-0.1.4/docx2csv/converter.py +263 -0
- docx2csv-0.1.4/docx2csv/core.py +77 -0
- docx2csv-0.1.4/docx2csv.egg-info/PKG-INFO +131 -0
- {docx2csv-0.1.1 → docx2csv-0.1.4}/docx2csv.egg-info/SOURCES.txt +8 -5
- docx2csv-0.1.4/docx2csv.egg-info/entry_points.txt +2 -0
- docx2csv-0.1.4/docx2csv.egg-info/requires.txt +11 -0
- docx2csv-0.1.4/docx2csv.egg-info/top_level.txt +5 -0
- docx2csv-0.1.4/pyproject.toml +55 -0
- {docx2csv-0.1.1 → docx2csv-0.1.4}/setup.cfg +10 -10
- docx2csv-0.1.4/setup.py +8 -0
- docx2csv-0.1.4/tests/test_cli.py +63 -0
- docx2csv-0.1.4/tests/test_converter.py +186 -0
- docx2csv-0.1.1/AUTHORS.rst +0 -10
- docx2csv-0.1.1/PKG-INFO +0 -92
- docx2csv-0.1.1/README.rst +0 -54
- docx2csv-0.1.1/bin/docx2csv +0 -40
- docx2csv-0.1.1/docx2csv/__init__.py +0 -7
- docx2csv-0.1.1/docx2csv/converter.py +0 -118
- docx2csv-0.1.1/docx2csv.egg-info/PKG-INFO +0 -92
- docx2csv-0.1.1/docx2csv.egg-info/not-zip-safe +0 -1
- docx2csv-0.1.1/docx2csv.egg-info/requires.txt +0 -4
- docx2csv-0.1.1/docx2csv.egg-info/top_level.txt +0 -1
- docx2csv-0.1.1/setup.py +0 -48
- {docx2csv-0.1.1 → docx2csv-0.1.4}/docx2csv.egg-info/dependency_links.txt +0 -0
|
File without changes
|
docx2csv-0.1.4/PKG-INFO
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: docx2csv
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: Extracts tables from .docx files and saves them as csv or xlsx
|
|
5
|
+
Author-email: Ivan Begtin <ivan@begtin.tech>
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/ivbeg/docx2csv
|
|
8
|
+
Keywords: docx,converter,tables
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: click>=8.1.6
|
|
23
|
+
Requires-Dist: openpyxl>=3.1.2
|
|
24
|
+
Requires-Dist: python-docx>=0.8.11
|
|
25
|
+
Provides-Extra: xls
|
|
26
|
+
Requires-Dist: xlwt>=1.3.0; extra == "xls"
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
30
|
+
Requires-Dist: flake8; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# docx2csv
|
|
34
|
+
|
|
35
|
+
Extracts tables from .docx files and saves them as CSV, TSV, XLSX, or JSON.
|
|
36
|
+
|
|
37
|
+
[](https://github.com/ivbeg/docx2csv/actions/workflows/ci.yml)
|
|
38
|
+
[](https://pypi.org/project/docx2csv/)
|
|
39
|
+
[](https://pypi.org/project/docx2csv/)
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install docx2csv
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For XLS (Excel 97) output support:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install docx2csv[xls]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
### Command Line
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Extract all tables as separate CSV files
|
|
59
|
+
docx2csv extract document.docx
|
|
60
|
+
|
|
61
|
+
# Extract as XLSX
|
|
62
|
+
docx2csv extract document.docx --format xlsx
|
|
63
|
+
|
|
64
|
+
# Extract as single CSV file with all tables
|
|
65
|
+
docx2csv extract document.docx --format csv --singlefile
|
|
66
|
+
|
|
67
|
+
# Filter tables by minimum row count
|
|
68
|
+
docx2csv extract document.docx --sizefilter 3
|
|
69
|
+
|
|
70
|
+
# Specify output location
|
|
71
|
+
docx2csv extract document.docx --output results.csv
|
|
72
|
+
|
|
73
|
+
# Analyze a document (list tables without extracting)
|
|
74
|
+
docx2csv analyze document.docx
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Python API
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from docx2csv import extract_tables, extract, analyze
|
|
81
|
+
|
|
82
|
+
# Get table data as Python objects
|
|
83
|
+
tables = extract_tables('document.docx')
|
|
84
|
+
for table in tables:
|
|
85
|
+
print(f"Table {table['id']}: {table['num_rows']} rows x {table['num_cols']} cols")
|
|
86
|
+
for row in table['data']:
|
|
87
|
+
print(row)
|
|
88
|
+
|
|
89
|
+
# Extract to file
|
|
90
|
+
extract('document.docx', format='xlsx', output='output.xlsx')
|
|
91
|
+
|
|
92
|
+
# Analyze document structure
|
|
93
|
+
info = analyze('document.docx')
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Output Formats
|
|
97
|
+
|
|
98
|
+
| Format | Extension | Description |
|
|
99
|
+
|--------|-----------|-------------|
|
|
100
|
+
| CSV | `.csv` | Comma-separated values |
|
|
101
|
+
| TSV | `.tsv` | Tab-separated values |
|
|
102
|
+
| XLSX | `.xlsx` | Excel 2007+ (via openpyxl) |
|
|
103
|
+
| XLS | `.xls` | Excel 97 (requires `pip install docx2csv[xls]`) |
|
|
104
|
+
| JSON | `.json` | Structured JSON with metadata |
|
|
105
|
+
|
|
106
|
+
## Requirements
|
|
107
|
+
|
|
108
|
+
- [click](https://github.com/pallets/click)
|
|
109
|
+
- [python-docx](https://github.com/python-openxml/python-docx)
|
|
110
|
+
- [openpyxl](https://openpyxl.readthedocs.io/)
|
|
111
|
+
|
|
112
|
+
## Contributing
|
|
113
|
+
|
|
114
|
+
Contributions are welcome! Every little bit helps.
|
|
115
|
+
|
|
116
|
+
1. Fork the repo on GitHub
|
|
117
|
+
2. Clone your fork: `git clone https://github.com/your-username/docx2csv.git`
|
|
118
|
+
3. Create a branch: `git checkout -b name-of-your-bugfix-or-feature`
|
|
119
|
+
4. Make your changes and add tests
|
|
120
|
+
5. Run tests: `pytest`
|
|
121
|
+
6. Commit and push: `git commit -m "Your message" && git push origin your-branch`
|
|
122
|
+
7. Submit a pull request
|
|
123
|
+
|
|
124
|
+
## Credits
|
|
125
|
+
|
|
126
|
+
- **Ivan Begtin** - Author and maintainer
|
|
127
|
+
- **Vsevolod Oparin** - Optimized table extraction code
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
BSD 3-Clause License. See [LICENSE](LICENSE) for details.
|
docx2csv-0.1.4/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# docx2csv
|
|
2
|
+
|
|
3
|
+
Extracts tables from .docx files and saves them as CSV, TSV, XLSX, or JSON.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/ivbeg/docx2csv/actions/workflows/ci.yml)
|
|
6
|
+
[](https://pypi.org/project/docx2csv/)
|
|
7
|
+
[](https://pypi.org/project/docx2csv/)
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install docx2csv
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
For XLS (Excel 97) output support:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install docx2csv[xls]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### Command Line
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Extract all tables as separate CSV files
|
|
27
|
+
docx2csv extract document.docx
|
|
28
|
+
|
|
29
|
+
# Extract as XLSX
|
|
30
|
+
docx2csv extract document.docx --format xlsx
|
|
31
|
+
|
|
32
|
+
# Extract as single CSV file with all tables
|
|
33
|
+
docx2csv extract document.docx --format csv --singlefile
|
|
34
|
+
|
|
35
|
+
# Filter tables by minimum row count
|
|
36
|
+
docx2csv extract document.docx --sizefilter 3
|
|
37
|
+
|
|
38
|
+
# Specify output location
|
|
39
|
+
docx2csv extract document.docx --output results.csv
|
|
40
|
+
|
|
41
|
+
# Analyze a document (list tables without extracting)
|
|
42
|
+
docx2csv analyze document.docx
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Python API
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from docx2csv import extract_tables, extract, analyze
|
|
49
|
+
|
|
50
|
+
# Get table data as Python objects
|
|
51
|
+
tables = extract_tables('document.docx')
|
|
52
|
+
for table in tables:
|
|
53
|
+
print(f"Table {table['id']}: {table['num_rows']} rows x {table['num_cols']} cols")
|
|
54
|
+
for row in table['data']:
|
|
55
|
+
print(row)
|
|
56
|
+
|
|
57
|
+
# Extract to file
|
|
58
|
+
extract('document.docx', format='xlsx', output='output.xlsx')
|
|
59
|
+
|
|
60
|
+
# Analyze document structure
|
|
61
|
+
info = analyze('document.docx')
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Output Formats
|
|
65
|
+
|
|
66
|
+
| Format | Extension | Description |
|
|
67
|
+
|--------|-----------|-------------|
|
|
68
|
+
| CSV | `.csv` | Comma-separated values |
|
|
69
|
+
| TSV | `.tsv` | Tab-separated values |
|
|
70
|
+
| XLSX | `.xlsx` | Excel 2007+ (via openpyxl) |
|
|
71
|
+
| XLS | `.xls` | Excel 97 (requires `pip install docx2csv[xls]`) |
|
|
72
|
+
| JSON | `.json` | Structured JSON with metadata |
|
|
73
|
+
|
|
74
|
+
## Requirements
|
|
75
|
+
|
|
76
|
+
- [click](https://github.com/pallets/click)
|
|
77
|
+
- [python-docx](https://github.com/python-openxml/python-docx)
|
|
78
|
+
- [openpyxl](https://openpyxl.readthedocs.io/)
|
|
79
|
+
|
|
80
|
+
## Contributing
|
|
81
|
+
|
|
82
|
+
Contributions are welcome! Every little bit helps.
|
|
83
|
+
|
|
84
|
+
1. Fork the repo on GitHub
|
|
85
|
+
2. Clone your fork: `git clone https://github.com/your-username/docx2csv.git`
|
|
86
|
+
3. Create a branch: `git checkout -b name-of-your-bugfix-or-feature`
|
|
87
|
+
4. Make your changes and add tests
|
|
88
|
+
5. Run tests: `pytest`
|
|
89
|
+
6. Commit and push: `git commit -m "Your message" && git push origin your-branch`
|
|
90
|
+
7. Submit a pull request
|
|
91
|
+
|
|
92
|
+
## Credits
|
|
93
|
+
|
|
94
|
+
- **Ivan Begtin** - Author and maintainer
|
|
95
|
+
- **Vsevolod Oparin** - Optimized table extraction code
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
BSD 3-Clause License. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""The main entry point.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
try:
|
|
10
|
+
from .core import cli
|
|
11
|
+
exit_status = cli()
|
|
12
|
+
except KeyboardInterrupt:
|
|
13
|
+
print("Ctrl-C pressed. Aborting")
|
|
14
|
+
sys.exit(0)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if __name__ == '__main__':
|
|
18
|
+
main()
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# -*- coding: utf8 -*-
|
|
2
|
+
|
|
3
|
+
import csv
|
|
4
|
+
import os
|
|
5
|
+
import json
|
|
6
|
+
import datetime
|
|
7
|
+
import warnings
|
|
8
|
+
|
|
9
|
+
from typing import List, Dict, Any, Optional
|
|
10
|
+
|
|
11
|
+
import openpyxl
|
|
12
|
+
|
|
13
|
+
from docx import Document
|
|
14
|
+
from docx.table import Table
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _validate_input(filename: str) -> None:
|
|
18
|
+
"""Validate that filename is a readable .docx file."""
|
|
19
|
+
if not os.path.exists(filename):
|
|
20
|
+
raise FileNotFoundError("File not found: %s" % filename)
|
|
21
|
+
if not os.path.isfile(filename):
|
|
22
|
+
raise ValueError("Path is not a file: %s" % filename)
|
|
23
|
+
if not filename.lower().endswith('.docx'):
|
|
24
|
+
warnings.warn("File does not have .docx extension: %s" % filename)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def __extract_table(table: Table, strip_space: bool = False) -> List[List[str]]:
|
|
28
|
+
"""Extracts table data from a table object using the public API.
|
|
29
|
+
|
|
30
|
+
Uses table.rows and row.cells instead of private XML attributes
|
|
31
|
+
(_tbl.tr_lst, tc_lst, grid_span, vMerge) for compatibility with
|
|
32
|
+
future python-docx releases.
|
|
33
|
+
|
|
34
|
+
Note: Merged cells are handled by tracking cell identity. Vertically
|
|
35
|
+
merged continuation cells replicate the value of the merge origin.
|
|
36
|
+
Horizontally duplicated cells (from gridSpan) are deduplicated.
|
|
37
|
+
"""
|
|
38
|
+
results: List[List[str]] = []
|
|
39
|
+
# Track the last value per column for vertical merge continuation
|
|
40
|
+
col_values: Dict[int, str] = {}
|
|
41
|
+
|
|
42
|
+
for row in table.rows:
|
|
43
|
+
r: List[str] = []
|
|
44
|
+
seen_tcs: set = set()
|
|
45
|
+
col_idx: int = 0
|
|
46
|
+
|
|
47
|
+
for cell in row.cells:
|
|
48
|
+
tc_id: int = id(cell._tc)
|
|
49
|
+
|
|
50
|
+
# Skip duplicates from horizontal merge (gridSpan)
|
|
51
|
+
if tc_id in seen_tcs:
|
|
52
|
+
continue
|
|
53
|
+
seen_tcs.add(tc_id)
|
|
54
|
+
|
|
55
|
+
# Check for vertical merge continuation by examining the
|
|
56
|
+
# cell's XML for the vMerge element with no val attribute
|
|
57
|
+
# (which means "continue" in OOXML)
|
|
58
|
+
tc_element = cell._tc
|
|
59
|
+
vmerge = tc_element.find(
|
|
60
|
+
'{http://schemas.openxmlformats.org/wordprocessingml/2006/main}vMerge'
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if vmerge is not None and vmerge.get(
|
|
64
|
+
'{http://schemas.openxmlformats.org/wordprocessingml/2006/main}val'
|
|
65
|
+
) is None:
|
|
66
|
+
# vMerge continuation — reuse the value from the same column
|
|
67
|
+
# in a previous row
|
|
68
|
+
if col_idx in col_values:
|
|
69
|
+
value = col_values[col_idx]
|
|
70
|
+
else:
|
|
71
|
+
value = cell.text.replace("\n", " ")
|
|
72
|
+
else:
|
|
73
|
+
value = cell.text.replace("\n", " ")
|
|
74
|
+
|
|
75
|
+
if strip_space:
|
|
76
|
+
value = value.strip()
|
|
77
|
+
|
|
78
|
+
col_values[col_idx] = value
|
|
79
|
+
r.append(value)
|
|
80
|
+
col_idx += 1
|
|
81
|
+
|
|
82
|
+
results.append(r)
|
|
83
|
+
|
|
84
|
+
return results
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def __store_table(tabdata: List[List[str]], filename: str, format: str = "csv") -> None:
|
|
88
|
+
"""Saves table data as csv/tsv/xls/xlsx file."""
|
|
89
|
+
if format == "csv":
|
|
90
|
+
with open(filename, "w", encoding='utf8') as f:
|
|
91
|
+
w = csv.writer(f, delimiter=",")
|
|
92
|
+
for row in tabdata:
|
|
93
|
+
w.writerow(row)
|
|
94
|
+
elif format == 'tsv':
|
|
95
|
+
with open(filename, 'w', encoding='utf8') as f:
|
|
96
|
+
w = csv.writer(f, delimiter='\t')
|
|
97
|
+
for row in tabdata:
|
|
98
|
+
w.writerow(row)
|
|
99
|
+
elif format == 'xls':
|
|
100
|
+
try:
|
|
101
|
+
import xlwt
|
|
102
|
+
except ImportError:
|
|
103
|
+
raise ImportError(
|
|
104
|
+
"XLS output requires xlwt. Install with: pip install docx2csv[xls]"
|
|
105
|
+
)
|
|
106
|
+
workbook = xlwt.Workbook()
|
|
107
|
+
ws = __xls_table_to_sheet(tabdata, workbook.add_sheet("0"))
|
|
108
|
+
workbook.save(filename)
|
|
109
|
+
elif format == "xlsx":
|
|
110
|
+
workbook = openpyxl.Workbook()
|
|
111
|
+
ws = __xlsx_table_to_sheet(tabdata, workbook.create_sheet("0"))
|
|
112
|
+
workbook.save(filename)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def __xls_table_to_sheet(table: List[List[str]], ws: Any) -> Any:
|
|
116
|
+
"""Write table data to an XLS worksheet."""
|
|
117
|
+
rn: int = 0
|
|
118
|
+
for row in table:
|
|
119
|
+
cn: int = 0
|
|
120
|
+
for c in row:
|
|
121
|
+
ws.write(rn, cn, c)
|
|
122
|
+
cn += 1
|
|
123
|
+
rn += 1
|
|
124
|
+
return ws
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def __xlsx_table_to_sheet(table: List[List[str]], ws: Any) -> Any:
|
|
128
|
+
"""Write table data to an XLSX worksheet."""
|
|
129
|
+
for row in table:
|
|
130
|
+
ws.append(row)
|
|
131
|
+
return ws
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def extract_tables(filename: str, strip_space: bool = True) -> List[Dict[str, Any]]:
|
|
135
|
+
"""Extracts tables from .DOCX files.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
filename: Path to the .docx file.
|
|
139
|
+
strip_space: If True, strip leading/trailing whitespace from cell values.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
List of dicts with keys: id, num_cols, num_rows, style, data.
|
|
143
|
+
"""
|
|
144
|
+
_validate_input(filename)
|
|
145
|
+
tables: List[Dict[str, Any]] = []
|
|
146
|
+
document = Document(filename)
|
|
147
|
+
n: int = 0
|
|
148
|
+
for table in document.tables:
|
|
149
|
+
n += 1
|
|
150
|
+
info: Dict[str, Any] = {}
|
|
151
|
+
info['id'] = n
|
|
152
|
+
info['num_cols'] = len(table.columns)
|
|
153
|
+
info['num_rows'] = len(table.rows)
|
|
154
|
+
info['style'] = table.style.name
|
|
155
|
+
tdata = __extract_table(table, strip_space=strip_space)
|
|
156
|
+
info['data'] = tdata
|
|
157
|
+
tables.append(info)
|
|
158
|
+
return tables
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def extract(
|
|
162
|
+
filename: str,
|
|
163
|
+
format: str = "csv",
|
|
164
|
+
sizefilter: int = 0,
|
|
165
|
+
singlefile: bool = False,
|
|
166
|
+
output: Optional[str] = None,
|
|
167
|
+
strip_space: bool = True,
|
|
168
|
+
) -> None:
|
|
169
|
+
"""Extracts tables from .docx files and saves them as csv, xls or xlsx files.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
filename: Path to the .docx file.
|
|
173
|
+
format: Output format — 'csv', 'tsv', 'xls', 'xlsx', or 'json'.
|
|
174
|
+
sizefilter: Exclude tables with fewer than this many rows.
|
|
175
|
+
singlefile: If True, write all tables to a single file.
|
|
176
|
+
output: Output file path. Default: same directory as input.
|
|
177
|
+
strip_space: If True, strip leading/trailing whitespace from cell values.
|
|
178
|
+
"""
|
|
179
|
+
_validate_input(filename)
|
|
180
|
+
tables = extract_tables(filename, strip_space=strip_space)
|
|
181
|
+
name: str = filename.rsplit(".", 1)[0]
|
|
182
|
+
fmt: str = format.lower()
|
|
183
|
+
n: int = 0
|
|
184
|
+
lfilter: int = int(sizefilter)
|
|
185
|
+
|
|
186
|
+
if singlefile:
|
|
187
|
+
if fmt == "xls":
|
|
188
|
+
try:
|
|
189
|
+
import xlwt
|
|
190
|
+
except ImportError:
|
|
191
|
+
raise ImportError(
|
|
192
|
+
"XLS output requires xlwt. Install with: pip install docx2csv[xls]"
|
|
193
|
+
)
|
|
194
|
+
workbook = xlwt.Workbook()
|
|
195
|
+
for t in tables:
|
|
196
|
+
if lfilter > len(t['data']):
|
|
197
|
+
continue
|
|
198
|
+
n += 1
|
|
199
|
+
__xls_table_to_sheet(t['data'], workbook.add_sheet(str(n)))
|
|
200
|
+
destname = output if output else name + ".%s" % (fmt)
|
|
201
|
+
workbook.save(destname)
|
|
202
|
+
elif fmt == "xlsx":
|
|
203
|
+
workbook = openpyxl.Workbook()
|
|
204
|
+
for t in tables:
|
|
205
|
+
if lfilter > len(t['data']):
|
|
206
|
+
continue
|
|
207
|
+
n += 1
|
|
208
|
+
__xlsx_table_to_sheet(t['data'], workbook.create_sheet(str(n)))
|
|
209
|
+
destname = output if output else name + ".%s" % (fmt)
|
|
210
|
+
workbook.save(destname)
|
|
211
|
+
elif fmt == "json":
|
|
212
|
+
report: Dict[str, Any] = {
|
|
213
|
+
'filename': filename,
|
|
214
|
+
'timestamp': datetime.datetime.now().isoformat(),
|
|
215
|
+
'num_tables': len(tables),
|
|
216
|
+
'tables': tables,
|
|
217
|
+
}
|
|
218
|
+
destname = output if output else name + ".%s" % (fmt)
|
|
219
|
+
with open(destname, 'w', encoding='utf8') as f:
|
|
220
|
+
json.dump(report, f, ensure_ascii=False, indent=4)
|
|
221
|
+
elif fmt in ("csv", "tsv"):
|
|
222
|
+
delimiter: str = "," if fmt == "csv" else "\t"
|
|
223
|
+
destname = output if output else name + ".%s" % fmt
|
|
224
|
+
with open(destname, "w", encoding='utf8') as f:
|
|
225
|
+
w = csv.writer(f, delimiter=delimiter)
|
|
226
|
+
for i, t in enumerate(tables):
|
|
227
|
+
if lfilter > len(t['data']):
|
|
228
|
+
continue
|
|
229
|
+
if i > 0:
|
|
230
|
+
w.writerow([])
|
|
231
|
+
for row in t['data']:
|
|
232
|
+
w.writerow(row)
|
|
233
|
+
else:
|
|
234
|
+
for t in tables:
|
|
235
|
+
if lfilter > len(t['data']):
|
|
236
|
+
continue
|
|
237
|
+
n += 1
|
|
238
|
+
destname = output if output else name + "_%d.%s" % (n, fmt)
|
|
239
|
+
__store_table(t['data'], destname, fmt)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def analyze(filename: str) -> List[Dict[str, Any]]:
|
|
243
|
+
"""Analyzes .docx file and returns table metadata.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
filename: Path to the .docx file.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
List of dicts with keys: id, num_cols, num_rows, style.
|
|
250
|
+
"""
|
|
251
|
+
_validate_input(filename)
|
|
252
|
+
tableinfo: List[Dict[str, Any]] = []
|
|
253
|
+
document = Document(filename)
|
|
254
|
+
n: int = 0
|
|
255
|
+
for table in document.tables:
|
|
256
|
+
n += 1
|
|
257
|
+
info: Dict[str, Any] = {}
|
|
258
|
+
info['id'] = n
|
|
259
|
+
info['num_cols'] = len(table.columns)
|
|
260
|
+
info['num_rows'] = len(table.rows)
|
|
261
|
+
info['style'] = table.style.name
|
|
262
|
+
tableinfo.append(info)
|
|
263
|
+
return tableinfo
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf8 -*-
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
import docx2csv
|
|
9
|
+
from docx.opc.exceptions import PackageNotFoundError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group()
|
|
13
|
+
def cli1():
|
|
14
|
+
"""Extracts tables from DOCX files as CSV or XLSX.
|
|
15
|
+
|
|
16
|
+
Use command: "docx2csv extract <filename>" to run extraction.
|
|
17
|
+
It will create files like filename_1.csv, filename_2.csv for each table found.
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@cli1.command()
|
|
24
|
+
@click.argument('filename')
|
|
25
|
+
@click.option('--format', '-f', default='csv', help='Output format: CSV, TSV, XLSX')
|
|
26
|
+
@click.option('--singlefile', '-s', is_flag=True, show_default=True, default=False, help='Outputs single file with multiple tables')
|
|
27
|
+
@click.option('--sizefilter', '-i', default=0, help='Filters table by minimum number of rows')
|
|
28
|
+
@click.option('--output', '-o', default=None, help='Choose location of output file, default same location as input')
|
|
29
|
+
def extract(filename, format, sizefilter, singlefile, output):
|
|
30
|
+
"""
|
|
31
|
+
Extracts tables from DOCX files as CSV or TSV or XLSX.
|
|
32
|
+
|
|
33
|
+
Use command: "docx2csv extract <filename>" to run extraction.
|
|
34
|
+
It will create files like filename_1.csv, filename_2.csv for each table found.
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
docx2csv.extract(filename, format, sizefilter, singlefile, output)
|
|
38
|
+
except FileNotFoundError as e:
|
|
39
|
+
click.echo("Error: %s" % e, err=True)
|
|
40
|
+
sys.exit(1)
|
|
41
|
+
except ValueError as e:
|
|
42
|
+
click.echo("Error: %s" % e, err=True)
|
|
43
|
+
sys.exit(1)
|
|
44
|
+
except PackageNotFoundError:
|
|
45
|
+
click.echo("Error: '%s' is not a valid .docx file." % filename, err=True)
|
|
46
|
+
sys.exit(1)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@click.group()
|
|
50
|
+
def cli2():
|
|
51
|
+
"""Analyses of DOCX file, lists all existing tables
|
|
52
|
+
"""
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@cli2.command()
|
|
57
|
+
@click.argument('filename')
|
|
58
|
+
def analyze(filename):
|
|
59
|
+
"""
|
|
60
|
+
Analyzes .docx file and finds tables
|
|
61
|
+
"""
|
|
62
|
+
from pprint import pprint
|
|
63
|
+
try:
|
|
64
|
+
tableinfo = docx2csv.analyze(filename)
|
|
65
|
+
pprint(tableinfo)
|
|
66
|
+
except FileNotFoundError as e:
|
|
67
|
+
click.echo("Error: %s" % e, err=True)
|
|
68
|
+
sys.exit(1)
|
|
69
|
+
except ValueError as e:
|
|
70
|
+
click.echo("Error: %s" % e, err=True)
|
|
71
|
+
sys.exit(1)
|
|
72
|
+
except PackageNotFoundError:
|
|
73
|
+
click.echo("Error: '%s' is not a valid .docx file." % filename, err=True)
|
|
74
|
+
sys.exit(1)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
cli = click.CommandCollection(sources=[cli1, cli2])
|