ProxyTee
TutorialUpdated 2026-10-297 min readBy Alex Mercer, Lead Network Architect

How to Scrape Indeed Job Postings with Python Using ProxyTee

Step-by-step tutorial on scraping Indeed job postings with Python, Playwright, and ProxyTee rotating residential proxies without IP blocks.

How to Scrape Indeed Job Postings with Python Using ProxyTee — ProxyTee Rotating Residential Proxies
Key Takeaways & Technical Summary

Step-by-step tutorial on scraping Indeed job postings with Python, Playwright, and ProxyTee rotating residential proxies without IP blocks.

Indeed is a valuable resource for job seekers and recruiters alike, offering a vast database of job listings. For those in need of large-scale data, manually collecting this information can be extremely time-consuming. This guide will show you how to scrape job listings from Indeed using Python with ProxyTee, and how to effectively navigate anti-bot challenges. ProxyTee offers unlimited residential proxies with global coverage and auto-rotation, ensuring seamless and efficient web scraping.

How to Scrape Indeed Job Postings with Python Using ProxyTee — Architecture & Proxy Configuration Workflow

What Is an Indeed Scraper?

An Indeed scraper is a tool that automates the extraction of job postings and related information from the Indeed website. This involves simulating human interactions on the site, navigating through search pages, and pinpointing the key data points such as job titles, company names, locations, and descriptions. Once extracted, this data can be organized and used for various purposes such as market analysis, competitive intelligence, and recruitment.

Data You Can Find on Indeed

Indeed provides access to a range of crucial job-related data, valuable for anyone looking to understand the employment landscape. Key data points you can scrape include:

  • Job titles: The role being advertised.
  • Company names: Details about the hiring companies.
  • Locations: The city, state, or country where the job is located.
  • Job descriptions: Details of job responsibilities and requirements.
  • Salary ranges: The pay scale, if provided.
  • Job types: Such as full-time, part-time, contract, or internship.
  • Posting dates: When the job was originally listed.
  • Tags and attributes: Keywords such as “Remote” or “Urgently Hiring”.
  • Ratings and reviews: Employer ratings and employee feedback.
  • Application options: If an "Easy Apply" option is available.

How to Scrape Indeed: Step-By-Step Guide

This tutorial will walk you through building a Python script to scrape job listings from Indeed. We'll focus on extracting data from the "data scientist" job postings page, while leveraging the power of ProxyTee to ensure reliable access.

Step #1: Project Setup

Ensure that you have Python 3 installed. Create a project directory:

mkdir indeed_scraper

Move into the new directory and create a virtual environment:

cd indeed_scraper
python -m venv env

Activate the environment, using:

./env/bin/activate # On Linux/macOS
env\Scripts\activate # On Windows

Finally, create a scraper.py file in your project.

Step #2: Choose the Right Scraping Library

Indeed dynamically loads content using JavaScript, so you'll need a tool that can handle JavaScript rendering, such as Selenium. ProxyTee's residential proxies can help bypass anti-scraping measures when used with Selenium, ensuring smooth data collection. You can also leverage ProxyTee's unlimited residential proxies which are perfect for high volume web scraping and provides a wide array of geo-locations to choose from.

Step #3: Install and Configure Selenium

Install Selenium using pip:

pip install -U selenium

Now, add the following code to your scraper.py to set up the WebDriver:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service

# Set up a controllable Chrome instance
driver = webdriver.Chrome(service=Service())

Do not forget to close the WebDriver at the end:

driver.quit()

Step #4: Visit the Target Page

Use Selenium's get() method to load the Indeed page:

driver.get("https://www.indeed.com/jobs?q=data+scientist&l=New+York%2C+NY&from=searchOnHP%2Cwhatautocomplete&vjk=45d1ba700870fbef")

Here is how scraper.py should look like:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service

# Set up a controllable Chrome instance
driver = webdriver.Chrome(service=Service())

# Open the target page in the browser
driver.get("https://www.indeed.com/jobs?q=data+scientist&l=New+York%2C+NY&from=searchOnHP%2Cwhatautocomplete&vjk=45d1ba700870fbef")

# Scraping logc...

# Close the web driver
driver.quit()

Step #5: Select the Job Posting Elements

Initialize an empty list to hold job data:

jobs = []

Select the container of job postings and the individual job elements:

