【爬虫】第七部分 scrapy

【爬虫】第七部分 scrapy



7. scrapy

7.1 基本使用

  1. pip install scrapy 安装

  2. 在终端运行以下代码: scrapy startproject 项目名字 创建爬虫项目(名字不要中文)

  3. cd 进入到 spiders,再运行下面创建爬虫文件的指令

在这里插入图片描述

scrapy genspider 爬虫文件名 要爬取的网页 创建爬虫文件(名字不要中文)

ex: scrapy genspider xxx www.xxx.com

这个时候我们就会看到spiders下面生成了xxx.py文件
在这里插入图片描述

  1. 运行爬虫文件 scrapy crawl 爬虫文件名字 ,注意运行之前到settings中将robot协议改了,才可以爬取

在这里插入图片描述


7.2 项目的文件结构

scrapy 项目的文件结构
    - 项目名
        - 项目名
           - spiders
                init
                自定义的爬虫文件
           - init
           - items  定义数据结构的地方
           - middleware 中间件  代理
           - pipelines  管道 用来处理下载下来的数据
           - settings 配置文件 robots协议 us定义等

7.3 response的方法和属性

extract(): 这个方法返回的是一个数组list里面包含了多个string,如果只有一个string,则返回[‘ABC’]这样的形式。

extract_first():这个方法返回的是一个string字符串是list数组里面的第一个字符串

import scrapy

class BaiduSpider(scrapy.Spider):
    # 爬虫文件的名字,用于运行爬虫的时候使用
    name = 'baidx'
    # 允许访问的域名
    allowed_domains = ['www.baidx.com']
    # 起始的url地址,指的是第一次要访问的域名
    # 这里需要注意一下,一般情况下将start_urls中 / 给去掉,不然容易出现一些问题
    start_urls = ['http://www.baidx.com']

    # 该方法中的response相当于 response = requests.get()
    def parse(self, response):
        res1 = response.text  # 拿到的是字符串,网页源码
        res2 = response.body  # 拿到的是二进制数据
        print("===========================================================")
        # 可以直接使用xpath的方法
        content = response.xpath('//input[@id="su"]/@value')
        # 使用extract()方法进行提取
        print(content.extract())

7.4 小案例

爬取汽车信息

import scrapy


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

    def parse(self, response):
        print("===========================")
        name_list = response.xpath('//div[@class="main-title"]/a/text()')
        price_list = response.xpath('//div[@class="main-lever-right"]/div/span/span/text()')
        for i in range(len(name_list)):
            res = name_list[i].extract() + '--->  ' + price_list[i].extract()
            print(res)


7.5 scrapy 工作原理

在这里插入图片描述

根据上述的流程图,绘制下图:

在这里插入图片描述

  1. 首先将spiders向引擎提供url
  2. 引擎将要爬取的url给调度器
  3. 调度器会将url生成请求对象放入到指定的队列中,队列中出队一个请求给引擎
  4. 引擎将请求件给下载器进行处理
  5. 下载器发送请求获取互联网数据
  6. 下载回来
  7. 下载器将数据返回给引擎
  8. 引擎将数据交给spiders,通过xpath解析数据
  9. 解析后的 数据 或者 url 交给引擎
  10. 引擎进行判断,如果是url则交给调度器,进行上述操作,如果是数据就交给管道保存下来

