Thứ Tư, 16 tháng 9, 2026

Web Scraping là gì? (Cho Python đi "nhặt dữ liệu" trên Internet thay vì ngồi copy từng dòng bằng tay.)

Bạn muốn mua một chiếc điện thoại giá rẻ nhất. Bạn phải mở 5 trang web khác nhau, gõ tên máy, rồi căng mắt so sánh từng đồng từ Shopee, Lazada đến Tiki. Đến lúc bạn gõ xong thì...máy đã hết hàng.

Có một kiểu công việc rất dễ khiến con người... mất niềm tin vào cuộc sống. 😭

Ví dụ bạn cần lấy danh sách sản phẩm từ một website:

Tên sản phẩm
Giá
Tên sản phẩm
Giá
Tên sản phẩm
Giá
...

Website có 500 sản phẩm.

Bạn mở website.

Copy.

Paste vào Excel.

Cuộn xuống.

Copy.

Paste.

Cuộn tiếp.

Copy.

Paste.

Sau khoảng 30 phút:

"Ủa... mình đang làm nghề gì vậy?"

😂

Nếu dữ liệu có cấu trúc và website cho phép việc truy cập tự động, Python có thể giúp chúng ta làm phần công việc lặp đi lặp lại này.

Đó chính là lúc Web Scraping xuất hiện.


Web Scraping là gì?

Nói đơn giản:

Web Scraping là quá trình dùng chương trình để truy cập một trang web, đọc nội dung của trang và lấy ra những dữ liệu mình cần.

Ví dụ:

Website
   ↓
Python truy cập
   ↓
Đọc HTML
   ↓
Tìm dữ liệu cần lấy
   ↓
Tên sản phẩm
Giá
Link
Hình ảnh
...
   ↓
Lưu lại

Con người nhìn website bằng trình duyệt.

Python thì không "nhìn" website theo cách của con người.

Nó nhận về dữ liệu từ máy chủ, thường là HTML, rồi chúng ta dùng code để tìm phần mình cần.


"Scraping" nghĩa là gì?

"Scrape" trong ngữ cảnh này có thể hiểu nôm na là:

cạo / lấy dữ liệu ra khỏi một nguồn.

Nghe hơi giống:

"Cạo lớp dữ liệu trên website."

😅

Nhưng thực tế không phải Python cầm cái xẻng đi cạo màn hình.

Nó đang đọc cấu trúc dữ liệu của trang web.


Ví dụ cực kỳ đơn giản

Giả sử website có HTML:

<h1>DatVietLapTrinh</h1>

<p>Học lập trình từ lỗi thật đến sản phẩm thật.</p>

Python có thể lấy nội dung HTML về.

Sau đó chúng ta có thể tìm:

<h1>

và lấy ra:

DatVietLapTrinh

Hoặc tìm:

<p>

để lấy:

Học lập trình từ lỗi thật đến sản phẩm thật.

Vậy Web Scraping về cơ bản gồm hai việc:

1. Lấy trang web về
2. Tìm dữ liệu mình cần trong đó

Python lấy website bằng cách nào?

Một thư viện rất phổ biến cho việc gửi HTTP request là:

requests

Nếu máy bạn chưa có:

python -m pip install requests

Sau đó:

import requests

url = "https://example.com"

response = requests.get(url)

print(response.status_code)

Nếu thành công, bạn có thể thấy:

200

Bạn có thể xem lại Bài #72? Không, khoan. 😄

Phần HTTP Status Code chúng ta đã gặp ở các bài trước, và 200 thường có nghĩa là request đã nhận được phản hồi thành công.


Lấy HTML về

Tiếp theo:

import requests

url = "https://example.com"

response = requests.get(url)

html = response.text

print(html)

response.text chứa nội dung dạng text mà server trả về.

Bạn có thể tưởng tượng:

Website
   ↓
requests.get()
   ↓
Server trả HTML
   ↓
response.text

Python lúc này đã có "nguyên liệu".

Nhưng có một vấn đề.

HTML của một website thực tế có thể dài kinh khủng.

Ví dụ:

