Scrapy's project layout and spider lifecycle are the frame every crawler you write will fit into.
Scrapy is a framework for crawling websites and extracting structured data. Unlike a requests-plus-parser script, it handles scheduling, concurrency, retries, throttling, deduplication and export for you; you write spiders that say what to fetch and how to parse it.
Install it with pip install scrapy, then:
scrapy startproject bookstorecreates a project:settings.py,items.py,pipelines.py,middlewares.pyand aspiders/package.scrapy genspider quotes quotes.toscrape.comadds a spider skeleton.scrapy crawl quotesruns the spider by itsname.- For a single file without a project,
scrapy runspider quotes.pyworks too.
A spider is a class subclassing scrapy.Spider with:
name: unique id used byscrapy crawl.allowed_domains: requests to other domains are dropped by the offsite middleware.start_urls: the first pages to fetch. For custom start requests, defineasync def start(self)and yieldscrapy.Requestobjects. Older code usesstart_requests(), deprecated in 2.13 and since removed, so current versions never call it.parse(self, response): the default callback, called with each downloaded response. It's a generator that yields extracted items (dicts) and new requests.
Everything is asynchronous: yielding a request doesn't download it immediately; Scrapy schedules it and calls your callback later, possibly interleaved with many other pages.
Example
# quotes.py (run with: scrapy runspider quotes.py -O quotes.json)
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
allowed_domains = ["quotes.toscrape.com"]
start_urls = ["https://quotes.toscrape.com/"]
def parse(self, response):
self.logger.info("Visited %s (status %s)", response.url, response.status)
for quote in response.css("div.quote"):
yield {
"text": quote.css("span.text::text").get(),
"author": quote.css("small.author::text").get(),
}
# $ scrapy startproject bookstore && cd bookstore
# $ scrapy genspider quotes quotes.toscrape.com
# $ scrapy crawl quotes
# ... 'item_scraped_count': 10, 'response_received_count': 2, ...Remember
- startproject creates the layout; genspider adds a spider
- A Spider has name, start_urls (or async start()) and parse callbacks
- Callbacks yield items and new requests
- Requests are scheduled and downloaded asynchronously
Watch out for
- Returning a list once at the end of parse instead of yielding items as you go (works, but delays everything and holds memory).