Trích Xuất Dữ Liệu Từ Các Trang Web Dùng Thư Viện BeautifulSoup

Một trong những nguồn dữ liệu vô tận có thể khai thác cho các dự án machine learning là từ các trang web và chúng ta có thể trích xuất các thông tin từ đó bằng cách sử dụng thư viện BeautifulSoup. Ngoài ra, để sử dụng hiệu quả thư viện này, chúng ta cũng cần cài đặt thêm các thư viện requestshtml5lib. Nếu chúng ta đã từng cài Anaconda ( có thể xem lại tại  https://ngocminhtran.com/2018/03/31/cai-dat-python-cho-machine-learning-va-tensorflow-trong-windows-10/ ) thì các thư viện này mặc nhiên đã được cài đặt.

Trích xuất dữ liệu từ tài liệu HTML

Để sử dụng thư viện BeautifulSoup và các thư viện khác chúng ta cần khai báo như sau:

from bs4 import BeautifulSoup import requests html = requests.get("http://www.example.com").text soup = BeautifulSoup(html, 'html5lib')

Đối tượng đầu tiên chúng ta cần làm việc là các thẻ HTML. Ví dụ chúng ta cần tìm thẻ p đầu tiên trong trang www.example.com, chúng ta dùng lệnh:

pFirst = soup.find('p')

hay

pFirst = soup.p

Chúng ta có thể dễ dàng trích xuất nội dung các thẻ, ví dụ trích nội dung thể p đầu tiên:

pFirst_content = soup.find('p').text

Chúng ta cũng có thể trích xuất các thuộc tính của thẻ như id hay class. Ví dụ trích xuất thuộc tính id của thẻ p đầu tiên:

pFirst_id = soup.p['id']

trong trường hợp này nếu thẻ p đầu tiên không có thuộc tính id thì sẽ phát sinh một lỗi. Một cách khác để trích xuất id như sau:

pFirst_id = soup.p.get('id')

với cách này, nếu thẻ p đầu tiên không chứa id thì sẽ trả về None.

Chúng ta cũng có thể tìm tất cả các thẻ cụ thể trong một tài liệu HTML, ví dụ tìm tất cả thẻ p như sau:

pAll = soup.find_all('p')

hay

pAll = soup('p')

Trả về các thuộc tính id của tất cả các thẻ p:

pAll_ids = [p for p in soup('p') if p.get('id')]

Bên cạnh thuộc tính id, một trong những thuộc tính phổ biến khác của các thẻ HTML là class. Có thể dễ dàng tìm một thẻ với một class nào đó, ví dụ tìm thẻ p có chứa thuộc tính class là title:

tittle =  soup('p', 'title')

trả về nội dung của thẻ p trên:

tittle =  soup('p', 'title').text

Chúng ta vừa lướt qua một số phương thức hữu ích của thư viện BeautifulSoup. Có thể tham khảo thêm cách sử dụng thư viện hữu ích BeautifulSoup tại https://www.crummy.com/software/BeautifulSoup/bs4/doc/ . Trước khi kết thúc bài viết, chúng ta sẽ sử dụng thư viện BeautifulSoup cho một trang web cụ thể.

Ví dụ minh họa: trích xuất thông tin về các cuốn sách trong danh mục English Books tại trang tiki.vn

Để tiên minh họa, trong bài viết này chúng ta sẽ trích xuất một vài thông tin đơn giản từ trang

https://tiki.vn/sach-tieng-anh/c320?src=tree. Để xem source của trang này trong Chrome, chúng ta dùng chức năng View page source hay đơn giản là nhấn tổ hợp Ctrl + U. Trong trang source bao gồm các đoạn mã HTML và JavaScript, tìm đến các thẻ div chứa nội dung các cuốn sách. Một trong các thẻ div cho một cuốn sách như sau:

<div data-seller-product-id="141826" data-title="Harry Potter Boxed Set: The Complete Collection Children's (Paperback) - Bloomsbury UK Edition" data-price="1249000" data-id="436515" data-score="" data-brand="J. K. Rowling" data-category="Nhà Sách Tiki/English Books/Fiction - Literature/Science Fiction & Fantasy" tpl="" rel="" class="product-item "> <a data-id="436515" data-score="" href="https://tiki.vn/harry-potter-boxed-set-the-complete-collection-children-s-paperback-bloomsbury-uk-edition-p436515.html?src=category-page-8322.320&2hi=1" title="Harry Potter Boxed Set: The Complete Collection Children's (Paperback) - Bloomsbury UK Edition" class="" > <div class="content "> <span class="image"> <img class="product-image img-responsive" src="https://salt.tikicdn.com/cache/200x200/ts/product/05/30/b0/b1ee828cc3341369acb765253a28901a.png" alt=""> <span class="product-right-icon"> <i class="tikicon icon-tikinow-20"></i> </span> </span> <p class="icons"> <i class="tikicon icon-tikinow"></i> </p> <p class="title"> <i class="tikicon icon-tikinow"></i> Harry Potter Boxed Set: The Complete... </p> <p class="author">J. K. Rowling</p> <p class="price-sale"> <span class="final-price"> 1.249.000 ₫ <span class="sale-tag sale-tag-square">-40%</span> </span> <span class="price-regular">2.079.000 ₫</span> <span class="sale-tag sale-tag-square">-40%</span> </p> </div> <div class="price-tag-wrap"> <p class="price-tag"> <span class="tag"> <span class="v2"> Nhập mã </span> <br/> <span class="code">TIKIMUADIKEOHET</span> </span> <span class="price"> Chỉ còn <span>1.149.080đ</span> </span> </p> </div> <div class="review-wrap"> <p class="rating"> <span class="rating-content"> <i class="star"></i> <i class="star"></i> <i class="star"></i> <i class="star"></i> <i class="star"></i> <span style="width:94%"> <i class="star"></i> <i class="star"></i> <i class="star"></i> <i class="star"></i> <i class="star"></i> </span> </span> </p> <p class="review">(438 nhận xét)</p> </div> <div class="ship-label-wrapper"> </div> </a> </div>

Các thẻ div này có chứa nhiều thuộc tính nhưng ở đây chúng ta chỉ quan tâm thuộc tính class cuối cùng với giá trị là product-item. Có thể dễ dàng đếm bao nhiêu cuốn sách hiển thị trên mỗi trang bằng cách đếm các div như sau:

from bs4 import BeautifulSoup import requests html = requests.get("https://tiki.vn/sach-tieng-anh/c320?src=tree").text soup = BeautifulSoup(html, 'html5lib') divs = soup("div","product-item") print(len(divs)) # kết quả là 48 tương ứng 48 cuốn sách mỗi trang.

Bên trong các div product-item chứa rất nhiều các thẻ mô tả cho các cuốn sách nhưng chúng ta chỉ quan tâm tối các thẻ p có các class là title, author và review. Đoạn mã sau đây sẽ truy xuất các thẻ p trong thẻ div product-item đầu tiên:

title = divs[0].find("p", "title").text author = divs[0].find("p", "author").text review = divs[0].find("p", "review").text

Giả sử thẻ div đầu tiên chính là thẻ div chúng ta đã copy để minh họa ở trên thì kết quả:

title =  Harry Potter Boxed Set: The Complete... author = J. K. Rowling review = (438 nhận xét)

Bây giờ chúng ta sẽ viết một hàm trả về nội dung các thẻ p từ một thẻ div như sau:

def book_info(div): if div.find("p", "title"): title = div.find("p", "title").text.strip() else: title ="" if div.find('p', 'author'): author = div.find('p', 'author').text else: author = "" if div.find('p', 'review'): review = div.find('p', 'review').text else: review = "" return { "Tiêu đề" : title, "Tác giả" : author, "Nhận xét" : review }

Hiển thị tiêu đề (title), tác giả (author) và số nhận xét (review) của tất cả 48 cuốn sách như sau:

divs = soup("div","product-item") for i in range(len(divs)): print("Cuốn sách thứ "+ str(i+1) + ":",book_info(divs[i]))

Thời điểm đang viết bài này thì hai cuốn đầu tiên là:

Cuốn sách thứ 1: {'Tiêu đề': 'Harry Potter Boxed Set: The Complete...', 'Tác giả': 'J. K. Rowling', 'Nhận xét': '(438 nhận xét)'} Cuốn sách thứ 2: {'Tiêu đề': 'Harry Potter: A History of Magic', 'Tác giả': 'British Library', 'Nhận xét': 'Chưa có nhận xét'}

Để ý rằng, có hai dạng nhận xét: dạng thứ nhất chứa số nhận xét (cuốn thứ 1) và dạng thứ hai là chưa có nhận xét nào (cuốn thứ hai). Giả sử chúng ta muốn trích xuất các thông tin về số nhận xét như sau:

  • Đối với dạng thứ nhất: chỉ hiển thị con số nhận xét, ví dụ 438
  • Đối với dạng thứ hai: trả về 0

Chúng ta thay đổi lại hàm book_info như sau:

def book_info(div): if div.find("p", "title"): title = div.find("p", "title").text.strip() else: title ="" if div.find('p', 'author'): author = div.find('p', 'author').text else: author = "" if div.find('p', 'review'): review = div.find('p', 'review').text else: review = "" if review == "Chưa có nhận xét": numofReviews = [0] else: numofReviews = [int(s) for s in re.findall(r'\b\d+\b', review)] return { "Tiêu đề" : title, "Tác giả" : author, "Số Nhận xét" : numofReviews[0] }

Hiển thị lại tất cả cuốn sách. Hai cuốn đầu tiên sẽ là:

Cuốn sách thứ 1: {'Tiêu đề': 'Harry Potter Boxed Set: The Complete...', 'Tác giả': 'J. K. Rowling', 'Số Nhận xét': 438} Cuốn sách thứ 2: {'Tiêu đề': 'Harry Potter: A History of Magic', 'Tác giả': 'British Library', 'Số Nhận xét': 0}

Cuối cùng, chúng ta cần thực hiện một vài thống kê như sau:

  • Hiển thị số lượng và các cuốn sách có số nhận xét nhiều nhất, thấp nhất.
  • Hiển thị số lượng và các cuốn sách chưa có nhận xét nào.

Đoạn mã thực hiện nhiệm vụ trên như sau:

review_list = [] for i in range(len(divs)): if book_info(divs[i])["Số Nhận xét"] > 0: review_list.append(book_info(divs[i])["Số Nhận xét"]) max_value = max(review_list) max_count = Counter(book_info(div)["Số Nhận xét"] for div in divs if book_info(div)["Số Nhận xét"] == max_value) min_value = min(review_list) min_count = Counter(book_info(div)["Số Nhận xét"] for div in divs if book_info(div)["Số Nhận xét"] == min_value) noreview_count = Counter(book_info(div)["Số Nhận xét"] for div in divs if book_info(div)["Số Nhận xét"] == 0) print("Có " + str(min_count[min_value]) + " cuốn sách với số nhận xét thấp nhất là: " + str(min_value) + ". Đó là các cuốn:") for i in range(len(divs)): if book_info(divs[i])["Số Nhận xét"] == min_value: print(book_info(divs[i])) print("Có " + str(max_count[max_value]) + " cuốn sách với số nhận xét nhiều nhất là: " + str(max_value)+ ". Đó là các cuốn:") for i in range(len(divs)): if book_info(divs[i])["Số Nhận xét"] == max_value: print(book_info(divs[i])) print("Có " + str(noreview_count[0]) + " cuốn sách chưa có nhận xét nào. Đó là các cuốn:") for i in range(len(divs)): if book_info(divs[i])["Số Nhận xét"] == 0: print(book_info(divs[i]))

Kết quả:

Có 5 cuốn sách với số nhận xét thấp nhất là: 2. Đó là các cuốn: {'Tiêu đề': 'The Great Gatsby', 'Tác giả': 'F. Scott Fitzgerald', 'Số Nhận xét': 2} {'Tiêu đề': "To All The Boys I'Ve Loved Before - Những...", 'Tác giả': '', 'Số Nhận xét': 2} {'Tiêu đề': 'The Tipping Point - Paperback', 'Tác giả': 'Malcolm Gladwell', 'Số Nhận xét': 2} {'Tiêu đề': "i-Learn My Phonics Grade 1 Student's Book...", 'Tác giả': 'Jenny Dooley - Virginia Evans', 'Số Nhận xét': 2} {'Tiêu đề': 'Diary Of A Wimpy Kid 05: The Ugly Truth', 'Tác giả': 'Jeff Kinney', 'Số Nhận xét': 2} Có 1 cuốn sách với số nhận xét nhiều nhất là: 438. Đó là các cuốn: {'Tiêu đề': 'Harry Potter Boxed Set: The Complete...', 'Tác giả': 'J. K. Rowling', 'Số Nhận xét': 438} Có 3 cuốn sách chưa có nhận xét nào. Đó là các cuốn: {'Tiêu đề': 'Harry Potter: A History of Magic', 'Tác giả': 'British Library', 'Số Nhận xét': 0} {'Tiêu đề': 'The Picture Of Dorian Gray', 'Tác giả': 'Oscar Wilde', 'Số Nhận xét': 0} {'Tiêu đề': "i-Learn My Phonics Grade 2 Student's Book...", 'Tác giả': 'Jenny Dooley - Virginia Evans', 'Số Nhận xét': 0}

Như vậy chúng ta đã hoàn thành một minh họa đơn giản về cách dùng thư viện BeautifulSoup trong thực tế. Có thể xem phần minh họa này với Jupyter Notebook.

Chia sẻ:

  • Tweet
  • Email
  • In
Thích Đang tải...

Related

Từ khóa » Thư Viện Beautifulsoup Python