<!DOCTYPE html>
<html>
<head>
    ...
</head>

<body>
    ...
    <div>
        ...
    </div>

    <section>
        ...
    </section>

    ...
</body>
</html>

Nếu chỉ:

print(response.text)

thì bạn có thể nhận được một đống HTML dài ngoằng.

Nhìn vào:

"Dữ liệu đâu?"

😂

Đây là lúc chúng ta cần công cụ để đọc và tìm trong HTML.


BeautifulSoup xuất hiện 🍲

Một thư viện rất nổi tiếng để phân tích HTML là:

BeautifulSoup

Cài đặt:

python -m pip install beautifulsoup4

Sau đó:

import requests
from bs4 import BeautifulSoup

url = "https://example.com"

response = requests.get(url)

soup = BeautifulSoup(response.text, "html.parser")

print(soup.title)

Ở đây:

BeautifulSoup(response.text, "html.parser")

giúp chúng ta biến HTML thành một cấu trúc dễ tìm kiếm hơn.

Có thể tưởng tượng:

HTML thô
   ↓
BeautifulSoup
   ↓
"Cây" HTML dễ tìm
   ↓
Tìm title
Tìm heading
Tìm link
Tìm paragraph
...

"Soup" là món ăn gì vậy? 😂

Không.

Bạn không cần mua hành, tiêu, nước mắm.

BeautifulSoup chỉ là tên của thư viện.

Nhưng cái tên khá dễ nhớ:

HTML đưa vào nồi → BeautifulSoup giúp mình "múc" phần dữ liệu cần lấy.

Python đôi khi đặt tên thư viện cũng khá có tâm. 😆


Lấy tiêu đề trang

Ví dụ:

import requests
from bs4 import BeautifulSoup

url = "https://example.com"

response = requests.get(url)

soup = BeautifulSoup(response.text, "html.parser")

print(soup.title)

Có thể nhận:

<title>Example Domain</title>

Nếu chỉ muốn lấy chữ bên trong:

print(soup.title.text)

Kết quả:

Example Domain

Lấy một thẻ HTML

Giả sử HTML:

<h1>Xin chào Python</h1>

Bạn có thể:

heading = soup.find("h1")

print(heading.text)

Kết quả:

Xin chào Python

Đây là một trong những ý tưởng quan trọng nhất của Web Scraping:

Biết dữ liệu mình muốn nằm ở đâu trong HTML.


Ví dụ dễ hình dung hơn

Giả sử HTML có:

<h1>Danh sách sản phẩm</h1>

<p>Sản phẩm A</p>
<p>Sản phẩm B</p>
<p>Sản phẩm C</p>

Ta có thể lấy tất cả thẻ <p>:

paragraphs = soup.find_all("p")

for paragraph in paragraphs:
    print(paragraph.text)

Kết quả:

Sản phẩm A
Sản phẩm B
Sản phẩm C

Thay vì:

Copy
Paste
Copy
Paste
Copy
Paste

Python làm:

Tìm
Tìm
Tìm
Tìm
...

nhanh hơn rất nhiều.


find()find_all() khác nhau thế nào?

Đây là chỗ người mới thường gặp.

find()

Tìm một phần tử đầu tiên phù hợp.

soup.find("p")

find_all()

Tìm tất cả phần tử phù hợp.

soup.find_all("p")

Nếu HTML có:

<p>A</p>
<p>B</p>
<p>C</p>

thì:

soup.find("p")

sẽ lấy:

A

Còn:

soup.find_all("p")

sẽ lấy:

A
B
C

Có thể nhớ rất đời thường:

find() = "Đưa tôi một cái."

find_all() = "Đưa tôi hết."


Lấy link trên website

HTML:

<a href="https://example.com">Trang chủ</a>

Python:

link = soup.find("a")

print(link.text)
print(link.get("href"))

Kết quả:

Trang chủ
https://example.com

Ở đây:

link.text

lấy nội dung hiển thị.

Còn:

link.get("href")

lấy giá trị của thuộc tính href.


Đây mới là lúc Web Scraping trở nên thú vị

