HomeLập trìnhPythonQuét web bằng...

Quét web bằng Python – Cách cạo phim khoa học viễn tưởng từ IMDB


Bạn đã bao giờ phải vật lộn để tìm tập dữ liệu cho dự án khoa học dữ liệu của mình chưa? Nếu bạn giống tôi, câu trả lời là có.

May mắn thay, có rất nhiều bộ dữ liệu miễn phí – nhưng đôi khi bạn muốn một cái gì đó cụ thể hơn hoặc riêng biệt. Vì vậy, quét web là một kỹ năng tốt cần có trong hộp công cụ của bạn để lấy dữ liệu từ trang web yêu thích của bạn.

Điều gì được đề cập trong bài báo này?

Bài viết này có một tập lệnh Python mà bạn có thể sử dụng để thu thập dữ liệu về phim khoa học viễn tưởng (hoặc bất kỳ thể loại nào bạn chọn!) từ trang web IMDB. Sau đó, nó có thể ghi những dữ liệu này vào một khung dữ liệu để khám phá thêm.

Tôi sẽ kết thúc bài viết này với một chút phân tích dữ liệu khám phá (EDA). Thông qua đó, bạn sẽ thấy bạn có thể thử thêm những dự án khoa học dữ liệu nào.

Tuyên bố miễn trừ trách nhiệm: mặc dù quét web là một cách tuyệt vời để lấy dữ liệu ra khỏi trang web theo chương trình, nhưng hãy làm như vậy một cách có trách nhiệm. Ví dụ: tập lệnh của tôi sử dụng chức năng ngủ để cố ý làm chậm các yêu cầu kéo, để không làm quá tải các máy chủ của IMDB. Một số trang web không hài lòng khi sử dụng công cụ quét web, vì vậy hãy sử dụng nó một cách khôn ngoan.

Tập lệnh quét web và làm sạch dữ liệu

Hãy chuyển sang tập lệnh cạo và chạy nó. Kịch bản lấy tiêu đề phim, năm, xếp hạng (PG-13, R, v.v.), thể loại, thời gian chạy, đánh giá và bình chọn cho từng phim. Bạn có thể chọn số lượng trang bạn muốn cạo dựa trên nhu cầu dữ liệu của mình.

Lưu ý: sẽ mất nhiều thời gian hơn khi bạn chọn nhiều trang hơn. Mất 40 phút để quét 200 trang web bằng Google Colab Notebook.

Đối với những người chưa dùng thử trước đây, Google Colab là một công cụ phát triển Python kiểu Jupyter Notebook dựa trên đám mây nằm trong bộ ứng dụng Google. Bạn có thể sử dụng nó ngay lập tức với nhiều gói đã được cài đặt phổ biến trong khoa học dữ liệu.

Dưới đây là hình ảnh về không gian làm việc Colab và bố cục của nó:

R9sAuHzGHrEvRK_hiAWsy4W41W72et6clD38gIYeAA6AtA32e97xxw0W5ub_96xmgSMTDB2VjRK-gz_YgYtZoV1YyCHjKftaB7-HD2NQ7qt_8hcdnDfqaibp0ONwPr9-4zO5gv3FuXdxiOMsN6eF8bA
Giới thiệu giao diện người dùng Google Colab

Với điều đó, chúng ta hãy đi sâu vào! Trước tiên, bạn phải luôn nhập các gói của mình dưới dạng ô riêng. Nếu bạn quên một gói, bạn chỉ có thể chạy lại ô đó. Điều này cắt giảm thời gian phát triển.

Lưu ý: một số gói này cần pip install package_name được chạy để cài đặt chúng đầu tiên. Nếu bạn chọn chạy mã cục bộ bằng thứ gì đó như Jupyter Notebook, bạn sẽ cần phải làm điều đó. Nếu muốn thiết lập và chạy nhanh chóng, bạn có thể sử dụng sổ ghi chép Google Colab. Điều này có tất cả những thứ này được cài đặt theo mặc định.

from requests import get
from bs4 import BeautifulSoup
from warnings import warn
from time import sleep
from random import randint
import numpy as np, pandas as pd
import seaborn as sns

