• 【爬虫】scrapy创建运行爬虫、解析页面(嵌套url)、自定义中间件(设置UserAgent和代理IP)、自定义管道(保存到mysql)


    1.说明

    scrapy是一个快速、高层次的屏幕抓取和web抓取框架,我们只需要在乎怎么提取数据和保存数据,其他的都交给scrapy完成,所以比较快速高效,而且功能强大,很多东西都是可以自定义配置的。

    在这里插入图片描述
    从图中可以看出scrapy的主要由引擎、管道、下载器、调度器、爬虫组成,至于它们的作用简单总结如下

    组件英文作用
    引擎ScrapyEngine负责整体工作流
    调度器Schedule安排拿到的URL按某种先后规则交给下载器
    下载器Downloader拿到URL之后从网络下载并交给爬虫
    爬虫Spider把下载器下载好的资源进行处理,比如说数据提取,如果有新的URL会交给调度器,如果没有,就把提取好的数据交给管道
    管道Pipeline对数据进行持久化存储,比如说保持到数据库

    官方文档:https://docs.scrapy.org/en/latest/index.html

    2.创建项目和爬虫

    使用pip安装好scrapy库之后,可以终端或cmd使用scrapy命令创建项目,之后再创建一个爬虫(spider)

    scrapy startproject 项目名
    cd 项目名
    scrapy genspider 爬虫名 域名
    
    举个栗子:
    scrapy startproject douban_movie
    cd douban_movie
    scrapy genspider douban_top250 movie.douban.com
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    项目结构如下
    在这里插入图片描述

    3.运行爬虫

    如果你把解析页面什么的都写好了可以使用下面的命令启动爬虫

    scrapy crawl 爬虫名 选项
    
    举个栗子
    # 默认管道支持json、 jsonlines、 jl、 csv、 xml、 marshal、 pickle文件格式
    scrapy crawl douban_top250 -o top250.json  # -o是追加模式,-O是覆盖模式
    scrapy crawl douban_top250 -o top250.csv
    scrapy crawl douban_top250 -O top250.csv --nolog  # 运行时不打印日志
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.数据结构(Item)

    我们需要定义数据结构的字段,即你最终清洗完数据得到的结构化数据项,定义一个Item类继承scrapy.Item即可

    items.py

    import scrapy
    
    
    class DoubanMovieItem(scrapy.Item):
        # define the fields for your item here like:
        # name = scrapy.Field()
        title = scrapy.Field()
        rating_num = scrapy.Field()
        quote = scrapy.Field()
        runtime = scrapy.Field()
        summary = scrapy.Field()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    5.解析页面(Spider)

    整个爬虫最主要的工作,就是下载器获取到数据的之后交给爬虫处理,爬虫这边需要提取自己所需要的数据,然后交给Item。如果你需要再访问新的链接,只需要生成一个新的Request交给调度器,Request对象有一个callback回调函数用于解析新的页面,当然也可以通过cb_kwargs参数把当前解析得到的数据传给回调函数,解析函数最后一定是yield一个Item

    douban_top250.py

    import scrapy
    from scrapy import Request, Selector
    from douban_movie.items import DoubanMovieItem
    
    class DoubanTop250Spider(scrapy.Spider):
        name = 'douban_top250'
        allowed_domains = ['movie.douban.com']
        start_urls = ['http://movie.douban.com/']
    
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36",
            "Cookie": '...',
        }
    
        def start_requests(self):
    
            for page in range(1):
                # 如果通过这种方式设置cookie,settings.py一定要设置COOKIES_ENABLED = False
                yield Request(url="https://movie.douban.com/top250?start={}&filter=".format(page * 25),
                              headers=DoubanTop250Spider.headers)
    
        def parse(self, response, **kwargs):
            # print("headers['User-Agent']: ", response.request.headers['User-Agent'])  # 查看当前User-Agent
            selector = Selector(response)
            lis = selector.xpath('//ol[@class="grid_view"]/li')
            # 实例化一个Item用于组织数据
            movie_item = DoubanMovieItem()
            for li in lis:
                detail_url = li.xpath('./div[@class="item"]/div[@class="info"]/div[@class="hd"]/a/@href').extract_first()
                movie_item["title"] = li.xpath(
                    './div/div[@class="info"]/div[@class="hd"]/a/span[@class="title"]/text()').extract_first()
                movie_item["rating_num"] = li.xpath(
                    './div/div[@class="info"]/div[@class="bd"]/div[@class="star"]/span[@class="rating_num"]/text()').extract_first()
                movie_item["quote"] = li.xpath(
                    './div/div[@class="info"]/div[@class="bd"]/p[@class="quote"]/span/text()').extract_first()
                # yield movie_item
                # 如果有新的页面需要爬取可以再yield一个Request并指定回调函数
                yield Request(url=detail_url, callback=self.parse_detail, cb_kwargs={"movie_item": movie_item},
                              headers=DoubanTop250Spider.headers)
    
        def parse_detail(self, response, **kwargs):
            selector = Selector(response)
            movie_item = kwargs.get("movie_item")
            movie_item["runtime"] = selector.xpath('//div[@id="info"]/span[@property="v:runtime"]/@content').extract_first()
            movie_item["summary"] = selector.xpath(
                '//span[@property="v:summary"]/text()').extract_first().strip()
            # 最后要生成一个Item
            yield movie_item
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    6.中间件(middlewares)

    scrapy有很多中间件,我们能看得到的是在middlewares.py中定义的蜘蛛中间件DoubanMovieSpiderMiddleware和下载中间件DoubanMovieDownloaderMiddleware,这两个类里面定义的方法类型。只要你想,你也可以随意定义中间件,例如我们定义一个设置随机UserAgent的中间件

    middlewares.py

    import random
    from fake_useragent import UserAgent
    from scrapy import signals
    from itemadapter import is_item, ItemAdapter
    
    # 自定义UserAgent中间件
    class RandomUserAgentMiddleware:
        def __init__(self):
            self.agent = UserAgent()
    
        @classmethod
        def from_crawler(cls, crawler):
            return cls()
    
        def process_request(self, request, spider):
            request.headers['User-Agent'] = self.agent.random
    
    # IP代理中间件
    class ProxyMiddleware(object):
        def process_request(self, request, spider):
            # 代理IP,可以维护自己的IP代理池然后随机取出IP
            proxies = ["https://47.115.4.9:3128", ...]
            request.meta["proxy"] = random.choice(proxies)
    
        def process_response(self, request, response, spider):
            if response.status != '200':
                # 不过滤,即重新进入调度器队列
                request.dont_filter = True
                return request
    
    
    # 蜘蛛中间件
    class DoubanMovieSpiderMiddleware:
    	...
    
    # 下载中间件
    class DoubanMovieDownloaderMiddleware:
        @classmethod
        def from_crawler(cls, crawler):
            ...
    	# 发起请求前执行
        def process_request(self, request, spider):
            return None
            
    	# 请求之后执行
        def process_response(self, request, response, spider):
            return response
        
    	# 抛异常时执行
        def process_exception(self, request, exception, spider):
            pass
         
    	# 爬虫开启之后执行
        def spider_opened(self, spider):
            spider.logger.info('Spider opened: %s' % spider.name)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    中间件只是一个普通类,只是实现了一些特殊函数(钩子函数),如果想要在scrapy中生效,需要在设置文件指定

    settings.py

    DOWNLOADER_MIDDLEWARES = {
    	# 中间件:权重,  权重值越小优先级越高
        # 'douban_movie.middlewares.DoubanMovieDownloaderMiddleware': 543,
        'douban_movie.middlewares.RandomUserAgentMiddleware': 500,
        'douban_movie.middlewares.ProxyMiddleware': 510,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    7.管道(pipeline)

    如果你要把管道数据保存成son、 jsonlines、 jl、 csv、 xml、 marshal、 pickle文件格式,可用使用启动爬虫时使用-o参数指定即可,但如果你需要保存其他文件格式,比如excel或者保持到数据库,那就需要在pipeline中写代码了

    pipelines.py

    class DoubanMoviePipeline:
        def open_spider(self, spider):
            pass
    
        def close_spider(self, spider):
            pass
    
        def process_item(self, item, spider):
            return item
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    比如说我们要把它保存到mysql数据库

    import pymysql
    from itemadapter import ItemAdapter
    
    class DoubanMoviePipeline:
        def __init__(self):
            self.conn = pymysql.connect(host="localhost", port=3306, user="", password="", database="", charset="utf8")
            self.cursor = self.conn.cursor()
            self.temp_data = list()
    
        # 打开爬虫时执行
        def open_spider(self, spider):
            pass
    
        # 关闭爬虫时执行
        def close_spider(self, spider):
            self._save_data()
            self.cursor.close()
            self.conn.close()
    
        # 每次获取到Item对象时执行
        def process_item(self, item, spider):
            title = item.get("title", "")
            rating_num = item.get("rating_num", "")
            quote = item.get("quote", "")
            runtime = item.get("runtime", "")
            summary = item.get("summary", "")
            self.temp_data.append((title, rating_num, quote, runtime, summary))
            if len(self.temp_data) == 100:
                self._save_data()
            return item
    
        def _save_data(self):
            if self.temp_data:
                self.conn.ping(reconnect=True)
                self.cursor.executemany(
                    'insert into douban_movies(title,rating_num,quote,runtime,summary) values(%s,%s,%s,%s,%s)',
                    self.temp_data)
                self.conn.commit()
                self.temp_data.clear()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    最后要在设置文件中启用pipeline

    settings.py

    ITEM_PIPELINES = {
        'douban_movie.pipelines.DoubanMoviePipeline': 300,
    }
    
    
    • 1
    • 2
    • 3
    • 4

    8.常用设置项

    settings.py

    ROBOTSTXT_OBEY = True  # 是否遵循robot协议
    DOWNLOAD_DELAY = 1  # 启用下载延迟
    AUTOTHROTTLE_ENABLED = True  # 是否自动限流,默认False
    COOKIES_ENABLED = False  # 是否禁用cookie
    # 下载中间件
    DOWNLOADER_MIDDLEWARES = {
        # 'douban_movie.middlewares.DoubanMovieDownloaderMiddleware': 543,
        'douban_movie.middlewares.RandomUserAgentMiddleware': 543,
    }
    # 启用管道
    ITEM_PIPELINES = {
        'douban_movie.pipelines.DoubanMoviePipeline': 300,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    用matlab做bp神经网络预测,matlab人工神经网络预测
    c语言tips-函数指针和指针函数
    javaWeb项目中:连接mysql的驱动问题
    [Gym 102423]-Elven Efficiency | 思维
    vue源码分析(八)—— update分析(首次渲染)
    米哈游(原神)一面算法原题
    单片机论文参考:3、基于单片机的电子万年历设计
    并发编程二、CPU多级缓存架构与MESI协议的诞生
    银行拓客难题如何破解?10套技术驱动的拓客模板,助你批量挖掘政策性客群!
    习题2.18
  • 原文地址:https://blog.csdn.net/qq_39147299/article/details/126153408