一, 基于终端指令的持久化存储

要保证爬虫文件的parse方法中有可迭代类型对象(一般是列表或字典)的返回,该返回值可以通过终端指令的形式写入指定格式的文件中,进行持久化存储的操作

持久化存储的命令:

scrapy crawl  爬虫名称  -o  文件名.后缀名
# 仅支持这几种文件格式:'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle'
# -*- coding: utf-8 -*-
import scrapy


class XiaopapaSpider(scrapy.Spider):
    name = 'xiaopapa'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://www.qiushibaike.com/text/']


    def parse(self, response):
        # xpath为response中的方法,可以直接用response直接调用xpath
        div_list = response.xpath("//div[@id='content-left']/div")
        # 创建空列表,用于存储获取的内容
        content_list = []
        for div in div_list:
            # xpath函数返回的为列表,列表中存放的数据为selector类型的数据
            # 我们解析的内容被封装在了selector对象中,需要调用extract()将数据取出
            # 如果可以保证xpath返回的列表中只有一条数据的时候可以用[0].extract()或者extract_first()
            author = div.xpath("./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()").extract_first()
            # 如果xpath返回的列表中有多条数据,则要使用extract()
            detail = "".join(div.xpath("./a[1]/div/span//text()").extract())

            dic = {
                "author":author,
                "detail":detail
            }
            content_list.append(dic)
        # 必须要有返回,返回一个可迭代对象
        return content_list

 

二 , 基于管道的持久化存储

scrapy框架中已经为我们专门集成好了高效,便捷的持久化操作功能,我们可以直接使用

两个有关文件 :
    items.py  :  数据结构模板文件,定义数据属性
    pipelines.py  : 管道文件,接收数据items,进行持久化操作

持久化存储流程:

  1. 爬虫文件爬取到数据后,需要将数据封装到items对象中
  2. 使用yield关键字将items对象提交给pipelines管道进行持久化操作
  3. 在管道文件中的process_item方法中接收爬虫文件提交过来的item对象,然后编写持久化存储的代码将items对象中存储的数据进行持久化存储
  4. settings.py配置文件中开启管道

爬取boos直聘有关python爬虫前三页的职位名称,薪资和公司名称,进行持久化存储:

items:

 1 # -*- coding: utf-8 -*-
 2 
 3 # Define here the models for your scraped items
 4 #
 5 # See documentation in:
 6 # https://doc.scrapy.org/en/latest/topics/items.html
 7 
 8 import scrapy
 9 
10 
11 class BoosItem(scrapy.Item):
12     # define the fields for your item here like:
13     # name = scrapy.Field()
14     job_name = scrapy.Field()
15     salary = scrapy.Field()
16     company = scrapy.Field()
View Code

爬虫文件:

 1 # -*- coding: utf-8 -*-
 2 import scrapy
 3 
 4 from boos.items import BoosItem
 5 
 6 
 7 class BoosDetilSpider(scrapy.Spider):
 8     name = 'boos_detil'
 9     # allowed_domains = ['www.boos.com']
10     start_urls = ['https://www.zhipin.com/job_detail/?query=python%E7%88%AC%E8%99%AB&scity=101010100&industry=&position=']
11 
12     url = "https://www.zhipin.com/c101010100/?query=python爬虫&page=%s&ka=page-1"
13     page = 1
14 
15     def parse(self, response):
16         li_list = response.xpath("//div[@class='job-list']/ul/li")
17         for li in li_list:
18             job_name = li.xpath("./div/div[1]/h3/a/div[1]/text()").extract_first()
19             salary = li.xpath("./div/div[1]/h3/a/span/text()").extract_first()
20             company = li.xpath("./div/div[2]/div/h3/a/text()").extract_first()
21 
22             # 实例化一个item对象
23             item = BoosItem()
24             # 将解析的数据封装到item中
25             item["job_name"] = job_name
26             item["salary"] = salary
27             item["company"] = company
28 
29             # 将item交给管道
30             yield item
31 
32         # 取前三页数据
33         if self.page <= 3:
34             self.page += 1
35             new_url = format(self.url%self.page)
36 
37             # 手动发送请求
38             yield scrapy.Request(url=new_url,callback=self.parse)
View Code

pipeline管道文件:

 1 # -*- coding: utf-8 -*-
 2 
 3 # Define your item pipelines here
 4 #
 5 # Don't forget to add your pipeline to the ITEM_PIPELINES setting
 6 # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
 7 
 8 
 9 class BoosPipeline(object):
