GitHub Resume
← Back to Research Log

Google Play Store Review Scraper

A production-ready web scraper for extracting app reviews using Selenium, with modern Python best practices.

Python Selenium Web Scraping Data Collection

Executive Summary

This project demonstrates a production-ready web scraper for collecting app reviews from the Google Play Store. The implementation showcases the evolution from a basic script to a robust, maintainable solution using modern Python best practices.


The Challenge

Collecting app reviews at scale from Google Play Store presents several challenges:

  • Dynamic Content: Reviews are loaded via JavaScript (lazy loading)
  • Pagination: β€œLoad More” button reveals additional reviews
  • Rate Limiting: Aggressive scraping can trigger blocks
  • Data Quality: Reviews may be truncated (short vs. full versions)

Before: The Original Approach

The initial implementation had several issues common in quick prototypes:

# Hardcoded paths and URLs
PATH = "C:\\Program Files (x86)\\chromedriver.exe"
driver = webdriver.Chrome(PATH)

# User input instead of CLI args
namefile = input('Input file name to export data : ')
count = input("Amount of data : ")

# Deprecated Selenium API
button = driver.find_element_by_css_selector('span.CwaK9')

# Duplicated logic
def dataload():
    try:
        for i in range(50):
            button = driver.find_element_by_css_selector('span.CwaK9')
            # ... same code repeated in except block
    except:
        try:
            # ... duplicated logic here
        except:
            scrolldown()

Problems identified:

  1. ❌ Hardcoded ChromeDriver path (breaks on different systems)
  2. ❌ Deprecated find_element_by_* methods
  3. ❌ Repetitive code blocks
  4. ❌ Bare except clauses (catches everything, including KeyboardInterrupt)
  5. ❌ No CLI arguments (requires interactive input)
  6. ❌ No logging or proper progress tracking

After: The Optimized Solution

1. Configuration with Dataclasses

@dataclass
class ScraperConfig:
    """Configuration for the scraper."""
    app_id: str
    output_file: str
    target_count: int = 1000
    language: str = "id"
    headless: bool = False
    scroll_pause: float = 2.0
    max_retries: int = 3
    backup_interval: int = 500

2. Modern Selenium 4+ API

# Before (deprecated)
driver.find_element_by_css_selector('span.CwaK9')

# After (modern)
from selenium.webdriver.common.by import By
driver.find_element(By.CSS_SELECTOR, 'span.CwaK9')

3. Centralized Selectors

class PlayStoreReviewScraper:
    SELECTORS = {
        "review_container": "div.LXrl4c",
        "review_item": "div.UD7Dzf",
        "short_comment": 'span[jsname="bN97Pc"]',
        "long_comment": 'span[jsname="fbQN7e"]',
        "load_more_button": "span.CwaK9",
    }

4. Robust Error Handling

from selenium.common.exceptions import (
    NoSuchElementException,
    TimeoutException,
    ElementClickInterceptedException,
)

def _click_load_more(self) -> bool:
    try:
        button = WebDriverWait(self.driver, 5).until(
            EC.element_to_be_clickable(
                (By.CSS_SELECTOR, self.SELECTORS["load_more_button"])
            )
        )
        button.click()
        return True
    except (NoSuchElementException, TimeoutException, ElementClickInterceptedException):
        return False

5. CLI Interface with argparse

# Now runs with simple command-line arguments
python webscrape.py --app-id com.gojek.app --output reviews.csv --count 1000 --headless

Architecture Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    PlayStoreReviewScraper                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  ScraperConfig (dataclass)                                  β”‚
β”‚  β”œβ”€β”€ app_id: str                                            β”‚
β”‚  β”œβ”€β”€ output_file: str                                       β”‚
β”‚  β”œβ”€β”€ target_count: int                                      β”‚
β”‚  β”œβ”€β”€ language: str                                          β”‚
β”‚  └── headless: bool                                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Core Methods                                               β”‚
β”‚  β”œβ”€β”€ scrape() β†’ Main loop                                   β”‚
β”‚  β”œβ”€β”€ _load_more_reviews() β†’ Pagination handling             β”‚
β”‚  β”œβ”€β”€ _extract_review_text() β†’ Text extraction               β”‚
β”‚  β”œβ”€β”€ _export_reviews() β†’ CSV export                         β”‚
β”‚  └── _create_backup() β†’ Automatic backups                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Improvements Summary

AspectBeforeAfter
ConfigurationHardcoded valuesDataclass + CLI args
Selenium APIDeprecated methodsModern By locators
Error HandlingBare exceptSpecific exceptions
Code StructureProcedural scriptOOP with single responsibility
LoggingPrint statementsPython logging module
Progress TrackingManual printsStructured progress updates
BackupsManualAutomatic interval-based
PortabilityWindows-only pathwebdriver-manager fallback

Usage

# Install dependencies
pip install selenium pandas webdriver-manager

# Basic usage
python webscrape.py --app-id com.gojek.app --count 500

# Full options
python webscrape.py \
    --app-id com.tokopedia.tkpd \
    --output tokopedia_reviews.csv \
    --count 2000 \
    --language en \
    --headless

Conclusion

This refactoring demonstrates how a quick prototype can be transformed into a maintainable, production-ready tool. The key principles applied were:

  1. Single Responsibility: Each method does one thing well
  2. Configuration as Data: Dataclasses make settings explicit
  3. Explicit Error Handling: Catch only what you expect
  4. CLI Over Input: Scripts should be automatable
  5. Progressive Enhancement: Backups and logging for reliability