进入到要创建 Scrapy 项目的目录,执行以下命令行代码:
scrapy startproject tutorial
执行之后将会在当前目录下创建一个名为 tutorial 的目录,目录内容如下:
tutorial/
scrapy.cfg # Scrapy 配置文件
tutorial/ # 项目的Python模块
__init__.py
items.py # 项目的item定义文件
middlewares.py # 项目中间件文件
pipelines.py # 项目pipeline文件
settings.py # 项目配置文件
spiders/ # 存放爬虫的目录
__init__.py
Spider 是自己定义的类,Scrapy 用于从网站抓取信息。Spider 必须是scrapy.Spider
的子类,并定义发出的初始请求、怎样跟随页面中的链接以及如何解析抓取的内容。
以下是一个 Spide 的样例,将其保存在 tutorial/spiders
目录下:
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)
response
是一个 TextResponse
实例,包含页面的内容。
parse() 方法通常解析响应,提取抓取的数据作为字典,并寻找新的 URL 和创建新的请求。
返回项目顶级目录,运行以下代码:
scrapy crawl quotes
通过在类中定义 start_urls
变量可以省略 start_requests()
方法,这一列表之后将会被用于 start_requests()
的默认实现,以创建 spider 的初始请求。
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
def parse(self, response):
page = response.url.split("/")[-2]
filename = 'quotes-%s.html' % page
with open(filename, 'wb') as f:
f.write(response.body)
因为
parse()
方法是 Scrapy 的默认回调函数,因此不用明确写明调用代码也会调用。
可以通过使用 Scrapy shell 来了解如何利用 Scrapy 提取数据。运行以下代码:
scrapy shell "http://quotes.toscrape.com/page/1/"
注意 Windows 下必须使用双引号!!!
通过 shell 操作,可以在 response 对象上使用 CSS 来选择元素:
>>> response.css('title')
此命令返回一个 SelectorList
列表对象。
提取 title 中的文本:
>>> response.css('title::text').getall()
不加
::text
提取整个标签
获取第一个结果:
>>> response.css('title::text').get()
使用正则表达式提取数据:
>>> response.css('title::text').re(r'Q\w+')
一个 Scrapy spider 通常生成许多包含从页面提取的数据的字典。使用 yield
关键字可以达到这一目的。
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
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(),
}
最简单地,使用 Feed exports:
scrapy crawl quotes -o quotes.json
推荐使用 JSON Lines:scrapy crawl quotes -o quotes.jl
提取标签中的属性:
>>> response.css('li.next a::attr(href)').get()
>>> response.css('li.next a').attrib['href']
上面两行代码执行效果相同,都会返回对应的链接。
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)
使用 response.urljoin()
来获取绝对地址,并 yield 一个新的指向下一页的 Request 对象。
当在回调函数中 yield 一个 Request 对象时,Scrapy 将会对其进行调度,并在请求结束后注册待执行的回调函数。
next_page = response.css('li.next a::attr(href)').get()
if next_page is not None:
yield response.follow(next_page, callback=self.parse)
response.follow()
支持相对地址,因此不必 join。
循环写法:
for href in response.css('li.next a::attr(href)'):
yield response.follow(href, callback=self.parse)
对于 标签,
response.follow()
会自动使用 href
属性:
for a in response.css('li.next a'):
yield response.follow(a, callback=self.parse)