Giả sử một trang có:

<div class="product">
    <h2>Áo thun</h2>
    <p class="price">199000</p>
</div>

Nếu chúng ta biết cấu trúc HTML, có thể tìm:

product = soup.find("div", class_="product")

name = product.find("h2").text
price = product.find("p", class_="price").text

print(name)
print(price)

Kết quả:

Áo thun
199000

Vậy thay vì lấy toàn bộ website, chúng ta có thể nói:

"Python, tao chỉ cần tên và giá. Những thứ còn lại mày bỏ qua."

😂


class_ tại sao lại có dấu gạch dưới?

HTML thường có:

<div class="product">

Trong BeautifulSoup, chúng ta có thể viết:

soup.find("div", class_="product")

Tại sao không viết:

class="product"

Bởi vì class là một từ khóa đặc biệt của Python.

Vì vậy BeautifulSoup dùng:

class_

Dấu _ nhỏ xíu nhưng khá quan trọng.


Web Scraping thực tế thường đi theo quy trình này

Một quy trình đơn giản:

1. Xác định website
        ↓
2. Gửi request
        ↓
3. Nhận HTML
        ↓
4. Phân tích HTML
        ↓
5. Tìm phần tử cần lấy
        ↓
6. Lấy text / link / thuộc tính
        ↓
7. Làm sạch dữ liệu
        ↓
8. Lưu vào CSV / JSON / database

Bạn đã học:

Bài #69: Đọc CSV

thì bây giờ có thể nối hai chuyện lại.

Ví dụ:

Website
   ↓
Python Scraping
   ↓
Tên sản phẩm
Giá
Link
   ↓
CSV

Đây chính là lúc những bài trước bắt đầu "nói chuyện" với nhau.


Ví dụ: lấy danh sách tiêu đề

Giả sử trang có nhiều:

<h2>...</h2>

Ta có thể:

import requests
from bs4 import BeautifulSoup

url = "https://example.com"

response = requests.get(url)

soup = BeautifulSoup(response.text, "html.parser")

titles = soup.find_all("h2")

for title in titles:
    print(title.text.strip())

strip() giúp bỏ khoảng trắng thừa ở đầu và cuối.

Ví dụ:

"   Sản phẩm A   "

thành:

"Sản phẩm A"

Nhìn nhỏ vậy thôi nhưng khi lấy dữ liệu thật, việc làm sạch như thế này rất thường xuyên xảy ra.


Nhưng khoan... Website nào cũng Scraping được sao?

Không.

Đây là phần rất quan trọng.

Web Scraping không có nghĩa:

"Thấy website là muốn lấy gì thì lấy."

Mỗi website có thể có:

  • điều khoản sử dụng riêng,
  • giới hạn request,
  • robots.txt,
  • cơ chế chống bot,
  • API chính thức,
  • dữ liệu yêu cầu đăng nhập,
  • dữ liệu cá nhân,
  • nội dung có bản quyền.

Vì vậy trước khi scrape một website, bạn cần kiểm tra xem việc truy cập và sử dụng dữ liệu có phù hợp với quy định của website và pháp luật áp dụng hay không.

Nếu website có API chính thức cho dữ liệu bạn cần, API thường là lựa chọn đáng cân nhắc hơn so với việc cố "cào" HTML.

Nói đơn giản:

Website đã có cửa chính thì đừng cố trèo cửa sổ. 😅


robots.txt là gì?

Bạn có thể gặp một file:

/robots.txt

Ví dụ:

https://example.com/robots.txt

File này thường chứa các chỉ dẫn dành cho crawler/bot.

Ví dụ có thể thấy:

User-agent: *
Disallow: /private/

Điều này cho biết website không muốn crawler truy cập khu vực đó theo chỉ dẫn của robots.txt.

Nhưng cần nhớ:

robots.txt không phải là "giấy phép pháp lý" cho mọi loại sử dụng dữ liệu.

Nó là một tín hiệu/chỉ dẫn kỹ thuật dành cho crawler.

Vì vậy đừng hiểu:

robots.txt cho phép
=
Mình được quyền lấy và sử dụng mọi dữ liệu.

Hai chuyện đó không giống nhau.


Đừng gửi request liên tục như máy điên 🤖

Giả sử bạn viết:

for i in range(100000):
    requests.get(url)

thì...

Xin chúc mừng.

Bạn vừa biến Python thành một người gõ cửa nhà người khác:

"Cốc cốc."

"Cốc cốc."

"Cốc cốc."

"Cốc cốc."

...

Website:

"Anh bạn... bình tĩnh." 😭

Khi scraping, cần tôn trọng giới hạn của website.

Không nên gửi hàng nghìn request vô tội vạ.

Có thể cần:

  • giới hạn tốc độ,
  • nghỉ giữa các request,
  • cache dữ liệu,
  • chỉ lấy thứ thực sự cần,
  • sử dụng API nếu có,
  • xử lý lỗi và retry hợp lý.

Một lỗi rất thường gặp: Status Code không phải 200

Bạn viết:

response = requests.get(url)

print(response.status_code)

Nhưng nhận:

403

Đừng lập tức nghĩ:

"BeautifulSoup hỏng."

BeautifulSoup chưa kịp làm gì cả. 😅

Request đã bị từ chối.

Bạn cần phân biệt:

Request
   ↓
Server
   ↓
403
   ↓
BeautifulSoup chưa phải vấn đề

Đây là tư duy debug rất quan trọng:

Lỗi xảy ra ở bước nào thì xử lý ở bước đó.


Một số Status Code bạn sẽ gặp

Ví dụ:

200 → thành công
301/302 → chuyển hướng
400 → request không hợp lệ
401 → cần xác thực
403 → bị từ chối
404 → không tìm thấy
429 → quá nhiều request
500 → lỗi phía server

Đặc biệt với Web Scraping:

403
429

là hai mã bạn có thể gặp trong những tình huống website hạn chế truy cập tự động.


Thêm User-Agent có phải lúc nào cũng giải quyết được không?

Bạn có thể thấy các ví dụ trên Internet viết:

headers = {
    "User-Agent": "Mozilla/5.0"
}

response = requests.get(
    url,
    headers=headers
)

Điều này có thể giúp một số website xử lý request giống request từ trình duyệt hơn.

Nhưng:

Không có nghĩa thêm User-Agent là vượt qua được mọi cơ chế chống bot.

Website có thể sử dụng nhiều cách khác để xác định request tự động.

Và quan trọng hơn:

Đừng xem việc giả lập trình duyệt như một cách để né các hạn chế mà website đã đặt ra.

Mục tiêu của bài này là học cách lấy dữ liệu hợp lệ, không phải chơi trò "trốn tìm" với server. 😄


Website hiện đại còn có một cái bẫy khác

Bạn mở trình duyệt và thấy:

Tên sản phẩm
Giá
Hình ảnh

Nhưng:

requests.get(url)

lại không thấy dữ liệu đó trong HTML.

Tại sao?

Có thể website sử dụng JavaScript để tải dữ liệu sau khi trang được mở.

Ví dụ:

Browser
   ↓
Tải HTML
   ↓
JavaScript chạy
   ↓
Gọi API
   ↓
Dữ liệu xuất hiện

Trong khi:

requests.get()
   ↓
Nhận HTML ban đầu

Python có thể không nhận được phần dữ liệu được JavaScript tạo ra sau đó.

Đây là lý do:

Không phải website nào cũng có thể scrape bằng requests + BeautifulSoup một cách đơn giản.

Nếu dữ liệu đến từ API, tìm hiểu API chính thức có thể là hướng tốt hơn.

Một số trường hợp khác có thể cần công cụ tự động hóa trình duyệt, nhưng đó là câu chuyện nâng cao hơn.


Web Scraping khác API như thế nào?

Có thể hình dung:

Scraping

Website
   ↓
HTML
   ↓
Python đọc HTML
   ↓
Tìm dữ liệu

API

Website / Server
   ↓
API
   ↓
Python request
   ↓
JSON
   ↓
