Nếu bạn muốn có cái nhìn tổng quan về web scraping trong Python, hãy học khóa Web Scraping with Python của DataCamp.
Trong hướng dẫn này, bạn sẽ học cách sử dụng Scrapy, một framework Python giúp bạn xử lý khối lượng dữ liệu lớn! Bạn sẽ học Scrapy bằng cách xây dựng một web scraper cho AliExpress.com, một trang thương mại điện tử. Bắt đầu thu thập nào!
Kiến thức cơ bản về HTML và CSS sẽ giúp bạn hiểu hướng dẫn này dễ dàng và nhanh hơn. Đọc bài viết này để ôn lại HTML và CSS.
Web scraping đã trở thành một cách hiệu quả để trích xuất thông tin từ web phục vụ ra quyết định và phân tích. Đây đã là một phần thiết yếu trong bộ công cụ của khoa học dữ liệu. Các nhà khoa học dữ liệu nên biết cách thu thập dữ liệu từ các trang web và lưu trữ dữ liệu đó theo nhiều định dạng khác nhau để phân tích tiếp theo.
Bất kỳ trang web nào bạn thấy trên internet đều có thể được thu thập thông tin và bất cứ thứ gì hiển thị trên một trang web đều có thể được trích xuất [2]. Mỗi trang web có cấu trúc và các phần tử riêng, vì vậy bạn cần viết trình thu thập/crawler (spider) của mình phù hợp với trang đang được trích xuất.
Scrapy cung cấp một framework mạnh mẽ để trích xuất dữ liệu, xử lý và sau đó lưu lại.
Scrapy sử dụng các spider, là những crawler độc lập được cung cấp một tập lệnh hướng dẫn [1]. Với Scrapy, việc xây dựng và mở rộng các dự án thu thập lớn trở nên dễ dàng hơn nhờ cho phép nhà phát triển tái sử dụng mã.
Scrapy so với BeautifulSoup
Trong phần này, bạn sẽ có cái nhìn tổng quan về một công cụ web scraping được dùng phổ biến là BeautifulSoup và so sánh nó với Scrapy.
Scrapy là một framework Python cho web scraping, cung cấp gói giải pháp hoàn chỉnh cho nhà phát triển mà không phải lo duy trì mã.
Beautiful Soup cũng được dùng rộng rãi cho web scraping. Đây là một gói Python để phân tích cú pháp tài liệu HTML và XML và trích xuất dữ liệu từ đó. Nó khả dụng cho Python 2.6+ và Python 3.
Dưới đây là một số khác biệt tóm tắt giữa chúng:
Scrapy
BeautifulSoup
Chức năng
---
Scrapy là gói hoàn chỉnh để tải trang web, xử lý và lưu vào tệp hoặc cơ sở dữ liệu
BeautifulSoup về cơ bản là một trình phân tích HTML và XML và cần các thư viện bổ sung như requests, urlib2 để mở URL và lưu kết quả [6]
Độ khó học
---
Scrapy là “cỗ máy” cho web scraping và cung cấp nhiều cách để trích xuất một trang web. Cần nhiều thời gian để học và hiểu cách Scrapy hoạt động, nhưng khi đã nắm được, việc tạo web crawler và chạy chỉ với một dòng lệnh trở nên rất dễ. Trở thành chuyên gia Scrapy có thể cần luyện tập và thời gian để học hết các chức năng.
BeautifulSoup tương đối dễ hiểu với người mới lập trình và có thể hoàn thành các tác vụ nhỏ rất nhanh
Tốc độ và tải
---
Scrapy xử lý các tác vụ lớn rất dễ dàng. Nó có thể thu thập một nhóm URL trong không quá một phút tùy kích thước nhóm và thực hiện rất mượt vì dùng Twister hoạt động bất đồng bộ (không chặn) để đồng thời.
BeautifulSoup được dùng hiệu quả cho các tác vụ trích xuất đơn giản. Nó chậm hơn Scrapy nếu bạn không dùng multiprocessing.
Mở rộng chức năng
---
Scrapy cung cấp Item pipelines cho phép bạn viết các hàm trong spider để xử lý dữ liệu như kiểm tra hợp lệ, loại bỏ dữ liệu và lưu vào cơ sở dữ liệu. Nó cung cấp Spider Contracts để kiểm thử spider và cho phép bạn tạo crawler tổng quát và sâu. Nó cho phép bạn quản lý nhiều biến như retry, chuyển hướng, v.v.
Nếu dự án không cần nhiều logic, BeautifulSoup là đủ tốt, nhưng nếu bạn cần nhiều tùy biến như proxy, quản lý cookie và pipeline dữ liệu, Scrapy là lựa chọn tốt nhất.
Thông tin: Đồng bộ nghĩa là bạn phải chờ một tác vụ kết thúc mới bắt đầu tác vụ tiếp theo, trong khi Bất đồng bộ cho phép bạn chuyển sang tác vụ khác trước khi tác vụ trước đó hoàn tất
Khi đã cài đặt Python 3.0 (trở lên), nếu bạn dùng anaconda, bạn có thể dùng conda để cài scrapy. Gõ lệnh sau trong anaconda prompt:
conda install -c conda-forge scrapy
Để cài anaconda, xem các hướng dẫn DataCamp cho Mac và Windows.
Hoặc, bạn có thể dùng Trình cài đặt gói Python pip. Cách này dùng được trên Linux, Mac và Windows:
pip install scrapy
Scrapy Shell
Scrapy cũng cung cấp một shell thu thập web gọi là Scrapy Shell để nhà phát triển kiểm thử giả định về hành vi của một trang. Hãy lấy một trang máy tính bảng tại trang thương mại điện tử AliExpress. Bạn có thể dùng Scrapy shell để xem trang web trả về những thành phần nào và bạn có thể dùng chúng theo yêu cầu của mình ra sao.
Mở dòng lệnh và gõ lệnh sau:
scrapy shell
Nếu bạn dùng anaconda, bạn cũng có thể gõ lệnh trên trong anaconda prompt. Kết quả trên dòng lệnh hoặc anaconda prompt sẽ tương tự như sau:
Bạn cần chạy một crawler trên trang web bằng lệnh fetch trong Scrapy shell. Một crawler hoặc spider sẽ đi qua trang web tải nội dung văn bản và metadata của nó.
Lưu ý: Luôn đặt URL trong dấu ngoặc, ngoặc đơn hoặc ngoặc kép đều được
Kết quả sẽ như sau:
Trình thu thập trả về một response có thể xem bằng lệnh view(response) trên shell:
view(response)
Và trang web sẽ được mở bằng trình duyệt mặc định.
Bạn có thể xem mã HTML thô bằng lệnh sau trong Scrapy shell:
print(response.text)
Bạn sẽ thấy đoạn mã tạo ra trang web. Đây là nội dung tương tự khi bạn nhấp chuột phải vào vùng trống bất kỳ trên một trang và chọn view source hoặc view page source. Vì bạn chỉ cần thông tin liên quan trong toàn bộ mã, hãy dùng công cụ dành cho nhà phát triển của trình duyệt để kiểm tra phần tử cần thiết. Hãy lấy các phần tử sau:
Tên máy tính bảng
Giá máy tính bảng
Số lượng đơn hàng
Tên cửa hàng
Nhấp chuột phải vào phần tử bạn muốn và chọn inspect như dưới đây:
Các công cụ dành cho nhà phát triển của trình duyệt sẽ hỗ trợ bạn rất nhiều khi web scraping. Bạn có thể thấy đó là thẻ <a> với class là product và phần văn bản chứa tên sản phẩm:
Sử dụng bộ chọn CSS để trích xuất
Bạn có thể trích xuất thông tin bằng thuộc tính phần tử hoặc bộ chọn CSS như class. Gõ lệnh sau trong Scrapy shell để trích xuất tên sản phẩm:
response.css(".product::text").extract_first()
Kết quả sẽ là:
extract_first() trích xuất phần tử đầu tiên thỏa mãn bộ chọn CSS. Nếu bạn muốn trích xuất tất cả tên sản phẩm, dùng extract():
response.css(".product::text").extract()
Đoạn mã sau sẽ trích xuất khoảng giá của các sản phẩm:
response.css(".value::text").extract()
Tương tự, bạn có thể thử với số đơn hàng và tên cửa hàng.
Sử dụng XPath để trích xuất
XPath là ngôn ngữ truy vấn để chọn các nút trong tài liệu XML [7]. Bạn có thể điều hướng qua tài liệu XML bằng XPath. Ở phía sau, Scrapy dùng XPath để điều hướng đến các mục trong tài liệu HTML. Các bộ chọn CSS bạn dùng ở trên cũng được chuyển thành XPath, nhưng trong nhiều trường hợp CSS rất dễ dùng. Tuy nhiên, bạn nên biết XPath hoạt động trong Scrapy như thế nào.
Mở Scrapy Shell và gõ fetch(https://pt.aliexpress.com/category/201005406/special-store.html/) như trước. Thử các đoạn mã sau [3]:
response.xpath('/html').extract()
Lệnh này sẽ hiển thị tất cả mã nằm dưới thẻ <html>. / nghĩa là con trực tiếp của nút. Nếu bạn muốn lấy các thẻ <div> dưới thẻ html, bạn sẽ viết [3]:
response.xpath('/html//div').extract()
Với XPath, bạn phải hiểu cách dùng / và // để điều hướng qua các nút con và hậu duệ. Đây là một hướng dẫn hữu ích về Các nút XPath và một số ví dụ để thử.
Nếu bạn muốn lấy tất cả thẻ <div>, bạn có thể khoan sâu mà không cần dùng /html [3]:
response.xpath("//div").extract()
Bạn có thể lọc thêm các nút bắt đầu và tới nút mong muốn bằng cách dùng thuộc tính và giá trị của chúng. Dưới đây là cú pháp dùng class và giá trị.
Web scraping có thể được dùng để tạo một bộ tổng hợp giúp bạn so sánh dữ liệu. Ví dụ, bạn muốn mua một máy tính bảng và muốn so sánh sản phẩm cùng giá cả, bạn có thể thu thập các trang mong muốn và lưu vào tệp Excel. Ở đây bạn sẽ thu thập thông tin máy tính bảng từ aliexpress.com.
Bây giờ, bạn sẽ tạo một spider tùy chỉnh cho cùng trang đó. Trước hết, bạn cần tạo một dự án Scrapy nơi mã và kết quả sẽ được lưu. Gõ lệnh sau trong dòng lệnh hoặc anaconda prompt.
scrapy startproject aliexpress
Lệnh này sẽ tạo một thư mục ẩn trong cài đặt python hoặc anaconda mặc định của bạn. aliexpress sẽ là tên thư mục. Bạn có thể đặt tên bất kỳ. Bạn có thể xem nội dung thư mục trực tiếp qua Explorer. Cấu trúc thư mục như sau:
tệp/thư mục
Mục đích
scrapy.cfg
tệp cấu hình triển khai
aliexpress/
Mô-đun Python của dự án, bạn sẽ import mã từ đây
__init.py__
Tệp khởi tạo
items.py
tệp items của dự án
pipelines.py
tệp pipelines của dự án
settings.py
tệp cài đặt của dự án
spiders/
thư mục nơi sau này bạn sẽ đặt các spider
__init.py__
Tệp khởi tạo
Sau khi tạo dự án, bạn sẽ chuyển vào thư mục vừa tạo và gõ lệnh sau:
Lệnh này tạo một tệp mẫu tên aliexpress_tablets.py trong thư mục spiders như đã nói ở trên. Mã trong tệp đó như sau:
import scrapy
classAliexpressTabletsSpider(scrapy.Spider):
name ='aliexpress_tablets'
allowed_domains =['aliexpress.com']
start_urls =['https://www.aliexpress.com/category/200216607/tablets.html']defparse(self, response):passĐược hỗ trợ bởi
Trong đoạn mã trên, bạn có thể thấy name, allowed_domains, sstart_urls và một hàm parse.
name: Name là tên của spider. Đặt tên hợp lý giúp bạn theo dõi tất cả các spider đã tạo. Tên phải là duy nhất vì sẽ được dùng để chạy spider khi dùng scrapy crawl name_of_spider.
allowed_domains (tùy chọn): Một danh sách python tùy chọn, chứa các miền được phép thu thập. Yêu cầu tới URL không thuộc danh sách này sẽ không được thu thập. Chỉ bao gồm domain của website (Ví dụ: aliexpress.com) chứ không phải toàn bộ URL được chỉ định ở start_urls, nếu không bạn sẽ nhận cảnh báo.
start_urls: Yêu cầu tới các URL được nêu. Danh sách URL nơi spider sẽ bắt đầu thu thập khi không chỉ định URL cụ thể [4]. Vì vậy, các trang đầu tiên được tải sẽ là những trang liệt kê ở đây. Các Request tiếp theo sẽ được tạo dần từ dữ liệu chứa trong các start URL [4].
parse(self, response): Hàm này sẽ được gọi mỗi khi một URL được thu thập thành công. Nó còn được gọi là callback. response (dùng trong Scrapy shell) được trả về sau khi thu thập sẽ được truyền vào hàm này, và bạn viết mã trích xuất bên trong nó!
Thông tin: Bạn có thể dùng BeautifulSoup bên trong hàm parse() của spider Scrapy để phân tích tài liệu HTML.
Lưu ý: Bạn có thể trích xuất dữ liệu qua bộ chọn css dùng response.css() như đã nói trong phần scrapy shell, nhưng cũng có thể dùng XPath (XML) cho phép truy cập phần tử con. Bạn sẽ thấy ví dụ của response.xpath() trong mã được chỉnh trong hàm pass().
Bạn sẽ chỉnh tệp aliexpress_tablet.py. Tôi đã thêm một URL khác vào start_urls. Bạn có thể thêm logic trích xuất vào hàm pass() như dưới đây:
# -*- coding: utf-8 -*-import scrapy
classAliexpressTabletsSpider(scrapy.Spider):
name ='aliexpress_tablets'
allowed_domains =['aliexpress.com']
start_urls =['https://www.aliexpress.com/category/200216607/tablets.html','https://www.aliexpress.com/category/200216607/tablets/2.html?site=glo&g=y&tag=']defparse(self, response):print("procesing:"+response.url)#Extract data using css selectors
product_name=response.css('.product::text').extract()
price_range=response.css('.value::text').extract()#Extract data using xpath
orders=response.xpath("//em[@title='Total Orders']/text()").extract()
company_name=response.xpath("//a[@class='store $p4pLog']/text()").extract()
row_data=zip(product_name,price_range,orders,company_name)#Making extracted data row wisefor item in row_data:#create a dictionary to store the scraped info
scraped_info ={#key:value'page':response.url,'product_name': item[0],#item[0] means product in the list and so on, index tells what value to assign'price_range': item[1],'orders': item[2],'company_name': item[3],}#yield or give the scraped info to scrapyyield scraped_info
Được hỗ trợ bởi
Thông tin: zip() nhận n iterable và trả về một danh sách các tuple. Phần tử thứ i của mỗi tuple được tạo từ phần tử thứ i của mỗi iterable. [8]
Từ khóa yield được dùng khi bạn định nghĩa một hàm generator. Hàm generator giống hàm bình thường ngoại trừ việc dùng yield thay vì return. Từ khóa yield được dùng khi hàm gọi cần một giá trị và hàm chứa yield sẽ giữ trạng thái cục bộ của nó và tiếp tục chạy từ nơi nó dừng lại sau khi trả giá trị cho hàm gọi. Ở đây yield đưa dictionary sinh ra cho Scrapy để xử lý và lưu lại!
Giờ bạn có thể chạy spider:
scrapy crawl aliexpress_tablets
Bạn sẽ thấy một đầu ra dài trên dòng lệnh như dưới đây:
Xuất dữ liệu
Bạn sẽ cần dữ liệu ở dạng CSV hoặc JSON để tiếp tục phân tích. Phần này của hướng dẫn sẽ chỉ bạn cách lưu tệp CSV và JSON cho dữ liệu này.
Để lưu tệp CSV, mở settings.py trong thư mục dự án và thêm các dòng sau:
FEED_URI [5]: Chỉ định vị trí tệp. Bạn có thể lưu tệp trên lưu trữ cục bộ hoặc cả FTP.
Cơ chế Feed Export của Scrapy cũng có thể thêm dấu thời gian và tên spider vào tên tệp của bạn, hoặc bạn có thể dùng chúng để xác định thư mục muốn lưu.
%(time)s: được thay bằng dấu thời gian khi feed được tạo [5]
Các thay đổi Feed bạn thực hiện trong settings.py sẽ áp dụng cho tất cả spider trong dự án. Bạn cũng có thể đặt cài đặt tùy chỉnh cho một spider cụ thể để ghi đè cài đặt trong tệp settings.py.
# -*- coding: utf-8 -*-import scrapy
classAliexpressTabletsSpider(scrapy.Spider):
name ='aliexpress_tablets'
allowed_domains =['aliexpress.com']
start_urls =['https://www.aliexpress.com/category/200216607/tablets.html','https://www.aliexpress.com/category/200216607/tablets/2.html?site=glo&g=y&tag=']
custom_settings={'FEED_URI':"aliexpress_%(time)s.json",'FEED_FORMAT':'json'}defparse(self, response):print("procesing:"+response.url)#Extract data using css selectors
product_name=response.css('.product::text').extract()
price_range=response.css('.value::text').extract()#Extract data using xpath
orders=response.xpath("//em[@title='Total Orders']/text()").extract()
company_name=response.xpath("//a[@class='store $p4pLog']/text()").extract()
row_data=zip(product_name,price_range,orders,company_name)#Making extracted data row wisefor item in row_data:#create a dictionary to store the scraped info
scraped_info ={#key:value'page':response.url,'product_name': item[0],#item[0] means product in the list and so on, index tells what value to assign'price_range': item[1],'orders': item[2],'company_name': item[3],}#yield or give the scraped info to Scrapyyield scraped_info
Được hỗ trợ bởi
response.url trả về URL của trang tạo ra response. Sau khi chạy crawler bằng scrapy crawl aliexpress_tablets bạn có thể xem tệp json:
Theo các liên kết
Hẳn bạn đã để ý có hai liên kết trong start_urls. Liên kết thứ hai là trang 2 của kết quả tìm kiếm máy tính bảng. Việc thêm tất cả liên kết là không thực tế. Một crawler nên có khả năng tự đi qua tất cả các trang và chỉ cần điểm bắt đầu trong start_urls.
Nếu một trang có các trang tiếp theo, bạn sẽ thấy bộ điều hướng ở cuối trang cho phép di chuyển qua lại giữa các trang. Trong trường hợp trong hướng dẫn này, bạn sẽ thấy như sau:
Đây là đoạn mã bạn sẽ thấy:
Như bạn thấy, có một thẻ <span> với class .ui-pagination-active là trang hiện tại bạn đang ở, và bên dưới là tất cả thẻ <a> với liên kết tới trang tiếp theo. Mỗi lần bạn phải lấy các thẻ <a> sau thẻ <span> này. Đến đây cần một chút CSS! Bạn phải lấy nút anh em (sibling) chứ không phải nút con, nên bạn cần tạo một bộ chọn css báo cho crawler tìm các thẻ <a> đứng sau thẻ <span> có class .ui-pagination-active.
Hãy nhớ! Mỗi trang web có cấu trúc riêng. Bạn sẽ phải nghiên cứu một chút về cấu trúc để lấy phần tử mong muốn. Luôn thử response.css(SELECTOR) trên Scrapy Shell trước khi viết vào mã.
Chỉnh aliexpress_tablets.py của bạn như dưới đây:
import scrapy
classAliexpressTabletsSpider(scrapy.Spider):
name ='aliexpress_tablets'
allowed_domains =['aliexpress.com']
start_urls =['https://www.aliexpress.com/category/200216607/tablets.html']
custom_settings={'FEED_URI':"aliexpress_%(time)s.csv",'FEED_FORMAT':'csv'}defparse(self, response):print("procesing:"+response.url)#Extract data using css selectors
product_name=response.css('.product::text').extract()
price_range=response.css('.value::text').extract()#Extract data using xpath
orders=response.xpath("//em[@title='Total Orders']/text()").extract()
company_name=response.xpath("//a[@class='store $p4pLog']/text()").extract()
row_data=zip(product_name,price_range,orders,company_name)#Making extracted data row wisefor item in row_data:#create a dictionary to store the scraped info
scraped_info ={#key:value'page':response.url,'product_name': item[0],#item[0] means product in the list and so on, index tells what value to assign'price_range': item[1],'orders': item[2],'company_name': item[3],}#yield or give the scraped info to scrapyyield scraped_info
NEXT_PAGE_SELECTOR ='.ui-pagination-active + a::attr(href)'
next_page = response.css(NEXT_PAGE_SELECTOR).extract_first()if next_page:yield scrapy.Request(
response.urljoin(next_page),
callback=self.parse)Được hỗ trợ bởi
Trong đoạn mã trên:
đầu tiên bạn trích xuất liên kết trang tiếp theo bằng next_page = response.css(NEXT_PAGE_SELECTOR).extract_first() và sau đó nếu biến next_page nhận được một liên kết và không rỗng, nó sẽ vào thân if.
response.urljoin(next_page): Phương thức parse() sẽ dùng phương thức này để tạo một URL mới và cung cấp một yêu cầu mới, sau đó sẽ được gửi tới callback. [9]
Sau khi nhận URL mới, nó sẽ thu thập liên kết đó, thực thi thân for và lại tìm trang tiếp theo. Quá trình này tiếp tục cho đến khi không còn liên kết trang tiếp theo.
Đến đây bạn có thể ngồi thư giãn và để spider tự thu thập tất cả các trang. Spider trên sẽ trích xuất từ mọi trang tiếp theo. Đó sẽ là rất nhiều scraping! Nhưng spider của bạn sẽ lo hết! Bên dưới bạn có thể thấy kích thước tệp đã đạt 1,1MB.
Scrapy làm giúp bạn!
Trong hướng dẫn này, bạn đã học về Scrapy, cách so sánh với BeautifulSoup, Scrapy Shell và cách viết spider của riêng bạn trong Scrapy. Scrapy xử lý toàn bộ phần nặng của việc viết mã cho bạn, từ tạo tệp và thư mục dự án đến xử lý URL trùng lặp, giúp bạn thực hiện web scraping mạnh mẽ chỉ trong vài phút và hỗ trợ mọi định dạng dữ liệu phổ biến để bạn đưa vào các chương trình khác. Hướng dẫn này chắc chắn sẽ giúp bạn hiểu Scrapy và framework của nó cũng như những gì bạn có thể làm với nó. Để thành thạo Scrapy, bạn sẽ cần đi qua tất cả các chức năng tuyệt vời mà nó cung cấp, nhưng hướng dẫn này đã giúp bạn có thể trích xuất nhóm trang web một cách hiệu quả.
Nếu bạn muốn có cái nhìn tổng quan về web scraping trong Python, hãy học khóa Web Scraping with Python của DataCamp.
Trong hướng dẫn này, bạn sẽ học cách sử dụng Scrapy, một framework Python giúp bạn xử lý khối lượng dữ liệu lớn! Bạn sẽ học Scrapy bằng cách xây dựng một web scraper cho AliExpress.com, một trang thương mại điện tử. Bắt đầu thu thập nào!
Kiến thức cơ bản về HTML và CSS sẽ giúp bạn hiểu hướng dẫn này dễ dàng và nhanh hơn. Đọc bài viết này để ôn lại HTML và CSS.
Tổng quan về Scrapy
Web scraping đã trở thành một cách hiệu quả để trích xuất thông tin từ web phục vụ ra quyết định và phân tích. Đây đã là một phần thiết yếu trong bộ công cụ của khoa học dữ liệu. Các nhà khoa học dữ liệu nên biết cách thu thập dữ liệu từ các trang web và lưu trữ dữ liệu đó theo nhiều định dạng khác nhau để phân tích tiếp theo.
Bất kỳ trang web nào bạn thấy trên internet đều có thể được thu thập thông tin và bất cứ thứ gì hiển thị trên một trang web đều có thể được trích xuất [2]. Mỗi trang web có cấu trúc và các phần tử riêng, vì vậy bạn cần viết trình thu thập/crawler (spider) của mình phù hợp với trang đang được trích xuất.
Scrapy sử dụng các
spider, là những crawler độc lập được cung cấp một tập lệnh hướng dẫn [1]. Với Scrapy, việc xây dựng và mở rộng các dự án thu thập lớn trở nên dễ dàng hơn nhờ cho phép nhà phát triển tái sử dụng mã.Scrapy so với BeautifulSoup
Trong phần này, bạn sẽ có cái nhìn tổng quan về một công cụ web scraping được dùng phổ biến là BeautifulSoup và so sánh nó với Scrapy.
Scrapy là một framework Python cho web scraping, cung cấp gói giải pháp hoàn chỉnh cho nhà phát triển mà không phải lo duy trì mã.
Beautiful Soup cũng được dùng rộng rãi cho web scraping. Đây là một gói Python để phân tích cú pháp tài liệu HTML và XML và trích xuất dữ liệu từ đó. Nó khả dụng cho Python 2.6+ và Python 3.
Dưới đây là một số khác biệt tóm tắt giữa chúng:
requests,urlib2để mở URL và lưu kết quả [6]Twisterhoạt động bất đồng bộ (không chặn) để đồng thời.multiprocessing.Thông tin: Đồng bộ nghĩa là bạn phải chờ một tác vụ kết thúc mới bắt đầu tác vụ tiếp theo, trong khi Bất đồng bộ cho phép bạn chuyển sang tác vụ khác trước khi tác vụ trước đó hoàn tất
Đây là một hướng dẫn BeautifulSoup thú vị của DataCamp để học thêm.
Cài đặt Scrapy
Khi đã cài đặt Python 3.0 (trở lên), nếu bạn dùng anaconda, bạn có thể dùng
condađể càiscrapy. Gõ lệnh sau trong anaconda prompt:conda install -c conda-forge scrapyĐể cài anaconda, xem các hướng dẫn DataCamp cho Mac và Windows.
Hoặc, bạn có thể dùng Trình cài đặt gói Python
pip. Cách này dùng được trên Linux, Mac và Windows:pip install scrapyScrapy Shell
Scrapy cũng cung cấp một shell thu thập web gọi là Scrapy Shell để nhà phát triển kiểm thử giả định về hành vi của một trang. Hãy lấy một trang máy tính bảng tại trang thương mại điện tử AliExpress. Bạn có thể dùng Scrapy shell để xem trang web trả về những thành phần nào và bạn có thể dùng chúng theo yêu cầu của mình ra sao.
Mở dòng lệnh và gõ lệnh sau:
scrapy shellNếu bạn dùng anaconda, bạn cũng có thể gõ lệnh trên trong anaconda prompt. Kết quả trên dòng lệnh hoặc anaconda prompt sẽ tương tự như sau:
Bạn cần chạy một crawler trên trang web bằng lệnh
fetchtrong Scrapy shell. Một crawler hoặc spider sẽ đi qua trang web tải nội dung văn bản và metadata của nó.fetch(https://pt.aliexpress.com/category/201005406/special-store.html)Lưu ý: Luôn đặt URL trong dấu ngoặc, ngoặc đơn hoặc ngoặc kép đều được
Kết quả sẽ như sau:
Trình thu thập trả về một response có thể xem bằng lệnh
view(response)trên shell:view(response)Và trang web sẽ được mở bằng trình duyệt mặc định.
Bạn có thể xem mã HTML thô bằng lệnh sau trong Scrapy shell:
print(response.text)Bạn sẽ thấy đoạn mã tạo ra trang web. Đây là nội dung tương tự khi bạn nhấp chuột phải vào vùng trống bất kỳ trên một trang và chọn view source hoặc view page source. Vì bạn chỉ cần thông tin liên quan trong toàn bộ mã, hãy dùng công cụ dành cho nhà phát triển của trình duyệt để kiểm tra phần tử cần thiết. Hãy lấy các phần tử sau:
Nhấp chuột phải vào phần tử bạn muốn và chọn
inspectnhư dưới đây:Các công cụ dành cho nhà phát triển của trình duyệt sẽ hỗ trợ bạn rất nhiều khi web scraping. Bạn có thể thấy đó là thẻ
<a>vớiclasslà product và phần văn bản chứa tên sản phẩm:Sử dụng bộ chọn CSS để trích xuất
Bạn có thể trích xuất thông tin bằng thuộc tính phần tử hoặc bộ chọn CSS như class. Gõ lệnh sau trong Scrapy shell để trích xuất tên sản phẩm:
response.css(".product::text").extract_first()Kết quả sẽ là:
extract_first()trích xuất phần tử đầu tiên thỏa mãn bộ chọn CSS. Nếu bạn muốn trích xuất tất cả tên sản phẩm, dùngextract():response.css(".product::text").extract()Đoạn mã sau sẽ trích xuất khoảng giá của các sản phẩm:
response.css(".value::text").extract()Tương tự, bạn có thể thử với số đơn hàng và tên cửa hàng.
Sử dụng XPath để trích xuất
XPath là ngôn ngữ truy vấn để chọn các nút trong tài liệu XML [7]. Bạn có thể điều hướng qua tài liệu XML bằng XPath. Ở phía sau, Scrapy dùng XPath để điều hướng đến các mục trong tài liệu HTML. Các bộ chọn CSS bạn dùng ở trên cũng được chuyển thành XPath, nhưng trong nhiều trường hợp CSS rất dễ dùng. Tuy nhiên, bạn nên biết XPath hoạt động trong Scrapy như thế nào.
Mở Scrapy Shell và gõ
fetch(https://pt.aliexpress.com/category/201005406/special-store.html/)như trước. Thử các đoạn mã sau [3]:response.xpath('/html').extract()Lệnh này sẽ hiển thị tất cả mã nằm dưới thẻ
<html>./nghĩa là con trực tiếp của nút. Nếu bạn muốn lấy các thẻ<div>dưới thẻhtml, bạn sẽ viết [3]:response.xpath('/html//div').extract()Với XPath, bạn phải hiểu cách dùng
/và//để điều hướng qua các nút con và hậu duệ. Đây là một hướng dẫn hữu ích về Các nút XPath và một số ví dụ để thử.Nếu bạn muốn lấy tất cả thẻ
<div>, bạn có thể khoan sâu mà không cần dùng/html[3]:response.xpath("//div").extract()Bạn có thể lọc thêm các nút bắt đầu và tới nút mong muốn bằng cách dùng thuộc tính và giá trị của chúng. Dưới đây là cú pháp dùng class và giá trị.
response.xpath("//div[@class='quote']/span[@class='text']").extract()response.xpath("//div[@class='quote']/span[@class='text']/text()").extract()Xét đoạn mã HTML sau:
Bạn muốn lấy văn bản bên trong thẻ
<a>, là nút con của<div>có classsite-notice-container container, bạn có thể làm như sau:response.xpath('//div[@class="site-notice-container container"]/a[@class="notice-close"]/text()').extract()Tạo dự án Scrapy và Spider tùy chỉnh
Web scraping có thể được dùng để tạo một bộ tổng hợp giúp bạn so sánh dữ liệu. Ví dụ, bạn muốn mua một máy tính bảng và muốn so sánh sản phẩm cùng giá cả, bạn có thể thu thập các trang mong muốn và lưu vào tệp Excel. Ở đây bạn sẽ thu thập thông tin máy tính bảng từ aliexpress.com.
Bây giờ, bạn sẽ tạo một spider tùy chỉnh cho cùng trang đó. Trước hết, bạn cần tạo một dự án Scrapy nơi mã và kết quả sẽ được lưu. Gõ lệnh sau trong dòng lệnh hoặc anaconda prompt.
scrapy startproject aliexpressLệnh này sẽ tạo một thư mục ẩn trong cài đặt python hoặc anaconda mặc định của bạn.
aliexpresssẽ là tên thư mục. Bạn có thể đặt tên bất kỳ. Bạn có thể xem nội dung thư mục trực tiếp qua Explorer. Cấu trúc thư mục như sau:__init.py____init.py__Sau khi tạo dự án, bạn sẽ chuyển vào thư mục vừa tạo và gõ lệnh sau:
[scrapy genspider aliexpress_tablets](https://pt.aliexpress.com/category/201005406/special-store.html)Lệnh này tạo một tệp mẫu tên
aliexpress_tablets.pytrong thư mụcspidersnhư đã nói ở trên. Mã trong tệp đó như sau:Trong đoạn mã trên, bạn có thể thấy
name,allowed_domains, sstart_urlsvà một hàmparse.scrapy crawl name_of_spider.response(dùng trong Scrapy shell) được trả về sau khi thu thập sẽ được truyền vào hàm này, và bạn viết mã trích xuất bên trong nó!Thông tin: Bạn có thể dùng BeautifulSoup bên trong hàm
parse()của spider Scrapy để phân tích tài liệu HTML.Lưu ý: Bạn có thể trích xuất dữ liệu qua bộ chọn css dùng
response.css()như đã nói trong phần scrapy shell, nhưng cũng có thể dùng XPath (XML) cho phép truy cập phần tử con. Bạn sẽ thấy ví dụ củaresponse.xpath()trong mã được chỉnh trong hàmpass().Bạn sẽ chỉnh tệp
aliexpress_tablet.py. Tôi đã thêm một URL khác vàostart_urls. Bạn có thể thêm logic trích xuất vào hàmpass()như dưới đây:Thông tin:
zip()nhận n iterable và trả về một danh sách các tuple. Phần tử thứ i của mỗi tuple được tạo từ phần tử thứ i của mỗi iterable. [8]Từ khóa
yieldđược dùng khi bạn định nghĩa một hàm generator. Hàm generator giống hàm bình thường ngoại trừ việc dùngyieldthay vìreturn. Từ khóayieldđược dùng khi hàm gọi cần một giá trị và hàm chứayieldsẽ giữ trạng thái cục bộ của nó và tiếp tục chạy từ nơi nó dừng lại sau khi trả giá trị cho hàm gọi. Ở đâyyieldđưa dictionary sinh ra cho Scrapy để xử lý và lưu lại!Giờ bạn có thể chạy spider:
scrapy crawl aliexpress_tabletsBạn sẽ thấy một đầu ra dài trên dòng lệnh như dưới đây:
Xuất dữ liệu
Bạn sẽ cần dữ liệu ở dạng CSV hoặc JSON để tiếp tục phân tích. Phần này của hướng dẫn sẽ chỉ bạn cách lưu tệp CSV và JSON cho dữ liệu này.
Để lưu tệp CSV, mở
settings.pytrong thư mục dự án và thêm các dòng sau:Sau khi lưu
settings.py, chạy lạiscrapy crawlaliexpress_tabletstrong thư mục dự án của bạn.Tệp CSV sẽ trông như sau:
Lưu ý: Mỗi lần bạn chạy spider, tệp sẽ được nối thêm dữ liệu.
Cơ chế Feed Export của Scrapy cũng có thể thêm dấu thời gian và tên spider vào tên tệp của bạn, hoặc bạn có thể dùng chúng để xác định thư mục muốn lưu.
%(time)s: được thay bằng dấu thời gian khi feed được tạo [5]%(name)s: được thay bằng tên spider [5]Ví dụ:
ftp://user:[email protected]/scraping/feeds/%(name)s/%(time)s.jsonCác thay đổi Feed bạn thực hiện trong
settings.pysẽ áp dụng cho tất cả spider trong dự án. Bạn cũng có thể đặt cài đặt tùy chỉnh cho một spider cụ thể để ghi đè cài đặt trong tệpsettings.py.response.urltrả về URL của trang tạo ra response. Sau khi chạy crawler bằngscrapy crawl aliexpress_tabletsbạn có thể xem tệp json:Theo các liên kết
Hẳn bạn đã để ý có hai liên kết trong
start_urls. Liên kết thứ hai là trang 2 của kết quả tìm kiếm máy tính bảng. Việc thêm tất cả liên kết là không thực tế. Một crawler nên có khả năng tự đi qua tất cả các trang và chỉ cần điểm bắt đầu trongstart_urls.Nếu một trang có các trang tiếp theo, bạn sẽ thấy bộ điều hướng ở cuối trang cho phép di chuyển qua lại giữa các trang. Trong trường hợp trong hướng dẫn này, bạn sẽ thấy như sau:
Đây là đoạn mã bạn sẽ thấy:
Như bạn thấy, có một thẻ
<span>với class.ui-pagination-activelà trang hiện tại bạn đang ở, và bên dưới là tất cả thẻ<a>với liên kết tới trang tiếp theo. Mỗi lần bạn phải lấy các thẻ<a>sau thẻ<span>này. Đến đây cần một chút CSS! Bạn phải lấy nút anh em (sibling) chứ không phải nút con, nên bạn cần tạo một bộ chọn css báo cho crawler tìm các thẻ<a>đứng sau thẻ<span>có class.ui-pagination-active.Chỉnh
aliexpress_tablets.pycủa bạn như dưới đây:Trong đoạn mã trên:
đầu tiên bạn trích xuất liên kết trang tiếp theo bằng
next_page = response.css(NEXT_PAGE_SELECTOR).extract_first()và sau đó nếu biếnnext_pagenhận được một liên kết và không rỗng, nó sẽ vào thânif.response.urljoin(next_page): Phương thức parse() sẽ dùng phương thức này để tạo một URL mới và cung cấp một yêu cầu mới, sau đó sẽ được gửi tới callback. [9]Sau khi nhận URL mới, nó sẽ thu thập liên kết đó, thực thi thân
forvà lại tìm trang tiếp theo. Quá trình này tiếp tục cho đến khi không còn liên kết trang tiếp theo.Đến đây bạn có thể ngồi thư giãn và để spider tự thu thập tất cả các trang. Spider trên sẽ trích xuất từ mọi trang tiếp theo. Đó sẽ là rất nhiều scraping! Nhưng spider của bạn sẽ lo hết! Bên dưới bạn có thể thấy kích thước tệp đã đạt 1,1MB.
Scrapy làm giúp bạn!
Trong hướng dẫn này, bạn đã học về Scrapy, cách so sánh với BeautifulSoup, Scrapy Shell và cách viết spider của riêng bạn trong Scrapy. Scrapy xử lý toàn bộ phần nặng của việc viết mã cho bạn, từ tạo tệp và thư mục dự án đến xử lý URL trùng lặp, giúp bạn thực hiện web scraping mạnh mẽ chỉ trong vài phút và hỗ trợ mọi định dạng dữ liệu phổ biến để bạn đưa vào các chương trình khác. Hướng dẫn này chắc chắn sẽ giúp bạn hiểu Scrapy và framework của nó cũng như những gì bạn có thể làm với nó. Để thành thạo Scrapy, bạn sẽ cần đi qua tất cả các chức năng tuyệt vời mà nó cung cấp, nhưng hướng dẫn này đã giúp bạn có thể trích xuất nhóm trang web một cách hiệu quả.
Để đọc thêm, bạn có thể tham khảo Tài liệu Scrapy chính thức.
Ngoài ra, đừng quên xem khóa học Web Scraping with Python của DataCamp.
Tài liệu tham khảo