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.
@@ -0,0 +1,11 @@
1
+ .venv/
2
+ __pycache__/
3
+ .pytest_cache/
4
+ *.pyc
5
+ *.pyo
6
+ *.pyd
7
+ *.egg-info/
8
+ build/
9
+ dist/
10
+ .coverage
11
+ htmlcov/
@@ -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).