Cách thực hiện quét web

Bạn có thể chạy đoạn mã sau để quét web thực tế. Nó sẽ kéo tất cả các cột được đề cập ở trên thành các mảng và điền vào chúng từng phim một, từng trang một.

Ngoài ra còn có một số bước làm sạch dữ liệu mà tôi đã thêm và ghi lại trong mã này. Ví dụ, tôi đã xóa dấu ngoặc đơn khỏi dữ liệu chuỗi đề cập đến năm của bộ phim. Sau đó tôi đã chuyển đổi chúng thành số nguyên. Những thứ như thế này làm cho việc phân tích và lập mô hình dữ liệu khám phá trở nên dễ dàng hơn.

Đọc thêm  chỉ số chuỗi phải là số nguyên – Cách khắc phục trong Python

Lưu ý rằng tôi sử dụng chức năng ngủ để tránh bị IMDB hạn chế khi chuyển qua các trang web của họ quá nhanh.

# Note this takes about 40 min to run if np.arange is set to 9951 as the stopping point.

pages = np.arange(1, 9951, 50) # Last time I tried, I could only go to 10000 items because after that the URI has no discernable pattern to combat webcrawlers; I just did 4 pages for demonstration purposes. You can increase this for your own projects.
headers = {'Accept-Language': 'en-US,en;q=0.8'} # If this is not specified, the default language is Mandarin

#initialize empty lists to store the variables scraped
titles = []
years = []
ratings = []
genres = []
runtimes = []
imdb_ratings = []
imdb_ratings_standardized = []
metascores = []
votes = []

for page in pages:
  
   #get request for sci-fi
   response = get("https://www.imdb.com/search/title?genres=sci-fi&"
                  + "start="
                  + str(page)
                  + "&explore=title_type,genres&ref_=adv_prv", headers=headers)
  
   sleep(randint(8,15))
   
   #throw warning for status codes that are not 200
   if response.status_code != 200:
       warn('Request: {}; Status code: {}'.format(requests, response.status_code))

   #parse the content of current iteration of request
   page_html = BeautifulSoup(response.text, 'html.parser')
      
   movie_containers = page_html.find_all('div', class_ = 'lister-item mode-advanced')
  
   #extract the 50 movies for that page
   for container in movie_containers:

       #conditional for all with metascore
       if container.find('div', class_ = 'ratings-metascore') is not None:

           #title
           title = container.h3.a.text
           titles.append(title)

           if container.h3.find('span', class_= 'lister-item-year text-muted unbold') is not None:
            
             #year released
             year = container.h3.find('span', class_= 'lister-item-year text-muted unbold').text # remove the parentheses around the year and make it an integer
             years.append(year)

           else:
             years.append(None) # each of the additional if clauses are to handle type None data, replacing it with an empty string so the arrays are of the same length at the end of the scraping

           if container.p.find('span', class_ = 'certificate') is not None:
            
             #rating
             rating = container.p.find('span', class_= 'certificate').text
             ratings.append(rating)

           else:
             ratings.append("")

           if container.p.find('span', class_ = 'genre') is not None:
            
             #genre
             genre = container.p.find('span', class_ = 'genre').text.replace("\n", "").rstrip().split(',') # remove the whitespace character, strip, and split to create an array of genres
             genres.append(genre)
          
           else:
             genres.append("")

           if container.p.find('span', class_ = 'runtime') is not None:

             #runtime
             time = int(container.p.find('span', class_ = 'runtime').text.replace(" min", "")) # remove the minute word from the runtime and make it an integer
             runtimes.append(time)

           else:
             runtimes.append(None)

           if float(container.strong.text) is not None:

             #IMDB ratings
             imdb = float(container.strong.text) # non-standardized variable
             imdb_ratings.append(imdb)

           else:
             imdb_ratings.append(None)

           if container.find('span', class_ = 'metascore').text is not None:

             #Metascore
             m_score = int(container.find('span', class_ = 'metascore').text) # make it an integer
             metascores.append(m_score)

           else:
             metascores.append(None)

           if container.find('span', attrs = {'name':'nv'})['data-value'] is not None:

             #Number of votes
             vote = int(container.find('span', attrs = {'name':'nv'})['data-value'])
             votes.append(vote)

           else:
               votes.append(None)

           else:
               votes.append(None)