Dữ liệu

API thường trả dữ liệu có cấu trúc rõ ràng hơn.

Ví dụ:

{
    "name": "Áo thun",
    "price": 199000
}

Trong khi HTML có thể là:

<div class="product">
    <h2>Áo thun</h2>
    <span class="price">199000</span>
</div>

Nếu website cung cấp API phù hợp và cho phép sử dụng, API thường dễ xử lý và ổn định hơn.


Một ví dụ hoàn chỉnh nhỏ

Để mọi thứ kết nối lại, đây là một ví dụ tối giản:

import requests
from bs4 import BeautifulSoup

url = "https://example.com"

response = requests.get(url, timeout=10)

if response.status_code == 200:

    soup = BeautifulSoup(
        response.text,
        "html.parser"
    )

    title = soup.title

    if title:
        print(title.text.strip())

else:
    print("Không lấy được trang.")
    print("Status code:", response.status_code)

Có vài điểm đáng chú ý.

timeout=10

requests.get(url, timeout=10)

Nghĩa là chúng ta không muốn chương trình ngồi chờ mãi nếu server không phản hồi như mong đợi.

Kiểm tra Status Code

if response.status_code == 200:

Không nên cứ nhận response là lập tức xử lý như thể mọi thứ đều ổn.

Kiểm tra title

if title:

Website không phải lúc nào cũng có cấu trúc đúng như bạn tưởng.

Đây chính là:

Debug + kiểm tra dữ liệu đầu vào.


Web Scraping không phải "copy website"

Đây cũng là một hiểu lầm phổ biến.

Scraping không nhất thiết có nghĩa:

"Tải toàn bộ website về máy."

Bạn có thể chỉ lấy:

Tên
Giá
Link
Ngày

hoặc:

Tên bài viết
URL

hoặc:

Danh sách sản phẩm

Sau đó lưu:

CSV
JSON
Database
Excel

Mục tiêu là:

Lấy đúng dữ liệu cần thiết, không phải lấy tất cả mọi thứ.


Web Scraping dùng vào việc gì?

Có rất nhiều trường hợp hợp pháp và phù hợp.

Ví dụ:

Theo dõi dữ liệu công khai

Website
   ↓
Lấy dữ liệu
   ↓
CSV
   ↓
Phân tích

Tổng hợp thông tin

Ví dụ một nguồn dữ liệu công khai cho phép truy cập tự động.

Website A
Website B
Website C
      ↓
    Python
      ↓
   Dữ liệu
      ↓
     CSV

Nghiên cứu dữ liệu

Lấy dữ liệu công khai để:

  • phân tích xu hướng,
  • thống kê,
  • làm project học tập,
  • thử nghiệm xử lý dữ liệu.

Automation

Đây mới là phần rất đáng chú ý.

Scraping có thể trở thành một bước trong một quy trình lớn hơn:

Lấy dữ liệu
    ↓
Làm sạch
    ↓
Phân tích
    ↓
Lưu database
    ↓
Tạo báo cáo

Và đó chính là cây cầu dẫn tới Automation.


Chua thêm 😭

Ngày xưa:

"Cần 100 dòng dữ liệu."

Bạn:

Copy
Paste
Copy
Paste
Copy
Paste

Sau một tiếng:

"Xong!"

Python:

"Cho tôi 5 phút."

Bạn:

"Làm gì?"

Python:

"Viết code."

Bạn:

"Xong chưa?"

Python:

"Chưa."

Bạn:

"..."

Python:

"Chờ tôi chạy."

😂

Nhưng khi code đã chạy ổn:

Website
   ↓
Python
   ↓
100 dòng
1.000 dòng
10.000 dòng

thì bạn bắt đầu hiểu tại sao người ta thích automation.


Nhưng đừng biến Web Scraping thành "đồ nghề spam"

Có một ranh giới rất quan trọng.

Web Scraping có thể là một kỹ năng lập trình hữu ích.

