resumesh-scrapers
Enterprise-Grade, Standalone, and Decoupled Web Scraping Engines for Portfolio Building
resumesh-scrapers is a clean, modular, and robust Python library containing standalone scraping services for GitHub, Dev.to, Medium, Substack, Behance, NPM, PyPI, and YouTube platforms. Originally developed as part of the ResuMesh ecosystem, it has been decoupled to serve as a reusable package for any resume, portfolio, or profile aggregator project.
🚀 Key Features
- Robust Core Network Engine: Centralized HTTP request handling with standard retry mechanisms powered by
tenacityand structured logging. - Fully Decoupled Models: Independent, clean data validation using
pydanticv2, keeping data structures separated from database constraints. - Extensible Architecture: Platform scrapers are isolated in a plug-and-play layout under
platforms/allowing quick addition of new integrations (e.g. LinkedIn, GitLab). - Detailed Exceptions: Standardized exceptions hierarchy inheriting from
ScraperErrorwith HTTP status code details.
🛠️ Installation
pip install resumesh-scrapers
For Local Development (Editable mode)
git clone https://github.com/AtaCanYmc/resumesh-scrapers.git
cd resumesh-scrapers
pip install -e .
💡 Quick Start
Here is a simple example showing how to scrape your repository, blog statistics, and YouTube videos:
import asyncio
from resumesh_scrapers import (
GitHubScraper,
DevToScraper,
MediumScraper,
SubstackScraper,
BehanceScraper,
YouTubeScraper,
GitHubRepositoryModel,
GitHubCommitModel,
GitHubUserModel,
DevToArticleModel,
MediumEntryModel,
SubstackEntryModel,
BehanceProjectModel,
YouTubeVideoModel,
)
async def main():
# 1. Scraping GitHub Repositories, README Repos, Commits, and User Lists
github = GitHubScraper()
repos: list[GitHubRepositoryModel] = await github.fetch_data(
username="octocat",
pat="ghp_...", # optional PAT token to bypass rate limit
include_forks=False
)
print(f"Fetched {len(repos)} repositories.")
# Fetch profile README repository details
readme_repo = await github.fetch_readme_repo("octocat")
if readme_repo:
print(f"Profile README Repository: {readme_repo.html_url}")
# Fetch recent commits (default since: last 7 days)
commits: list[GitHubCommitModel] = await github.fetch_commits("octocat")
print(f"Fetched {len(commits)} commits.")
# Fetch followers and following list
followers: list[GitHubUserModel] = await github.fetch_followers("octocat", per_page=10)
print(f"Fetched {len(followers)} followers.")
# 2. Scraping Dev.to Articles
devto = DevToScraper()
devto_posts: list[DevToArticleModel] = await devto.fetch_data("atacanymc")
print(f"Fetched {len(devto_posts)} Dev.to articles.")
# 3. Scraping Substack Publications
substack = SubstackScraper()
substack_posts: list[SubstackEntryModel] = await substack.fetch_data("atacan")
print(f"Fetched {len(substack_posts)} Substack posts.")
# 4. Scraping Behance Projects
behance = BehanceScraper()
behance_projects: list[BehanceProjectModel] = await behance.fetch_data(
username="atacanymc",
api_key="your_behance_api_key" # optional, falls back to HTML scraping if not provided
)
print(f"Fetched {len(behance_projects)} Behance projects.")
# 5. Scraping YouTube Video Metadata
youtube = YouTubeScraper()
video_data: YouTubeVideoModel = await youtube.fetch_video("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
print(f"Fetched YouTube video: {video_data.title} ({video_data.view_count:,} views)")
if __name__ == "__main__":
asyncio.run(main())
🗺️ Clean Architecture
The codebase has been refactored to enforce separation of concerns, decoupling models, platforms, and network layers:
src/resumesh_scrapers/
├── core/ # Shared network and utility systems
│ ├── client.py # Central HTTP requester with tenacity retries
│ └── __init__.py
├── platforms/ # Individual platform scrapers
│ ├── github.py
│ ├── devto.py
│ ├── medium.py
│ ├── substack.py
│ ├── behance.py
│ ├── npm.py
│ ├── pypi.py
│ ├── youtube.py
│ └── __init__.py
└── models/ # Platform-specific Pydantic validation schemas
├── github.py
├── devto.py
├── medium.py
├── substack.py
├── behance.py
├── npm.py
├── pypi.py
├── youtube.py
└── __init__.py
📊 Models & Capabilities
| Platform | Scraper Class | Response Model | Captured Data Features |
|---|---|---|---|
| GitHub | GitHubScraper |
GitHubRepositoryModelGitHubCommitModelGitHubUserModel |
Repos: Stars, Forks, languages, watchers. Commits: author, message, sha, repo, date, HTML link. Users: login, avatar URL, HTML link (followers/following). |
| Dev.to | DevToScraper |
DevToArticleModel |
Title, URL, Tags, Reading time, Publishing date |
| Medium | MediumScraper |
MediumEntryModel |
Title, RSS Summary, UTM-stripped link, Category tags |
| Substack | SubstackScraper |
SubstackEntryModel |
Title, RSS Summary, link, tags |
| Behance | BehanceScraper |
BehanceProjectModel |
Project title, gallery URL, appreciation count, publication dates (supports API client keys) |
| NPM | NpmScraper |
NpmSearchResultModel |
Maintainer packages, keywords, version history, publisher metadata |
| PyPI | PyPIScraper |
PyPiPackageModel |
Releases, download statistics, license, project metadata |
| YouTube | YouTubeScraper |
YouTubeVideoModel |
Video title, duration, view count, like count, comment count, channel info, thumbnail, tags, categories (via yt-dlp) |
⚠️ Exception Handling
All scraper exceptions inherit from ScraperError to simplify integration errors:
from resumesh_scrapers.exceptions import ScraperError, GitHubScraperError
try:
repos = await github_scraper.fetch_data("some_username")
except GitHubScraperError as e:
print(f"GitHub API Error: {e.message} (HTTP {e.status_code})")
except ScraperError as e:
print(f"Generic Scraping Exception: {e}")
🤝 Contributing
We welcome contributions to add more platforms (such as LinkedIn, GitLab, or Dribbble) or optimize parsers. Please open a Pull Request or file an issue to discuss your ideas!
- Fork the Project.
- Create your Feature Branch (
git checkout -b feature/AmazingFeature). - Commit your Changes (
git commit -m 'Add some AmazingFeature'). - Push to the Branch (
git push origin feature/AmazingFeature). - Open a Pull Request.
📄 License
Distributed under the Apache 2.0 License. See LICENSE for more information.
Metadata
Release files for resumesh-scrapers 0.9.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| resumesh_scrapers-0.9.0.tar.gz | 37.9 kB | With provenance | Details for resumesh_scrapers-0.9.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| resumesh_scrapers-0.9.0-py3-none-any.whl | Python 3 | none | any | Details for resumesh_scrapers-0.9.0-py3-none-any.whl |
Total release size: 71.3 kB
Release files / resumesh_scrapers-0.9.0.tar.gz
| Download URL | resumesh_scrapers-0.9.0.tar.gz |
|---|---|
| Size | 37.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ba4df0809cd9249a078f1f54b097a622201c9de2d25ab17010c4896e7f6cefa1
|
|
BLAKE2b-256 checksum How to use checksums |
af1d97309994e6e441fb79440df46679c93f8a9ad3dc0d959e896b84bd8447f2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logRelease files / resumesh_scrapers-0.9.0-py3-none-any.whl
| Download URL | resumesh_scrapers-0.9.0-py3-none-any.whl |
|---|---|
| Size | 33.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2cff284c87ecc46b89170d6d9302d5c3cc9c7346ea11b50abd8721eb0a88c5a5
|
|
BLAKE2b-256 checksum How to use checksums |
264af463f55812f4180798475c7c7a24b70ea096111bda44172c4f6e3b860e8c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logSecurity for resumesh-scrapers 0.9.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for resumesh_scrapers-0.9.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 31, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.