jobs_container_element = driver.find_element(By.CSS_SELECTOR, "#mosaic-provider-jobcards")
job_elements = jobs_container_element.find_elements(By.CSS_SELECTOR, "[data-testid=\"slider_item\"]")

Remember to import By:

from selenium.webdriver.common.by import By

Step #6: Scrape the Job Main Info

Now, loop through each job element and extract the job title, URL, company name, and location:

for job_element in job_elements:
    title_element = job_element.find_element(By.CSS_SELECTOR, "h2.jobTitle")
    title = title_element.text

    url_element = title_element.find_element(By.CSS_SELECTOR, "a")
    url = url_element.get_attribute("href")

    company_element =job_element.find_element(By.CSS_SELECTOR, "[data-testid=\"company-name\"]")
    company = company_element.text

    location_element = job_element.find_element(By.CSS_SELECTOR, "[data-testid=\"text-location\"]")
    location = location_element.text

Step #7: Scrape the Job Details

Next, extract the job tags, easy apply availability, and description:

    tags = []
    tags_container_element = job_element.find_element(By.CSS_SELECTOR, ".jobMetaDataGroup")
    tag_elements = tags_container_element.find_elements(By.CSS_SELECTOR, "[data-testid=\"attribute_snippet_testid\"]")
    for tag_element in tag_elements:
        tag = tag_element.text
        tags.append(tag)

    # Check whether the \"Easy Apply\" element is on the page
    try:
        job_element.find_element(By.CSS_SELECTOR, "[data-testid=\"indeedApply\"]")
        easily_apply = True
    except NoSuchElementException:
        easily_apply = False

    description = []
    description_container_element = job_element.find_element(By.CSS_SELECTOR, "[role=\"presentation\"]")
    description_elements = description_container_element.find_elements(By.CSS_SELECTOR, "ul li")
    for description_element in description_elements:
        description_item_text = description_element.text
        # Ignore empty description strings
        if (description_item_text != ""):
            description.append(description_item_text)

Be sure to handle the NoSuchElementException and to create a data structure to contain your data:

from selenium.common import NoSuchElementException

    job = {
        "title": title,
        "url": url,
        "company": company,
        "location": location,
        "tags": tags,
        "easily_apply": easily_apply,
        "description": description
    }
    jobs.append(job)

Step #8: Collect the Scraped Data

The scraped data from each job is now stored in a job dictionary which will be appended to the jobs array.

Step #9: Export the Scraped Data to CSV

Export the scraped data to a CSV file for analysis and portability:

import csv

csv_file = "scraped_jobs.csv"
csv_headers = ["title", "url", "company", "location", "tags", "easily_apply", "description"]

