简介
scrapy是一个基于python的爬虫框架,提供了一套比较完整的爬虫解决方案,包括模拟cookie,referer,ajax等,并且支持代理,失败重试等爬虫攻防操作。可以让你专注于数据本身而不需要处理太多底层的东西。
今天整理一部分操作要点在这里以供速查和快速入门。
项目
既然scrapy是一个框架,我们需要先创建项目文件夹
scrapy startproject tutorial
第三个参数就是项目的名字,建议按网站建立不同项目,因为一个项目共享一套配置,包括数据导出格式,使用哪些中间件等,如果不同网站的爬虫放在同一个项目可能需要反复修改这些配置导致混乱。但是一个项目可以有多个爬虫,比如爬列表的,爬详情的,爬标签的……
tutorial/
scrapy.cfg # 部署配置文件
tutorial/ # 项目代码文件夹
__init__.py
items.py # 项目item类
middlewares.py # 项目中间件类
pipelines.py # 项目流水线类
settings.py # 配置文件
spiders/ # 放置各种爬虫代码
__init__.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)
这个例子来自官方文档,展示了一个爬虫最最基本的两个部分。发送请求和分析数据。分别对应 start_requests 和 parse 两个方法。这就是我前面说的使用scrapy只需要专注于数据本身,就是这两块。后面的各种改进也是围绕这两块展开。
另外有一个关键属性就是name,表示爬虫的名字,是爬虫的唯一标识。我们在启动这个爬虫的时候需要用到。
scrapy crawl quotes
这个命令就是执行这个爬虫了。start_requests负责发起请求,他一定是yield一个Request对象,Request需要指定回调是parse。parse接收response对象,包含所有的响应信息,这个例子咱们不做更多的操作,直接保存返回数据为html。后面展示怎么从response中提取我们需要的信息。
选择器
官方提供了一种学习提取数据的方法是用shell模式
scrapy shell 'http://quotes.toscrape.com/page/1/'
这样可以在shell里面交互式编码,查看提取数据的结果。咱们先介绍css选择器,因为只要有一点前端基础的人都懂css选择器的语法。
>>> response.css('title')
[<Selector xpath='descendant-or-self::title' data='<title>Quotes to Scrape</title>'>]
>>> response.css('title::text').getall()
['Quotes to Scrape']
>>> response.css('title').getall()
['<title>Quotes to Scrape</title>']
>>> response.css('title::text').get()
'Quotes to Scrape'
>>> response.css('title::text')[0].get()
'Quotes to Scrape'
>>> response.css('title::text').re(r'Quotes.*')
['Quotes to Scrape']
>>> response.css('title::text').re(r'Q\w+')
['Quotes']
>>> response.css('title::text').re(r'(\w+) to (\w+)')
['Quotes', 'Scrape']
>>> tags = quote.css("div.tags a.tag::text").getall()
>>> tags
['change', 'deep-thoughts', 'thinking', 'world']
>>> response.css('li.next a::attr(href)').get()
'/page/2/'
这里有两个css选择器的扩展语法就是 ::text 和 ::attr 分别获取标签文本和属性。属性还可以用这种方式获得:
>>> response.css('li.next a').attrib['href']
'/page/2'
get方法还可以指定默认值
response.css(query).get(default='').strip()
用css选择器很容易定位到我们需要的数据,当然scrapy还提供另一种选择器就是xpath。这个也很经典,但是现在好像使用场景远远少于css,所以有兴趣的朋友自行学习这种新的语法。
返回数据和分页
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
]
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)
parse方法中yield一个字典就可以返回数据了,我们在执行的时候可以用 -o 参数指定输出到一个文件。默认的输出格式是jsonline,也就是一行一个json字符串,文件输出的格式还可以是csv, json, xml。json与jsonline的区别是json是输出一个json字符串包含所有结果。
scrapy crawl quotes -o quotes.json
如果要继续爬取下一页也可以在parse中产生下一页的request,不过在请求下一页的时候要注意相对路径的问题,上例得到的url就是一个相对路径,所以需要用response.urljoin方法来转为绝对路径。
结语
至此已经可以爬取一个分页列表的数据并把它保存在文件里了。如果一个网站没有设置反爬的话这里是完全没问题了。