上一期介绍了python爬虫框架Scrapy的安装和项目结构,具体内容可参考Mac使用Scrapy爬虫(一)
这一次我们先来小试牛刀,看看Scrapy能爬什么以及怎么爬去
一、最简单的爬虫
先在生成项目文件夹下的spiders文件夹下建立如下 firstCrawl文件:
然后在项目根目录下运行,-o参数会将爬取的内容都放入json文件
$:scrapy crawl myfirstCrawl -o myFirstCrawlData.json(注意这个需要和申明spider类的名字相同)
import scrapy
class QuotesSpider(scrapy.Spider):
name = "myfirstCrawl"
# start_urls = [
# 'http://quotes.toscrape.com/page/1/',
# 'http://quotes.toscrape.com/page/2/',
# ]
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):
##这里返回值其实是一个迭代器。每执一次,就会从迭代器中取一个元素
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').extract_first(),
'author': quote.css('small.author::text').extract_first(),
'tags': quote.css('div.tags a.tag::text').extract(),
}
##获取下一个需要爬去的地址,这个在迭代器中返回的是一个Request实例。他包含要爬去的地址和所需要进行的回调函数
next_page = response.css('li.next a::attr(href)').extract_first()
if next_page is not None:
next_page = response.urljoin(next_page)
yield scrapy.Request(next_page, callback=self.parse)
## 可以使用下面的缩写,来添加下一个需要爬去的内容
## yield response.follow(next_page, callback=self.parse)
以上是申明了一个Spider类,也就是用生成了一个爬虫器,他继承了scrapy下的Scpider类。
- name:用来标记改spider的名称,该名称在整个项目中唯一
- start_requests方法:这个方法需要返回一个起始爬去请求组。可以以数组的形式,或者也可以返回一个迭代器。在改方法下,就可以在请求前,或者在请求之后对请求来的内容做一些处理
- start_urls:同start_requests方法相同,也是需要爬去的地址。但是这个数组只是包含了需要爬取的地址
- parseh方法:用来处理每个Request请求所得到的Response。他的入参response是TextResponse的一个实例。因此,他包含了请求网页的内容。除了在这里可以进行对页面的处理之外,还可以从返回的内容中提取页面,并交给迭代器继续爬去。同时scrapy的schedule机制会自动甄别url,如果该url已经爬取过,将不会再次爬取
以上代码将所给两个网页爬取过来之后,将去到的数据生成一个迭代器,然后返回。之后数据会被写入myFirstCrawlData.json文件。
二、Scrapy 爬虫运行机理:
爬虫发生了什么?
1.从start_requests函数开始,定义需要爬取的Requests,并且定义对改Requests函数进行处理的回调函数,默认是parse函数。
2.迭代请求得到的Request,进入其回调函数,在这里进行处理。回调函数需要返回,其返回形式可以使一个字典,一个Item对象,一个新的Request请求或者是以上这些内容的一个迭代器。
3.在parse函数中,使用Selector类从Request–contents中提取所需数据。同样,也可以使用pyhton比较火的 BeautifulSoup, lxml 模块进行提取。
4.最后,进行数据持久化,或写入文件中。
三、Scrapy的Spider类:
对于Scrapy,我们的每一个特定网站的爬虫,其实是一个Spider类的实例,或者其子类的实例。
Spider类是最基础的类,其只包含了最基础的start_requests函数和parse函数。即通过start_requests获取请求,之后使用parse函数进行解析。
Spider子类有:
- CrawlSpider
- XMLFeedSpider
- CSVFeedSpider
- SitemapSpider
下面讲解下区别:
1、CrawlSpider
CrawlSpider使用与爬去有一定规律的网页。他提供了一个Rules机制,可以实现制定好爬虫的Rule规则,之后每一个符合改规则的新的Request会被提取出来。该子类的主要区别是,它具有rules和parse_start_url
- rules:一个Rule对象的数组或单例,使用Rule来定义爬去的规则,符合该规则的链接都会被提取出来
Rule(LinkExtractor(allow=('kid\.jpg', ), deny=('vipkid\.jpg', ), callback='parse_item')),
以上规则会提取页面中匹配了kid.jpg的链接,但是不会匹配包含了vipkid.jpg的链接。可以有多个的Rule,但是实际匹配中,如果匹配成功了一条规则,之后的将默认不会匹配。具体Rule的匹配规则,可见官网 https://doc.scrapy.org/en/latest/topics/spiders.html#generic-spiders
- parse_item:用来处理从start_urls获取来的初始的爬去Response,他可以返回Item对象,Request对象或他们的迭代形式
实例:
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
class MySpider(CrawlSpider):
name = 'baidu.com'
allowed_domains = ['baidu.com']
start_urls = ['http://www.baidu.com']
rules = (
Rule(LinkExtractor(allow=('category\.php', ), deny=('subsection\.php', ))),
Rule(LinkExtractor(allow=('item\.php', )), callback='parse_item'),
)
def parse_item(self, response):
item = scrapy.Item()
item['id'] = response.xpath('//td[@id="item_id"]/text()').re(r'ID: (\d+)')
item['name'] = response.xpath('//td[@id="item_name"]/text()').extract()
item['description'] = response.xpath('//td[@id="item_description"]/text()').extract()
return item