Khung dữ liệu gấu trúc lấy làm mảng dữ liệu đầu vào cho từng cột của chúng theo cặp khóa: giá trị. Tôi đã thực hiện thêm một số bước làm sạch dữ liệu tại đây để hoàn tất quá trình làm sạch dữ liệu.

Sau khi bạn chạy ô sau, bạn sẽ có một khung dữ liệu với dữ liệu bạn đã loại bỏ.

sci_fi_df = pd.DataFrame({'movie': titles,
                      'year': years,
                      'rating': ratings,
                      'genre': genres,
                      'runtime_min': runtimes,
                      'imdb': imdb_ratings,
                      'metascore': metascores,
                      'votes': votes}
                      )

sci_fi_df.loc[:, 'year'] = sci_fi_df['year'].str[-5:-1] # two more data transformations after scraping
# Drop 'ovie' bug
# Make year an int
sci_fi_df['n_imdb'] = sci_fi_df['imdb'] * 10
final_df = sci_fi_df.loc[sci_fi_df['year'] != 'ovie'] # One small issue with the scrape on these two movies so just dropping those ones.
final_df.loc[:, 'year'] = pd.to_numeric(final_df['year'])

Phân tích dữ liệu thăm dò

Bây giờ bạn đã có dữ liệu, một trong những điều đầu tiên bạn có thể muốn làm là tìm hiểu thêm về nó ở cấp độ cao. Các lệnh sau đây là cái nhìn đầu tiên hữu ích về bất kỳ dữ liệu nào và chúng ta sẽ sử dụng chúng trong phần tiếp theo:

final_df.head()

Lệnh này hiển thị cho bạn 5 hàng đầu tiên trong khung dữ liệu của bạn. Nó giúp bạn thấy rằng không có gì lạ và mọi thứ đã sẵn sàng để phân tích. Bạn có thể xem đầu ra ở đây:

TCYKlpEKIJOVJIAtIGN4wzDhCySaYIXI9cyBizZxR3XHsAQO_YH9mh626hCq8fdItaAF0N0cxSs1PP1eYujRsOt8HgeXtcC3hff-y0Jl4tvN__itH97iXqb6DrN6wJrngdsNaKQTQag5StHfOIcy5A0
Năm hàng dữ liệu đầu tiên được xuất ra bằng cách sử dụng final_df.head() chỉ huy
final_df.describe()

Lệnh này sẽ cung cấp cho bạn giá trị trung bình, độ lệch chuẩn và các tóm tắt khác. Đếm có thể hiển thị cho bạn nếu có bất kỳ giá trị null nào trong một số cột, đây là thông tin hữu ích cần biết. Ví dụ: cột năm hiển thị cho bạn phạm vi phim được loại bỏ – từ năm 1927 đến năm 2022.

Đọc thêm  Cách giải quyết các vấn đề về Leetcode với Python One-Liners

Bạn có thể xem đầu ra bên dưới và kiểm tra những cái khác:

Zeo_Y8ipyIejyYIBa2Aaocz4obHNlMVU76YTylZGl_wpRovYVFNS4e0m1DYAwkcqhpoYikJFL_dSgZSH-qoghJM3VMXESMuykrfs1e3JuXRkrp9iEZhPPnqGvsSamdYQe6Noz0Q0OA-Wen616-pmbDQ
Đang chạy final_df.describe() tạo thống kê tóm tắt hiển thị số lượng điểm dữ liệu, giá trị trung bình, độ lệch chuẩn, v.v.
final_df.info()

Lệnh này cho bạn biết các kiểu dữ liệu mà bạn đang làm việc trong mỗi cột của mình.

Là một nhà khoa học dữ liệu, thông tin này có thể hữu ích cho bạn. Một số chức năng và phương pháp cần các loại dữ liệu nhất định. Bạn cũng có thể đảm bảo rằng các loại dữ liệu cơ bản của mình ở định dạng phù hợp với bản chất của chúng.