with open(csv_file, mode="w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=csv_headers)
    writer.writeheader()
    for job in jobs:
        writer.writerow({
            "title": job["title"],
            "url": job["url"],
            "company": job["company"],
            "location": job["location"],
            "tags": ";".join(job["tags"]),
            "easily_apply": "Yes" if job["easily_apply"] else "No",
            "description": ";".join(job["description"])
        })

Remember to import csv from the standard library.

Step #10: Put It All Together

Here's the complete scraper.py:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.common import NoSuchElementException
import csv

# Set up a controllable Chrome instance
driver = webdriver.Chrome(service=Service())

# Open the target page in the browser
driver.get("https://www.indeed.com/jobs?q=data+scientist&l=New+York%2C+NY&from=searchOnDesktopSerp")

# A data structure where to store the scraped job openings
jobs = []

# Select the job opening elements on the page
jobs_container_element = driver.find_element(By.CSS_SELECTOR, "#mosaic-provider-jobcards")
job_elements = jobs_container_element.find_elements(By.CSS_SELECTOR, "[data-testid=\"slider_item\"]")

# Scrape each job opening on the page
for job_element in job_elements:
    title_element = job_element.find_element(By.CSS_SELECTOR, "h2.jobTitle")
    title = title_element.text

    url_element = title_element.find_element(By.CSS_SELECTOR, "a")
    url = url_element.get_attribute("href")

    company_element =job_element.find_element(By.CSS_SELECTOR, "[data-testid=\"company-name\"]")
    company = company_element.text

    location_element = job_element.find_element(By.CSS_SELECTOR, "[data-testid=\"text-location\"]")
    location = location_element.text

    tags = []
    tags_container_element = job_element.find_element(By.CSS_SELECTOR, ".jobMetaDataGroup")
    tag_elements = tags_container_element.find_elements(By.CSS_SELECTOR, "[data-testid=\"attribute_snippet_testid\"]")
    for tag_element in tag_elements:
        tag = tag_element.text
        tags.append(tag)

    # Check whether the \"Easy Apply\" element is on the page
    try:
        job_element.find_element(By.CSS_SELECTOR, "[data-testid=\"indeedApply\"]")
        easily_apply = True
    except NoSuchElementException:
        easily_apply = False

    description = []
    description_container_element = job_element.find_element(By.CSS_SELECTOR, "[role=\"presentation\"]")
    description_elements = description_container_element.find_elements(By.CSS_SELECTOR, "ul li")
    for description_element in description_elements:
        description_item_text = description_element.text
        # Ignore empty description strings
        if (description_item_text != ""):
            description.append(description_item_text)

    # Store the scraped data
    job = {
        "title": title,
        "url": url,
        "company": company,
        "location": location,
        "tags": tags,
        "easily_apply": easily_apply,
        "description": description
    }
    jobs.append(job)

# Export the scraped data to an output CSV file
csv_file = "jobs.csv"
csv_headers = ["title", "url", "company", "location", "tags", "easily_apply", "description"]

with open(csv_file, mode="w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=csv_headers)
    writer.writeheader()
    for job in jobs:
        writer.writerow({
            "title": job["title"],
            "url": job["url"],
            "company": job["company"],
            "location": job["location"],
            "tags": ";".join(job["tags"]),
            "easily_apply": "Yes" if job["easily_apply"] else "No",
            "description": ";".join(job["description"])
        })

# Close the web driver
driver.quit()

Run the scraper:

python3 script.py # On Linux/macOS
python script.py # On Windows

A CSV file called jobs.csv should appear with the extracted data.

Unlock Indeed Data With Ease

Indeed is known for employing anti-bot techniques, including CAPTCHAs, which can disrupt web scraping projects and even result in IP bans. Bypassing these measures is essential to conduct effective web scraping on Indeed. ProxyTee helps to overcome these challenges and ensure uninterrupted data collection.

While this guide has provided insights into scraping Indeed, the platform often introduces new protective measures. This can be very frustrating when you only want to gather job market information.

Consider using ProxyTee to automate your proxy handling, ensuring reliable access and smooth data collection while helping you maintain an effective web scraping setup. ProxyTee offers affordable access to residential proxies and flexible rotation, with simple API integration (Simple API) to help you customize how you use its service. This not only helps to reduce cost, but allows you to be compliant by being respectful of robots.txt at all times.

Conclusion

In this tutorial, you learned what an Indeed scraper is and how to build one using Python and Selenium. While manual scraping is feasible, scaling these operations without robust proxy management can be time-consuming, tedious and frustrating. ProxyTee offers an array of features, including a user-friendly interface that ensures a seamless web scraping experience, whether you are collecting market research or conducting competitive analysis.

Whether you are looking for job openings, or wanting to analyze job market trends, make use of ProxyTee today. Get access to high quality residential proxies that rotate automatically with Auto rotation and a wide array of location targets(Global coverage) . We offer competitive pricing and ensure you receive a service that is value for your money!

To scale this workflow in production without bandwidth throttling, explore our LinkedIn & Job Board Scraping Proxies, Python Requests & HTTPX Integration, United States Residential Proxies, and ProxyTee vs. Bright Data Benchmark.

Technical FAQ

Frequently Asked Questions

When scraping Indeed job postings with Python, modern anti-bot firewalls (Cloudflare, Akamai, DataDome, PerimeterX) inspect ASN ownership, request velocity, and TLS fingerprints. Routing your traffic through ProxyTee's 30,000,000+ real household residential IPs across 100+ countries replaces flagged datacenter subnets with authentic consumer ISP connections.

Tags:PythonWeb ScrapingJob Scraping
AM
Alex Mercer • Lead Network & Web Scraping Architect
Technically Reviewed by Marcus Vance • Director of Proxy Infrastructure & Network Security

Principal infrastructure engineer at ProxyTee specializing in rotating residential proxy networks, SOCKS5/HTTP tunneling, TLS fingerprint mitigation, and distributed data extraction pipelines.