Nhưng không nên dùng nó để:

  • lấy dữ liệu cá nhân trái phép,
  • vượt qua quyền truy cập,
  • né giới hạn của website,
  • phá hệ thống,
  • spam request,
  • thu thập dữ liệu trái với điều khoản hoặc pháp luật áp dụng.

Một programmer tốt không chỉ hỏi:

"Code này chạy được không?"

Mà còn hỏi:

"Mình có nên làm việc này theo cách này không?"

Đó cũng là một phần của tư duy lập trình thực tế.


Checklist Web Scraping cho người mới

Trước khi viết code, hãy kiểm tra:

☑ Website có cho phép truy cập tự động không?

☑ Có API chính thức không?

☑ Dữ liệu mình cần có phải dữ liệu công khai không?

☑ Có cần đăng nhập không?

robots.txt nói gì?

☑ Điều khoản sử dụng của website có hạn chế gì không?

☑ Website trả HTML trực tiếp hay dữ liệu được JavaScript tải sau?

☑ Request có trả 200 không?

☑ Có cần timeout không?

☑ Có cần giới hạn tốc độ request không?

☑ Mình có thật sự cần lấy toàn bộ dữ liệu không?

☑ Dữ liệu sau khi lấy sẽ lưu ở đâu?

CSV?
JSON?
Excel?
Database?

Nếu trả lời được những câu này trước khi code, bạn đã tránh được kha khá "hố". 😄


FAQ nhanh

1. Web Scraping có khó không?

Phần cơ bản không quá khó.

Một quy trình đơn giản có thể chỉ gồm:

requests
+
BeautifulSoup

Khó hơn khi website:

  • dùng JavaScript nhiều,
  • có pagination,
  • có login,
  • có CAPTCHA,
  • có chống bot,
  • dữ liệu thay đổi cấu trúc.

2. Có cần học HTML trước không?

Nên.

Bạn không cần trở thành chuyên gia HTML.

Nhưng nên hiểu:

<html>
<head>
<body>
<div>
<p>
<h1>
<a>
<img>
class
id
href
src

Vì Web Scraping thực chất là:

đọc cấu trúc HTML để tìm dữ liệu.


3. BeautifulSoup có tải website không?

Không.

Thường ta dùng:

requests

để lấy HTML.

Sau đó:

BeautifulSoup

để phân tích HTML.

Có thể nhớ:

requests = đi lấy

BeautifulSoup = ngồi bóc

😂


4. Web Scraping có phải AI không?

Không.

Web Scraping là kỹ thuật lấy dữ liệu từ web.

AI có thể được kết hợp vào quy trình sau này để:

  • phân loại dữ liệu,
  • tóm tắt,
  • trích xuất thông tin,
  • xử lý văn bản.

Nhưng scraping bản thân nó không phải AI.


5. Có thể lưu dữ liệu scraping vào CSV không?

Có.

Và đây là lúc kiến thức từ bài #69 phát huy tác dụng.

Website
   ↓
Scraping
   ↓
Python
   ↓
CSV

Bạn đang bắt đầu ghép những mảnh kiến thức cũ thành một project hoàn chỉnh.


Tổng kết

Web Scraping nghe có vẻ "cao siêu", nhưng bản chất ban đầu khá dễ hiểu:

Dùng code để lấy dữ liệu từ website và xử lý dữ liệu đó.

Một quy trình cơ bản:

Website
   ↓
requests
   ↓
HTML
   ↓
BeautifulSoup
   ↓
Tìm dữ liệu
   ↓
Làm sạch
   ↓
Lưu lại

Hai thư viện đầu tiên cần nhớ:

python -m pip install requests

và:

python -m pip install beautifulsoup4

Sau đó:

import requests
from bs4 import BeautifulSoup

Những thứ quan trọng đầu tiên:

requests.get()
response.text
response.status_code

BeautifulSoup()
find()
find_all()
get()

Nhưng còn một thứ quan trọng hơn code:

Đừng scrape chỉ vì "scrape được".

Hãy biết:

  • dữ liệu ở đâu,
  • website cho phép gì,
  • mình cần dữ liệu nào,
  • lấy bao nhiêu là đủ,
  • lấy xong để làm gì.