Dưới đây là một số tip để tránh tốn quá nhiều thời gian cào dữ liệu.
Sử dụng tối đa số lượng hiển thị
Một số website bán hàng cho phép hiển thị số lượng item khác nhau, chẳng hạn như 10, 50 hay 100. Vậy nên nếu website cho chúng chúng ta làm điều đó thì hãy luôn để nó hiển thị nhiều nhất có thể. Như thế chúng ta sẽ tiết kiệm được rất nhiều request phải gửi đi. Chẳng hạn, để cào 3000 item, thay vì phải gửi 300 request, mỗi request trả về 10 item thì chúng ta có thể gửi chỉ 30 request, mỗi request trả về 100 item.
Chấp nhận đánh đổi dữ liệu
Rất nhiều nguồn dữ liệu giới hạn số lượng request được gửi cùng 1 thời điểm vì nhiều lí do, chẳng hạn như chống autobot, chống ddos…v.v Do đó, đôi khi chúng ta nên chỉ cào những gì thực sự quan trọng, thay vì lấy hết tất cả mọi thứ có trên website.
Cào ngay trong response
Phương thức xpath()
trả về giá trị là một đối tượng Selector, đây có thể hiểu như là một element trong HTML, nghĩa là nó có đoạn code HTML của lấy được từ nguồn dữ liệu. Do đó chúng ta cũng có thể sử dụng phương thức xpath()
ngay trên chính Selector đó.
Để ví dụ thì chúng ta quay lại bài cào dữ liệu phân trang, ở file manual.py,
chúng ta định nghĩa Spider có nhiệm vụ cào dữ liệu trên trang quotes.toscrape.com. Bây giờ chúng ta sửa lại file này như sau:
# -*- coding: utf-8 -*- import scrapy from scrapy.loader import ItemLoader from scrapy.loader.processors import MapCompose, Join from ex.items import ExItem from scrapy import Request from urllib import parse class ManuelSpider(scrapy.Spider): name = "manual" allowed_domains = ["web"] start_urls = ( 'http://quotes.toscrape.com/', ) total_scraped = 0 def parse(self, response): next_selectors = response.xpath('//*[contains(@class, "next")]//a//@href') for url in next_selectors.extract(): yield Request(parse.urljoin(response.url, url), callback=self.parse, dont_filter=True) row_selectors = response.xpath('//*[contains(@class, "row")]') for selector in row_selectors: yield self.getItem(selector, response) def getItem(self, selector, response): ld = ItemLoader(item=ExItem(), selector=selector) ld.add_xpath('author', '//*[contains(@class, "author")]//text()') self.total_scraped = self.total_scraped + 1 print('Total scrapes:', self.total_scraped) return ld.load_item()
Ở đoạn code trên, chúng ta chỉ cào dữ liệu theo chiều ngang, tức là chỉ gửi Request tới những nút Next thôi. Còn phần cào theo chiều dọc thì ban đầu chúng ta lấy selector của element có class là row, sau đó trong câu lệnh yield
chúng ta truyền selector này vào hàm getItem(),
và lại được truyền vào đối tượng ItemLoad,
đoạn sau vẫn như cũ, chỉ khác là chúng ta lấy dữ liệu từ element có class author thay vì author-title như lần trước.
Mặc dù trong bài trước thì chúng ta cũng có thể làm việc này bằng cách lấy xpath của author ngay trên trang chính thay vì gửi request tới từng trang của author, tuy nhiên đoạn code ở đây có mục đích là giới thiệu về cách cào dữ liệu ngay bên trong các Selector có sẵn, thay vì phải gửi lại request mới, do đó giúp tiết kiệm thời gian.
$ scrapy crawl manual ... {'author': ['Albert Einstein', 'J.K. Rowling', 'Jane Austen', ... } ...