Ví dụ: xếp hạng 5 sao phải là số float hoặc int (nếu số thập phân không được phép). Nó không phải là một chuỗi vì nó là một số. Dưới đây là tóm tắt về định dạng dữ liệu cho từng biến sau khi cạo:

PT7Fa9XFYErtorVw6bNxw7Q1mI-p2_hlKgTbTs90RRpALPDlqd95F_EOwCQ7cV2cDymqZ-mXIa_0blqxxJ5wZ8Bznzd0iFyTB6kFroIUK2DJNzfRZgwgsRHr0pjDyE1ZUrQILf-22wnnK856KMRIo
Đang chạy final_df.info() dẫn đến việc hiển thị cho bạn biết bạn có bao nhiêu giá trị trong mỗi cột và loại dữ liệu của chúng là gì.

Lệnh tiếp theo để tìm hiểu thêm về các biến của bạn sẽ tạo ra một bản đồ nhiệt. Bản đồ nhiệt cho thấy mối tương quan giữa tất cả các biến định lượng của bạn. Đây là một cách nhanh chóng để đánh giá các mối quan hệ có thể tồn tại giữa các biến. Tôi thích xem các hệ số hơn là cố giải mã mã màu, vì vậy tôi sử dụng annot=True tranh luận.

sns.heatmap(final_df.corr(), annot=True);

Lệnh trên tạo ra hình ảnh trực quan hóa sau bằng cách sử dụng gói trực quan hóa dữ liệu Seaborn:

niHLKP7bps1EpZ_39u5k3dPDF0Xuz8Zuhal8Bbc8wtImKUv50M_7fEH65rCAkrTglGtZTJpZ2sRfIE0E6Kjn9m_CYGkRct83_3wWzVp0rnHA8nh5UuveFO0OqtjVfoOzMsKGq0lZ2uxw66Lp4g69a
Một bản đồ nhiệt của các mối tương quan sau khi chạy sns.heatmap(final_df.corr(), annot=True);

Bạn có thể thấy rằng mối tương quan mạnh nhất là giữa điểm IMDB và điểm siêu dữ liệu. Điều này không có gì đáng ngạc nhiên vì có khả năng hai hệ thống đánh giá phim sẽ đánh giá tương tự nhau.

Mối tương quan mạnh nhất tiếp theo mà bạn có thể thấy là giữa xếp hạng IMDB và số phiếu bầu. Điều này thật thú vị bởi vì khi số lượng phiếu bầu tăng lên, bạn sẽ có một mẫu đánh giá dân số mang tính đại diện hơn. Tuy nhiên, thật kỳ lạ khi thấy rằng có một mối liên hệ yếu ớt giữa hai điều này.

Số lượng phiếu bầu tăng lên khi thời gian chạy cũng tăng lên.

Bạn cũng có thể thấy một mối liên hệ tiêu cực nhẹ giữa IMDB hoặc metascore và năm bộ phim ra mắt. Chúng tôi sẽ xem xét điều này ngay sau đây.

Bạn có thể kiểm tra một số mối quan hệ này một cách trực quan thông qua biểu đồ phân tán với mã này:

x = final_df['n_imdb']
y = final_df['votes']
plt.scatter(x, y, alpha=0.5) # s= is size var, c= is color var
plt.xlabel("IMDB Rating Standardized")
plt.ylabel("Number of Votes")
plt.title("Number of Votes vs. IMDB Rating")
plt.ticklabel_format(style="plain")
plt.show()

Điều này dẫn đến hình dung này:

vvqxh5VwbHPoypyGlNBstgZW8puVWKa5m_hl6MYB_r78OfRC7TWBx9jxjf8PFflJO93hq83ZdIqX97uq6C_WjlZV5jorCDgtU3U3_dESuUgsStfLEgkeiikHTq2noabW_tPJQRRGpFrVmQ90gja4xAo
Xếp hạng IMDB so với số phiếu bầu

Hiệp hội trên cho thấy một số ngoại lệ. Nói chung, chúng tôi thấy số lượng bình chọn nhiều hơn cho các phim có xếp hạng IMDB từ 85 trở lên. Có ít bài đánh giá hơn về các bộ phim có xếp hạng từ 75 trở xuống.

