1.scrapy爬虫框架

  • 简介

    • Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架,可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中
  • 安装scrapy

    pip install scrapy -i https://pypi.douban.com/simple
    
    • 如果出现错误,则回顾视频资料解决

1.1、scrapy项目的创建以及运行

  • 创建scrapy项目

    • cmd输入:scrapy startproject 项目名称

image-20230810221056198.png

  • 项目组成

    spiders
        __init__.py
        自定义的爬虫文件.py:        由我们自己创建,是实现爬虫核心功能的文件
    __init__.py
    itmes.py:        定义数据结构的地方,是一个继承自scrapy.Item的累
    middlewares.py:        中间件——代理
    pipelines.py:        管道文件,里面只有一个类,用于处理下载数据的后续处理,默认是300优先级,值越小优先级越高(1-1000)
    settings.py:        配置文件,比如:是否遵守robots协议,User-Agent定义等
    
  • 创建爬虫文件

    • 跳转到spiders文件夹:cd 目录名字/目录名字/spiders
    • scrapy genspider 爬虫名字 网页的域名

image-20230810221104460.png

  • 爬虫文件的基本组成

    继承scrapy.Spider类
      name = "baidu"            -> 运行爬虫文件时使用的名字
      allowed_domains            -> 爬虫允许的域名,在爬取的时候,如果不是此域名之下的url,会被过滤掉
      start_urls                -> 声明了爬虫的起始地址,可以写多个url,一般是一个
      parse(self,response)    -> 解析数据的回调函数
    

    ```python import scrapy

class BaiduSpider(scrapy.Spider):

  # 爬虫的名字,用于运行爬虫的时候使用的值
  name = "baidu"
  # 允许访问的域名
  allowed_domains = ["www.baidu.com"]
  # 起始的url地址,指的是第一次要访问的域名
  # start_urls 是在 allowed_domains的前面添加一个http://,在后面添加一个/
  start_urls = ["http://www.baidu.com/"]
  # 是执行了start_urls之后执行的方法,方法中的response就是返回的那个对象
  # 相当于 response = urllib.request.urlopen()——response = requests.get()
  def parse(self, response):
      print("学习漫长的代码路程中...")

- 运行爬虫文件:

  - scrapy crawl 爬虫名称

![image-20230810221113044.png](images/image-20230810221113044.png)

  **注意点:在settings文件中,有存在遵守robots协议,所以访问的域名为百度时则无法有预期的响应效果**

  ```python
  # robots协议:各大网站中商讨的一个协议(互相都不爬,遵守则设置该值 = True)【一般情况下,不用遵守!】
  # ROBOTSTXT_OBEY = True

1.2、.scrapy架构组成

  • 引擎
    • 自动运行,无需关注,会组织所有的请求对象,分发给下载器
  • 下载器
    • 从引擎处获取到请求对象后,请求数据
  • spiders
    • Spider类定义了如何爬取某个网站。包括了爬取的动作(就是执行爬虫的)
  • 调度器
    • 有自己的调度规则,无需关注
  • 管道
    • 最终处理数据的管道,会预留接口供我们处理数据

当Item在Spider中被收集后,它将会被传递到Item Pipline,一些组件会按照一定的顺序执行对Item的处理。每个Item pipeline组件是实现了简单方法的Python类。他们接受到Item并通过它执行一些行为,同时也决定此Item是否继续通过pipeline,或是被丢弃而不在进行处理

1.3、.scrapy工作原理

  1. 引擎向Spider要url
  2. 引擎将要爬取的url给调度器
  3. 调度器会将url生成请求对象放入到指定的队列中
  4. 从队列中出队一个请求
  5. 引擎将请求交给下载器进行处理
  6. 下载器发送请求获取互联网数据
  7. 下载器将数据返回给引擎
  8. 引擎再将数据再次给到Spiders
  9. spiders通过xpath解析该数据,得到数据或者url
  10. spiders将数据或url交给引擎
  11. 引擎判断是数据还是url,是数据,交给管道(item pipkeline)处理,是url交给调度器处理 -> 回到第二步