7.6 管道的使用

  1. 创建项目 : scrapy startproject dangdang

  2. 进入到spiders去创建爬虫文件 : scrapy genspider dang_data https://book.dangdang.com/01.03.htm?ref=book-01-A

  3. 编写创建出来的爬虫文件

    import scrapy
    # 导入定义数据结构
    from dangdang.items import DangdangItem
    
    class DangDataSpider(scrapy.Spider):
        name = 'dang_data'
        allowed_domains = ['https://book.dangdang.com/01.03.htm?ref=book-01-A']
        start_urls = ['https://book.dangdang.com/01.03.htm?ref=book-01-A']
    
        def parse(self, response):
            # src   //ul[@class="list_aa"]//li[@type="rollitem"]//li/a/img/@src
            # bookname //ul[@class="list_aa"]//li[@type="rollitem"]//li/p[@class="name"]/a/text()
            # author  //ul[@class="list_aa"]//li[@type="rollitem"]//li/p[@class="author"]/text()
            base = response.xpath('//ul[@class="list_aa"]//li[@type="rollitem"]//li')
            # 只要是selector对象就可以继续调用xpath
            for item in base:
                # 在这里需要注意加上 .  ,表示当前
                src = item.xpath('./a/img/@src').extract_first()
                bookname = item.xpath('./p[@class="name"]/a/text()').extract_first()
                author = item.xpath('./p[@class="author"]/text()').extract_first()
                # book实际上就是通过items整理好的对象,将该对象交给pipelines下载
                book = DangdangItem(src=src, bookname=bookname, author=author)
                # 使用yield 把对象交给pipelines
                yield book
    
  4. 在items文件中定义数据结构

    import scrapy
    
    class DangdangItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        # 在这里需要去定义你要下载的数据有哪些
        # 图片
        src = scrapy.Field()
        # 书名
        bookname = scrapy.Field()
        # 作者
        author = scrapy.Field()
    
  5. settings去开启管道

在这里插入图片描述

  1. 在pipelines文件中编写

    # 在使用管道前,需要去settings下开启管道
    class DangdangPipeline:
        # 这是一个在爬虫文件开始之前就执行的一个方法
        def open_spider(self, spider):
            self.f = open('book_msg.json', 'w', encoding='utf-8')
    
        def process_item(self, item, spider):
            # 这里的item就是yield返回过来的对象
            # write方法必须接受一个字符串而不能是其他的对象,需要强转
            self.f.write(str(item))
            # 注意一定要返回
            return item
    
        # 在爬虫文件执行完之后执行的一个方法:
        def close_spider(self, spider):
            self.f.close()
    
    

7.7 多管道下载

  1. 在pipelines原先的基础上模仿写

    # 在使用管道前,需要去settings下开启管道
    class DangdangPipeline:
        # 这是一个在爬虫文件开始之前就执行的一个方法
        def open_spider(self, spider):
            self.f = open('book_msg.json', 'w', encoding='utf-8')
    
        def process_item(self, item, spider):
            # 这里的item就是yield返回过来的对象
            # write方法必须接受一个字符串而不能是其他的对象,需要强转
            self.f.write(str(item))
            return item
    
        # 在爬虫文件执行完之后执行的一个方法:
        def close_spider(self, spider):
            self.f.close()
    
    
    import urllib.request
    
    
    # 开启多管道下载
    class DangdangDownload:
        def process_item(self, item, spider):
            url = 'https:' + item.get('src')
            filename = './books/' + item.get('bookname') + '.png'
            urllib.request.urlretrieve(url=url, filename=filename)
            return item
    
    
  2. 到settings文件中去添加新的管道,一样模仿这写

    # 开启管道,管道是有优先级的,优先级的范围是1-1000,值越小优先级越高
    ITEM_PIPELINES = {
        'dangdang.pipelines.DangdangPipeline': 300,
        # 开启多管道
        'dangdang.pipelines.DangdangDownload': 301
    }
    

7.8 下载分页类型和get请求的使用

创建出来的爬虫文件

import scrapy
import json
from pptmodel.items import PptmodelItem


class PptDemoSpider(scrapy.Spider):
    name = 'ppt_demo'
    # 对于下载分页这种类型,allowed_domains需要写成域名
    allowed_domains = ['theuser.zhuisoft.com']
    start_urls = [
        'http://theuser.zhuisoft.com/template/ajax_web/data_list?class=&type_id=0&order_by=0&title=&format=&page=0&num=40']

    def parse(self, response):
        res = response.text
        data = json.loads(res).get('data')
        for item in data:
            title = item.get('title')
            img = item.get('cover_img')
            ppt = PptmodelItem(title=title, img=img)
            yield ppt
        
        # 下载3页
        for i in range(1, 3):
            url = f'http://theuser.zhuisoft.com/template/ajax_web/data_list?class=&type_id=0&order_by=0&title=&format=&page={i}&num=40'
            # scrapy.Request 就是scrapy的get请求,注意这里调用函数不能写括号
            yield scrapy.Request(url=url, callback=self.parse)


