参考:tutorial
贴出架构架构图以便参考:
本tutorial涉及到的内容:
- 创建一个scrapy工程
- 写一个spider爬网页并提取数据
- 使用命令行导出scrapy数据
- 将spider改成递归跟踪链接
- 给spider传递参数
Creating a project
自己先创建一个目录,然后运行如下命令:
scrapy startproject tutorial
此命令根据内置的模板成生一个目录及部分文件:
tutorial/
scrapy.cfg # deploy configuration file
tutorial/ # project's Python module, you'll import your code from here
__init__.py
items.py # project items definition file
middlewares.py # project middlewares file
pipelines.py # project pipelines file
settings.py # project settings file
spiders/ # a directory where you'll later put your spiders
__init__.py
tutorial/spider目录下边放spider的代码,spider是一个类定义,必需是scrapy.Spider的子类,直接或者间接。
spider代码实现的功能包括:向ENGINE提供初始的request,如何处理response中的链接、以及如何从response中提取想要的数据这三件事情。
在这个目录下边创建文件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成员:SPIDER的唯一标识,在整个工程中不能重复。从架构图上可以看出来,每个SPIDER都在向ENGINE提交REQUEST,ENGINE会将REQUEST放入SCHEDULER队列,同时ENGINE会将RESPONSE交给parse处理,在这里name起到一个标识的作用,相当于消息系统中的定阅发布系统。
- start_request方法:生成初始request的地方。这个方法即可以返回request列表,也可以是一个持续不断生成request的生成器
- parse方法:解析拿到的response。通常包含两个功能,一个是提取数据,另一个是从RESPONSE中采集链接生成新REQUEST,然后交给ENGINE。
运行SPIDER:
scrapy crawl quotes
上边的例子只有两个起始url,在parse中没有提取数据,也没有跟踪新的连接,只是把response保存成文件,因此在生成两个文件后,此spider就结束运行了。
Extracting data in our spider
在parse方法中可以通过CSS选择器或者XPATH表达式提取数据:
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(),
}
可以看到,提取出来以后,组装成dict,然后yield,最终ENGINE会拿到这个数据。
Storing the scraped data
运行如下命令,Scrapy会将dict转换成json格式并输出到本地文件系统:
scrapy crawl quotes -o quotes.json
这个命令在每次运行时会将原始的文件替换掉,如果运行如下命令,则是在原始旧文件的基础上追加数据:
scrapy crawl quotes -o quotes.jl
Following links
代码如下:
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在提取数据的同时,还会将其中的链接提取出来并交给ENGINE去调度。
这里的新连接提取比较简单,还应该包含连接整形、过滤、去重、提交限速等问题,这些应该会在后边介绍到。
A shortcut for creating Requests
parse方法在提交新链接的请求时目前大概有两种方式,一种是上例中的scrapy.Request方法,这个方法里边的参数next_page是绝对路径。
另一个是response.follow方法,这个里边的参数next_page是相对路径。
Using spider arguments
带参数的命令:
scrapy crawl quotes -o quotes-humor.json -a tag=humor
-a tag=humor就是参数。
参数会被传递给scrapy.Spider的__init__方法,并且默认会成为SPIDER的属性。
下边的代码表示了访问传入参数的方法:
import scrapy
class QuotesSpider(scrapy.Spider):
name = "quotes"
def start_requests(self):
url = 'http://quotes.toscrape.com/'
tag = getattr(self, 'tag', None)
if tag is not None:
url = url + 'tag/' + tag
yield scrapy.Request(url, self.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(),
}
next_page = response.css('li.next a::attr(href)').get()
if next_page is not None:
yield response.follow(next_page, self.parse)