Vẽ các hộp này xung quanh dữ liệu có thể cho bạn thấy ý tôi là gì. Có khoảng hai nhóm có cường độ khác nhau:

QEUbjZrtSiLCbdcXIR1MN0MKvcCZgVxeW2sPzMo4KL36pjCQq87rkRdgKKwK2yWSh2Uz0HMoIckyOa0qcNX4hCQok_kuuyqq4PddFHVuC5Tzyg9-WdZobdZgWfOpW1PnKWFKfQLaDAEDXoDHfiuU5mY
Hai nhóm cốt lõi trong dữ liệu

Một điều khác có thể thú vị để xem là có bao nhiêu phim trong mỗi xếp hạng. Điều này có thể cho bạn biết Khoa học viễn tưởng có xu hướng đạt được vị trí nào trong dữ liệu xếp hạng. Sử dụng mã này để có được một biểu đồ thanh xếp hạng:

ax = final_df['rating'].value_counts().plot(kind='bar',
                                   figsize=(14,8),
                                   title="Number of Movies by Rating")
ax.set_xlabel("Rating")
ax.set_ylabel("Number of Movies")
ax.plot();

Mã đó dẫn đến biểu đồ này cho chúng ta thấy rằng R và PG-13 chiếm phần lớn các phim Khoa học viễn tưởng này trên IMDB.

7896rs2HtqgI4nIPyI-vUU5w43C3_Dcuyc_DdjUOudq76aIHstBINNVf5e0-1G3MUZzFgKDzK_2Jhsnno5swbXIoZwMuxqg1icY8aPbxWjOsCIm3BB9lObzY7HiDSAhmLTfcpfi2HWdWnVobrkUjBcp
Số lượng phim theo xếp hạng

Tôi đã thấy rằng có một vài bộ phim được xếp hạng là “Được phê duyệt” và tò mò không biết đó là gì. Bạn có thể lọc khung dữ liệu bằng mã này để đi sâu vào điều đó:

final_df[final_df['rating'] == 'Approved']

Điều này tiết lộ rằng hầu hết những bộ phim này được thực hiện trước những năm 80:

Đọc thêm  Cách xây dựng ứng dụng web trên điện thoại của bạn – Hướng dẫn ứng dụng Android Python & Pydroid
OIxMNDTgcXcPo_Wy8N7miq44OOAai4o-A8upYa1pNbqWjDVzPduRxNcMgUPuG9-OFyNd1AFgwWeq_o4E3Kv9pXy8xVSH7p6ZZi9uoOy78dBFK0LjvDnN9k7WYDTiZYxwpVgCiqXok6jvMoW7
Tất cả Xếp hạng Phim “Được Phê duyệt”

Tôi đã truy cập trang web của MPAA và không thấy đề cập đến họ trên trang thông tin xếp hạng của họ. Nó phải đã được loại bỏ tại một số điểm.

Bạn cũng có thể kiểm tra xem có năm hoặc thập kỷ nào vượt trội hơn những năm hoặc thập kỷ khác trong các bài đánh giá hay không. Tôi đã lấy điểm số trung bình theo năm và vẽ biểu đồ đó bằng đoạn mã sau để khám phá thêm:

# What are the average metascores by year?
final_df.groupby('year')['metascore'].mean().plot(kind='bar', figsize=(16,8), title="Avg. Metascore by Year", xlabel="Year", ylabel="Avg. Metascore")
plt.xticks(rotation=90)
plt.plot();

Điều này dẫn đến biểu đồ sau:

BTwJBQhq0zBTr5UH5J3n7CSR6k3Ft8l9GBQ_czZRlu_LO192AXd0G_ozwXzsb6pctS-8lHCvgVLx6VZ7hWH-trp8C4oFAPCGufh2gq-F2WV96u90xt05KqUGYCqSFpmXxPEsFKSZQceglNItwChRnfE
Trung bình Metascore theo năm phim