image-20230810221121557.png

  • 案例:汽车之家
import scrapy


class CarSpider(scrapy.Spider):
    name = "car"
    allowed_domains = ["car.autohome.com.cn/price/brand-15.html"]
    start_urls = ["https://car.autohome.com.cn/price/brand-15.html"]

    def parse(self, response):
        print("=============================")
        names = response.xpath("//div[@class='main-title']/a/text()")
        money = response.xpath("//span[@class='font-arial']/text()")
        for i in range(len(names)):
            print(f"车名:{names[i].extract()}——价格:{money[i].extract()}")

2.scrapy shell

  • 概述

    • Scrapy终端,是一个交互终端,供在未启动spider的情况下尝试及调试自己的爬取代码。测试提取数据
  • 安装

    • pip install ipython
    • 提供只能的自动补全,高亮输出及其它特性
  • 应用——cmd

    scrapy shell www.baidu.com
    scrapy shell http://www.baidu.com
    scrapy shell "www.baidu.com"
    
  • 语法

    response对象:
        response.text    -二进制网页数据
        response.body    -网页数据
        response.url    -url
        response.status    -状态码
    response的解析:
        response.xpath()【常用】
                使用xpath路径查询特定元素,返回一个selector列表对象
        response.css()
                使用css_selector查询元素,返回一个selector列表对象
                获取内容:response.css("#su::text").extract_first()
                获取属性:response.css("#su::arrt('value')").extract_first()
    selectort对象(通过xpath方法调用返回的是selector列表)
        extract()
            提取selector对象的值【提取不到报错】
        extract_first()
            提取selector列表中的第一个值【取不到会返回空值】
        【注意:每一个selector对象都可以再次去使用xpath或者css方法】
    

