5-爬虫-Scrapy爬虫框架环境安装及使用、数据解析、持久化存储、redis数据库的使用、全站数据的爬取

scrapy基本介绍

基本介绍:基于异步爬虫的框架。高性能的数据解析,高性能的持久化存储,全站数据爬取,增量式爬虫,分布式爬虫......

scrapy环境的安装

- Linux:

  pip install scrapy

- Windows:

  a. pip install wheel

  b. 下载twisted https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted

     - twisted插件是scrapy实现异步操作的三方组件。
  c. 进入下载目录,执行 pip install Twisted‑17.1.0‑cp35‑cp35m‑win_amd64.whl

  d. pip install pywin32

  e. pip install scrapy

scrapy基本使用

- 1.创建一个工程:scrapy startproject proName
  - 工程的目录结构:
  - spiders:爬虫包/文件夹
    - 要求:必须要存储一个或者多页爬虫文件
  - settings.py:配置文件
- 2.创建爬虫文件
  - cd proName
  - scrapy genspider spiderName www.xxx.com
- 3.执行工程
  - scrapy crawl spiderName
  - 重点关注的日志信息:ERROR类型的日志信息
    - settings.py:LOG_LEVEL = 'ERROR'
    - settings.py:不遵从robots协议 (将True改为False)
    - settings.py:UA伪装    

       USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36'

 spiders下的爬虫文件内容介绍

cd proName
scrapy genspider spiderName www.xxx.com

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


class FirstSpider(scrapy.Spider):
    #爬虫文件名称:当前爬虫文件的唯一标识
    name = 'first'
    #允许的域名(用来做限定)
    # allowed_domains = ['www.taobao.com']
    #起始的url列表:存储即将要发起请求的url,列表存有的url都会被框架自动进行get请求的发送
    start_urls = ['https://www.sogou.com/','https://www.taobao.com']
    #数据解析
    #参数response就是请求成功后对应的响应对象
    def parse(self, response):
        print(response)

数据解析

- 使用xpath进行数据解析
- 注意:使用xpath表达式解析出来的内容不是直接为字符串,而是Selector对象,想要的
字符串数据是存储在该对象中。
- extract():如果xpath返回的列表元素有多个
- extract_first():如果xpath返回的列表元素只有一个

import scrapy


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

    def parse(self, response):
        div_list = response.xpath('//*[@id="content"]/div/div[2]/div')
        for div in div_list:
            # 返回的并不是字符串形式的作者名称,而是返回了一个Selector对象
            # 想要的字符串形式的作者名称是存储在该对象中
            # 需要将Selector对象中存储的字符串形式的作者名称取出
            # author = div.xpath('./div[1]/a[2]/h2/text()')[0]  # 这样取出得是一个对象需要在后边加extract才能取出文本
            author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()
            # Selector.extract():将Selector对象中的字符串取出
            # [].extract_first():将列表中的第一个列表元素(Selector)中的字符串取出
            content = div.xpath('./a/div/span//text()').extract()
            # [].extract():将列表中的每一个列表元素Selector对象中的字符串取出
            content = ''.join(content)
            print(author, content)

持久化存储

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

只可以将parse方法的返回值存储到制定后缀的文本文件中(只支持这些文本文件类型'json', 'jsonlines', 'jl', 'csv', 'xml', 'marshal', 'pickle')

局限性:
    - 1.只可以将parse方法返回值进行持久化存储
    - 2.只可以将数据存储到文件中无法写入到数据库
 存储指令:scrapy crawl spiderName -o filePath

import scrapy


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

    # 基于终端指令进行持久化存储
    def parse(self, response):
        div_list = response.xpath('//*[@id="content"]/div/div[2]/div')
        all_list = []
        for div in div_list:
            author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()
            content = div.xpath('./a/div/span//text()').extract()
            content = ''.join(content)
            dic = {
                'author': author,
                'content': content
            }
            all_list.append(dic)
        return all_list

基于管道的持久化存储

1.在爬虫文件中进行数据解析

2.在items.py文件中定义相关的属性

  - 属性的个数要和解析出来的字段个数同步

3.将解析出来的数据存储到item类型的对象中

4.将item提交给管道

5.在管道中接收item对象且将该对象中存储的数据做任意形式的持久化存储

6.在配置文件中开启管道机制

qiubai.py
import scrapy
from qiubaiPro.items import QiubaiproItem


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

    def parse(self, response):
        div_list = response.xpath('//*[@id="content"]/div/div[2]/div')
        all_list = []
        for div in div_list:
            author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()
            content = div.xpath('./a/div/span//text()').extract()
            content = ''.join(content)

            # 实例化item类型的对象
            item = QiubaiproItem()
            # 只能通过中括号调属性,不能用点
            item['author'] = author
            item['content'] = content

            # 将item提交给管道
            yield item 
item.py
import scrapy


class QiubaiproItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    author = scrapy.Field()
    content = scrapy.Field()
pipelines.py
class QiubaiproPipeline:
    fp = None
    def open_spider(self,spider):#方法只会在爬虫开始时执行一次
        print('i am open_spider()')
        self.fp = open('./qiubai.txt','w',encoding='utf-8')
    #用来接收item对象,该方法调用的次数取决于爬虫文件像管道提交item的次数
    #参数:
        #- item:就是接收到的item对象
        #- spider:爬虫类实例化的对象
    def process_item(self, item, spider):
        author = item['author']
        content = item['content']
        self.fp.write(author+':'+content)

        return item
    def close_spider(self,spider):#在爬虫结束时被执行一次
        print('i am close_spider()')
        self.fp.close()
