Python爬虫5.11 — scrapy框架结合selenium+chromedriver的使用

Python爬虫5.11 — scrapy框架结合selenium+chromedriver的使用

综述

本系列文档用于对Python爬虫技术的学习进行简单的教程讲解,巩固自己技术知识的同时,万一一不小心又正好对你有用那就更好了。
Python 版本是3.7.4

上篇文章我们讲述了下载器中间件的概念,以及如何使用下载器中间件如何使用下载器中间件进行动态随机设置请求头和设置代理IP的方法。这一篇文章我们就讲述一个少高级一点的中间件用法,那就是Scrapy+selenium+chromedriver进行结合使用的方法。

为什么我们要继续这种结合使用呢?这是由于目前的网站很多都是使用动态加载
生成页面,我们在直接访问链接的时候获取不到页面上的信息(去分析其ajax请求接口规则又太过复杂),所以我们使用这一方案进行爬取页面信息。

原理讲解

通过我们之前对Scrapy的学习我们知道Scrapy框架的架构可以分成五大模块(spiderenginedownloaderscheduleritem pipelines)+两个中间件(spider中间件downloader中间件)。以及Scrapy的执行流程(为了方便查看,在这里在展示一遍):

  1. 引擎从Spiders中获取到最初的要爬取的请求(Requests);
  2. 引擎安排请求(Requests)到调度器中,并向调度器请求下一个要爬取的请求(Requests);
  3. 调度器返回下一个要爬取的请求(Requests)给引擎;
  4. 引擎将上步中得到的请求(Requests)通过下载器中间件(Downloader Middlewares)发送给下载器(Downloader ),这个过程中下载器中间件(Downloader Middlewares)中的process_request()函数会被调用到;
  5. 一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(Downloader Middlewares)发送给引擎,这个过程中下载器中间件(Downloader Middlewares)中的process_response()函数会被调用到;
  6. 引擎从下载器中得到上步中的Response并通过Spider中间件(Spider Middlewares)发送给Spider处理,这个过程中Spider中间件(Spider Middlewares)中的process_spider_input()函数会被调用到;
  7. Spider处理Response并通过Spider中间件(Spider Middlewares)返回爬取到的Item及(跟进的)新的Request给引擎,这个过程中Spider中间件(Spider Middlewares)的process_spider_output()函数会被调用到;
  8. 引擎将上步中Spider处理的其爬取到的Item给Item 管道(Pipeline),将Spider处理的Request发送给调度器,并向调度器请求可能存在的下一个要爬取的请求(Requests);
  9. (从第二步)重复直到调度器中没有更多的请求(Requests)。

并且通过前面一章我们学习了中间件的用法。那么使用Scrapy+selenium+chromedriver进行结合开发的原理就是将selenium+chromedriver写到中间件中,在执行到第四步的时候直接在process_request()返回response对象给引擎,就不会再去下载器中进行请求访问(也就是我们没有使用scrapy框架的downloader模块,直接在下载器中间件中完成了请求,并且返回了请求结果resopnse给引擎)。

开发实例