3.yield

  • 带有yield的函数不在是一个普通函数,而是一个生成器generator,可用于迭代

  • yield是一个类似 return的关键字,迭代一次遇到yield时就返回yield后面的值,下一次迭代时,从上一次迭代遇到的yield后面的代码开始执行

  • 案例:当当网

    • dangdang.py

    ```python import scrapy

    from scrapy_four_dangdang.items import ScrapyFourDangdangItem

class DangdangSpider(scrapy.Spider): name = "dangdang"

  # 如果是多页下载的话,那么必须要调整的是allowed_domains的范围,一般情况下只写域名
  allowed_domains = ["category.dangdang.com"]
  start_urls = ["http://category.dangdang.com/cp01.01.02.00.00.00.html"]

  # 第二页:http://category.dangdang.com/pg2-cp01.01.02.00.00.00.html
  # 第三页:http://category.dangdang.com/pg3-cp01.01.02.00.00.00.html
  baseUrl = "http://category.dangdang.com/pg"
  page = 1
  def parse(self, response):
      # piplines 下载数据
      # items     定义数据结构
      # 定位信息:定位src图片资源时,img取src属性时会导致都是同一张图片
      #           原因:懒加载——真正的图片路径是@data-original
      # src = response.xpath("//ul[@id='component_59']/li//img/@data-original")
      # name = response.xpath("//ul[@id='component_59']/li//img/@alt")
      # price = response.xpath("//ul[@id='component_59']/li//p[@class='price']/span[1]/text()")
      # 所有的selector对象都可以再次调用xpath——将共同部分抽取出来
      li_List = response.xpath("//ul[@id='component_59']/li")
      for i in li_List:
          # 第一个src时,会发现取到None,因为网页中第一个的图片资源为img 而其它为懒加载data_original
          src = i.xpath(".//img/@data-original").extract_first()
          if src:
              src = src
          else:
              src = i.xpath(".//img/@src").extract_first()

          name = i.xpath(".//img/@alt").extract_first()
          price = i.xpath(".//p[@class='price']/span[1]/text()").extract_first()

          book = ScrapyFourDangdangItem(src=src,name=name,price=price)


          # yield 获取一个book,则返回一个book到piplelines去下载
          yield book
      if(self.page<100):
          self.page+=1
          url = f"{self.baseUrl}{self.page}-cp01.01.02.00.00.00.html"
          # 怎么去调用parse方法继续请求获取数据并下载
          # scrapy.Request就是scrpay的get请求(scrapy.Request(url,callback)),url就是请求地址 callback就是要执行的函数注意不加()
          yield scrapy.Request(url=url,callback=self.parse)

  - items.py

  ```python
  # Define here the models for your scraped items
  #
  # See documentation in:
  # https://docs.scrapy.org/en/latest/topics/items.html

  import scrapy


  class ScrapyFourDangdangItem(scrapy.Item):
      # define the fields for your item here like:
      # name = scrapy.Field()
      # 通俗说,itmes就是要下载的数据都有什么
      src = scrapy.Field()
      name = scrapy.Field()
      price = scrapy.Field()
  • pipelines.py

    # 如果要使用管道,需要在settings中开启
    class ScrapyFourDangdangPipeline:
      # 在爬虫文件开始执行前执行该方法
      def open_spider(self,spider):
          self.f = open("./book.json","w",encoding="UTF-8")
          print("======================================")
      # item就是yield后面的book对象
      def process_item(self, item, spider):
          # 在执行前打开文件,执行完毕后关闭文件来解决频繁操作文件的问题
          self.f.write(str(item))
          # 以下这种方式不推荐,因为每传递一个对象,就打开一次文件,对文件的操作过于频繁
          """
              1.write()方法必须写字符串,不能是对象
              2.操作文件的方式为 a 追加状态,因为打开一次则会覆盖前面的的内容
              with open("./book.json","a",encoding="UTF-8") as f:
                  f.write(str(item))
          """
          return item
    
      # 在爬虫文件开始执行后执行该方法
      def close_spider(self,spider):
          print("======================================")
          self.f.close()
    # 开启多条疏通管道
    # 1.定义管道类
    # 2.在settings中开启管道———— "scrapy_four_dangdang.pipelines.ScrapyFourDangdangPipeline_img":301,
    class ScrapyFourDangdangPipeline_img:
      def process_item(self, item, spider):
          url = f"http:{item.get('src')}"
          fileName = f"./booksImg/{item.get('name')}.jpg"
          import urllib.request
          urllib.request.urlretrieve(url=url,filename=fileName)
          return item
    
  • settings.py【打开疏通管道,并设置多条疏通管道】

    ITEM_PIPELINES = {
      # 管道可以有很多个,存在优先级,范围(1-1000)值越小,优先级越高
     "scrapy_four_dangdang.pipelines.ScrapyFourDangdangPipeline": 300,
     "scrapy_four_dangdang.pipelines.ScrapyFourDangdangPipeline_img":301,
    }
    

4.CrawlSpider

  • 概述

    • 继承自scrapy.Spider。CrawlSpider可以定义规则,再解析html内容的时候,可以根据链接规则取出指定的链接,然后再想这些链接发送请求
  • 提取链接

    scrapy.linkextractors.LinkExtractor(
        allow = (),            # 正则表达式,提取符合正则的链接
        deny = (),            # (不用)正则表达式,不提去符合正则的链接
        allow_domains = ()    # (不用)允许的域名
        deny_domains = ()    # (不用)不允许的域名
        restrict_xpaths = ()# xpath,提取符合xpath规则的链接
        restrict_css = ()    # 提取符合选择器规则的链接
    )
    
  • 使用

    • 导入

image-20230810221139441.png

  • 正则表达式获取

image-20230810221145179.png

  • 得到响应结果

image-20230810221150546.png

  • 通过xpath路径获取

image-20230810221155547.png

5.CralSpider案例

读书网数据入库

  • 创建项目

    1.创建项目 scrapy startproject scrapy_six_readBook
    2.切换到spiders cd scrapy_six_readBook\scrapy_six_readBook\spiders
    3.创建爬虫类 scrapy gensipder -t crawl read www.dushu.com
    
  • read.py

import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule

from scrapy_six_readBook.items import ScrapySixReadbookItem