settings.py

在settings.py中解除该代码得注释

ITEM_PIPELINES = {
  #300:表示管道的优先级,数值越小优先级越高,优先级越高表示该管道越先被执行
    'qiubaiPro.pipelines.QiubaiproPipeline': 300, 
    'qiubaiPro.pipelines.MysqlPipeline': 301,
    'qiubaiPro.pipelines.redisPipeLine': 302,
}

管道细节处理

在配置文件中,管道对应的数值表示的是优先级
什么情况下需要使用多个管道类?
  - 数据备份。一个管道类表示将数据存储到一种形式的载体中。
  想要将数据存储到mysql一份,redis一份,需要有两个管道类来实现。
思考:
  - 爬虫文件向管道提交的item只会提交给优先级最高的那一个管道类
  - proces_item方法中的return item的作用
    - 将item对象提交给下一个即将被执行的管道类

from itemadapter import ItemAdapter


class QiubaiproPipeline:
    fp = None

    def open_spider(self, spider):
        print('打开文件')
        self.fp = open('./qiubai.txt', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        author = item['author']
        content = item['content']
        self.fp.write(author + ':' + content)
        return item

    def close_spider(self, spider):
        print('关闭文件')
        self.fp.close()


import pymysql

#封装一个管道类,将数据存储到mysql中
class MysqlPipeline:
    conn = None
    cursor = None

    def open_spider(self, spider):
        self.conn = pymysql.Connect(
            host='127.0.0.1',
            port=3306,
            user='root',
            password='123',
            charset='utf8',
            db='spider',
        )

    def process_item(self, item, spider):
        author = item['author']
        content = item['content']
        self.cursor = self.conn.cursor()
        sql = 'insert into qiubai values("%s","%s")' % (author, content)

        try:
            self.cursor.execute(sql)
            self.conn.commit()
        except Exception as e:
            print(e)
            self.conn.rollback()

    def close_spider(self, spider):
        self.cursor.close()
        self.conn.close()


import redis


class redisPipeline:
    conn = None

    def open_spider(self, spider):
        self.conn = redis.Redis(host='127.0.0.1', port=6379)

    def process_item(self, item, spider):
        self.conn.lpush('qiubaiData', item)

redis数据库的使用

 redis是一个非关系型数据库

下载安装

  中文官网:http://www.redis.cn/

启动  

  1.启动redis的服务器端

    - redis-server
  2.启动redis的客户端
    - redis-cli

具体使用

  查看所有数据:keys *
  删除所有数据:flushall
  set集合:
    - 插入数据: sadd 集合的名称 存储的值
    - 查看数据:smembers 集合的名称
    - set集合自动去重
  list列表
    - 插入数据:lpush 列表的名称 插入的值
    - 查看数据:lrange 列表名称 0 -1
    - 查看长度:llen 列表名称
    - 可以存储重复的数据

python中使用需要导包

   pip install -U redis==2.10.6

  必须下载2.10.6,其他版本不能往里边存字典

import redis


class redisPipeline:
    conn = None

    def open_spider(self, spider):
        self.conn = redis.Redis(host='127.0.0.1', port=6379)

    def process_item(self, item, spider):
        self.conn.lpush('qiubaiData', item)

全站数据爬取

将所有页码对应的数据进行爬取+存储
- 手动请求发送:
  - 通过代码的形式进行请求发送get请求
    - yield scrapy.Request(url,callback):
      - 可以对指定url发起get请求,回调callback进行数据解析
  - 手动发起post请求:
    - yield scrapy.FormRequest(url, formdata, callback)
- 问题:start_urls列表中的url是如何发起post请求?
  重写父类如下方法即可:
  def start_requests(self):
    for url in self.start_urls:
      yield scrapy.FormRequest(url=url, formdata,callback=self.parse)

duanzi.py

import scrapy
from duanziPro.items import DuanziproItem


class DuanziSpider(scrapy.Spider):
    name = 'duanzi'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://duanziwang.com/category/一句话段子/1/']
    url_model = 'https://duanziwang.com/category/一句话段子/%d/'
    page = 2

    def start_requests(self):
        for url in self.start_urls:
            yield scrapy.Request(url=url, callback=self.parse11)

    def parse11(self, response):
        article_list = response.xpath('/html/body/section/div/div/main/article')
        for article in article_list:
            title = article.xpath('./div[1]/h1/a/text()').extract_first()
            content = article.xpath('./div[2]/p/text()').extract_first()

            item = DuanziproItem()
            item['title'] = title
            item['content'] = content
            yield item
        if self.page < 3:
            new_url = format(self.url_model %self.page)
            self.page += 1
            yield scrapy.Request(url=new_url, callback=self.parse11)

pipelines.py

from itemadapter import ItemAdapter


class DuanziproPipeline:
    fp = None

    def open_spider(self, spider):
        print(spider.name)     # duanzi
        self.fp = open('./duanzi.txt', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        self.fp.write(item['title'] + ':' + item['content']+'
')
        return item

    def close_spider(self, spider):
        self.fp.close()
原文地址:https://www.cnblogs.com/wgwg/p/13267360.html