7.9 下载多层级类型

创建出来的爬虫文件

import scrapy
from movie_demo.items import MovieDemoItem


class MvSpider(scrapy.Spider):
    name = 'mv'
    allowed_domains = ['0dytt.com']
    start_urls = ['https://0dytt.com/frim/1.html']

    """
        案例描述: 
            1. 需要在首页拿到电影名和跳转的链接
            2. 请求跳转的链接,xpath解析,拿到电影海报
    """

    def parse(self, response):
        # href  //div[@class="hy-video-list"]//li/a/@href
        # title //div[@class="hy-video-list"]//li/a/@title

        base = response.xpath('//div[@class="hy-video-list"]//li/a')
        for i in base:
            href = 'https://0dytt.com' + i.xpath('./@href').extract_first()
            film_name = i.xpath('./@title').extract_first()

            # 这个时候去请求拿到的新链接
            # 在这里使用meta参数用来将电影名传入parse_picture
            yield scrapy.Request(url=href, callback=self.parse_picture, meta={"film_name": film_name})

    # 因为页面结构的不同不能像上一个案例那样调用自己,所以需要创建一个新的函数
    def parse_picture(self, response):
        all_actor = ''
        style = response.xpath('//div[@class="hy-video-details clearfix"]//a/@style').extract_first()
        lead_actor = response.xpath('//div[@class="hy-video-details clearfix"]//ul/li[1]//a/text()').extract()
        for actor in lead_actor:
            all_actor = all_actor + actor
        film_name = response.meta['film_name']
        start = style.find('(')
        end = style.find(')')
        src = 'http' + style[start + 6:end]

        info = MovieDemoItem(src=src, film_name=film_name, lead_actor=all_actor)
        yield info

pipelines文件

class MovieDemoPipeline:
    def open_spider(self, spider):
        self.f = open('movie.json', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        self.f.write(str(item) + ',')
        return item

    def end_spider(self, spider):
        self.f.close()


import urllib.request
import random


class MoviePicture:
    def process_item(self, item, spider):
        src = item.get('src')
        filename = 'posters/' + item.get('film_name') + '.jpg'
        # 在这里遇到了防爬,所以需要进行伪装
        ua_list = ['Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0',
                   'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36',
                   'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36 Edg/103.0.1264.62',
                   'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0',
                   'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36 SE 2.X MetaSr 1.0'
                   ]
        # 创建opener对象
        opener = urllib.request.build_opener()
        # 给opener添加请求头
        opener.addheaders = [('User-Agent', random.choice(ua_list))]
        # 将opener设置为全局安装
        urllib.request.install_opener(opener)
        urllib.request.urlretrieve(url=src, filename=filename)
        return item


7.10 post请求的使用

import scrapy
import json


class TranslateSpider(scrapy.Spider):
    name = 'translate'
    allowed_domains = ['fanyi.baidu.com']
    # start_urls = ['http://fanyi.baidu.com/sug']
    # def parse(self, response):
    #     pass
    """
        这里的写法,和我们使用get请求就不一样了
        因为post请求需要携带参数,而start_urls第一次请求没有办法携带,
        导致parse函数接收不到response,所以注释掉
    """

    def start_requests(self):
        url = 'http://fanyi.baidu.com/sug'
        data = {
            "kw": "hello"
        }
        # scrapy.FormRequest()就是post请求
        yield scrapy.FormRequest(url=url, formdata=data, callback=self.parse_1)

    def parse_1(self, response):
        res = response.text
        print(json.loads(res))


总结

以上就是今天要讲的内容,希望对大家有所帮助!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值