class ReadSpider(CrawlSpider):
    name = "read"
    allowed_domains = ["www.dushu.com"]
    # 爬取数据时,第一页的数据回导致丢失,因为路径不存在正则表达式当中【注意】 1188_1
    start_urls = ["http://www.dushu.com/book/1188_1.html"]

    rules = (Rule(LinkExtractor(allow=r"/book/1188_\d+\.html"), callback="parse_item", follow=True),)

    def parse_item(self, response):
        # 爬取书的名字和图片
        list = response.xpath("//div[@class='bookslist']//img")
        for i in list:
            name = i.xpath("./@alt").extract_first()
            img = i.xpath("./@data-original").extract_first()

            book = ScrapySixReadbookItem(name=name,img=img)
            yield book
  • items.py
class ScrapySixReadbookItem(scrapy.Item):
    name = scrapy.Field()
    img = scrapy.Field()
  • piplines管道
class ScrapySixReadbookPipeline:
    def open_spider(self,spider):
        self.f = open("./book.json","w",encoding="UTF-8")
    def process_item(self, item, spider):
        self.f.write(str(item))
        return item
    def close_spider(self,spider):
        self.f.close()

import pymysql
class ScrapySixReadbookPipeline_MySQL:
    def open_spider(self,spider):
        # 创建链接
        self.conn = pymysql.connect(
            host="localhost",
            port=3306,
            user="root",
            password="root",
            charset="utf8"
        )
        # 选择数据库
        self.conn.select_db("python")
        # 获取游标
        self.cursor = self.conn.cursor()
    def process_item(self, item, spider):
        # sql = "insert into readbook(name,img) values('{}','{}')".format(item["name"],item["img"])
        sql = f"insert into readbook(name,img) values('{item['name']}','{item['img']}')"
        # print(sql)
        self.cursor.execute(sql)
        self.conn.commit()
        return item
    def close_spider(self,spider):

        self.cursor.close()
        self.conn.close()
  • settings.py
ITEM_PIPELINES = {
   "scrapy_six_readBook.pipelines.ScrapySixReadbookPipeline": 300,
   # ScrapySixReadbookPipeline_MySQL
   "scrapy_six_readBook.pipelines.ScrapySixReadbookPipeline_MySQL":301,
}

6.日志信息和日志等级

  • 日志级别

    • CRITICAL:严重错误
    • ERROR:一般错误
    • WARNING:警告
    • INFO:一般信息
    • DEBUG:调试信息

    默认的日志等级是DEBUG

  • settings.py文件设置

    # 指定日志的级别
    #LOG_LEVEL = "WARNING"
    # 将日志打印出到指定文佳佳
    LOG_FILE = "./logdemo.log"
    
    1. LOG_FILE:将屏幕显示的信息全部记录到文件中,屏幕不再显示,注意文件后缀一定是.log
    2. LOG_LEVEL:设置日志显示的等级,就是显示哪些,不显示哪些

7.scrapy的post请求

1.重写start_requests方法:
    def start_requests(self)
2. start_requests的返回值
    scrapy.FormRequest(url,headers,callback,formdata)
        url:发送的post地址
        headers:可以定制头信息
        callback:回调函数
        formdata:post所携带的数据,这是一个字典
  • 爬虫类
import scrapy


class PostSpider(scrapy.Spider):
    name = "post"
    allowed_domains = ["fanyi.baidu.com"]
    # post请求,如果没有参数,那么这个请求就没有任意
    # 因此start_urls没有用了——响应的parse方法也没用了
    """
        start_urls = ["http://fanyi.baidu.com/"]

        def parse(self, response):
            pass
    """
    # post请求
    def start_requests(self):
        url = "https://fanyi.baidu.com/sug"
        data = {
            "kw":"final"
        }
        yield scrapy.FormRequest(url=url,formdata=data,callback=self.parse_second)

    def parse_second(self, response):
        import json
        print(json.loads(response.text))

8.代理

(1) 到settings.py打开一个选项
    DOWNLOADER_MIDDLEWARES = {
        'postproject.middlewares.Proxy': 543,
    }
(2)到middlewares.py写代码
    def process_request(self, request, spider):
    request.meta['proxy'] = 'https://113.68.202.10:9999'
    return None

results matching ""

    No results matching ""