#python: Tự động crawl dữ liệu trang web đơn giản với BeautifulSoup

Hieu Nguyen
4 min readDec 31, 2019

--

Series tự học Python

Bài viết này là những ghi chép lại để nhớ và khi cần thì tìm lại trong quá trình tự học về Python, BeautifulSoup và Pandas. Nguồn tham khảo xin xem thêm tại link này.

— Update 02/02/2020 —

Đặt vấn đề

Khi nghiên cứu, học tập về khoa học phân tích dữ liệu, anh em sẽ cần nguồn dữ liệu đầy đủ để thực hành. Thông thường anh em sẽ tìm trên Internet những tập tin CSV hoặc các trang chia sẻ API để chúng ta truy cập. Tuy nhiên đôi khi những dữ liệu anh em muốn lại chỉ cần như một phần của trang web. Lúc đó chúng ta sẽ cần đến các kỹ thuật rút trích nội dung website để lấy dữ liệu từ trang web mình cần.

OK đó là lúc chúng ta cần đọc phần tiếp theo bên dưới đây. Tuy nhiên lưu ý bài viết này các bạn cần phải có kiến thức cơ bản về HTML, CSS đã nhé.

Trước tiên, chúng ta cần cài đặt Python trên máy tính. Hoặc bạn có thể sử dụng Jupyter Notebook thì càng tiện. Trong bài này mình không nhắc lại cách cài đặt hai cái trên nữa.

Bài này thực hành cách crawl dữ liệu phim từ IMDB

Chúng ta sẽ lấy dữ liệu từ trang này

1. Chuẩn bị thư viện

Chúng ta sẽ sử dụng thư viện request để lấy toàn bộ dữ liệu 1 trang web về, sau đó sử dụng Beautifulsoup để định dạng, chuẩn hóa và trích xuất các nội dung mình cần. Cuối cùng là sử dụng pandas để trình bày dữ liệu và lưu ra tập tin csv phục vụ cho khoa học dữ liệu.

Ta cài đặt thư viện để sử dụng cho bài này bằng lệnh sau:

pip install pandas requests bs4

2. Import thư viện

Sau khi cài đặt ta import thư viện để sử dụng

import bs4
import pandas
import requests

3. scrape dữ liệu của IMDb

Ta sử dụng hàm request.get để lấy dữ liệu từ trang IMDb về. Thư viện request sẽ tạo một yêu cầu GET cho máy chủ web để tải xuống nội dung HTML của một trang web nhất định cho chúng ta.

url = 'https://www.imdb.com/search/title/?count=100&groups=top_1000&sort=user_rating%27' # các bạn thay link của trang mình cần lấy dữ liệu tại đâydef get_page_content(url):
page = requests.get(url,headers={"Accept-Language":"en-US"})
return bs4.BeautifulSoup(page.text,"html.parser")
soup = get_page_content(url)

Sau khi hoàn tất, chúng ta sẽ có một đối tượng Response. Đối tượng này có thuộc tính status_code để anh em kiểm tra xem việc download dữ liệu thành công hay không.

Khi mã status_code trả về kết quả 200 thì anh em có thể in ra nội dung HTML bằng cách sử dụng thuộc tính content

4. Trích xuất từng dữ liệu thô

Ta sẽ trích xuất từng dữ liệu thô của trang như: tên phim, năm phát hành, điểm đánh giá, số lượt bình chọn…

Việc chúng ta muốn lấy trường nào trong số dữ liệu crawl về được thì các anh em chỉ cần chuột phải vào trang web → chọn Inspect element. Sau đó tìm đến thuộc tính tương ứng.

Ở đây chúng ta lựa chọn class làm giá trị tìm kiếm và lưu chúng vào các biến tương ứng.

— Update 29/10/2020 from Son Nguyen

Phần crawl data runtime, certificate và genre có thể ko tồn tại nên sẽ dẫn tới lỗi ko tạo đc pandas DataFrame. Mình góp code chỉnh lại như sau để đảm bảo data crawl cho từng trường ko bị lộn:

certificate = []runtime = []genre = []for movie in soup.findAll('p',class_='text-muted'):if ((not movie.findAll('span',class_='certificate')) &(not movie.findAll('span',class_='runtime')) &(not movie.findAll('span',class_='genre'))):continuecertificate.append('' if not [ce.text for ce in movie.findAll('span',class_='certificate')] else[ce.text for ce in movie.findAll('span',class_='certificate')][0])runtime.append('' if not [rt.text for rt in movie.findAll('span',class_='runtime')] else[rt.text for rt in movie.findAll('span',class_='runtime')][0])genre.append('' if not [gr.text for gr in movie.findAll('span',class_="genre")] else[rt.text for rt in movie.findAll('span',class_='genre')][0])

Updated: Đoạn code dưới đây có thể không hoạt động trong nhiều tình huống, vậy hãy sử dụng đoạn code bên trên. Thanks

movies = soup.findAll('h3', class_='lister-item-header')titles = [movie.find('a').text for movie in movies]release = [rs.find('span',class_="lister-item-year text-muted unbold").text for rs in movies]rate = movie.find('div', 'inline-block ratings-imdb-rating')['data-value']certificate = [ce.text for ce in soup.findAll('span',class_='certificate')]runtime = [rt.text for rt in soup.findAll('span',class_='runtime')]genre = [gr.text for gr in soup.findAll('span',class_="genre")]rates = [rate['data-value'] for rate in soup.findAll('div',class_='inline-block ratings-imdb-rating')]

5. Trình bày dữ liệu với Pandas

Chúng ta sử dụng pandas để trình diễn dữ liệu nhìn cho trực quan. Đơn giản chỉ cần pandas.DataFrame() là đủ

pandas.DataFrame({'titles':titles, 
'release':release,
'certificate':certificate,
'runtime':runtime,
'genre': genre,
'rates': rates})

Hy vọng những dòng viết trên đây sẽ giúp được một số bạn trong việc tiếp cận Python và thu thập dữ liệu đơn giản.

--

--

Hieu Nguyen

Đang nghiên cứu về Python và Machine Learning. Rất mong được trao đổi thêm với các bạn cùng lĩnh vực