下面我们就已爬取简书网数据为例进行实例开发。

  1. 使用scrapy startproject jianshu_spider命令创建项目;
  2. 使用scrapy genspider -t crawl jianshu jianshu.com 创建爬虫;
  3. 开发spider目录下爬虫文件jianshu.py代码如下:
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    
    from jianshu_spider.items import JianshuSpiderItem
    
    
    class JianshuSpider(CrawlSpider):
        name = 'jianshu'
        allowed_domains = ['jianshu.com']
        start_urls = ['https://www.jianshu.com/']
    
        # 分析简书文章链接发现其链接地址结构为:域名 + /p/ + 12为数字字母组合字符串
        rules = (
            Rule(LinkExtractor(allow=r'.*/p/[0-9a-z]{12}.*'), callback='parse_detail', follow=True),
        )
    
        def parse_detail(self, response):
            """
            进行爬取结果分析
            :param response:
            :return:
            """
            title = response.xpath('//h1[@class="title"]/text()').get()
            avatar = response.xpath('//a[@class="avatar"]/img/@src').get()
            author = response.xpath('//span[@class="name"]/text()').get()
            pub_time = response.xpath('//span[@class="publish-time"]/text()').get()
            url = response.url
            url1 = url.split('?')[0]
            article_id = url1.split('/')[-1]
            content = response.xpath('//div[@class="show-content"]').get()
    
            word_count = response.xpath('//span[@class="wordage"]/text()').get()
            comment_count = response.xpath('//span[@class="comments-count"]/text()').get()
            like_count = response.xpath('//span[@class="likes-count"]/text()').get()
            read_count = response.xpath('//span[@class="views-count"]/text()').get()
            subjects = ','.join(response.xpath('//div[@class="include-collection"]/a/div/text()').getall())
    
            item = JianshuSpiderItem(
                title=title,
                avatar=avatar,
                author=author,
                pub_time=pub_time,
                origin_url=url1,
                article_id=article_id,
                content=content,
                word_count=word_count,
                comment_count=comment_count,
                like_count=like_count,
                read_count=read_count,
                subjects=subjects
            )
            yield item
    
  4. 开发items.py代码如下:
    import scrapy
    
    
    class JianshuSpiderItem(scrapy.Item):
        """
        定义所需字段
        """
        title = scrapy.Field()
        content = scrapy.Field()
        article_id = scrapy.Field()
        origin_url = scrapy.Field()
        author = scrapy.Field()
        avatar = scrapy.Field()
        pub_time = scrapy.Field()
        read_count = scrapy.Field()
        like_count = scrapy.Field()
        word_count = scrapy.Field()
        subjects = scrapy.Field()
        comment_count = scrapy.Field()
    
  5. 开发middlewares.py中间件代码如下:
    import time
    from scrapy.http.response.html import HtmlResponse
    from selenium import webdriver
    
    
    class SeleniumDownloadMiddleware(object):
        """
        selenium 下载中间件
        """
    
        def __init__(self):
            self.driver = webdriver.Chrome(executable_path=r'E:\Python_Code\s1\chromedriver_win32\chromedriver.exe')
    
        def process_request(self, request, spider):
            self.driver.get(request.url)
            time.sleep(1)
            try:
                while True:
                    show_more = self.driver.find_element_by_class_name('show-more')
                    show_more.click()
                    time.sleep(3)
                    if not show_more:
                        break
            except:
                pass
            # 获得网页源代码
            source = self.driver.page_source
            # 构造response对象 | 进行返回
            response = HtmlResponse(url=self.driver.current_url, body=source, request=request, encoding='utf-8')
            return response
    
  6. 开发piplines.py代码如下:
    import pymysql
    from pymysql import cursors
    from twisted.enterprise import adbapi
    
    
    # 进行数据处理
    class JianshuSpiderPipeline(object):
        """
        这种方法只能同步进行保存到数据库
        """
    
        def __init__(self):
            db_parames = {
                'host': '127.0.0.1',
                'port': 3306,
                'user': 'root',
                'password': 'root',
                'database': 'jianshu_spider',
                'charset': 'utf8'
            }
            self.conn = pymysql.connect(**db_parames)
            self.cursor = self.conn.cursor()
            self._sql = None
    
        def process_item(self, item, spider):
            self.cursor.execute(self.sql, (
                item['title'], item['content'], item['author'], item['avatar'], item['pub_time'], item['article_id'],
                item['origin_url'],))
            # self.cursor.commit()
            print(item)
            return item
    
        @property
        def sql(self):
            if not self._sql:
                self._sql = """
                INSERT INTO article(id,title,content,author,avatar,pub_time,article_id,origin_url)
                VALUE(null,%s,%s,%s,%s,%s,%s,%s)
                """
                return self._sql
            return self._sql
    
    
    class JianshuTwistedPipeline(object):
        """
        使用Twisted进行异步保存到数据库
        """
    
        def __init__(self):
            db_parames = {
                'host': '127.0.0.1',
                'port': 3306,
                'user': 'root',
                'password': 'root',
                'database': 'jianshu_spider',
                'charset': 'utf8',
                'cursorclass': cursors.DictCursor
            }
    
            # 定义数据库连接池
            self.dbpool = adbapi.ConnectionPool('pymysql', **db_parames)
            self._sql = None
    
        @property
        def sql(self):
            if not self._sql:
                self._sql = """
                    INSERT INTO article(id,title,content,author,avatar,pub_time,article_id,origin_url,word_count)
                    VALUE(null,%s,%s,%s,%s,%s,%s,%s,%s)
                    """
                return self._sql
            return self._sql
    
        def process_item(self, item, spider):
            defer = self.dbpool.runInteraction(self.insert_item, item)
            defer.addErrback(self.handle_error, item, spider)
    
            print(item)
            return item
    
        def insert_item(self, cursor, item):
            cursor.execute(self.sql, (
                item['title'], item['content'], item['author'], item['avatar'], item['pub_time'], item['article_id'],
                item['origin_url'], item['word_count']))
    
        def handle_error(self, error, item, spider):
            print('=' * 15 + 'error' + '=' * 15)
            print(error)
            print('=' * 15 + 'error' + '=' * 15)
    
  7. 进行设置setting.py文件相关配置更改如下:
    # 开启item pipelines
    ITEM_PIPELINES = {
        'jianshu_spider.pipelines.JianshuTwistedPipeline': 300,
        # 'jianshu_spider.pipelines.JianshuSpiderPipeline': 300,
    }
    
    # 开启中间件
    DOWNLOADER_MIDDLEWARES = {
       'jianshu_spider.middlewares.SeleniumDownloadMiddleware': 543,
    }
    

将数据库相关表设计好,运行代码即可。

其他博文链接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章