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ạiCon 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:
DatVietLapTrinhHoặ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à:
requestsNếu máy bạn chưa có:
python -m pip install requestsSau đó:
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:
200Bạ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.textPython 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à:
BeautifulSoupCài đặt:
python -m pip install beautifulsoup4Sau đó:
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 DomainLấ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 CThay vì:
Copy
Paste
Copy
Paste
Copy
PastePython làm:
Tìm
Tìm
Tìm
Tìm
...nhanh hơn rất nhiều.
find() và 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:
ACòn:
soup.find_all("p")sẽ lấy:
A
B
CCó 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.textlấ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
199000Vậ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 / databaseBạ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.txtVí dụ:
https://example.com/robots.txtFile 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.txtkhô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
429là 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 ảnhNhư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ệnTrong khi:
requests.get()
↓
Nhận HTML ban đầuPython 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 + BeautifulSoupmộ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ệuAPI
Website / Server
↓
API
↓
Python request
↓
JSON
↓
Dữ liệuAPI 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àyhoặc:
Tên bài viết
URLhoặc:
Danh sách sản phẩmSau đó lưu:
CSV
JSON
Database
ExcelMụ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íchTổ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
↓
CSVNghiê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áoVà đó 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
PasteSau 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òngthì 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
+
BeautifulSoupKhó 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
srcVì 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
↓
CSVBạ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ạiHai thư viện đầu tiên cần nhớ:
python -m pip install requestsvà:
python -m pip install beautifulsoup4Sau đó:
import requests
from bs4 import BeautifulSoupNhữ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ì.