10     fp = None
11 
12     def open_spider(self,spider):
13         print("开始爬虫......")
14         self.fp = open("./boos.txt","w",encoding="utf-8")
15 
16     # 爬虫文件每向管道提交一次item,则该方法就会被调用一次
17     # 参数item就是管道接收的item类型对象
18     def process_item(self, item, spider):
19         self.fp.write(item["job_name"]+":"+item["salary"]+":"+item["company"]+"\n")
20         # 返回给下一个即将被执行的管道类
21         return item
22 
23     def close_spider(self,spider):
24         print("结束爬虫")
25         self.fp.close()
View Code

settings配置文件67-69行:

1 ITEM_PIPELINES = {
2    'boos.pipelines.BoosPipeline': 300,  # 300表示优先级,每写一个类在这里注册一次
3 }
View Code

 

基于mysql的持久化存储:

只有pipeline添加了一个持久化存储的类,并且在settings中注册,其他没有改变

pipeline管道文件添加类:

import pymysql
 1 class mysqlPileLine(object):
 2     conn = None
 3     cursor =None
 4     def open_spider(self,spider):
 5         self.conn = pymysql.Connect(host='127.0.0.1',port=3306,user='root',password='',db='scrapy',charset="utf8")
 6         print(self.conn)
 7     def process_item(self, item, spider):
 8         self.cursor = self.conn.cursor()
 9         # print(item)
10         #print('insert into boss values ("%s","%s","%s")'%(item['job_name'],item['salary'],item['company']))
11         try:
12             print('insert into boss values ("%s","%s","%s")'%(item['job_name'],item['salary'],item['company']))
13             self.cursor.execute('insert into boss values ("%s","%s","%s")'%(item['job_name'],item['salary'],item['company']))
14             self.conn.commit()
15         except Exception as e:
16             print(e)
17             self.conn.rollback()
18     def close_spider(self,spider):
19         self.conn.close()
20         self.cursor.close()
View Code

settings注册:

1 ITEM_PIPELINES = {
2    #'bossPro.pipelines.BossproPipeline': 300,
3     'bossPro.pipelines.mysqlPileLine': 301,
4 }
View Code

 

基于redis的持久化存储:

pipeline管道文件添加类:

from redis import Redis
 1 class redisPileLine(object):
 2     conn = None
 3     def open_spider(self,spider):
 4         self.conn = Redis(host='127.0.0.1',port=6379)
 5         print(self.conn)
 6     def process_item(self, item, spider):
 7         # print(item)
 8         dic = {
 9             'name':item['job_name'],
10             'salary':item['salary'],
11             'company':item['company']
12         }
13         self.conn.lpush('boss',dic)
View Code

settings注册:

1 ITEM_PIPELINES = {
2    #'bossPro.pipelines.BossproPipeline': 300,
3    #'bossPro.pipelines.mysqlPileLine': 301,
4    'bossPro.pipelines.redisPileLine': 302,
5 }
View Code

 

三, 递归解析

当需要爬取一个多页面的网站的时候,每一个页面对应的url都是不同的,这时候就需要对这些不同的url依次发起请求,然后再通过对应的解析方法进行内容的解析

实现方案 :

  1, 将每一个页面对应的url放到爬虫文件的起始url列表中,但是因为比较麻烦,所以不推荐这样做

  2, 使用Request方法,手动发送请求

代码展示:

# -*- coding: utf-8 -*-
import scrapy

from boos.items import BoosItem


class BoosDetilSpider(scrapy.Spider):
    name = 'boos_detil'
    # allowed_domains = ['www.boos.com']
    start_urls = ['https://www.zhipin.com/job_detail/?query=python%E7%88%AC%E8%99%AB&scity=101010100&industry=&position=']

    url = "https://www.zhipin.com/c101010100/?query=python爬虫&page=%s&ka=page-1"
    page = 1

    def parse(self, response):
        li_list = response.xpath("//div[@class='job-list']/ul/li")
        for li in li_list:
            job_name = li.xpath("./div/div[1]/h3/a/div[1]/text()").extract_first()
            salary = li.xpath("./div/div[1]/h3/a/span/text()").extract_first()
            company = li.xpath("./div/div[2]/div/h3/a/text()").extract_first()

            # 实例化一个item对象
            item = BoosItem()
            # 将解析的数据封装到item中
            item["job_name"] = job_name
            item["salary"] = salary
            item["company"] = company

            # 将item交给管道
            yield item

        # 取前三页数据
        if self.page <= 3:
            self.page += 1
            new_url = format(self.url%self.page)

            # 使用Request方法手动发送请求,进行递归解析
            yield scrapy.Request(url=new_url,callback=self.parse)