openscreener 0.1.0__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.
- openscreener-0.1.0/.gitignore +11 -0
- openscreener-0.1.0/LICENSE +21 -0
- openscreener-0.1.0/PKG-INFO +374 -0
- openscreener-0.1.0/README.md +344 -0
- openscreener-0.1.0/pyproject.toml +63 -0
- openscreener-0.1.0/src/openscreener/__init__.py +8 -0
- openscreener-0.1.0/src/openscreener/batch_stock.py +42 -0
- openscreener-0.1.0/src/openscreener/exceptions.py +13 -0
- openscreener-0.1.0/src/openscreener/index.py +17 -0
- openscreener-0.1.0/src/openscreener/parsers/__init__.py +25 -0
- openscreener-0.1.0/src/openscreener/parsers/_helpers.py +236 -0
- openscreener-0.1.0/src/openscreener/parsers/_html.py +154 -0
- openscreener-0.1.0/src/openscreener/parsers/balance_sheet_parser.py +10 -0
- openscreener-0.1.0/src/openscreener/parsers/cash_flow_parser.py +10 -0
- openscreener-0.1.0/src/openscreener/parsers/index_parser.py +137 -0
- openscreener-0.1.0/src/openscreener/parsers/peers_parser.py +22 -0
- openscreener-0.1.0/src/openscreener/parsers/profit_loss_parser.py +10 -0
- openscreener-0.1.0/src/openscreener/parsers/pros_cons_parser.py +15 -0
- openscreener-0.1.0/src/openscreener/parsers/quarterly_parser.py +10 -0
- openscreener-0.1.0/src/openscreener/parsers/ratios_parser.py +11 -0
- openscreener-0.1.0/src/openscreener/parsers/shareholding_parser.py +14 -0
- openscreener-0.1.0/src/openscreener/parsers/summary_parser.py +46 -0
- openscreener-0.1.0/src/openscreener/scraper.py +91 -0
- openscreener-0.1.0/src/openscreener/screener.py +9 -0
- openscreener-0.1.0/src/openscreener/stock.py +1128 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Navaneeth
|
|
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.
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openscreener
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Playwright-powered Python package for extracting structured financial data from Screener.in.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Na1neeth/openscreener
|
|
6
|
+
Project-URL: Repository, https://github.com/Na1neeth/openscreener
|
|
7
|
+
Project-URL: Issues, https://github.com/Na1neeth/openscreener/issues
|
|
8
|
+
Author: Navaneeth
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: finance,playwright,screener,screener.in,stocks
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: playwright>=1.43
|
|
24
|
+
Requires-Dist: rich>=13.7
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: twine>=6.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# openscreener
|
|
32
|
+
|
|
33
|
+
`openscreener` is a Playwright-powered Python library for extracting structured financial data from [Screener.in](https://www.screener.in/).
|
|
34
|
+
|
|
35
|
+
It loads live stock and index pages, detects the page type, and returns normalized Python dictionaries and lists for the sections you care about.
|
|
36
|
+
|
|
37
|
+
## Highlights
|
|
38
|
+
|
|
39
|
+
- High-level APIs for single stocks, indexes, and batch stock fetches
|
|
40
|
+
- Normalized outputs for summary, analysis, peers, quarterly results, profit and loss, balance sheet, cash flow, ratios, and shareholding
|
|
41
|
+
- Index support with constituent pagination handling
|
|
42
|
+
- Pretty terminal output, JSON export, and optional pandas DataFrame conversion
|
|
43
|
+
- Helpful errors when a section is missing or the wrong class is used for a page
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
`openscreener` requires Python `3.10+`.
|
|
48
|
+
|
|
49
|
+
Install the package:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install openscreener
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Install Playwright browser binaries:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python -m playwright install chromium
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Install pandas if you want `to_dataframe()` support:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install pandas
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Install development dependencies when working on the repo:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install -e .[dev]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Quick Start
|
|
74
|
+
|
|
75
|
+
### Stock
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from openscreener import Stock
|
|
79
|
+
|
|
80
|
+
stock = Stock("TCS")
|
|
81
|
+
|
|
82
|
+
summary = stock.summary()
|
|
83
|
+
print(summary["company_name"])
|
|
84
|
+
print(summary["current_price"])
|
|
85
|
+
print(summary["ratios"]["market_cap"])
|
|
86
|
+
|
|
87
|
+
analysis = stock.pros_cons()
|
|
88
|
+
print(analysis["pros"][0])
|
|
89
|
+
|
|
90
|
+
payload = stock.fetch(["summary", "ratios", "shareholding"])
|
|
91
|
+
print(payload["ratios"]["roce_percent"])
|
|
92
|
+
|
|
93
|
+
stock.pretty("summary")
|
|
94
|
+
stock.pretty("cash_flow")
|
|
95
|
+
print(stock.metadata())
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Index
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from openscreener import Index
|
|
102
|
+
|
|
103
|
+
index = Index("CNX500")
|
|
104
|
+
|
|
105
|
+
print(index.page_type()) # index
|
|
106
|
+
print(index.summary()["company_name"])
|
|
107
|
+
|
|
108
|
+
constituents = index.constituents(limit=70)
|
|
109
|
+
print(constituents["returned_companies"])
|
|
110
|
+
print(constituents["companies"][0]["symbol"])
|
|
111
|
+
|
|
112
|
+
index.pretty("constituents", constituents_limit=20)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Batch
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from openscreener import Stock
|
|
119
|
+
|
|
120
|
+
batch = Stock.batch(["TCS", "INFY"])
|
|
121
|
+
|
|
122
|
+
ratios_by_symbol = batch.fetch("ratios")
|
|
123
|
+
print(ratios_by_symbol["TCS"]["roce_percent"])
|
|
124
|
+
|
|
125
|
+
payload_by_symbol = batch.fetch(["summary", "shareholding"])
|
|
126
|
+
print(payload_by_symbol["INFY"]["summary"]["company_name"])
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### JSON And DataFrame Helpers
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from openscreener import Stock
|
|
133
|
+
|
|
134
|
+
stock = Stock("TCS")
|
|
135
|
+
|
|
136
|
+
print(stock.to_json())
|
|
137
|
+
|
|
138
|
+
frame = stock.to_dataframe("peers")
|
|
139
|
+
print(frame.head())
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Public API
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from openscreener import BatchStock, Index, PlaywrightScraper, Stock
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### `Stock`
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
Stock(symbol: str, consolidated: bool = False, scraper: PlaywrightScraper | None = None)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Main methods:
|
|
155
|
+
|
|
156
|
+
- `summary()`
|
|
157
|
+
- `pros_cons()`
|
|
158
|
+
- `pros()`
|
|
159
|
+
- `cons()`
|
|
160
|
+
- `peers()`
|
|
161
|
+
- `quarterly_results()`
|
|
162
|
+
- `profit_loss()`
|
|
163
|
+
- `balance_sheet()`
|
|
164
|
+
- `cash_flow()`
|
|
165
|
+
- `ratios()`
|
|
166
|
+
- `shareholding(frequency="quarterly")`
|
|
167
|
+
- `shareholding_quarterly()`
|
|
168
|
+
- `shareholding_yearly()`
|
|
169
|
+
- `fetch(sections, constituents_limit=None)`
|
|
170
|
+
- `all()`
|
|
171
|
+
- `available_sections()`
|
|
172
|
+
- `page_type()`
|
|
173
|
+
- `is_stock()`
|
|
174
|
+
- `is_index()`
|
|
175
|
+
- `pretty(section=None, constituents_limit=None)`
|
|
176
|
+
- `print_section(section, constituents_limit=None)`
|
|
177
|
+
- `to_json(indent=2, constituents_limit=None)`
|
|
178
|
+
- `to_dataframe(section)`
|
|
179
|
+
- `metadata()`
|
|
180
|
+
|
|
181
|
+
### `Index`
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
Index(symbol: str, scraper: PlaywrightScraper | None = None)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Main methods:
|
|
188
|
+
|
|
189
|
+
- `summary()`
|
|
190
|
+
- `constituents(limit=None)`
|
|
191
|
+
- `fetch(sections, constituents_limit=None)`
|
|
192
|
+
- `all(constituents_limit=None)`
|
|
193
|
+
- `available_sections()`
|
|
194
|
+
- `page_type()`
|
|
195
|
+
- `pretty(section=None, constituents_limit=None)`
|
|
196
|
+
- `print_section(section, constituents_limit=None)`
|
|
197
|
+
- `to_json(indent=2, constituents_limit=None)`
|
|
198
|
+
- `to_dataframe(section)`
|
|
199
|
+
- `metadata()`
|
|
200
|
+
|
|
201
|
+
### `BatchStock`
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
BatchStock(
|
|
205
|
+
symbols,
|
|
206
|
+
consolidated: bool = False,
|
|
207
|
+
scraper: PlaywrightScraper | None = None,
|
|
208
|
+
)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Main method:
|
|
212
|
+
|
|
213
|
+
- `fetch(sections)`
|
|
214
|
+
|
|
215
|
+
Shortcut constructor:
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
from openscreener import Stock
|
|
219
|
+
|
|
220
|
+
batch = Stock.batch(["TCS", "INFY"])
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### `PlaywrightScraper`
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
PlaywrightScraper(
|
|
227
|
+
base_url="https://www.screener.in/company/{symbol}{path_suffix}",
|
|
228
|
+
consolidated=False,
|
|
229
|
+
headless=True,
|
|
230
|
+
timeout_ms=30000,
|
|
231
|
+
)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Main methods:
|
|
235
|
+
|
|
236
|
+
- `fetch_page(symbol)`
|
|
237
|
+
- `fetch_pages(symbols)`
|
|
238
|
+
- `fetch_constituent_pages(symbol, page_numbers, page_size=50)`
|
|
239
|
+
|
|
240
|
+
## Supported Sections
|
|
241
|
+
|
|
242
|
+
### Stock Sections
|
|
243
|
+
|
|
244
|
+
| Canonical section | Accepted aliases | Method | Return shape |
|
|
245
|
+
| --- | --- | --- | --- |
|
|
246
|
+
| `summary` | `summary` | `summary()` | `dict` |
|
|
247
|
+
| `analysis` | `analysis`, `pros_cons` | `pros_cons()` | `dict` |
|
|
248
|
+
| `peers` | `peers` | `peers()` | `dict` |
|
|
249
|
+
| `quarterly_results` | `quarters`, `quarterly_results` | `quarterly_results()` | `list[dict]` |
|
|
250
|
+
| `profit_loss` | `profit-loss`, `profit_loss` | `profit_loss()` | `list[dict]` |
|
|
251
|
+
| `balance_sheet` | `balance-sheet`, `balance_sheet` | `balance_sheet()` | `list[dict]` |
|
|
252
|
+
| `cash_flow` | `cash-flow`, `cash_flow` | `cash_flow()` | `list[dict]` |
|
|
253
|
+
| `ratios` | `ratios` | `ratios()` | `dict` |
|
|
254
|
+
| `shareholding` | `shareholding` | `shareholding()` | `list[dict]` |
|
|
255
|
+
|
|
256
|
+
### Index Sections
|
|
257
|
+
|
|
258
|
+
| Canonical section | Accepted aliases | Method | Return shape |
|
|
259
|
+
| --- | --- | --- | --- |
|
|
260
|
+
| `summary` | `summary` | `summary()` | `dict` |
|
|
261
|
+
| `constituents` | `constituents`, `companies` | `constituents(limit=None)` | `dict` |
|
|
262
|
+
|
|
263
|
+
### Helper-Only Section Names
|
|
264
|
+
|
|
265
|
+
These work with `pretty()`, `print_section()`, and `to_dataframe()` where applicable:
|
|
266
|
+
|
|
267
|
+
- `pros`
|
|
268
|
+
- `cons`
|
|
269
|
+
- `shareholding_quarterly`
|
|
270
|
+
- `shareholding_yearly`
|
|
271
|
+
|
|
272
|
+
## Behavior Notes
|
|
273
|
+
|
|
274
|
+
- `Stock("TCS")` is for stock pages.
|
|
275
|
+
- `Index("CNX500")` or `Index("NIFTY")` is for index pages.
|
|
276
|
+
- `page_type()` returns `stock`, `index`, or `unknown`.
|
|
277
|
+
- Using the wrong class for a page raises `EntityTypeMismatchError`.
|
|
278
|
+
- `available_sections()` depends on whether the resolved page is a stock or an index.
|
|
279
|
+
- `stock.fetch("ratios")` returns `{"ratios": {...}}`.
|
|
280
|
+
- `index.all(constituents_limit=100)` limits the returned constituent rows in the payload.
|
|
281
|
+
- `summary()["ratios"]` contains top-card metrics such as market cap, current price, high/low, and similar values.
|
|
282
|
+
- `ratios()` returns the latest annual ratios row, not the whole historical ratios table.
|
|
283
|
+
- `shareholding()` defaults to quarterly data.
|
|
284
|
+
- `metadata()` returns source metadata such as symbol, entity type, currency, units, and company or index name when available.
|
|
285
|
+
|
|
286
|
+
## Output Helpers
|
|
287
|
+
|
|
288
|
+
Pretty-print one section or the full payload:
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from openscreener import Stock
|
|
292
|
+
|
|
293
|
+
stock = Stock("TCS")
|
|
294
|
+
|
|
295
|
+
stock.pretty()
|
|
296
|
+
stock.pretty("summary")
|
|
297
|
+
stock.print_section("pros")
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Index pretty-printing works the same way:
|
|
301
|
+
|
|
302
|
+
```python
|
|
303
|
+
from openscreener import Index
|
|
304
|
+
|
|
305
|
+
index = Index("CNX500")
|
|
306
|
+
index.pretty("constituents", constituents_limit=50)
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
If `pandas` is installed, you can convert tabular sections to DataFrames:
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
from openscreener import Stock
|
|
313
|
+
|
|
314
|
+
stock = Stock("TCS")
|
|
315
|
+
frame = stock.to_dataframe("cash_flow")
|
|
316
|
+
print(frame.head())
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
If `pandas` is not installed, `to_dataframe()` raises an `ImportError` with an install hint.
|
|
320
|
+
|
|
321
|
+
## Data Conventions
|
|
322
|
+
|
|
323
|
+
- Numeric values are converted to `int` or `float` where possible.
|
|
324
|
+
- Missing values are returned as `None`.
|
|
325
|
+
- Period labels remain strings such as `Dec 2025`, `Mar 2025`, or `TTM`.
|
|
326
|
+
- Monetary values and units follow Screener's presentation.
|
|
327
|
+
- Default metadata reports `INR` currency and `crores` units.
|
|
328
|
+
|
|
329
|
+
## Development
|
|
330
|
+
|
|
331
|
+
Repository layout:
|
|
332
|
+
|
|
333
|
+
```text
|
|
334
|
+
src/openscreener/ Package source
|
|
335
|
+
src/openscreener/parsers/ Section parsers
|
|
336
|
+
tests/ Automated tests
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Run the local checks:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
python -m pytest -q
|
|
343
|
+
python -m build --no-isolation
|
|
344
|
+
python -m twine check dist/*
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Releasing A New Version
|
|
348
|
+
|
|
349
|
+
1. Bump the version in `pyproject.toml`.
|
|
350
|
+
2. Run the test and build checks.
|
|
351
|
+
3. Upload the new distribution files.
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
python -m pytest -q
|
|
355
|
+
python -m build --no-isolation
|
|
356
|
+
python -m twine check dist/*
|
|
357
|
+
python -m twine upload dist/*
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Each PyPI upload must use a new version number.
|
|
361
|
+
|
|
362
|
+
## Limitations
|
|
363
|
+
|
|
364
|
+
- Parsing depends on Screener.in's current HTML structure.
|
|
365
|
+
- Live usage requires Playwright and installed browser binaries.
|
|
366
|
+
- Missing sections raise `SectionNotFoundError`.
|
|
367
|
+
- Large index fetches depend on Screener's pagination remaining accessible.
|
|
368
|
+
- The project exposes a Python API; it does not currently provide a packaged CLI.
|
|
369
|
+
|
|
370
|
+
Use the live scraper responsibly and in a way that respects Screener.in's terms and rate limits.
|
|
371
|
+
|
|
372
|
+
## License
|
|
373
|
+
|
|
374
|
+
MIT. See [`LICENSE`](./LICENSE).
|