建立一个最简单的工程
这是固定步骤。
scrapy startproject tutorial
第一个Spider
在spider文件夹下建立py文件 quotes_spider.py。
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
def start_requests(self):
urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
def parse(self, response):
page = response.url.split("/")[-2]
filename = 'quotes-%s.html' % page
with open(filename, 'wb') as f:
f.write(response.body)
self.log('Saved file %s' % filename)
- name在工程内唯一,定义爬虫名。
- start_request()方法返回Request对象,
- parse()方法用于处理response,也能生成新的Request请求。
- 这里scrapy.Request是被自动调度的对象,其中指定了url和返回response后的回调函数parse。如果没有重写start_requests方法,那么会有一个默认的方法,这时候就只用写一个start_urls的list就行了,parse会被调用处理这些url。
运行Spider
scrapy crawl quotes
之后就能看到文件夹下面会出现几个爬取的网页。到此为止,下载的网页只是简单地保存成了文件,还没有进行处理。
shell中简单取出数据
使用scrapy的shell可以进行一些即时交互,便于调试和学习。
scrapy shell "http://quotes.toscrape.com/page/1/"
之后在shell中输入命令就能马上得到一些结果。css、xpath选择器是最常用的(re()正则方法也很重要),具体的语法用到的时候再学就行。实在摸不着头脑最好对网页和xml的结构进行一下复习。
css:
>>> response.css('title')
[<Selector xpath='descendant-or-self::title' data='<title>Quotes to Scrape</title>'>]
xpath:
>>> response.xpath('//title/text()').get()
'Quotes to Scrape'
Spider中取出数据
修改parse()方法,返回一个dict(parse()方法只能返回新的request、dict对象或者是None,如果返回不允许返回的类型,就会报错,具体可以参看文档),注意用yield关键字(与generator语法有关)。
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('small.author::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
到这里我们虽然的到了一些数据,但是我们想持久化存储这些爬到的数据该怎么办?首先试试json存一下(其实还有个jl文件,好奇可以搜索一下,是一个流类型的json格式):
scrapy crawl quotes -o quotes.json
然而,我们又会想,输出一个json就完事了?我们如果想对数据进行一些加工该怎么办呢?这时候pipelines.py就派上用场了。
跟踪超链接
urls太不方便!每个网址都放在list中,我如果爬一万个网页这是不方便的。假设网页里已经有了超链接(例如a标志),那么先把它取出来吧!
在shell中可以实验:
>>> response.css('li.next a::attr(href)').get()
'/page/2/'
把parse()方法修改成下面的样子:
def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').get(),
'author': quote.css('small.author::text').get(),
'tags': quote.css('div.tags a.tag::text').getall(),
}
next_page = response.css('li.next a::attr(href)').get()
if next_page is not None:
next_page = response.urljoin(next_page)
yield scrapy.Request(next_page, callback=self.parse)
注意这里urljoin()是把相对url转换成完整的url。然后由于这个yield,每个request完成后,下一个request就会加入调度中了。
凡是固定的语法都可以进行精简,对于框架来说自然是越简单越好,下面也能理解成是一个语法糖:
if next_page is not None:
yield response.follow(next_page, callback=self.parse)
命令行工具传参数之类的具体可以参看文档。另外yield返回的dict完全可以用一个返回dict的函数替换,这样可以使代码更加模块化。
总结(一)
世界上最好的教程就是官网的文档,无论学习什么框架,如果仅仅从网上随便搜到的教程上学习,一方面别人写的不一定好理解,另一方面框架的时效性是非常强的,如果看的是旧文档,最后发现版本和自己使用的不对头是非常令人扫兴的。