Bây giờ tôi không nói rằng tôi biết tại sao, nhưng có một sự suy giảm dần dần, nhẹ khi bạn tiến bộ qua lịch sử trong biến metascore trung bình. Có vẻ như xếp hạng đã ổn định trong khoảng 55-60 trong vài thập kỷ qua. Điều này có thể là do chúng tôi có nhiều dữ liệu hơn về các phim mới hơn hoặc các phim mới hơn có xu hướng được đánh giá nhiều hơn.

final_df['year'].value_counts().plot(kind='bar', figsize=[20,9])

Chạy đoạn mã trên và bạn sẽ thấy rằng bộ phim năm 1927 chỉ có một mẫu của 1 đánh giá. Điểm số đó sau đó bị sai lệch và thổi phồng quá mức. Bạn cũng sẽ thấy rằng những bộ phim gần đây được thể hiện tốt hơn trong các bài đánh giá như tôi đã nghi ngờ:

d2C-t1DLqSjRY8DpeoudyBNHG4SevXCZXFK4xoaw3QHpj_j4qnEf479Tn7wNyBqOwKazR5GVidaRZ79XB5Eo36msA8LRBxNaJu_9Xk1VKE5oeo2Pue1TLnbjMX3y48Gc5xfOBnlZ1x9rdTktI_N2B
Số lượng phim theo năm

Ý tưởng dự án khoa học dữ liệu để thực hiện điều này hơn nữa

Bạn có các biến văn bản, phân loại và số ở đây. Có một số tùy chọn bạn có thể thử khám phá thêm.

Một điều bạn có thể làm là sử dụng Quy trình ngôn ngữ tự nhiên (NLP) để xem liệu có bất kỳ quy ước đặt tên nào đối với xếp hạng phim hoặc trong thế giới Khoa học viễn tưởng (hoặc nếu bạn chọn làm một thể loại khác, bất kỳ thể loại nào bạn chọn!) .

Bạn cũng có thể thay đổi mã quét web để thu hút nhiều thể loại hơn. Cùng với đó, bạn có thể tạo cơ sở dữ liệu liên thể loại mới để xem liệu có quy ước đặt tên theo thể loại hay không.

Sau đó, bạn có thể thử dự đoán thể loại dựa trên tên của bộ phim. Bạn cũng có thể thử dự đoán xếp hạng IMDB dựa trên thể loại hoặc năm phim ra mắt. Ý tưởng thứ hai sẽ hoạt động tốt hơn trong vài thập kỷ qua vì hầu hết các quan sát đều ở đó.

Tôi hy vọng hướng dẫn này khơi dậy sự tò mò trong bạn về thế giới khoa học dữ liệu và những gì có thể!

Bạn sẽ thấy trong phân tích dữ liệu khám phá luôn có nhiều câu hỏi hơn để đặt ra. Làm việc với ràng buộc đó là sắp xếp thứ tự ưu tiên dựa trên (các) mục tiêu kinh doanh. Điều quan trọng là phải bắt đầu với những mục tiêu đó ngay từ đầu, nếu không bạn có thể ở trong vùng dữ liệu để khám phá mãi mãi.

Nếu lĩnh vực khoa học dữ liệu khiến bạn hứng thú và bạn muốn mở rộng bộ kỹ năng của mình cũng như tham gia lĩnh vực này một cách chuyên nghiệp, hãy cân nhắc xem Lộ trình nghề nghiệp khoa học dữ liệu của Springboard. Trong khóa học này, Springboard sẽ hướng dẫn bạn qua tất cả các khái niệm chính một cách chuyên sâu với một cố vấn chuyên gia 1:1 đi cùng bạn để hỗ trợ bạn trên hành trình của mình.

Tôi đã viết các bài báo khác định hình các dự án khoa học dữ liệu liên quan đến các vấn đề kinh doanh và hướng dẫn các phương pháp kỹ thuật để giải quyết chúng trên Phương tiện của tôi. Kiểm tra chúng nếu bạn quan tâm!

Chúc mừng mã hóa!

Riley



Zik.vn – Biên dịch & Biên soạn Lại

spot_img

Create a website from scratch

Just drag and drop elements in a page to get started with Newspaper Theme.

Buy Now ⟶

Bài viết liên quang

DMCA.com Protection Status