Web Scraping Basics in Python
In this lesson: Scrape web pages in Python using requests and HTML parsing — basics, ethics and a simple working example. In Hindi & English.
What is web scraping?
Web scraping means a program downloads a web page and pulls out the information you need – prices, titles, tables, links – instead of copying it by hand. Python is the most popular language for it because of two libraries: requests (download the page) and BeautifulSoup (find things inside the HTML).
| Step | Tool |
|---|---|
| 1. Check you are allowed (terms, robots.txt, API available?) | Browser, urllib.robotparser |
| 2. Download the HTML | requests.get() |
| 3. Parse and find elements | BeautifulSoup, CSS selectors |
| 4. Clean and save | csv, json, pandas |
| 5. Be polite | Delay between requests, identify yourself |
pip install requests beautifulsoup4
Scrape only public data, respect the website’s terms and robots.txt, never collect personal data (phone numbers, emails) without permission, do not bypass logins or CAPTCHAs, and keep a delay so you do not overload the server. If the site offers an API, use the API.
Step 1: parse HTML with BeautifulSoup
Practise on a small HTML file first. Save this as price_list.html:
<html><body>
<h1>Sharma Sweets – Price List</h1>
<table id="prices">
<tr><th>Sweet</th><th>Price</th><th>Stock</th></tr>
<tr class="item"><td>Kaju Katli</td><td>₹900/kg</td><td>Yes</td></tr>
<tr class="item"><td>Ghewar</td><td>₹650/kg</td><td>Yes</td></tr>
<tr class="item sold"><td>Kalakand</td><td>₹600/kg</td><td>No</td></tr>
</table>
<a href="/order">Order online</a> <a href="https://wa.me/919829012345">WhatsApp</a>
</body></html>from bs4 import BeautifulSoup
with open("price_list.html", encoding="utf-8") as f:
soup = BeautifulSoup(f.read(), "html.parser")
print(soup.h1.get_text(strip=True)) # first <h1>
print(soup.find("td").text) # first <td>
print(len(soup.find_all("tr", class_="item")), "items")
for row in soup.find_all("tr", class_="item"):
name, price, stock = [td.get_text(strip=True) for td in row.find_all("td")]
print(f"{name:<11} {price:<9} in stock: {stock}")Step 2: CSS selectors with select()
If you know CSS, select() and select_one() are the fastest way to find elements. Right-click an element in Chrome → Inspect to see its tags and classes.
from bs4 import BeautifulSoup
soup = BeautifulSoup(open("price_list.html", encoding="utf-8").read(), "html.parser")
print([td.text for td in soup.select("#prices tr.item td:first-child")])
print(soup.select_one("tr.sold td").text, "is sold out")
for a in soup.select("a[href]"):
print(a.text, "→", a["href"])| Selector | Finds |
|---|---|
h2 | all h2 tags |
.price | class="price" |
#prices | id="prices" |
table tr.item | tr with class item inside a table |
a[href] | links that have an href |
td:first-child | first cell of each row |
Step 3: scrape a real page
Now download a real page – the lesson list of this Python course – and extract every lesson link. (Scraping CodingEasily for learning is allowed.)
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
URL = "https://codingeasily.in/tutorial/python/"
headers = {"User-Agent": "StudentScraper/1.0 (learning project)"}
r = requests.get(URL, headers=headers, timeout=10)
r.raise_for_status()
soup = BeautifulSoup(r.text, "html.parser")
links = soup.select("a.topic-card-link")
print("lessons found:", len(links))
for a in links[:5]:
title = a.select_one(".name.english-content").get_text(strip=True)
print("-", title, "→", urljoin(URL, a["href"]))urljoin turns relative links such as /tutorial/python/lists/ into full URLs.
Step 4: check robots.txt in code
from urllib import robotparser
rp = robotparser.RobotFileParser()
rp.set_url("https://codingeasily.in/robots.txt")
rp.read()
for path in ["/tutorial/python/", "/search/", "/config/"]:
allowed = rp.can_fetch("StudentScraper", "https://codingeasily.in" + path)
print(f"{path:<20} allowed: {allowed}")
print("crawl delay:", rp.crawl_delay("StudentScraper"), "second(s)")Step 5: save a table to CSV (with polite delays)
import csv, time
import requests
from bs4 import BeautifulSoup
BASE = "https://codingeasily.in"
pages = ["/blog/"] # add more pages to this list
rows = []
for path in pages:
html = requests.get(BASE + path, timeout=10, headers={"User-Agent": "StudentScraper/1.0"}).text
soup = BeautifulSoup(html, "html.parser")
for h3 in soup.select("h3[data-en]"):
rows.append({"title": h3["data-en"], "title_hi": h3["data-hi"]})
time.sleep(1) # be polite: 1 request per second
with open("blog_titles.csv", "w", newline="", encoding="utf-8-sig") as f:
w = csv.DictWriter(f, fieldnames=["title", "title_hi"])
w.writeheader()
w.writerows(rows)
print(len(rows), "titles saved. First two:")
for r in rows[:2]:
print("-", r["title"], "|", r["title_hi"])Shortcut: HTML tables with pandas
import pandas as pd
from io import StringIO
html = open("price_list.html", encoding="utf-8").read()
table = pd.read_html(StringIO(html), attrs={"id": "prices"})[0]
table["Price (₹)"] = table["Price"].str.extract(r"(\d+)").astype(int)
print(table[["Sweet", "Price (₹)", "Stock"]])When scraping will not work
- JavaScript-rendered pages: the HTML you download is almost empty because data loads later. Look in DevTools → Network for a JSON API, or use a browser tool such as Playwright.
- Login-only data: do not scrape it unless it is your own account and the terms allow it.
- Blocking (403/429): slow down, respect robots.txt, or ask the site owner for access.
- Changing layout: selectors break when the site is redesigned – write code that fails clearly.
Practice tasks
- From
price_list.html, print only sweets that are in stock with prices as numbers. - Scrape the Python course page and count how many lessons are in each group.
- Save the blog titles to JSON instead of CSV.
- Write a function that returns
Noneinstead of crashing when a selector finds nothing.
Summary
- Check permission first: terms, robots.txt, and whether an API exists.
requests.get(with timeout and User-Agent) downloads;BeautifulSoupparses.- Use
find/find_allor CSS selectors withselect. - Clean text with
get_text(strip=True), build full links withurljoin, save with csv/json/pandas. - Add delays and never collect personal data.
